@resvary/postgres 0.5.0 → 0.6.1

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/LICENSE CHANGED
@@ -1,13 +1,13 @@
1
- Copyright 2025-2026 horn111
2
-
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
6
-
7
- https://www.apache.org/licenses/LICENSE-2.0
8
-
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
1
+ Copyright 2025-2026 horn111
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ https://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md CHANGED
@@ -1,32 +1,32 @@
1
- # @resvary/postgres
2
-
3
- Postgres `CreditStore` and `ReceiptStore` implementations for Resvary multi-process deployments. Resvary supports PostgreSQL 16–18. Stores never migrate automatically.
4
-
5
- ```bash
6
- npm install @resvary/sdk @resvary/postgres
7
- DATABASE_URL=postgres://... npx resvary-postgres migrate
8
- ```
9
-
10
- ```ts
11
- import { createPostgresCreditStore } from '@resvary/postgres';
12
-
13
- const store = createPostgresCreditStore({ connectionString: process.env.DATABASE_URL! });
14
- // Use store with CreditLedger, then close the owned pool during shutdown.
15
- await store.close();
16
- ```
17
-
18
- Pass either `connectionString` or an existing `pg.Pool`. A store created from a connection string owns and closes its pool. A store given a pool never closes it.
19
-
20
- Credit transactions and bundled receipt ledger operations run at `SERIALIZABLE` isolation with bounded retry. Schema v2 adds database checks, foreign keys, and transaction-hash uniqueness. Apply migrations before starting application or worker processes.
21
-
22
- CLI commands:
23
-
24
- ```text
25
- resvary-postgres status
26
- resvary-postgres migrate
27
- resvary-postgres import-sqlite --sqlite .resvary/resvary.sqlite --dry-run
28
- resvary-postgres import-sqlite --sqlite .resvary/resvary.sqlite
29
- resvary-postgres verify-import --sqlite .resvary/resvary.sqlite
30
- ```
31
-
32
- SQLite import requires Node.js 24. Stop every writer before importing. Verification compares entity payloads, balances, ledger totals, and open reservations.
1
+ # @resvary/postgres
2
+
3
+ Postgres `CreditStore` and `ReceiptStore` implementations for Resvary multi-process deployments. Resvary supports PostgreSQL 16–18. Stores never migrate automatically.
4
+
5
+ ```bash
6
+ npm install @resvary/sdk @resvary/postgres
7
+ DATABASE_URL=postgres://... npx resvary-postgres migrate
8
+ ```
9
+
10
+ ```ts
11
+ import { createPostgresCreditStore } from '@resvary/postgres';
12
+
13
+ const store = createPostgresCreditStore({ connectionString: process.env.DATABASE_URL! });
14
+ // Use store with CreditLedger, then close the owned pool during shutdown.
15
+ await store.close();
16
+ ```
17
+
18
+ Pass either `connectionString` or an existing `pg.Pool`. A store created from a connection string owns and closes its pool. A store given a pool never closes it.
19
+
20
+ Credit transactions and bundled receipt ledger operations run at `SERIALIZABLE` isolation with bounded retry. Schema v2 adds database checks, foreign keys, and transaction-hash uniqueness. Apply migrations before starting application or worker processes.
21
+
22
+ CLI commands:
23
+
24
+ ```text
25
+ resvary-postgres status
26
+ resvary-postgres migrate
27
+ resvary-postgres import-sqlite --sqlite .resvary/resvary.sqlite --dry-run
28
+ resvary-postgres import-sqlite --sqlite .resvary/resvary.sqlite
29
+ resvary-postgres verify-import --sqlite .resvary/resvary.sqlite
30
+ ```
31
+
32
+ SQLite import requires Node.js 24. Stop every writer before importing. Verification compares entity payloads, balances, ledger totals, and open reservations.
@@ -0,0 +1,3 @@
1
+ export type PostgresCliOptions = Record<string, string | boolean>;
2
+ export declare function parsePostgresCliOptions(args: string[]): PostgresCliOptions;
3
+ //# sourceMappingURL=cli-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-options.d.ts","sourceRoot":"","sources":["../src/cli-options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;AAElE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,kBAAkB,CA6B1E"}
@@ -0,0 +1,35 @@
1
+ export function parsePostgresCliOptions(args) {
2
+ const valueOptions = new Set(['schema', 'sqlite']);
3
+ const booleanOptions = new Set(['dry-run']);
4
+ const options = {};
5
+ for (let index = 0; index < args.length; index += 1) {
6
+ const arg = args[index];
7
+ if (!arg?.startsWith('--'))
8
+ throw new Error(`Unexpected argument: ${arg}`);
9
+ const separator = arg.indexOf('=');
10
+ const key = arg.slice(2, separator === -1 ? undefined : separator);
11
+ if (!valueOptions.has(key) && !booleanOptions.has(key)) {
12
+ throw new Error(`Unknown option: --${key}`);
13
+ }
14
+ const inlineValue = separator === -1 ? undefined : arg.slice(separator + 1);
15
+ const next = args[index + 1];
16
+ if (booleanOptions.has(key)) {
17
+ const candidate = inlineValue ?? (next === 'true' || next === 'false' ? next : undefined);
18
+ if (candidate !== undefined && candidate !== 'true' && candidate !== 'false') {
19
+ throw new Error(`--${key} must be true or false`);
20
+ }
21
+ options[key] = candidate === undefined ? true : candidate === 'true';
22
+ if (inlineValue === undefined && candidate !== undefined)
23
+ index += 1;
24
+ continue;
25
+ }
26
+ const value = inlineValue ?? next;
27
+ if (!value || value.startsWith('--'))
28
+ throw new Error(`--${key} requires a value`);
29
+ options[key] = value;
30
+ if (inlineValue === undefined)
31
+ index += 1;
32
+ }
33
+ return options;
34
+ }
35
+ //# sourceMappingURL=cli-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-options.js","sourceRoot":"","sources":["../src/cli-options.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAAC,IAAc;IACpD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IACnD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;QAC3E,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,WAAW,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC7B,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,WAAW,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC1F,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;gBAC7E,MAAM,IAAI,KAAK,CAAC,KAAK,GAAG,wBAAwB,CAAC,CAAC;YACpD,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC;YACrE,IAAI,WAAW,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS;gBAAE,KAAK,IAAI,CAAC,CAAC;YACrE,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,WAAW,IAAI,IAAI,CAAC;QAClC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,KAAK,GAAG,mBAAmB,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACrB,IAAI,WAAW,KAAK,SAAS;YAAE,KAAK,IAAI,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/dist/cli.js CHANGED
@@ -1,11 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { getPostgresMigrationStatus, migratePostgres } from './index.js';
3
+ import { parsePostgresCliOptions } from './cli-options.js';
3
4
  async function main() {
4
5
  const [command, ...rest] = process.argv.slice(2);
5
- const options = parseOptions(rest);
6
- const connectionString = optionString(options, 'database-url') ?? process.env.DATABASE_URL;
6
+ if (command === '--help' || command === '-h' || command === 'help') {
7
+ process.stdout.write(`${usage()}\n`);
8
+ return;
9
+ }
10
+ const options = parsePostgresCliOptions(rest);
11
+ const connectionString = process.env.DATABASE_URL;
7
12
  if (!connectionString)
8
- throw new Error('DATABASE_URL or --database-url is required');
13
+ throw new Error('DATABASE_URL is required');
9
14
  const schema = optionString(options, 'schema') ?? process.env.RESVARY_POSTGRES_SCHEMA ?? 'public';
10
15
  const base = { connectionString, schema };
11
16
  switch (command) {
@@ -35,42 +40,11 @@ async function main() {
35
40
  return;
36
41
  }
37
42
  default:
38
- throw new Error('Usage: resvary-postgres <status|migrate|import-sqlite|verify-import> [--database-url URL] [--schema NAME] [--sqlite PATH] [--dry-run]');
43
+ throw new Error(usage());
39
44
  }
40
45
  }
