@moneypot/hub 1.20.0-dev.1 → 1.20.0-dev.3

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.
@@ -1,7 +1,8 @@
1
1
  import { QueryResult, QueryResultRow } from "pg";
2
2
  import { PgClientResult } from "postgraphile/@dataplan/pg";
3
3
  import * as pg from "pg";
4
- export type QueryExecutor = Pick<pg.Pool, "query"> | Pick<pg.ClientBase, "query">;
4
+ import { Prettify } from "../util.js";
5
+ export type QueryExecutor = Prettify<Pick<pg.Pool, "query"> | Pick<pg.ClientBase, "query">>;
5
6
  type ResultType<T> = PgClientResult<T> | QueryResult<T extends QueryResultRow ? T : never>;
6
7
  export declare function maybeOneRow<T>(result: ResultType<T>): T | undefined;
7
8
  export declare function exactlyOneRow<T>(result: ResultType<T>): T;
@@ -1,5 +1,10 @@
1
1
  import { PgClientInTransaction } from "../../db/transaction.js";
2
+ import { QueryExecutor } from "../../db/util.js";
2
3
  import { DbLiability } from "../types.js";
3
4
  export declare function dbLockLiability(pool: PgClientInTransaction, { id }: {
4
5
  id: string;
5
6
  }): Promise<DbLiability | null>;
