@moneypot/hub 1.19.1 → 1.19.4
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/README.md +1 -0
- package/dist/src/db/types.d.ts +2 -0
- package/dist/src/pg-versions/014-take-request-transfer-retry.sql +11 -0
- package/dist/src/process-transfers/websocket-processor.js +5 -0
- package/dist/src/server/graphile.config.d.ts +4 -1
- package/dist/src/server/graphile.config.js +6 -7
- package/dist/src/server/index.js +4 -2
- package/dist/src/take-request/process-take-request.d.ts +5 -0
- package/dist/src/take-request/process-take-request.js +175 -7
- package/package.json +30 -27
package/README.md
CHANGED
|
@@ -80,6 +80,7 @@ You should always keep your hub server up to date as soon as possible. Never ins
|
|
|
80
80
|
### 1.19.x
|
|
81
81
|
|
|
82
82
|
- All of hub's internal graphql-schema-modifying plugins have been moved to hub's set of required plugins.
|
|
83
|
+
- Added `FAILED` take request auto-handling
|
|
83
84
|
|
|
84
85
|
### 1.18.x
|
|
85
86
|
|
package/dist/src/db/types.d.ts
CHANGED
|
@@ -114,6 +114,8 @@ export type DbTakeRequest = {
|
|
|
114
114
|
mp_transfer_status: DbTransferStatusKind | null;
|
|
115
115
|
transfer_needs_completion: boolean;
|
|
116
116
|
transfer_completion_attempted_at: Date | null;
|
|
117
|
+
transfer_failure_count: number;
|
|
118
|
+
transfer_first_failure_at: Date | null;
|
|
117
119
|
refunded_at: Date | null;
|
|
118
120
|
updated_at: Date;
|
|
119
121
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
ALTER TABLE hub.take_request
|
|
2
|
+
ADD COLUMN IF NOT EXISTS transfer_failure_count int DEFAULT 0,
|
|
3
|
+
ADD COLUMN IF NOT EXISTS transfer_first_failure_at timestamptz;
|
|
4
|
+
|
|
5
|
+
CREATE INDEX IF NOT EXISTS take_request_transfer_retry_idx
|
|
6
|
+
ON hub.take_request(casino_id, status, transfer_first_failure_at)
|
|
7
|
+
WHERE mp_transfer_id IS NULL AND status = 'PROCESSING';
|
|
8
|
+
|
|
9
|
+
CREATE INDEX IF NOT EXISTS take_request_failed_refund_idx
|
|
10
|
+
ON hub.take_request(casino_id, status, refunded_at)
|
|
11
|
+
WHERE mp_transfer_id IS NULL AND status = 'FAILED' AND refunded_at IS NULL;
|
|
@@ -61,6 +61,11 @@ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controll
|
|
|
61
61
|
return true;
|
|
62
62
|
},
|
|
63
63
|
keepAlive: 1_000,
|
|
64
|
+
retryWait: async (retries) => {
|
|
65
|
+
const baseDelay = Math.min(1000 * Math.pow(2, retries), 30_000);
|
|
66
|
+
const jitter = Math.floor(Math.random() * 10_000);
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, baseDelay + jitter));
|
|
68
|
+
},
|
|
64
69
|
});
|
|
65
70
|
client.on("connected", () => {
|
|
66
71
|
logger.info(`[websocketProcessor] Connected to websocket ${graphqlUrl}`);
|
|
@@ -37,4 +37,7 @@ export declare function createPreset({ plugins, exportSchemaSDLPath, extraPgSche
|
|
|
37
37
|
context: ServerContext;
|
|
38
38
|
enableChat: boolean;
|
|
39
39
|
enablePlayground: boolean;
|
|
40
|
-
}):
|
|
40
|
+
}): {
|
|
41
|
+
preset: GraphileConfig.Preset;
|
|
42
|
+
pgService: GraphileConfig.PgServiceConfiguration<"@dataplan/pg/adaptors/pg">;
|
|
43
|
+
};
|
|
@@ -91,15 +91,14 @@ export function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abo
|
|
|
91
91
|
if (enableChat) {
|
|
92
92
|
mutablePlugins.push(HubChatCreateUserMessagePlugin, HubChatCreateSystemMessagePlugin, HubChatSubscriptionPlugin, HubChatMuteUserPlugin, HubChatUnmuteUserPlugin, HubChatAfterIdConditionPlugin, HubChatModManagementPlugin);
|
|
93
93
|
}
|
|
94
|
+
const pgService = makePgService({
|
|
95
|
+
connectionString: config.DATABASE_URL,
|
|
96
|
+
schemas: [...extraPgSchemas, "hub"],
|
|
97
|
+
});
|
|
94
98
|
const preset = {
|
|
95
99
|
extends: [PostGraphileAmberPreset],
|
|
96
100
|
disablePlugins: ["NodePlugin"],
|
|
97
|
-
pgServices: [
|
|
98
|
-
makePgService({
|
|
99
|
-
connectionString: config.DATABASE_URL,
|
|
100
|
-
schemas: [...extraPgSchemas, "hub"],
|
|
101
|
-
}),
|
|
102
|
-
],
|
|
101
|
+
pgServices: [pgService],
|
|
103
102
|
schema: {
|
|
104
103
|
dontSwallowErrors: true,
|
|
105
104
|
exportSchemaSDLPath,
|
|
@@ -175,7 +174,7 @@ export function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abo
|
|
|
175
174
|
},
|
|
176
175
|
plugins: mutablePlugins,
|
|
177
176
|
};
|
|
178
|
-
return preset;
|
|
177
|
+
return { preset, pgService };
|
|
179
178
|
}
|
|
180
179
|
function getSessionIdFromWebsocketCtx(ws) {
|
|
181
180
|
const value = ws.normalizedConnectionParams?.authorization;
|
package/dist/src/server/index.js
CHANGED
|
@@ -46,7 +46,7 @@ function createExpressServer(context) {
|
|
|
46
46
|
}
|
|
47
47
|
export function createHubServer({ configureApp, plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, context, enableChat, enablePlayground, port, }) {
|
|
48
48
|
const expressServer = createExpressServer(context);
|
|
49
|
-
const preset = createPreset({
|
|
49
|
+
const { preset, pgService } = createPreset({
|
|
50
50
|
plugins: plugins ?? defaultPlugins,
|
|
51
51
|
exportSchemaSDLPath,
|
|
52
52
|
extraPgSchemas: extraPgSchemas ?? [],
|
|
@@ -78,10 +78,12 @@ export function createHubServer({ configureApp, plugins, exportSchemaSDLPath, ex
|
|
|
78
78
|
});
|
|
79
79
|
},
|
|
80
80
|
shutdown: async () => {
|
|
81
|
+
nodeServer.closeAllConnections();
|
|
82
|
+
await pgService.release?.();
|
|
83
|
+
await pgl.release();
|
|
81
84
|
await new Promise((resolve) => {
|
|
82
85
|
nodeServer.close(() => resolve());
|
|
83
86
|
});
|
|
84
|
-
await pgl.release();
|
|
85
87
|
},
|
|
86
88
|
};
|
|
87
89
|
}
|
|
@@ -25,3 +25,8 @@ export declare function completeTransfer({ mpTakeRequestId, takeRequestId, mpTra
|
|
|
25
25
|
casinoId: string;
|
|
26
26
|
pool: pg.Pool;
|
|
27
27
|
}): Promise<void>;
|
|
28
|
+
export declare function processFailedRequestRefunds({ casinoId, abortSignal, pool, }: {
|
|
29
|
+
casinoId: string;
|
|
30
|
+
abortSignal: AbortSignal;
|
|
31
|
+
pool: pg.Pool;
|
|
32
|
+
}): Promise<void>;
|
|
@@ -193,6 +193,7 @@ export async function processTakeRequests({ abortSignal, controllerId, casinoId,
|
|
|
193
193
|
pool,
|
|
194
194
|
});
|
|
195
195
|
await processStuckRequests({ casinoId, graphqlClient, abortSignal, pool });
|
|
196
|
+
await processFailedRequestRefunds({ casinoId, abortSignal, pool });
|
|
196
197
|
}
|
|
197
198
|
async function fetchPendingTakeRequests(graphqlClient, controllerId) {
|
|
198
199
|
const result = await graphqlClient.request(MP_PAGINATE_PENDING_TAKE_REQUESTS, {
|
|
@@ -382,6 +383,7 @@ async function attemptTransfer({ pgClient, takeRequestId, mpTakeRequestId, mpExp
|
|
|
382
383
|
}
|
|
383
384
|
const result = transferResult.transferCurrencyExperienceToUser.result;
|
|
384
385
|
let transferId = null;
|
|
386
|
+
let needsImmediateRefund = false;
|
|
385
387
|
let resultStatus = LocalTakeRequestStatus.PROCESSING;
|
|
386
388
|
switch (result.__typename) {
|
|
387
389
|
case "TransferSuccess":
|
|
@@ -400,6 +402,7 @@ async function attemptTransfer({ pgClient, takeRequestId, mpTakeRequestId, mpExp
|
|
|
400
402
|
}
|
|
401
403
|
else {
|
|
402
404
|
resultStatus = LocalTakeRequestStatus.FAILED;
|
|
405
|
+
needsImmediateRefund = true;
|
|
403
406
|
}
|
|
404
407
|
break;
|
|
405
408
|
}
|
|
@@ -409,8 +412,8 @@ async function attemptTransfer({ pgClient, takeRequestId, mpTakeRequestId, mpExp
|
|
|
409
412
|
await pgClient.query({
|
|
410
413
|
text: `
|
|
411
414
|
UPDATE hub.take_request
|
|
412
|
-
SET status = $1,
|
|
413
|
-
mp_transfer_id = $2,
|
|
415
|
+
SET status = $1,
|
|
416
|
+
mp_transfer_id = $2,
|
|
414
417
|
mp_transfer_status = $3,
|
|
415
418
|
transfer_needs_completion = $4,
|
|
416
419
|
mp_status = $5
|
|
@@ -427,18 +430,61 @@ async function attemptTransfer({ pgClient, takeRequestId, mpTakeRequestId, mpExp
|
|
|
427
430
|
takeRequestId,
|
|
428
431
|
],
|
|
429
432
|
});
|
|
433
|
+
if (needsImmediateRefund) {
|
|
434
|
+
logger.info({ takeRequestId }, `[attemptTransfer] Take request already terminal on MP, refunding`);
|
|
435
|
+
await refundTakeRequest(pgClient, takeRequestId);
|
|
436
|
+
}
|
|
430
437
|
return transferId;
|
|
431
438
|
}
|
|
432
439
|
catch (error) {
|
|
433
|
-
logger.error(error, `[attemptTransfer] Error`);
|
|
440
|
+
logger.error(error, `[attemptTransfer] Error creating transfer`);
|
|
441
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
442
|
+
const shouldRetry = error instanceof Error && isNetworkError(error);
|
|
434
443
|
await pgClient.query({
|
|
435
444
|
text: `
|
|
436
445
|
UPDATE hub.take_request
|
|
437
|
-
SET
|
|
438
|
-
|
|
446
|
+
SET transfer_failure_count = COALESCE(transfer_failure_count, 0) + 1,
|
|
447
|
+
transfer_first_failure_at = COALESCE(transfer_first_failure_at, now()),
|
|
448
|
+
debug = COALESCE(debug, '') || ' | Transfer attempt failed: ' || $2::text
|
|
449
|
+
WHERE id = $1
|
|
439
450
|
`,
|
|
440
|
-
values: [
|
|
451
|
+
values: [takeRequestId, errorMessage.slice(0, 200)],
|
|
441
452
|
});
|
|
453
|
+
if (!shouldRetry) {
|
|
454
|
+
logger.info({ takeRequestId }, `[attemptTransfer] Non-retryable error, marking as FAILED and refunding`);
|
|
455
|
+
await pgClient.query({
|
|
456
|
+
text: `
|
|
457
|
+
UPDATE hub.take_request
|
|
458
|
+
SET status = $1,
|
|
459
|
+
debug = COALESCE(debug, '') || ' | Non-retryable error, refunding'
|
|
460
|
+
WHERE id = $2
|
|
461
|
+
`,
|
|
462
|
+
values: [LocalTakeRequestStatus.FAILED, takeRequestId],
|
|
463
|
+
});
|
|
464
|
+
await refundTakeRequest(pgClient, takeRequestId);
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
const TRANSFER_RETRY_TIMEOUT_SECONDS = 30;
|
|
468
|
+
const takeRequestAfterAttempt = await pgClient
|
|
469
|
+
.query({
|
|
470
|
+
text: `SELECT transfer_first_failure_at FROM hub.take_request WHERE id = $1`,
|
|
471
|
+
values: [takeRequestId],
|
|
472
|
+
})
|
|
473
|
+
.then(exactlyOneRow);
|
|
474
|
+
const elapsedMs = Date.now() - takeRequestAfterAttempt.transfer_first_failure_at.getTime();
|
|
475
|
+
if (elapsedMs >= TRANSFER_RETRY_TIMEOUT_SECONDS * 1000) {
|
|
476
|
+
logger.error({ takeRequestId, elapsedMs }, `[attemptTransfer] Retry timeout (${TRANSFER_RETRY_TIMEOUT_SECONDS}s) reached, marking as FAILED and refunding`);
|
|
477
|
+
await pgClient.query({
|
|
478
|
+
text: `
|
|
479
|
+
UPDATE hub.take_request
|
|
480
|
+
SET status = $1,
|
|
481
|
+
debug = COALESCE(debug, '') || ' | Transfer retry timeout reached, refunding'
|
|
482
|
+
WHERE id = $2
|
|
483
|
+
`,
|
|
484
|
+
values: [LocalTakeRequestStatus.FAILED, takeRequestId],
|
|
485
|
+
});
|
|
486
|
+
await refundTakeRequest(pgClient, takeRequestId);
|
|
487
|
+
}
|
|
442
488
|
return null;
|
|
443
489
|
}
|
|
444
490
|
}
|
|
@@ -720,7 +766,10 @@ async function processStuckRequests({ casinoId, graphqlClient, abortSignal, pool
|
|
|
720
766
|
WHERE casino_id = $1
|
|
721
767
|
AND status = $2
|
|
722
768
|
AND mp_transfer_id IS NULL
|
|
723
|
-
AND
|
|
769
|
+
AND (
|
|
770
|
+
transfer_first_failure_at IS NULL
|
|
771
|
+
OR transfer_first_failure_at < now() - interval '10 seconds'
|
|
772
|
+
)
|
|
724
773
|
LIMIT 10
|
|
725
774
|
`,
|
|
726
775
|
values: [casinoId, LocalTakeRequestStatus.PROCESSING],
|
|
@@ -741,6 +790,41 @@ async function processStuckRequests({ casinoId, graphqlClient, abortSignal, pool
|
|
|
741
790
|
});
|
|
742
791
|
}
|
|
743
792
|
}
|
|
793
|
+
export async function processFailedRequestRefunds({ casinoId, abortSignal, pool, }) {
|
|
794
|
+
if (abortSignal.aborted) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
const failedRequests = await pool
|
|
798
|
+
.query({
|
|
799
|
+
text: `
|
|
800
|
+
SELECT id, mp_take_request_id
|
|
801
|
+
FROM hub.take_request
|
|
802
|
+
WHERE casino_id = $1
|
|
803
|
+
AND status = $2
|
|
804
|
+
AND mp_transfer_id IS NULL
|
|
805
|
+
AND refunded_at IS NULL
|
|
806
|
+
AND reserved_amount > 0
|
|
807
|
+
LIMIT 10
|
|
808
|
+
`,
|
|
809
|
+
values: [casinoId, LocalTakeRequestStatus.FAILED],
|
|
810
|
+
})
|
|
811
|
+
.then((result) => result.rows);
|
|
812
|
+
if (failedRequests.length > 0) {
|
|
813
|
+
logger.info(`[processFailedRequestRefunds] Found ${failedRequests.length} FAILED requests needing refunds`);
|
|
814
|
+
}
|
|
815
|
+
for (const request of failedRequests) {
|
|
816
|
+
if (abortSignal.aborted) {
|
|
817
|
+
break;
|
|
818
|
+
}
|
|
819
|
+
await withPgPoolTransaction(pool, async (pgClient) => {
|
|
820
|
+
await PgAdvisoryLock.forMpTakeRequestProcessing(pgClient, {
|
|
821
|
+
mpTakeRequestId: request.mp_take_request_id,
|
|
822
|
+
casinoId,
|
|
823
|
+
});
|
|
824
|
+
await refundTakeRequest(pgClient, request.id);
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
}
|
|
744
828
|
async function loadRequiredEntities(pgClient, params) {
|
|
745
829
|
const { type, currencyKey, casinoId } = params;
|
|
746
830
|
const dbCurrency = await pgClient
|
|
@@ -926,6 +1010,90 @@ async function updateTakeRequestStatus(pgClient, takeRequestId, status) {
|
|
|
926
1010
|
values: [status, takeRequestId],
|
|
927
1011
|
});
|
|
928
1012
|
}
|
|
1013
|
+
async function refundTakeRequest(pgClient, takeRequestId) {
|
|
1014
|
+
const takeRequestData = await pgClient
|
|
1015
|
+
.query({
|
|
1016
|
+
text: `
|
|
1017
|
+
SELECT *
|
|
1018
|
+
FROM hub.take_request
|
|
1019
|
+
WHERE id = $1
|
|
1020
|
+
FOR UPDATE
|
|
1021
|
+
`,
|
|
1022
|
+
values: [takeRequestId],
|
|
1023
|
+
})
|
|
1024
|
+
.then(exactlyOneRow);
|
|
1025
|
+
if (takeRequestData.refunded_at) {
|
|
1026
|
+
logger.debug({ takeRequestId }, `[refundTakeRequest] Already refunded, skipping`);
|
|
1027
|
+
return false;
|
|
1028
|
+
}
|
|
1029
|
+
if (takeRequestData.reserved_amount <= 0) {
|
|
1030
|
+
logger.debug({ takeRequestId }, `[refundTakeRequest] No reserved amount to refund`);
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
1033
|
+
const dbLockedBalance = await pgClient
|
|
1034
|
+
.query({
|
|
1035
|
+
text: `
|
|
1036
|
+
SELECT id, amount
|
|
1037
|
+
FROM hub.balance
|
|
1038
|
+
WHERE user_id = $1
|
|
1039
|
+
AND experience_id = $2
|
|
1040
|
+
AND casino_id = $3
|
|
1041
|
+
AND currency_key = $4
|
|
1042
|
+
FOR UPDATE
|
|
1043
|
+
`,
|
|
1044
|
+
values: [
|
|
1045
|
+
takeRequestData.user_id,
|
|
1046
|
+
takeRequestData.experience_id,
|
|
1047
|
+
takeRequestData.casino_id,
|
|
1048
|
+
takeRequestData.currency_key,
|
|
1049
|
+
],
|
|
1050
|
+
})
|
|
1051
|
+
.then(maybeOneRow);
|
|
1052
|
+
if (!dbLockedBalance) {
|
|
1053
|
+
logger.error({ takeRequestId, takeRequestData }, `[refundTakeRequest] Balance record not found - cannot refund`);
|
|
1054
|
+
await pgClient.query({
|
|
1055
|
+
text: `
|
|
1056
|
+
UPDATE hub.take_request
|
|
1057
|
+
SET debug = COALESCE(debug, '') || ' | Refund failed: balance record not found'
|
|
1058
|
+
WHERE id = $1
|
|
1059
|
+
`,
|
|
1060
|
+
values: [takeRequestId],
|
|
1061
|
+
});
|
|
1062
|
+
return false;
|
|
1063
|
+
}
|
|
1064
|
+
await pgClient.query({
|
|
1065
|
+
text: `
|
|
1066
|
+
UPDATE hub.balance
|
|
1067
|
+
SET amount = amount + $1
|
|
1068
|
+
WHERE id = $2
|
|
1069
|
+
`,
|
|
1070
|
+
values: [takeRequestData.reserved_amount, dbLockedBalance.id],
|
|
1071
|
+
});
|
|
1072
|
+
await insertAuditLog(pgClient, "player-balance", {
|
|
1073
|
+
balanceId: dbLockedBalance.id,
|
|
1074
|
+
balanceOld: dbLockedBalance.amount,
|
|
1075
|
+
balanceNew: dbLockedBalance.amount + takeRequestData.reserved_amount,
|
|
1076
|
+
balanceDelta: takeRequestData.reserved_amount,
|
|
1077
|
+
action: "hub:take_request:refund",
|
|
1078
|
+
refType: "hub.take_request",
|
|
1079
|
+
refId: takeRequestId,
|
|
1080
|
+
});
|
|
1081
|
+
await pgClient.query({
|
|
1082
|
+
text: `
|
|
1083
|
+
UPDATE hub.take_request
|
|
1084
|
+
SET refunded_at = now(),
|
|
1085
|
+
debug = COALESCE(debug, '') || $2,
|
|
1086
|
+
updated_at = now()
|
|
1087
|
+
WHERE id = $1
|
|
1088
|
+
`,
|
|
1089
|
+
values: [
|
|
1090
|
+
takeRequestId,
|
|
1091
|
+
` | Refunded ${takeRequestData.reserved_amount} base units of ${takeRequestData.currency_key}`,
|
|
1092
|
+
],
|
|
1093
|
+
});
|
|
1094
|
+
logger.info({ takeRequestId, amount: takeRequestData.reserved_amount }, `[refundTakeRequest] Successfully refunded`);
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
929
1097
|
function isNetworkError(e) {
|
|
930
1098
|
return (e.message?.includes("ECONNREFUSED") ||
|
|
931
1099
|
e.message?.includes("ECONNRESET") ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moneypot/hub",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.4",
|
|
4
4
|
"author": "moneypot.com",
|
|
5
5
|
"homepage": "https://moneypot.com/hub",
|
|
6
6
|
"keywords": [
|
|
@@ -48,47 +48,50 @@
|
|
|
48
48
|
"build-dashboard": "cd ./dashboard && pnpm run build && rm -rf ../dist/dashboard && cp -Rv dist ../dist/dashboard",
|
|
49
49
|
"test:startup": "dropdb --if-exists hub-test && createdb hub-test && SUPERUSER_DATABASE_URL=postgres://localhost:5432/hub-test DATABASE_URL=postgres://localhost:5432/hub-test tsx src/test-startup.ts",
|
|
50
50
|
"test": "vitest run",
|
|
51
|
-
"test:watch": "vitest"
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"test:bail": "pnpm test -- --bail --silent --reporter=basic 2>&1 | head -25"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
|
-
"@graphile-contrib/pg-omit-archived": "
|
|
55
|
+
"@graphile-contrib/pg-omit-archived": "4.0.0-rc.1",
|
|
55
56
|
"@moneypot/hash-herald": "^1.0.0",
|
|
56
57
|
"@moneypot/pg-upgrade-schema": "^2.1.0",
|
|
57
|
-
"@noble/curves": "^1.5.0",
|
|
58
58
|
"dotenv": "^17.2.3",
|
|
59
|
-
"express": "^5.
|
|
60
|
-
"graphql": "^16.
|
|
61
|
-
"graphql-request": "^7.
|
|
62
|
-
"graphql-ws": "^6.0.
|
|
63
|
-
"jose": "^6.
|
|
64
|
-
"pg": "^8.
|
|
65
|
-
"pg-connection-string": "^2.
|
|
59
|
+
"express": "^5.2.1",
|
|
60
|
+
"graphql": "^16.12.0",
|
|
61
|
+
"graphql-request": "^7.4.0",
|
|
62
|
+
"graphql-ws": "^6.0.6",
|
|
63
|
+
"jose": "^6.1.3",
|
|
64
|
+
"pg": "^8.16.3",
|
|
65
|
+
"pg-connection-string": "^2.9.1",
|
|
66
66
|
"pino": "^10.1.0",
|
|
67
|
-
"postgraphile": "
|
|
68
|
-
"tsafe": "^1.
|
|
69
|
-
"zod": "^4.1.
|
|
67
|
+
"postgraphile": "5.0.0-rc.3",
|
|
68
|
+
"tsafe": "^1.8.12",
|
|
69
|
+
"zod": "^4.1.13"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@eslint/js": "^9.
|
|
72
|
+
"@eslint/js": "^9.39.2",
|
|
73
73
|
"@graphql-codegen/cli": "^6.1.0",
|
|
74
74
|
"@graphql-codegen/client-preset": "^5.2.1",
|
|
75
75
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
76
|
-
"@types/express": "^5.0.
|
|
77
|
-
"@types/node": "^25.0.
|
|
78
|
-
"@types/pg": "^8.
|
|
79
|
-
"@types/supertest": "^6.0.
|
|
80
|
-
"eslint": "^9.
|
|
81
|
-
"globals": "^16.
|
|
82
|
-
"pino-pretty": "^13.
|
|
83
|
-
"prettier": "^3.
|
|
84
|
-
"supertest": "^7.
|
|
85
|
-
"tsx": "^4.
|
|
86
|
-
"typescript": "^5.
|
|
87
|
-
"typescript-eslint": "^8.0
|
|
76
|
+
"@types/express": "^5.0.6",
|
|
77
|
+
"@types/node": "^25.0.1",
|
|
78
|
+
"@types/pg": "^8.16.0",
|
|
79
|
+
"@types/supertest": "^6.0.3",
|
|
80
|
+
"eslint": "^9.39.2",
|
|
81
|
+
"globals": "^16.5.0",
|
|
82
|
+
"pino-pretty": "^13.1.3",
|
|
83
|
+
"prettier": "^3.7.4",
|
|
84
|
+
"supertest": "^7.1.4",
|
|
85
|
+
"tsx": "^4.21.0",
|
|
86
|
+
"typescript": "^5.9.3",
|
|
87
|
+
"typescript-eslint": "^8.49.0",
|
|
88
88
|
"vitest": "^4.0.15"
|
|
89
89
|
},
|
|
90
90
|
"peerDependencies": {
|
|
91
91
|
"graphile-config": "^1.0.0-rc.2"
|
|
92
92
|
},
|
|
93
|
+
"optionalDependencies": {
|
|
94
|
+
"@moneypot/server": "file:../moneypot-server"
|
|
95
|
+
},
|
|
93
96
|
"license": "UNLICENSED"
|
|
94
97
|
}
|