41
- function parseOptions(args) {
42
- const valueOptions = new Set(['database-url', 'schema', 'sqlite']);
43
- const booleanOptions = new Set(['dry-run']);
44
- const options = {};
45
- for (let index = 0; index < args.length; index += 1) {
46
- const arg = args[index];
47
- if (!arg?.startsWith('--'))
48
- throw new Error(`Unexpected argument: ${arg}`);
49
- const separator = arg.indexOf('=');
50
- const key = arg.slice(2, separator === -1 ? undefined : separator);
51
- if (!valueOptions.has(key) && !booleanOptions.has(key)) {
52
- throw new Error(`Unknown option: --${key}`);
53
- }
54
- const inlineValue = separator === -1 ? undefined : arg.slice(separator + 1);
55
- const next = args[index + 1];
56
- if (booleanOptions.has(key)) {
57
- const candidate = inlineValue ?? (next === 'true' || next === 'false' ? next : undefined);
58
- if (candidate !== undefined && candidate !== 'true' && candidate !== 'false') {
59
- throw new Error(`--${key} must be true or false`);
60
- }
61
- options[key] = candidate === undefined ? true : candidate === 'true';
62
- if (inlineValue === undefined && candidate !== undefined)
63
- index += 1;
64
- continue;
65
- }
66
- const value = inlineValue ?? next;
67
- if (!value || value.startsWith('--'))
68
- throw new Error(`--${key} requires a value`);
69
- options[key] = value;
70
- if (inlineValue === undefined)
71
- index += 1;
72
- }
73
- return options;
46
+ function usage() {
47
+ return 'Usage: resvary-postgres <status|migrate|import-sqlite|verify-import> [--schema NAME] [--sqlite PATH] [--dry-run]';
74
48
  }
75
49
  function optionString(options, key) {
76
50
  const value = options[key];
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/credit.js CHANGED
@@ -100,39 +100,39 @@ export class PostgresCreditStore {
100
100
  }
101
101
  async claimOutboxEvents(input) {
102
102
  const outbox = table(this.handle, 'resvary_outbox_events');
103
- const result = await this.handle.pool.query(`WITH candidates AS (
104
- SELECT id FROM ${outbox}
105
- WHERE ($5::text IS NULL OR project_id = $5)
106
- AND ((status = 'pending' AND next_attempt_at <= $1)
107
- OR (status = 'processing' AND lease_expires_at <= $1))
108
- ORDER BY next_attempt_at ASC, created_at ASC
109
- FOR UPDATE SKIP LOCKED
110
- LIMIT $2
111
- )
112
- UPDATE ${outbox} AS event
113
- SET status = 'processing', attempt_count = event.attempt_count + 1,
114
- lease_owner = $3, lease_expires_at = $1 + $4, last_attempt_at = $1
115
- FROM candidates
116
- WHERE event.id = candidates.id
117
- RETURNING event.payload::text AS payload, event.status, event.attempt_count,
118
- event.next_attempt_at::text, event.lease_owner, event.lease_expires_at::text,
103
+ const result = await this.handle.pool.query(`WITH candidates AS (
104
+ SELECT id FROM ${outbox}
105
+ WHERE ($5::text IS NULL OR project_id = $5)
106
+ AND ((status = 'pending' AND next_attempt_at <= $1)
107
+ OR (status = 'processing' AND lease_expires_at <= $1))
108
+ ORDER BY next_attempt_at ASC, created_at ASC
109
+ FOR UPDATE SKIP LOCKED
110
+ LIMIT $2
111
+ )
112
+ UPDATE ${outbox} AS event
113
+ SET status = 'processing', attempt_count = event.attempt_count + 1,
114
+ lease_owner = $3, lease_expires_at = $1 + $4, last_attempt_at = $1
115
+ FROM candidates
116
+ WHERE event.id = candidates.id
117
+ RETURNING event.payload::text AS payload, event.status, event.attempt_count,
118
+ event.next_attempt_at::text, event.lease_owner, event.lease_expires_at::text,
119
119
  event.last_attempt_at::text, event.last_error, event.delivered_at::text`, [input.now, input.limit, input.workerId, input.leaseMs, input.projectId ?? null]);
120
120
  return result.rows.map(parseOutboxRow);
121
121
  }
122
122
  async completeOutboxEvent(eventId, workerId, deliveredAt, attemptCount) {
123
- const result = await this.handle.pool.query(`UPDATE ${table(this.handle, 'resvary_outbox_events')}
124
- SET status = 'delivered', delivered_at = $3, next_attempt_at = $3,
125
- lease_owner = NULL, lease_expires_at = NULL, last_error = NULL
126
- WHERE id = $1 AND status = 'processing' AND lease_owner = $2
123
+ const result = await this.handle.pool.query(`UPDATE ${table(this.handle, 'resvary_outbox_events')}
124
+ SET status = 'delivered', delivered_at = $3, next_attempt_at = $3,
125
+ lease_owner = NULL, lease_expires_at = NULL, last_error = NULL
126
+ WHERE id = $1 AND status = 'processing' AND lease_owner = $2
127
127
  AND ($4::integer IS NULL OR attempt_count = $4)`, [eventId, workerId, deliveredAt, attemptCount ?? null]);
128
128
  if (result.rowCount !== 1)
129
129
  throw new Error(`Outbox lease lost for event ${eventId}`);
130
130
  }
131
131
  async failOutboxEvent(input) {
132
- const result = await this.handle.pool.query(`UPDATE ${table(this.handle, 'resvary_outbox_events')}
133
- SET status = $4, next_attempt_at = $5, lease_owner = NULL,
134
- lease_expires_at = NULL, last_error = $3
135
- WHERE id = $1 AND status = 'processing' AND lease_owner = $2
132
+ const result = await this.handle.pool.query(`UPDATE ${table(this.handle, 'resvary_outbox_events')}
133
+ SET status = $4, next_attempt_at = $5, lease_owner = NULL,
134
+ lease_expires_at = NULL, last_error = $3
135
+ WHERE id = $1 AND status = 'processing' AND lease_owner = $2
136
136
  AND ($6::integer IS NULL OR attempt_count = $6)`, [
137
137
  input.eventId,
138
138
  input.workerId,
@@ -148,9 +148,9 @@ export class PostgresCreditStore {
148
148
  return this.listOutboxEvents({ projectId, status: 'dead_letter' });
149
149
  }
150
150
  async requeueOutboxEvent(eventId, now) {
151
- const result = await this.handle.pool.query(`UPDATE ${table(this.handle, 'resvary_outbox_events')}
152
- SET status = 'pending', attempt_count = 0, next_attempt_at = $2,
153
- lease_owner = NULL, lease_expires_at = NULL, last_error = NULL, delivered_at = NULL
151
+ const result = await this.handle.pool.query(`UPDATE ${table(this.handle, 'resvary_outbox_events')}
152
+ SET status = 'pending', attempt_count = 0, next_attempt_at = $2,
153
+ lease_owner = NULL, lease_expires_at = NULL, last_error = NULL, delivered_at = NULL
154
154
  WHERE id = $1 AND status = 'dead_letter'`, [eventId, now]);
155
155
  if (result.rowCount !== 1)
156
156
  throw new Error(`Dead-letter event not found: ${eventId}`);
@@ -416,8 +416,8 @@ function reader(db, handle) {
416
416
  };
417
417
  }
418
418
  function outboxSelect(tableName, suffix) {
419
- return `SELECT payload::text AS payload, status, attempt_count,
420
- next_attempt_at::text, lease_owner, lease_expires_at::text,
419
+ return `SELECT payload::text AS payload, status, attempt_count,
420
+ next_attempt_at::text, lease_owner, lease_expires_at::text,
421
421
  last_attempt_at::text, last_error, delivered_at::text FROM ${tableName} ${suffix}`;
422
422
  }
423
423
  function parseOutboxRow(row) {
@@ -448,7 +448,7 @@ async function filteredAll(db, sql, values, filter) {
448
448
  async function insert(db, handle, name, columns, values, payload, conflictColumns = ['id']) {
449
449
  const allColumns = [...columns, 'payload'];
450
450
  const params = allColumns.map((_, index) => `$${index + 1}`).join(', ');
451
- await db.query(`INSERT INTO ${table(handle, name)} (${allColumns.join(', ')}) VALUES (${params})
451
+ await db.query(`INSERT INTO ${table(handle, name)} (${allColumns.join(', ')}) VALUES (${params})
452
452
  ON CONFLICT (${conflictColumns.join(', ')}) DO NOTHING`, [...values, serializeReceiptStoreValue(payload)]);
453
453
  }
454
454
  async function upsert(db, handle, name, columns, values, payload) {
@@ -458,7 +458,7 @@ async function upsert(db, handle, name, columns, values, payload) {
458
458
  .filter((column) => column !== 'id')
459
459
  .map((column) => `${column} = EXCLUDED.${column}`)
460
460
  .join(', ');
461
- await db.query(`INSERT INTO ${table(handle, name)} (${allColumns.join(', ')}) VALUES (${params})
461
+ await db.query(`INSERT INTO ${table(handle, name)} (${allColumns.join(', ')}) VALUES (${params})
462
462
  ON CONFLICT (id) DO UPDATE SET ${updates}`, [...values, serializeReceiptStoreValue(payload)]);
463
463
  }
464
464
  function matchesBalanceFilter(value, filter) {
package/dist/health.js CHANGED
@@ -6,7 +6,7 @@ export async function checkPostgresHealth(config) {
6
6
  try {
7
7
  await handle.pool.query('SELECT 1');
8
8
  const version = await handle.pool.query(`SELECT version FROM ${table(handle, 'resvary_schema_migrations')} ORDER BY version ASC`);
9
- const outbox = await handle.pool.query(`SELECT status, COUNT(*)::text AS count FROM ${table(handle, 'resvary_outbox_events')}
9
+ const outbox = await handle.pool.query(`SELECT status, COUNT(*)::text AS count FROM ${table(handle, 'resvary_outbox_events')}
10
10
  WHERE status IN ('pending', 'dead_letter') GROUP BY status`);
11
11
  const counts = new Map(outbox.rows.map((row) => [row.status, Number(row.count)]));
12
12
  const appliedVersions = version.rows.map((row) => row.version);
@@ -295,8 +295,8 @@ async function buildReport(source, client, schema, sqliteSchemaVersion, postgres
295
295
  sourceLedger.set(key, (sourceLedger.get(key) ?? 0n) + BigInt(String(entry.deltaUnits)));
296
296
  }
297
297
  }
298
- const targetLedgerResult = await client.query(`SELECT account_id, payload->>'bucket' AS bucket, SUM(delta_units)::text AS total
299
- FROM ${table({ schema }, 'resvary_ledger_entries')}
298
+ const targetLedgerResult = await client.query(`SELECT account_id, payload->>'bucket' AS bucket, SUM(delta_units)::text AS total
299
+ FROM ${table({ schema }, 'resvary_ledger_entries')}
300
300
  GROUP BY account_id, payload->>'bucket'`);
301
301
  const targetLedger = new Map(targetLedgerResult.rows.map((row) => [`${row.account_id}:${row.bucket}`, BigInt(row.total)]));
302
302
  for (const key of new Set([...sourceLedger.keys(), ...targetLedger.keys()])) {
@@ -4,9 +4,9 @@ const MIGRATION_LOCK_ID = 7_226_519_918;
4
4
  export async function getPostgresMigrationStatus(config) {
5
5
  const handle = createPostgresHandle(config);
6
6
  try {
7
- const exists = await handle.pool.query(`SELECT EXISTS (
8
- SELECT 1 FROM information_schema.tables
9
- WHERE table_schema = $1 AND table_name = 'resvary_schema_migrations'
7
+ const exists = await handle.pool.query(`SELECT EXISTS (
8
+ SELECT 1 FROM information_schema.tables
9
+ WHERE table_schema = $1 AND table_name = 'resvary_schema_migrations'
10
10
  ) AS exists`, [handle.schema]);
11
11
  let currentVersion = 0;
12
12
  if (exists.rows[0]?.exists) {
@@ -31,11 +31,11 @@ export async function migratePostgres(config) {
31
31
  try {
32
32
  await client.query('SELECT pg_advisory_lock($1)', [MIGRATION_LOCK_ID]);
33
33
  await client.query(`CREATE SCHEMA IF NOT EXISTS "${handle.schema}"`);
34
- await client.query(`
35
- CREATE TABLE IF NOT EXISTS ${table(handle, 'resvary_schema_migrations')} (
36
- version INTEGER PRIMARY KEY,
37
- applied_at BIGINT NOT NULL
38
- )
34
+ await client.query(`
35
+ CREATE TABLE IF NOT EXISTS ${table(handle, 'resvary_schema_migrations')} (
36
+ version INTEGER PRIMARY KEY,
37
+ applied_at BIGINT NOT NULL
38
+ )
39
39
  `);
40
40
  const current = await client.query(`SELECT version FROM ${table(handle, 'resvary_schema_migrations')} ORDER BY version ASC`);
41
41
  const currentVersion = assertMigrationHistory(current.rows.map((row) => row.version));
@@ -70,101 +70,101 @@ export async function applyV1(client, schema) {
70
70
  const t = (name) => table({ schema }, name);
71
71
  await client.query('BEGIN');
72
72
  try {
73
- await client.query(`
74
- CREATE TABLE ${t('resvary_credit_accounts')} (
75
- id TEXT PRIMARY KEY, project_id TEXT NOT NULL, customer_id TEXT NOT NULL,
76
- currency TEXT NOT NULL, posted_units NUMERIC(78,0) NOT NULL,
77
- reserved_units NUMERIC(78,0) NOT NULL, updated_at BIGINT NOT NULL, payload JSONB NOT NULL,
78
- UNIQUE(project_id, customer_id)
79
- );
80
- CREATE TABLE ${t('resvary_credit_grants')} (
81
- id TEXT PRIMARY KEY, account_id TEXT NOT NULL, amount_units NUMERIC(78,0) NOT NULL,
82
- created_at BIGINT NOT NULL, payload JSONB NOT NULL
83
- );
84
- CREATE INDEX resvary_credit_grants_account ON ${t('resvary_credit_grants')}(account_id);
85
- CREATE TABLE ${t('resvary_meters')} (
86
- id TEXT PRIMARY KEY, project_id TEXT NOT NULL, meter_key TEXT NOT NULL, payload JSONB NOT NULL,
87
- UNIQUE(project_id, meter_key)
88
- );
89
- CREATE TABLE ${t('resvary_price_versions')} (
90
- id TEXT PRIMARY KEY, meter_id TEXT NOT NULL, version INTEGER NOT NULL,
91
- created_at BIGINT NOT NULL, payload JSONB NOT NULL, UNIQUE(meter_id, version)
92
- );
93
- CREATE TABLE ${t('resvary_credit_reservations')} (
94
- id TEXT PRIMARY KEY, account_id TEXT NOT NULL, project_id TEXT NOT NULL,
95
- customer_id TEXT NOT NULL, status TEXT NOT NULL,
96
- reserved_units NUMERIC(78,0) NOT NULL, expires_at BIGINT NOT NULL,
97
- created_at BIGINT NOT NULL, payload JSONB NOT NULL
98
- );
99
- CREATE INDEX resvary_credit_reservations_open
100
- ON ${t('resvary_credit_reservations')}(project_id, customer_id, status, expires_at);
101
- CREATE TABLE ${t('resvary_usage_events')} (
102
- id TEXT PRIMARY KEY, account_id TEXT NOT NULL, received_at BIGINT NOT NULL, payload JSONB NOT NULL
103
- );
104
- CREATE TABLE ${t('resvary_usage_receipts')} (
105
- id TEXT PRIMARY KEY, account_id TEXT NOT NULL, reservation_id TEXT NOT NULL UNIQUE,
106
- usage_event_id TEXT NOT NULL UNIQUE, charged_units NUMERIC(78,0) NOT NULL,
107
- created_at BIGINT NOT NULL, payload JSONB NOT NULL
108
- );
109
- CREATE INDEX resvary_usage_receipts_account
110
- ON ${t('resvary_usage_receipts')}(account_id, created_at);
111
- CREATE TABLE ${t('resvary_ledger_entries')} (
112
- id TEXT PRIMARY KEY, account_id TEXT NOT NULL, delta_units NUMERIC(78,0) NOT NULL,
113
- balance_after_units NUMERIC(78,0) NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL
114
- );
115
- CREATE INDEX resvary_ledger_entries_account
116
- ON ${t('resvary_ledger_entries')}(account_id, created_at);
117
- CREATE TABLE ${t('resvary_funding_intents')} (
118
- id TEXT PRIMARY KEY, project_id TEXT NOT NULL, customer_id TEXT NOT NULL,
119
- status TEXT NOT NULL, requested_units NUMERIC(78,0) NOT NULL,
120
- created_at BIGINT NOT NULL, payload JSONB NOT NULL
121
- );
122
- CREATE TABLE ${t('resvary_funding_transactions')} (
123
- id TEXT PRIMARY KEY, funding_intent_id TEXT NOT NULL, rail TEXT NOT NULL,
124
- network TEXT NOT NULL, external_payment_id_norm TEXT NOT NULL, tx_hash_norm TEXT,
125
- amount_units NUMERIC(78,0) NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL,
126
- UNIQUE(rail, network, external_payment_id_norm)
127
- );
128
- CREATE UNIQUE INDEX resvary_funding_transactions_tx_hash
129
- ON ${t('resvary_funding_transactions')}(network, tx_hash_norm)
130
- WHERE tx_hash_norm IS NOT NULL;
131
- CREATE TABLE ${t('resvary_idempotency_keys')} (
132
- scope TEXT NOT NULL, key TEXT NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL,
133
- PRIMARY KEY(scope, key)
134
- );
135
- CREATE TABLE ${t('resvary_outbox_events')} (
136
- id TEXT PRIMARY KEY, project_id TEXT NOT NULL, type TEXT NOT NULL, status TEXT NOT NULL,
137
- created_at BIGINT NOT NULL, attempt_count INTEGER NOT NULL DEFAULT 0,
138
- next_attempt_at BIGINT NOT NULL, lease_owner TEXT, lease_expires_at BIGINT,
139
- last_attempt_at BIGINT, last_error TEXT, delivered_at BIGINT, payload JSONB NOT NULL
140
- );
141
- CREATE INDEX resvary_outbox_events_due
142
- ON ${t('resvary_outbox_events')}(project_id, status, next_attempt_at, created_at);
143
-
144
- CREATE TABLE ${t('resvary_invoices')} (
145
- id TEXT PRIMARY KEY, status TEXT NOT NULL, customer_id TEXT, amount_units NUMERIC(78,0) NOT NULL,
146
- created_at BIGINT NOT NULL, payload JSONB NOT NULL
147
- );
148
- CREATE INDEX resvary_invoices_status ON ${t('resvary_invoices')}(status);
149
- CREATE INDEX resvary_invoices_customer_id ON ${t('resvary_invoices')}(customer_id);
150
- CREATE TABLE ${t('resvary_receipts')} (
151
- id TEXT PRIMARY KEY, invoice_id TEXT NOT NULL, tx_hash_norm TEXT, status TEXT NOT NULL,
152
- amount_units NUMERIC(78,0) NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL
153
- );
154
- CREATE UNIQUE INDEX resvary_receipts_invoice_tx_hash
155
- ON ${t('resvary_receipts')}(invoice_id, tx_hash_norm) WHERE tx_hash_norm IS NOT NULL;
156
- CREATE TABLE ${t('arc_webhook_events')} (
157
- id TEXT PRIMARY KEY, type TEXT NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL
158
- );
159
- CREATE TABLE ${t('arc_webhook_deliveries')} (
160
- id TEXT PRIMARY KEY, event_id TEXT NOT NULL, event_type TEXT NOT NULL, attempt INTEGER NOT NULL,
161
- status TEXT NOT NULL, received_at BIGINT NOT NULL, payload JSONB NOT NULL
162
- );
163
- CREATE INDEX arc_webhook_deliveries_event_id ON ${t('arc_webhook_deliveries')}(event_id);
164
- CREATE TABLE ${t('arc_watcher_cursors')} (
165
- key TEXT PRIMARY KEY, network TEXT NOT NULL, invoice_id TEXT, memo_id_norm TEXT,
166
- next_from_block NUMERIC(78,0) NOT NULL, updated_at BIGINT NOT NULL, payload JSONB NOT NULL
167
- );
73
+ await client.query(`
74
+ CREATE TABLE ${t('resvary_credit_accounts')} (
75
+ id TEXT PRIMARY KEY, project_id TEXT NOT NULL, customer_id TEXT NOT NULL,
76
+ currency TEXT NOT NULL, posted_units NUMERIC(78,0) NOT NULL,
77
+ reserved_units NUMERIC(78,0) NOT NULL, updated_at BIGINT NOT NULL, payload JSONB NOT NULL,
78
+ UNIQUE(project_id, customer_id)
79
+ );
80
+ CREATE TABLE ${t('resvary_credit_grants')} (
81
+ id TEXT PRIMARY KEY, account_id TEXT NOT NULL, amount_units NUMERIC(78,0) NOT NULL,
82
+ created_at BIGINT NOT NULL, payload JSONB NOT NULL
83
+ );
84
+ CREATE INDEX resvary_credit_grants_account ON ${t('resvary_credit_grants')}(account_id);
85
+ CREATE TABLE ${t('resvary_meters')} (
86
+ id TEXT PRIMARY KEY, project_id TEXT NOT NULL, meter_key TEXT NOT NULL, payload JSONB NOT NULL,
87
+ UNIQUE(project_id, meter_key)
88
+ );
89
+ CREATE TABLE ${t('resvary_price_versions')} (
90
+ id TEXT PRIMARY KEY, meter_id TEXT NOT NULL, version INTEGER NOT NULL,
91
+ created_at BIGINT NOT NULL, payload JSONB NOT NULL, UNIQUE(meter_id, version)
92
+ );
93
+ CREATE TABLE ${t('resvary_credit_reservations')} (
94
+ id TEXT PRIMARY KEY, account_id TEXT NOT NULL, project_id TEXT NOT NULL,
95
+ customer_id TEXT NOT NULL, status TEXT NOT NULL,
96
+ reserved_units NUMERIC(78,0) NOT NULL, expires_at BIGINT NOT NULL,
97
+ created_at BIGINT NOT NULL, payload JSONB NOT NULL
98
+ );
99
+ CREATE INDEX resvary_credit_reservations_open
100
+ ON ${t('resvary_credit_reservations')}(project_id, customer_id, status, expires_at);
101
+ CREATE TABLE ${t('resvary_usage_events')} (
102
+ id TEXT PRIMARY KEY, account_id TEXT NOT NULL, received_at BIGINT NOT NULL, payload JSONB NOT NULL
103
+ );
104
+ CREATE TABLE ${t('resvary_usage_receipts')} (
105
+ id TEXT PRIMARY KEY, account_id TEXT NOT NULL, reservation_id TEXT NOT NULL UNIQUE,
106
+ usage_event_id TEXT NOT NULL UNIQUE, charged_units NUMERIC(78,0) NOT NULL,
107
+ created_at BIGINT NOT NULL, payload JSONB NOT NULL
108
+ );
109
+ CREATE INDEX resvary_usage_receipts_account
110
+ ON ${t('resvary_usage_receipts')}(account_id, created_at);
111
+ CREATE TABLE ${t('resvary_ledger_entries')} (
112
+ id TEXT PRIMARY KEY, account_id TEXT NOT NULL, delta_units NUMERIC(78,0) NOT NULL,
113
+ balance_after_units NUMERIC(78,0) NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL
114
+ );
115
+ CREATE INDEX resvary_ledger_entries_account
116
+ ON ${t('resvary_ledger_entries')}(account_id, created_at);
117
+ CREATE TABLE ${t('resvary_funding_intents')} (
118
+ id TEXT PRIMARY KEY, project_id TEXT NOT NULL, customer_id TEXT NOT NULL,
119
+ status TEXT NOT NULL, requested_units NUMERIC(78,0) NOT NULL,
120
+ created_at BIGINT NOT NULL, payload JSONB NOT NULL
121
+ );
122
+ CREATE TABLE ${t('resvary_funding_transactions')} (
123
+ id TEXT PRIMARY KEY, funding_intent_id TEXT NOT NULL, rail TEXT NOT NULL,
124
+ network TEXT NOT NULL, external_payment_id_norm TEXT NOT NULL, tx_hash_norm TEXT,
125
+ amount_units NUMERIC(78,0) NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL,
126
+ UNIQUE(rail, network, external_payment_id_norm)
127
+ );
128
+ CREATE UNIQUE INDEX resvary_funding_transactions_tx_hash
129
+ ON ${t('resvary_funding_transactions')}(network, tx_hash_norm)
130
+ WHERE tx_hash_norm IS NOT NULL;
131
+ CREATE TABLE ${t('resvary_idempotency_keys')} (
132
+ scope TEXT NOT NULL, key TEXT NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL,
133
+ PRIMARY KEY(scope, key)
134
+ );
135
+ CREATE TABLE ${t('resvary_outbox_events')} (
136
+ id TEXT PRIMARY KEY, project_id TEXT NOT NULL, type TEXT NOT NULL, status TEXT NOT NULL,
137
+ created_at BIGINT NOT NULL, attempt_count INTEGER NOT NULL DEFAULT 0,
138
+ next_attempt_at BIGINT NOT NULL, lease_owner TEXT, lease_expires_at BIGINT,
139
+ last_attempt_at BIGINT, last_error TEXT, delivered_at BIGINT, payload JSONB NOT NULL
140
+ );
141
+ CREATE INDEX resvary_outbox_events_due
142
+ ON ${t('resvary_outbox_events')}(project_id, status, next_attempt_at, created_at);
143
+
144
+ CREATE TABLE ${t('resvary_invoices')} (
145
+ id TEXT PRIMARY KEY, status TEXT NOT NULL, customer_id TEXT, amount_units NUMERIC(78,0) NOT NULL,
146
+ created_at BIGINT NOT NULL, payload JSONB NOT NULL
147
+ );
148
+ CREATE INDEX resvary_invoices_status ON ${t('resvary_invoices')}(status);
149
+ CREATE INDEX resvary_invoices_customer_id ON ${t('resvary_invoices')}(customer_id);
150
+ CREATE TABLE ${t('resvary_receipts')} (
151
+ id TEXT PRIMARY KEY, invoice_id TEXT NOT NULL, tx_hash_norm TEXT, status TEXT NOT NULL,
152
+ amount_units NUMERIC(78,0) NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL
153
+ );
154
+ CREATE UNIQUE INDEX resvary_receipts_invoice_tx_hash
155
+ ON ${t('resvary_receipts')}(invoice_id, tx_hash_norm) WHERE tx_hash_norm IS NOT NULL;
156
+ CREATE TABLE ${t('arc_webhook_events')} (
157
+ id TEXT PRIMARY KEY, type TEXT NOT NULL, created_at BIGINT NOT NULL, payload JSONB NOT NULL
158
+ );
159
+ CREATE TABLE ${t('arc_webhook_deliveries')} (
160
+ id TEXT PRIMARY KEY, event_id TEXT NOT NULL, event_type TEXT NOT NULL, attempt INTEGER NOT NULL,
161
+ status TEXT NOT NULL, received_at BIGINT NOT NULL, payload JSONB NOT NULL
162
+ );
163
+ CREATE INDEX arc_webhook_deliveries_event_id ON ${t('arc_webhook_deliveries')}(event_id);
164
+ CREATE TABLE ${t('arc_watcher_cursors')} (
165
+ key TEXT PRIMARY KEY, network TEXT NOT NULL, invoice_id TEXT, memo_id_norm TEXT,
166
+ next_from_block NUMERIC(78,0) NOT NULL, updated_at BIGINT NOT NULL, payload JSONB NOT NULL
167
+ );
168
168
  `);
169
169
  await client.query(`INSERT INTO ${t('resvary_schema_migrations')}(version, applied_at) VALUES (1, $1)`, [Date.now()]);
170
170
  await client.query('COMMIT');
@@ -178,79 +178,79 @@ async function applyV2(client, schema) {
178
178
  const t = (name) => table({ schema }, name);
179
179
  await client.query('BEGIN');
180
180
  try {
181
- const duplicateReceipt = await client.query(`SELECT tx_hash_norm, COUNT(*)::text AS count
182
- FROM ${t('resvary_receipts')}
183
- WHERE tx_hash_norm IS NOT NULL
184
- GROUP BY tx_hash_norm
185
- HAVING COUNT(*) > 1
181
+ const duplicateReceipt = await client.query(`SELECT tx_hash_norm, COUNT(*)::text AS count
182
+ FROM ${t('resvary_receipts')}
183
+ WHERE tx_hash_norm IS NOT NULL
184
+ GROUP BY tx_hash_norm
185
+ HAVING COUNT(*) > 1
186
186
  LIMIT 1`);
187
187
  if (duplicateReceipt.rows[0]) {
188
188
  throw new Error(`Cannot enforce receipt transaction uniqueness: ${duplicateReceipt.rows[0].count} receipts use ${duplicateReceipt.rows[0].tx_hash_norm}`);
189
189
  }
190
- await client.query(`
191
- DROP INDEX IF EXISTS "${schema}".resvary_receipts_invoice_tx_hash;
192
- CREATE UNIQUE INDEX resvary_receipts_tx_hash
193
- ON ${t('resvary_receipts')}(tx_hash_norm)
194
- WHERE tx_hash_norm IS NOT NULL;
195
-
196
- ALTER TABLE ${t('resvary_credit_accounts')}
197
- ADD CONSTRAINT resvary_credit_accounts_nonnegative
198
- CHECK (posted_units >= 0 AND reserved_units >= 0 AND reserved_units <= posted_units);
199
- ALTER TABLE ${t('resvary_credit_grants')}
200
- ADD CONSTRAINT resvary_credit_grants_amount_positive CHECK (amount_units > 0),
201
- ADD CONSTRAINT resvary_credit_grants_account_fk FOREIGN KEY (account_id)
202
- REFERENCES ${t('resvary_credit_accounts')}(id);
203
- ALTER TABLE ${t('resvary_price_versions')}
204
- ADD CONSTRAINT resvary_price_versions_version_positive CHECK (version > 0),
205
- ADD CONSTRAINT resvary_price_versions_meter_fk FOREIGN KEY (meter_id)
206
- REFERENCES ${t('resvary_meters')}(id);
207
- ALTER TABLE ${t('resvary_credit_reservations')}
208
- ADD CONSTRAINT resvary_credit_reservations_status
209
- CHECK (status IN ('open', 'committed', 'released', 'expired')),
210
- ADD CONSTRAINT resvary_credit_reservations_units_nonnegative CHECK (reserved_units >= 0),
211
- ADD CONSTRAINT resvary_credit_reservations_account_fk FOREIGN KEY (account_id)
212
- REFERENCES ${t('resvary_credit_accounts')}(id);
213
- ALTER TABLE ${t('resvary_usage_events')}
214
- ADD CONSTRAINT resvary_usage_events_account_fk FOREIGN KEY (account_id)
215
- REFERENCES ${t('resvary_credit_accounts')}(id);
216
- ALTER TABLE ${t('resvary_usage_receipts')}
217
- ADD CONSTRAINT resvary_usage_receipts_units_nonnegative CHECK (charged_units >= 0),
218
- ADD CONSTRAINT resvary_usage_receipts_account_fk FOREIGN KEY (account_id)
219
- REFERENCES ${t('resvary_credit_accounts')}(id),
220
- ADD CONSTRAINT resvary_usage_receipts_reservation_fk FOREIGN KEY (reservation_id)
221
- REFERENCES ${t('resvary_credit_reservations')}(id),
222
- ADD CONSTRAINT resvary_usage_receipts_usage_event_fk FOREIGN KEY (usage_event_id)
223
- REFERENCES ${t('resvary_usage_events')}(id);
224
- ALTER TABLE ${t('resvary_ledger_entries')}
225
- ADD CONSTRAINT resvary_ledger_entries_balance_nonnegative CHECK (balance_after_units >= 0),
226
- ADD CONSTRAINT resvary_ledger_entries_account_fk FOREIGN KEY (account_id)
227
- REFERENCES ${t('resvary_credit_accounts')}(id);
228
- ALTER TABLE ${t('resvary_funding_intents')}
229
- ADD CONSTRAINT resvary_funding_intents_status
230
- CHECK (status IN ('pending', 'confirmed', 'failed')),
231
- ADD CONSTRAINT resvary_funding_intents_units_positive CHECK (requested_units > 0);
232
- ALTER TABLE ${t('resvary_funding_transactions')}
233
- ADD CONSTRAINT resvary_funding_transactions_units_positive CHECK (amount_units > 0),
234
- ADD CONSTRAINT resvary_funding_transactions_intent_fk FOREIGN KEY (funding_intent_id)
235
- REFERENCES ${t('resvary_funding_intents')}(id);
236
- ALTER TABLE ${t('resvary_outbox_events')}
237
- ADD CONSTRAINT resvary_outbox_events_status
238
- CHECK (status IN ('pending', 'processing', 'delivered', 'dead_letter')),
239
- ADD CONSTRAINT resvary_outbox_events_attempt_nonnegative CHECK (attempt_count >= 0);
240
- ALTER TABLE ${t('resvary_invoices')}
241
- ADD CONSTRAINT resvary_invoices_status
242
- CHECK (status IN ('open', 'observed', 'paid', 'expired', 'refunded', 'void')),
243
- ADD CONSTRAINT resvary_invoices_amount_positive CHECK (amount_units > 0);
244
- ALTER TABLE ${t('resvary_receipts')}
245
- ADD CONSTRAINT resvary_receipts_status CHECK (status IN ('paid', 'refunded')),
246
- ADD CONSTRAINT resvary_receipts_amount_positive CHECK (amount_units > 0),
247
- ADD CONSTRAINT resvary_receipts_invoice_fk FOREIGN KEY (invoice_id)
248
- REFERENCES ${t('resvary_invoices')}(id);
249
- ALTER TABLE ${t('arc_webhook_deliveries')}
250
- ADD CONSTRAINT arc_webhook_deliveries_status CHECK (status IN ('verified', 'failed')),
251
- ADD CONSTRAINT arc_webhook_deliveries_attempt_positive CHECK (attempt > 0);
252
- ALTER TABLE ${t('arc_watcher_cursors')}
253
- ADD CONSTRAINT arc_watcher_cursors_block_nonnegative CHECK (next_from_block >= 0);
190
+ await client.query(`
191
+ DROP INDEX IF EXISTS "${schema}".resvary_receipts_invoice_tx_hash;
192
+ CREATE UNIQUE INDEX resvary_receipts_tx_hash
193
+ ON ${t('resvary_receipts')}(tx_hash_norm)
194
+ WHERE tx_hash_norm IS NOT NULL;
195
+
196
+ ALTER TABLE ${t('resvary_credit_accounts')}
197
+ ADD CONSTRAINT resvary_credit_accounts_nonnegative
198
+ CHECK (posted_units >= 0 AND reserved_units >= 0 AND reserved_units <= posted_units);
199
+ ALTER TABLE ${t('resvary_credit_grants')}
200
+ ADD CONSTRAINT resvary_credit_grants_amount_positive CHECK (amount_units > 0),
201
+ ADD CONSTRAINT resvary_credit_grants_account_fk FOREIGN KEY (account_id)
202
+ REFERENCES ${t('resvary_credit_accounts')}(id);
203
+ ALTER TABLE ${t('resvary_price_versions')}
204
+ ADD CONSTRAINT resvary_price_versions_version_positive CHECK (version > 0),
205
+ ADD CONSTRAINT resvary_price_versions_meter_fk FOREIGN KEY (meter_id)
206
+ REFERENCES ${t('resvary_meters')}(id);
207
+ ALTER TABLE ${t('resvary_credit_reservations')}
208
+ ADD CONSTRAINT resvary_credit_reservations_status
209
+ CHECK (status IN ('open', 'committed', 'released', 'expired')),
210
+ ADD CONSTRAINT resvary_credit_reservations_units_nonnegative CHECK (reserved_units >= 0),
211
+ ADD CONSTRAINT resvary_credit_reservations_account_fk FOREIGN KEY (account_id)
212
+ REFERENCES ${t('resvary_credit_accounts')}(id);
213
+ ALTER TABLE ${t('resvary_usage_events')}
214
+ ADD CONSTRAINT resvary_usage_events_account_fk FOREIGN KEY (account_id)
215
+ REFERENCES ${t('resvary_credit_accounts')}(id);
216
+ ALTER TABLE ${t('resvary_usage_receipts')}
217
+ ADD CONSTRAINT resvary_usage_receipts_units_nonnegative CHECK (charged_units >= 0),
218
+ ADD CONSTRAINT resvary_usage_receipts_account_fk FOREIGN KEY (account_id)
219
+ REFERENCES ${t('resvary_credit_accounts')}(id),
220
+ ADD CONSTRAINT resvary_usage_receipts_reservation_fk FOREIGN KEY (reservation_id)
221
+ REFERENCES ${t('resvary_credit_reservations')}(id),
222
+ ADD CONSTRAINT resvary_usage_receipts_usage_event_fk FOREIGN KEY (usage_event_id)
223
+ REFERENCES ${t('resvary_usage_events')}(id);
224
+ ALTER TABLE ${t('resvary_ledger_entries')}
225
+ ADD CONSTRAINT resvary_ledger_entries_balance_nonnegative CHECK (balance_after_units >= 0),
226
+ ADD CONSTRAINT resvary_ledger_entries_account_fk FOREIGN KEY (account_id)
227
+ REFERENCES ${t('resvary_credit_accounts')}(id);
228
+ ALTER TABLE ${t('resvary_funding_intents')}
229
+ ADD CONSTRAINT resvary_funding_intents_status
230
+ CHECK (status IN ('pending', 'confirmed', 'failed')),
231
+ ADD CONSTRAINT resvary_funding_intents_units_positive CHECK (requested_units > 0);
232
+ ALTER TABLE ${t('resvary_funding_transactions')}
233
+ ADD CONSTRAINT resvary_funding_transactions_units_positive CHECK (amount_units > 0),
234
+ ADD CONSTRAINT resvary_funding_transactions_intent_fk FOREIGN KEY (funding_intent_id)
235
+ REFERENCES ${t('resvary_funding_intents')}(id);
236
+ ALTER TABLE ${t('resvary_outbox_events')}
237
+ ADD CONSTRAINT resvary_outbox_events_status
238
+ CHECK (status IN ('pending', 'processing', 'delivered', 'dead_letter')),
239
+ ADD CONSTRAINT resvary_outbox_events_attempt_nonnegative CHECK (attempt_count >= 0);
240
+ ALTER TABLE ${t('resvary_invoices')}
241
+ ADD CONSTRAINT resvary_invoices_status
242
+ CHECK (status IN ('open', 'observed', 'paid', 'expired', 'refunded', 'void')),
243
+ ADD CONSTRAINT resvary_invoices_amount_positive CHECK (amount_units > 0);
244
+ ALTER TABLE ${t('resvary_receipts')}
245
+ ADD CONSTRAINT resvary_receipts_status CHECK (status IN ('paid', 'refunded')),
246
+ ADD CONSTRAINT resvary_receipts_amount_positive CHECK (amount_units > 0),
247
+ ADD CONSTRAINT resvary_receipts_invoice_fk FOREIGN KEY (invoice_id)
248
+ REFERENCES ${t('resvary_invoices')}(id);
249
+ ALTER TABLE ${t('arc_webhook_deliveries')}
250
+ ADD CONSTRAINT arc_webhook_deliveries_status CHECK (status IN ('verified', 'failed')),
251
+ ADD CONSTRAINT arc_webhook_deliveries_attempt_positive CHECK (attempt > 0);
252
+ ALTER TABLE ${t('arc_watcher_cursors')}
253
+ ADD CONSTRAINT arc_watcher_cursors_block_nonnegative CHECK (next_from_block >= 0);
254
254
  `);
255
255
  await client.query(`INSERT INTO ${t('resvary_schema_migrations')}(version, applied_at) VALUES (2, $1)`, [Date.now()]);
256
256
  await client.query('COMMIT');
package/dist/receipt.js CHANGED
@@ -8,11 +8,11 @@ class PostgresReceiptStoreView {
8
8
  this.handle = handle;
9
9
  }
10
10
  async saveInvoice(value) {
11
- await this.db.query(`INSERT INTO ${table(this.handle, 'resvary_invoices')}
12
- (id, status, customer_id, amount_units, created_at, payload)
13
- VALUES ($1, $2, $3, $4, $5, $6)
14
- ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status,
15
- customer_id = EXCLUDED.customer_id, amount_units = EXCLUDED.amount_units,
11
+ await this.db.query(`INSERT INTO ${table(this.handle, 'resvary_invoices')}
12
+ (id, status, customer_id, amount_units, created_at, payload)
13
+ VALUES ($1, $2, $3, $4, $5, $6)
14
+ ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status,
15
+ customer_id = EXCLUDED.customer_id, amount_units = EXCLUDED.amount_units,
16
16
  created_at = EXCLUDED.created_at, payload = EXCLUDED.payload`, [
17
17
  value.id,
18
18
  value.status,
@@ -30,9 +30,9 @@ class PostgresReceiptStoreView {
30
30
  (!filter.customerId || value.customerId === filter.customerId));
31
31
  }
32
32
  async saveReceipt(value) {
33
- await this.db.query(`INSERT INTO ${table(this.handle, 'resvary_receipts')}
34
- (id, invoice_id, tx_hash_norm, status, amount_units, created_at, payload)
35
- VALUES ($1, $2, $3, $4, $5, $6, $7)
33
+ await this.db.query(`INSERT INTO ${table(this.handle, 'resvary_receipts')}
34
+ (id, invoice_id, tx_hash_norm, status, amount_units, created_at, payload)
35
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
36
36
  ON CONFLICT DO NOTHING`, [
37
37
  value.id,
38
38
  value.invoiceId,
@@ -47,28 +47,28 @@ class PostgresReceiptStoreView {
47
47
  return this.one(`SELECT payload::text AS payload FROM ${table(this.handle, 'resvary_receipts')} WHERE id = $1`, [id]);
48
48
  }
49
49
  getReceiptByTxHash(txHash, invoiceId) {
50
- return this.one(`SELECT payload::text AS payload FROM ${table(this.handle, 'resvary_receipts')}
50
+ return this.one(`SELECT payload::text AS payload FROM ${table(this.handle, 'resvary_receipts')}
51
51
  WHERE tx_hash_norm = $1 ${invoiceId ? 'AND invoice_id = $2' : ''} LIMIT 1`, invoiceId ? [txHash.toLowerCase(), invoiceId] : [txHash.toLowerCase()]);
52
52
  }
53
53
  listReceipts() {
54
54
  return this.all(`SELECT payload::text AS payload FROM ${table(this.handle, 'resvary_receipts')} ORDER BY created_at ASC`);
55
55
  }
56
56
  async saveWebhookEvent(value) {
57
- await this.db.query(`INSERT INTO ${table(this.handle, 'arc_webhook_events')}(id, type, created_at, payload)
58
- VALUES ($1, $2, $3, $4)
59
- ON CONFLICT (id) DO UPDATE SET type = EXCLUDED.type,
57
+ await this.db.query(`INSERT INTO ${table(this.handle, 'arc_webhook_events')}(id, type, created_at, payload)
58
+ VALUES ($1, $2, $3, $4)
59
+ ON CONFLICT (id) DO UPDATE SET type = EXCLUDED.type,
60
60
  created_at = EXCLUDED.created_at, payload = EXCLUDED.payload`, [value.id, value.type, value.createdAt, serializeReceiptStoreValue(value)]);
61
61
  }
62
62
  async listWebhookEvents(filter = {}) {
63
63
  return (await this.all(`SELECT payload::text AS payload FROM ${table(this.handle, 'arc_webhook_events')} ORDER BY created_at ASC`)).filter((value) => !filter.type || value.type === filter.type);
64
64
  }
65
65
  async saveWebhookDelivery(value) {
66
- await this.db.query(`INSERT INTO ${table(this.handle, 'arc_webhook_deliveries')}
67
- (id, event_id, event_type, attempt, status, received_at, payload)
68
- VALUES ($1, $2, $3, $4, $5, $6, $7)
69
- ON CONFLICT (id) DO UPDATE SET event_id = EXCLUDED.event_id,
70
- event_type = EXCLUDED.event_type, attempt = EXCLUDED.attempt,
71
- status = EXCLUDED.status, received_at = EXCLUDED.received_at,
66
+ await this.db.query(`INSERT INTO ${table(this.handle, 'arc_webhook_deliveries')}
67
+ (id, event_id, event_type, attempt, status, received_at, payload)
68
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
69
+ ON CONFLICT (id) DO UPDATE SET event_id = EXCLUDED.event_id,
70
+ event_type = EXCLUDED.event_type, attempt = EXCLUDED.attempt,
71
+ status = EXCLUDED.status, received_at = EXCLUDED.received_at,
72
72
  payload = EXCLUDED.payload`, [
73
73
  value.id,
74
74
  value.eventId,
@@ -93,12 +93,12 @@ class PostgresReceiptStoreView {
93
93
  return this.all(`SELECT payload::text AS payload FROM ${table(this.handle, 'arc_watcher_cursors')} ORDER BY updated_at ASC`);
94
94
  }
95
95
  async saveWatcherCursor(value) {
96
- await this.db.query(`INSERT INTO ${table(this.handle, 'arc_watcher_cursors')}
97
- (key, network, invoice_id, memo_id_norm, next_from_block, updated_at, payload)
98
- VALUES ($1, $2, $3, $4, $5, $6, $7)
99
- ON CONFLICT (key) DO UPDATE SET network = EXCLUDED.network,
100
- invoice_id = EXCLUDED.invoice_id, memo_id_norm = EXCLUDED.memo_id_norm,
101
- next_from_block = EXCLUDED.next_from_block, updated_at = EXCLUDED.updated_at,
96
+ await this.db.query(`INSERT INTO ${table(this.handle, 'arc_watcher_cursors')}
97
+ (key, network, invoice_id, memo_id_norm, next_from_block, updated_at, payload)
98
+ VALUES ($1, $2, $3, $4, $5, $6, $7)
99
+ ON CONFLICT (key) DO UPDATE SET network = EXCLUDED.network,
100
+ invoice_id = EXCLUDED.invoice_id, memo_id_norm = EXCLUDED.memo_id_norm,
101
+ next_from_block = EXCLUDED.next_from_block, updated_at = EXCLUDED.updated_at,
102
102
  payload = EXCLUDED.payload`, [
103
103
  value.key,
104
104
  value.network,
package/package.json CHANGED
@@ -1,49 +1,55 @@
1
- {
2
- "name": "@resvary/postgres",
3
- "version": "0.5.0",
4
- "description": "Postgres persistence and migration tooling for Resvary",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "bin": {
9
- "resvary-postgres": "dist/cli.js"
10
- },
11
- "exports": {
12
- ".": {
13
- "types": "./dist/index.d.ts",
14
- "import": "./dist/index.js"
15
- },
16
- "./import-sqlite": {
17
- "types": "./dist/import-sqlite.d.ts",
18
- "import": "./dist/import-sqlite.js"
19
- }
20
- },
21
- "files": [
22
- "dist",
23
- "README.md",
24
- "LICENSE"
25
- ],
26
- "publishConfig": {
27
- "access": "public"
28
- },
29
- "scripts": {
30
- "build": "tsc",
31
- "dev": "tsc --watch",
32
- "clean": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.tsbuildinfo',{force:true})\"",
33
- "test": "vitest run"
34
- },
35
- "engines": {
36
- "node": ">=20.0.0"
37
- },
38
- "dependencies": {
39
- "@resvary/sdk": "0.5.0",
40
- "pg": "^8.16.3"
41
- },
42
- "devDependencies": {
43
- "@types/node": "^22.0.0",
44
- "@types/pg": "^8.15.5",
45
- "typescript": "^5.6.0",
46
- "vitest": "^4.1.10"
47
- },
48
- "license": "Apache-2.0"
49
- }
1
+ {
2
+ "name": "@resvary/postgres",
3
+ "version": "0.6.1",
4
+ "description": "Postgres persistence and migration tooling for Resvary",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "resvary-postgres": "dist/cli.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./import-sqlite": {
17
+ "types": "./dist/import-sqlite.d.ts",
18
+ "import": "./dist/import-sqlite.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "dev": "tsc --watch",
32
+ "clean": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.tsbuildinfo',{force:true})\"",
33
+ "test": "vitest run"
34
+ },
35
+ "engines": {
36
+ "node": ">=20.0.0"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/horn111/resvary.git",
41
+ "directory": "packages/postgres"
42
+ },
43
+ "dependencies": {
44
+ "@resvary/sdk": "0.6.1",
45
+ "pg": "^8.16.3"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.20.1",
49
+ "@types/pg": "^8.15.5",
50
+ "typescript": "^5.6.0",
51
+ "vitest": "^4.1.11"
52
+ },
53
+ "license": "Apache-2.0",
54
+ "gitHead": "10c79e7e5bdecebc8a56b735466dc6a75b4a1f43"
55
+ }