7
+ export declare function dbGetLiabilityByRef(pgClient: QueryExecutor, { refType, refId }: {
8
+ refType: string;
9
+ refId: string;
10
+ }): Promise<DbLiability | null>;
@@ -1,3 +1,4 @@
1
+ import { maybeOneRow } from "../../db/util.js";
1
2
  export async function dbLockLiability(pool, { id }) {
2
3
  const result = await pool.query({
3
4
  text: `
@@ -10,3 +11,16 @@ export async function dbLockLiability(pool, { id }) {
10
11
  });
11
12
  return result.rows[0] ?? null;
12
13
  }
14
+ export async function dbGetLiabilityByRef(pgClient, { refType, refId }) {
15
+ return pgClient
16
+ .query({
17
+ text: `
18
+ SELECT *
19
+ FROM hub.liability
20
+ WHERE ref_type = $1 AND ref_id = $2
21
+ `,
22
+ values: [refType, refId],
23
+ })
24
+ .then(maybeOneRow)
25
+ .then((row) => row ?? null);
26
+ }
@@ -1,2 +1,2 @@
1
- export { dbLockLiability } from "./db.js";
2
- export { startLiabilityExpirationProcessor, processExpiredLiabilities, } from "./processor.js";
1
+ export { dbLockLiability, dbGetLiabilityByRef } from "./db.js";
2
+ export { startLiabilityDeadlineProcessor, processDeadlinedLiabilities, processOneDeadlinedLiability, } from "./processor.js";
@@ -1,2 +1,2 @@
1
- export { dbLockLiability } from "./db.js";
2
- export { startLiabilityExpirationProcessor, processExpiredLiabilities, } from "./processor.js";
1
+ export { dbLockLiability, dbGetLiabilityByRef } from "./db.js";
2
+ export { startLiabilityDeadlineProcessor, processDeadlinedLiabilities, processOneDeadlinedLiability, } from "./processor.js";
@@ -1,15 +1,20 @@
1
1
  import * as pg from "pg";
2
+ import { type PgClientInTransaction } from "../../db/transaction.js";
2
3
  import { LiabilityConfigInternal } from "../public/config.js";
3
4
  import { QueryExecutor } from "../../db/util.js";
4
5
  export declare function dbGetLiabilityIdsPastDeadline(pgClient: QueryExecutor, { limit }?: {
5
6
  limit?: number;
6
7
  }): Promise<string[]>;
7
- export declare function startLiabilityExpirationProcessor<RefType extends string>({ signal, pool, liabilityConfig, }: {
8
+ export declare function startLiabilityDeadlineProcessor<RefType extends string>({ signal, pool, liabilityConfig, }: {
8
9
  signal: AbortSignal;
9
10
  pool: pg.Pool;
10
11
  liabilityConfig: LiabilityConfigInternal<RefType>;
11
12
  }): void;
12
- export declare function processExpiredLiabilities<RefType extends string>({ pool, liabilityConfig, signal, }: {
13
+ export declare function processOneDeadlinedLiability<RefType extends string>(pgClient: PgClientInTransaction, { liabilityConfig, liabilityId, }: {
14
+ liabilityConfig: LiabilityConfigInternal<RefType>;
15
+ liabilityId: string;
16
+ }): Promise<void>;
17
+ export declare function processDeadlinedLiabilities<RefType extends string>({ pool, liabilityConfig, signal, }: {
13
18
  pool: pg.Pool;
14
19
  liabilityConfig: LiabilityConfigInternal<RefType>;
15
20
  signal: AbortSignal;
@@ -1,6 +1,8 @@
1
1
  import { logger } from "../../logger.js";
2
2
  import { withPgPoolTransaction, } from "../../db/transaction.js";
3
3
  import { dbLockHouseBankroll, dbLockPlayerBalance } from "../../db/public.js";
4
+ import { exactlyOneRow, maybeOneRow } from "../../db/util.js";
5
+ import { abortableTimeout } from "../../util.js";
4
6
  const BATCH_SIZE = 100;
5
7
  export async function dbGetLiabilityIdsPastDeadline(pgClient, { limit = 100 } = {}) {
6
8
  const result = await pgClient.query({
@@ -17,7 +19,8 @@ export async function dbGetLiabilityIdsPastDeadline(pgClient, { limit = 100 } =
17
19
  return result.rows.map((r) => r.id);
18
20
  }
19
21
  async function dbTryLockLiabilityById(pgClient, id) {
20
- const result = await pgClient.query({
22
+ return pgClient
23
+ .query({
21
24
  text: `
22
25
  SELECT *
23
26
  FROM hub.liability
@@ -26,15 +29,16 @@ async function dbTryLockLiabilityById(pgClient, id) {
26
29
  FOR UPDATE SKIP LOCKED
27
30
  `,
28
31
  values: [id],
29
- });
30
- return result.rows[0] ?? null;
32
+ })
33
+ .then(maybeOneRow)
34
+ .then((row) => row ?? null);
31
35
  }
32
- export function startLiabilityExpirationProcessor({ signal, pool, liabilityConfig, }) {
36
+ export function startLiabilityDeadlineProcessor({ signal, pool, liabilityConfig, }) {
33
37
  if (signal.aborted) {
34
- logger.info(`[startLiabilityExpirationProcessor] AbortSignal aborted. Not starting processor.`);
38
+ logger.info(`[startLiabilityDeadlineProcessor] AbortSignal aborted. Not starting processor.`);
35
39
  return;
36
40
  }
37
- logger.info(`Starting liability expiration processor`);
41
+ logger.info(`Starting liability deadline processor`);
38
42
  (async () => {
39
43
  let shouldStop = false;
40
44
  signal.addEventListener("abort", () => {
@@ -42,19 +46,71 @@ export function startLiabilityExpirationProcessor({ signal, pool, liabilityConfi
42
46
  });
43
47
  while (!shouldStop && !signal.aborted) {
44
48
  try {
45
- await processExpiredLiabilities({ pool, liabilityConfig, signal });
49
+ await processDeadlinedLiabilities({ pool, liabilityConfig, signal });
46
50
  }
47
51
  catch (e) {
48
- logger.error(e, `Error processing expired liabilities`);
52
+ logger.error(e, `Error processing deadlined liabilities`);
49
53
  }
50
54
  if (!shouldStop && !signal.aborted) {
51
- await new Promise((resolve) => setTimeout(resolve, liabilityConfig._options.pollIntervalMs));
55
+ await abortableTimeout(liabilityConfig._options.pollIntervalMs, signal);
52
56
  }
53
57
  }
54
- logger.info(`Liability expiration processor stopped`);
58
+ logger.info(`Liability deadline processor stopped`);
55
59
  })();
56
60
  }
57
- export async function processExpiredLiabilities({ pool, liabilityConfig, signal, }) {
61
+ export async function processOneDeadlinedLiability(pgClient, { liabilityConfig, liabilityId, }) {
62
+ const dbLockedLiability = await dbTryLockLiabilityById(pgClient, liabilityId);
63
+ if (!dbLockedLiability) {
64
+ return;
65
+ }
66
+ if (dbLockedLiability.deadline > new Date()) {
67
+ throw new Error(`Cannot process liability ${dbLockedLiability.id}: deadline has not passed (deadline: ${dbLockedLiability.deadline.toISOString()})`);
68
+ }
69
+ const refTypeConfig = liabilityConfig._refTypes[dbLockedLiability.ref_type];
70
+ if (!refTypeConfig?.handleDeadline) {
71
+ logger.error(`No handler configured for liability ref_type "${dbLockedLiability.ref_type}" (id: ${dbLockedLiability.id}). Skipping.`);
72
+ return;
73
+ }
74
+ const dbLockedPlayerBalance = await dbLockPlayerBalance(pgClient, {
75
+ userId: dbLockedLiability.user_id,
76
+ casinoId: dbLockedLiability.casino_id,
77
+ experienceId: dbLockedLiability.experience_id,
78
+ currencyKey: dbLockedLiability.currency_key,
79
+ });
80
+ if (!dbLockedPlayerBalance) {
81
+ logger.error(`No player balance found for liability ${dbLockedLiability.id}. Skipping.`);
82
+ return;
83
+ }
84
+ const dbLockedHouseBankroll = await dbLockHouseBankroll(pgClient, {
85
+ casinoId: dbLockedLiability.casino_id,
86
+ currencyKey: dbLockedLiability.currency_key,
87
+ });
88
+ if (!dbLockedHouseBankroll) {
89
+ logger.error(`No house bankroll found for liability ${dbLockedLiability.id}.`);
90
+ return;
91
+ }
92
+ try {
93
+ await refTypeConfig.handleDeadline({
94
+ pgClientInTransaction: pgClient,
95
+ lockedLiability: dbLockedLiability,
96
+ lockedPlayerBalance: dbLockedPlayerBalance,
97
+ lockedHouseBankroll: dbLockedHouseBankroll,
98
+ });
99
+ const { resolved_at } = await pgClient
100
+ .query(`SELECT resolved_at FROM hub.liability WHERE id = $1`, [dbLockedLiability.id])
101
+ .then(exactlyOneRow);
102
+ if (!resolved_at) {
103
+ throw new Error(`Handler returned LIABILITY_RESOLVED but did not resolve liability ${dbLockedLiability.id} (ref_type: ${dbLockedLiability.ref_type}, ref_id: ${dbLockedLiability.ref_id}). ` +
104
+ `Handler must call dbResolveLiabilityByRef before returning.`);
105
+ }
106
+ logger.debug(`Resolved liability ${dbLockedLiability.id}`);
107
+ }
108
+ catch (e) {
109
+ logger.error(e, `Error handling deadline for liability ${dbLockedLiability.id}`);
110
+ throw e;
111
+ }
112
+ }
113
+ export async function processDeadlinedLiabilities({ pool, liabilityConfig, signal, }) {
58
114
  const liabilityIds = await dbGetLiabilityIdsPastDeadline(pool, {
59
115
  limit: BATCH_SIZE,
60
116
  });
@@ -68,55 +124,10 @@ export async function processExpiredLiabilities({ pool, liabilityConfig, signal,
68
124
  break;
69
125
  }
70
126
  await withPgPoolTransaction(pool, async (pgClient) => {
71
- const dbLockedLiability = await dbTryLockLiabilityById(pgClient, liabilityId);
72
- if (!dbLockedLiability) {
73
- return;
74
- }
75
- const refTypeConfig = liabilityConfig._refTypes[dbLockedLiability.ref_type];
76
- if (!refTypeConfig?.handleDeadline) {
77
- logger.error(`No handler configured for liability ref_type "${dbLockedLiability.ref_type}" (id: ${dbLockedLiability.id}). Skipping.`);
78
- return;
79
- }
80
- const dbLockedPlayerBalance = await dbLockPlayerBalance(pgClient, {
81
- userId: dbLockedLiability.user_id,
82
- casinoId: dbLockedLiability.casino_id,
83
- experienceId: dbLockedLiability.experience_id,
84
- currencyKey: dbLockedLiability.currency_key,
127
+ await processOneDeadlinedLiability(pgClient, {
128
+ liabilityConfig,
129
+ liabilityId,
85
130
  });
86
- if (!dbLockedPlayerBalance) {
87
- logger.error(`No player balance found for liability ${dbLockedLiability.id}. Skipping.`);
88
- return;
89
- }
90
- const dbLockedHouseBankroll = await dbLockHouseBankroll(pgClient, {
91
- casinoId: dbLockedLiability.casino_id,
92
- currencyKey: dbLockedLiability.currency_key,
93
- });
94
- if (!dbLockedHouseBankroll) {
95
- logger.error(`No house bankroll found for liability ${dbLockedLiability.id}.`);
96
- return;
97
- }
98
- try {
99
- await refTypeConfig.handleDeadline({
100
- pgClientInTransaction: pgClient,
101
- lockedLiability: dbLockedLiability,
102
- lockedPlayerBalance: dbLockedPlayerBalance,
103
- lockedHouseBankroll: dbLockedHouseBankroll,
104
- });
105
- const { resolved_at } = await pgClient
106
- .query(`SELECT resolved_at FROM hub.liability WHERE id = $1`, [
107
- dbLockedLiability.id,
108
- ])
109
- .then((r) => r.rows[0]);
110
- if (!resolved_at) {
111
- throw new Error(`Handler returned LIABILITY_RESOLVED but did not resolve liability ${dbLockedLiability.id} (ref_type: ${dbLockedLiability.ref_type}, ref_id: ${dbLockedLiability.ref_id}). ` +
112
- `Handler must call dbResolveLiabilityByRef before returning.`);
113
- }
114
- logger.debug(`Resolved liability ${dbLockedLiability.id}`);
115
- }
116
- catch (e) {
117
- logger.error(e, `Error handling expiration for liability ${dbLockedLiability.id}`);
118
- throw e;
119
- }
120
131
  });
121
132
  }
122
133
  }
@@ -1,7 +1,6 @@
1
1
  import { DbLockedHouseBankrollWithAvailable, DbLockedPlayerBalance, PgClientInTransaction } from "../../db/index.js";
2
2
  import { Branded } from "../../util.js";
3
- import type { DbLiability } from "../types.js";
4
- import { dbCreateLiability, dbResolveLiabilityByRef } from "./db.js";
3
+ import type { DbLiability, NewLiability } from "../types.js";
5
4
  export type DbLockedLiability<RefType extends string = string> = DbLiability & {
6
5
  ref_type: RefType;
7
6
  };
@@ -23,8 +22,14 @@ export declare function createLiabilityConfig<RefType extends string>(refTypes:
23
22
  [K in RefType]: LiabilityRefTypeConfig<K>;
24
23
  }, options?: Partial<LiabilityConfigOptions>): LiabilityConfig<RefType>;
25
24
  export type LiabilityConfig<RefType extends string> = {
26
- dbCreateLiability: typeof dbCreateLiability<RefType>;
27
- dbResolveLiabilityByRef: typeof dbResolveLiabilityByRef<RefType>;
25
+ dbCreateLiability: <T extends RefType>(pgClient: PgClientInTransaction, args: NewLiability<T>) => Promise<DbLiability & {
26
+ ref_type: T;
27
+ }>;
28
+ dbResolveLiabilityByRef: <T extends RefType>(pgClient: PgClientInTransaction, args: {
29
+ refType: T;
30
+ refId: string;
31
+ lockedBankrollId: string;
32
+ }) => Promise<boolean>;
28
33
  };
29
34
  export type LiabilityConfigOptions = {
30
35
  pollIntervalMs: number;
@@ -1,7 +1,7 @@
1
1
  import * as db from "../db/index.js";
2
2
  import { logger } from "../logger.js";
3
3
  import { assert } from "tsafe";
4
- import { isUuid } from "../util.js";
4
+ import { isUuid, abortableTimeout } from "../util.js";
5
5
  import { createGraphqlClient } from "../graphql-client.js";
6
6
  import { GET_CURRENCIES } from "../graphql-queries.js";
7
7
  import { processTakeRequests } from "../take-request/process-take-request.js";
@@ -75,7 +75,7 @@ export function startPollingProcessor({ casinoId, signal, pool, }) {
75
75
  const now = Date.now();
76
76
  const timeToWait = Math.max(0, processorState.backoffTime - (now - processorState.lastAttempt));
77
77
  if (timeToWait > 0) {
78
- await new Promise((resolve) => setTimeout(resolve, timeToWait));
78
+ await abortableTimeout(timeToWait, signal);
79
79
  }
80
80
  processorState.lastAttempt = Date.now();
81
81
  const casino = await dbGetCasinoById(pool, casinoId);
@@ -227,7 +227,6 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, pool,
227
227
  }
228
228
  async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoInfo, signal, pool, }) {
229
229
  let hasNextPage = true;
230
- const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
231
230
  while (hasNextPage && !signal.aborted) {
232
231
  if (signal.aborted) {
233
232
  logger.info(`[processTransfersUntilEmpty] Aborted by graceful shutdown.`);
@@ -276,7 +275,7 @@ async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoIn
276
275
  logger.info(`[processTransfersUntilEmpty] Aborted by graceful shutdown.`);
277
276
  break;
278
277
  }
279
- await timeout(1000);
278
+ await abortableTimeout(1000, signal);
280
279
  }
281
280
  return afterCursor;
282
281
  }
@@ -7,6 +7,7 @@ import { TRANSFER_FIELDS } from "./graphql.js";
7
7
  import { logger } from "../logger.js";
8
8
  import { processTransfer } from "./process-transfer.js";
9
9
  import assert from "assert";
10
+ import { abortableTimeout } from "../util.js";
10
11
  import { mpGetTakeRequest, processSingleTakeRequest, } from "../take-request/process-take-request.js";
11
12
  function httpToWs(url) {
12
13
  if (url.protocol === "http:") {
@@ -64,7 +65,7 @@ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controll
64
65
  retryWait: async (retries) => {
65
66
  const baseDelay = Math.min(1000 * Math.pow(2, retries), 30_000);
66
67
  const jitter = Math.floor(Math.random() * 10_000);
67
- await new Promise((resolve) => setTimeout(resolve, baseDelay + jitter));
68
+ await abortableTimeout(baseDelay + jitter, signal);
68
69
  },
69
70
  });
70
71
  client.on("connected", () => {
@@ -7,7 +7,7 @@ const postgraphile = typeof postgraphileImport === "function"
7
7
  import { createPreset, defaultPlugins } from "./graphile.config.js";
8
8
  import { createServerContext, closeServerContext, } from "../context.js";
9
9
  import { initializeTransferProcessors } from "../process-transfers/index.js";
10
- import { startLiabilityExpirationProcessor } from "../liability/internal/index.js";
10
+ import { startLiabilityDeadlineProcessor } from "../liability/internal/index.js";
11
11
  import express from "express";
12
12
  import { logger } from "../logger.js";
13
13
  import cors from "./middleware/cors.js";
@@ -87,7 +87,7 @@ export function createHubServer({ configureApp, plugins, exportSchemaSDLPath, ex
87
87
  pool: context.superuserPool,
88
88
  });
89
89
  if (liabilityConfig) {
90
- startLiabilityExpirationProcessor({
90
+ startLiabilityDeadlineProcessor({
91
91
  signal: abortController.signal,
92
92
  pool: context.superuserPool,
93
93
  liabilityConfig: liabilityConfig,
@@ -1,13 +1,16 @@
1
- import { Pool, PoolClient } from "pg";
1
+ import * as pg from "pg";
2
2
  import { DbUser, DbPlayerBalance, DbHashChain, DbCasino, DbExperience, DbCurrency, DbHouseBankroll } from "../db/types.js";
3
3
  import { GraphQLClient } from "graphql-request";
4
4
  import { ServerOptions } from "../index.js";
5
+ import type { QueryExecutor } from "../db/util.js";
6
+ import { LiabilityConfigInternal } from "../liability/public/config.js";
7
+ export { type QueryExecutor };
5
8
  export declare function uniqueTestId(): string;
6
9
  export type HubTestServer = {
7
10
  port: number;
8
11
  stop: () => Promise<void>;
9
12
  destroy: () => Promise<void>;
10
- dbPool: Pool;
13
+ dbPool: pg.Pool;
11
14
  playgroundCasinoId: string;
12
15
  authenticate: (userId: DbUser["id"], experienceId: DbExperience["id"]) => Promise<{
13
16
  sessionKey: string;
@@ -21,44 +24,48 @@ export type TestServerConfig = {
21
24
  extraPgSchemas?: string[];
22
25
  };
23
26
  export declare function startTestServer({ plugins, userDatabaseMigrationsPath, testId, extraPgSchemas, }: TestServerConfig): Promise<HubTestServer>;
24
- type PgClient = Pool | PoolClient;
25
- export declare function createUser(pgClient: PgClient, { casinoId, uname, }: {
27
+ export declare function createUser(pgClient: QueryExecutor, { casinoId, uname, }: {
26
28
  casinoId: DbCasino["id"];
27
29
  uname: string;
28
30
  }): Promise<DbUser>;
29
- export declare function createPlayerBalance(pgClient: PgClient, { userId, experienceId, currencyKey, amount, }: {
31
+ export declare function createPlayerBalance(pgClient: QueryExecutor, { userId, experienceId, currencyKey, amount, }: {
30
32
  userId: DbUser["id"];
31
33
  experienceId: DbExperience["id"];
32
34
  currencyKey: DbCurrency["key"];
33
35
  amount: number;
34
36
  }): Promise<DbPlayerBalance>;
35
- export declare function createHashChain(pgClient: PgClient, { userId, experienceId, casinoId, maxIterations, }: {
37
+ export declare function createHashChain(pgClient: QueryExecutor, { userId, experienceId, casinoId, maxIterations, }: {
36
38
  userId: DbUser["id"];
37
39
  experienceId: DbExperience["id"];
38
40
  casinoId: DbCasino["id"];
39
41
  maxIterations?: number;
40
42
  }): Promise<DbHashChain>;
41
- export declare function createExperience(pgClient: PgClient, options: {
43
+ export declare function createExperience(pgClient: QueryExecutor, options: {
42
44
  casinoId: DbCasino["id"];
43
45
  } | {
44
46
  casinoId: DbCasino["id"];
45
47
  mpExperienceId: string;
46
48
  name: string;
47
49
  }): Promise<DbExperience>;
48
- export declare function createCurrency(pgClient: PgClient, { key, casinoId, displayUnitName, displayUnitScale, }: {
50
+ export declare function createCurrency(pgClient: QueryExecutor, { key, casinoId, displayUnitName, displayUnitScale, }: {
49
51
  key: DbCurrency["key"];
50
52
  casinoId: DbCasino["id"];
51
53
  displayUnitName: string;
52
54
  displayUnitScale: number;
53
55
  }): Promise<DbCurrency>;
54
- export declare function createHouseBankroll(pgClient: PgClient, { casinoId, currencyKey, amount, }: {
56
+ export declare function createHouseBankroll(pgClient: QueryExecutor, { casinoId, currencyKey, amount, }: {
55
57
  casinoId: DbCasino["id"];
56
58
  currencyKey: DbCurrency["key"];
57
59
  amount: number;
58
60
  }): Promise<DbHouseBankroll>;
59
61
  export { createUser as createPlayer };
60
- export declare function getPlayerBalance(pgClient: PgClient, { userId, experienceId, currencyKey, }: {
62
+ export declare function getPlayerBalance(pgClient: QueryExecutor, { userId, experienceId, currencyKey, }: {
61
63
  userId: DbUser["id"];
62
64
  experienceId: DbExperience["id"];
63
65
  currencyKey: DbCurrency["key"];
64
66
  }): Promise<DbPlayerBalance | null>;
67
+ export declare function processOneDeadlinedLiability<RefType extends string>(dbPool: pg.Pool, { liabilityConfig, refType, refId, }: {
68
+ liabilityConfig: LiabilityConfigInternal<RefType>;
69
+ refType: RefType;
70
+ refId: string;
71
+ }): Promise<void>;
@@ -1,13 +1,14 @@
1
- import { Client, Pool } from "pg";
1
+ import * as pg from "pg";
2
2
  import { randomBytes, randomUUID } from "node:crypto";
3
3
  import { exec } from "node:child_process";
4
4
  import { promisify } from "node:util";
5
5
  const execAsync = promisify(exec);
6
6
  import { DbHashKind, } from "../db/types.js";
7
- import { exactlyOneRow, maybeOneRow } from "../db/index.js";
7
+ import { exactlyOneRow, maybeOneRow, withPgPoolTransaction, } from "../db/index.js";
8
8
  import { GraphQLClient } from "graphql-request";
9
9
  import { defaultPlugins, runMigrations } from "../index.js";
10
10
  import { createHubServer } from "../server/index.js";
11
+ import { dbGetLiabilityByRef, processOneDeadlinedLiability as internalProcessOneDeadlinedLiability, } from "../liability/internal/index.js";
11
12
  let testCounter = 0;
12
13
  export function uniqueTestId() {
13
14
  const workerId = process.env.VITEST_POOL_ID ?? "0";
@@ -20,7 +21,7 @@ export async function startTestServer({ plugins = [...defaultPlugins], userDatab
20
21
  await execAsync(`psql -U postgres -c "CREATE DATABASE \\"${dbName}\\""`);
21
22
  }
22
23
  catch (_e) {
23
- const pool = new Pool({ connectionString });
24
+ const pool = new pg.Pool({ connectionString });
24
25
  try {
25
26
  await pool.query(`
26
27
  DROP SCHEMA IF EXISTS hub CASCADE;
@@ -38,7 +39,7 @@ export async function startTestServer({ plugins = [...defaultPlugins], userDatab
38
39
  process.env.LOG_LEVEL = process.env.LOG_LEVEL ?? "error";
39
40
  process.env.HASHCHAINSERVER_URL = "mock-server";
40
41
  process.env.HASHCHAINSERVER_APPLICATION_SECRET = "test-secret";
41
- const pgClient = new Client({ connectionString });
42
+ const pgClient = new pg.Client({ connectionString });
42
43
  await pgClient.connect();
43
44
  try {
44
45
  await runMigrations({
@@ -61,7 +62,7 @@ export async function startTestServer({ plugins = [...defaultPlugins], userDatab
61
62
  postgraphileDatabaseUrl: connectionString,
62
63
  });
63
64
  const { port } = await hubServer.listen();
64
- const dbPool = new Pool({ connectionString });
65
+ const dbPool = new pg.Pool({ connectionString });
65
66
  const url = `http://localhost:${port}`;
66
67
  const graphqlUrl = `${url}/graphql`;
67
68
  const { rows } = await dbPool.query("SELECT id FROM hub.casino WHERE is_playground = true LIMIT 1");
@@ -174,3 +175,15 @@ export async function getPlayerBalance(pgClient, { userId, experienceId, currenc
174
175
  .then(maybeOneRow)
175
176
  .then((row) => row ?? null);
176
177
  }
178
+ export async function processOneDeadlinedLiability(dbPool, { liabilityConfig, refType, refId, }) {
179
+ await withPgPoolTransaction(dbPool, async (pgClient) => {
180
+ const dbLiability = await dbGetLiabilityByRef(pgClient, { refType, refId });
181
+ if (!dbLiability) {
182
+ throw new Error(`Liability not found: refType=${refType}, refId=${refId}`);
183
+ }
184
+ return internalProcessOneDeadlinedLiability(pgClient, {
185
+ liabilityConfig,
186
+ liabilityId: dbLiability.id,
187
+ });
188
+ });
189
+ }
@@ -3,6 +3,9 @@ declare const BRAND: unique symbol;
3
3
  export type Branded<Brand, T> = T & {
4
4
  readonly [BRAND]: Brand;
5
5
  };
6
+ export type Prettify<T> = {
7
+ [K in keyof T]: T[K];
8
+ } & {};
6
9
  export declare function isUuid(input: any): boolean;
7
10
  export type Result<V, E> = {
8
11
  ok: true;
@@ -13,4 +16,5 @@ export type Result<V, E> = {
13
16
  };
14
17
  export declare function extractFirstZodErrorMessage(e: z.ZodError): string;
15
18
  export declare function uuidEqual(a: string, b: string): boolean;
19
+ export declare function abortableTimeout(delay: number, signal: AbortSignal): Promise<void>;
16
20
  export {};
package/dist/src/util.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { assert } from "tsafe";
2
+ import { addAbortListener } from "node:events";
2
3
  const UUID_REGEX = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/;
3
4
  export function isUuid(input) {
4
5
  return typeof input === "string" && UUID_REGEX.test(input);
@@ -11,3 +12,17 @@ export function uuidEqual(a, b) {
11
12
  assert(isUuid(b), `Expected valid uuid string: "${b}"`);
12
13
  return a.toLowerCase() === b.toLowerCase();
13
14
  }
15
+ export function abortableTimeout(delay, signal) {
16
+ if (signal.aborted)
17
+ return Promise.resolve();
18
+ return new Promise((resolve) => {
19
+ const disposable = addAbortListener(signal, () => {
20
+ clearTimeout(timeout);
21
+ resolve();
22
+ });
23
+ const timeout = setTimeout(() => {
24
+ disposable[Symbol.dispose]();
25
+ resolve();
26
+ }, delay);
27
+ });
28
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneypot/hub",
3
- "version": "1.20.0-dev.1",
3
+ "version": "1.20.0-dev.3",
4
4
  "author": "moneypot.com",
5
5
  "homepage": "https://moneypot.com/hub",
6
6
  "keywords": [