@moneypot/hub 1.16.1 → 1.16.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,9 +1,9 @@
1
1
  import { context, lambda } from "postgraphile/grafast";
2
- import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
2
+ import { gql, extendSchema } from "postgraphile/utils";
3
3
  import { jsonParse } from "postgraphile/@dataplan/json";
4
4
  import { listenWithFilter } from "./listen-with-filter.js";
5
5
  import { logger } from "../logger.js";
6
- export const HubPutAlertPlugin = makeExtendSchemaPlugin(() => {
6
+ export const HubPutAlertPlugin = extendSchema((_build) => {
7
7
  return {
8
8
  typeDefs: gql `
9
9
  extend type Subscription {
@@ -6,7 +6,7 @@ export declare class ListenWithFilterStep<TTopics extends {
6
6
  }, TTopic extends keyof TTopics, TPayloadStep extends Step, TFilterInput> extends Step<TTopics[TTopic][]> {
7
7
  itemPlan: (itemPlan: __ItemStep<TTopics[TTopic]>) => TPayloadStep;
8
8
  filterFn: (item: unknown, filterInput: TFilterInput) => boolean;
9
- static $$export: {
9
+ static $export: {
10
10
  moduleName: string;
11
11
  exportName: string;
12
12
  };
@@ -14,9 +14,10 @@ export declare class ListenWithFilterStep<TTopics extends {
14
14
  private pubsubDep;
15
15
  private topicDep;
16
16
  private filterInputDep;
17
- constructor(pubsubOrPlan: Step<GrafastSubscriber<TTopics> | null> | GrafastSubscriber<TTopics> | null, topicOrPlan: Step<TTopic> | string, itemPlan: ((itemPlan: __ItemStep<TTopics[TTopic]>) => TPayloadStep) | undefined, filterInputPlan: Step<TFilterInput>, filterFn: (item: unknown, filterInput: TFilterInput) => boolean);
17
+ private initialEventDep;
18
+ constructor(pubsubOrPlan: Step<GrafastSubscriber<TTopics> | null> | GrafastSubscriber<TTopics> | null, topicOrPlan: Step<TTopic> | string, itemPlan: ((itemPlan: __ItemStep<TTopics[TTopic]>) => TPayloadStep) | undefined, filterInputPlan: Step<TFilterInput>, filterFn: (item: unknown, filterInput: TFilterInput) => boolean, $initialEvent?: Step<TTopics[TTopic]>);
18
19
  execute({ indexMap, values, stream, }: ExecutionDetails<readonly [GrafastSubscriber<TTopics>, TTopic]>): GrafastResultStreamList<TTopics[TTopic]>;
19
20
  }
20
21
  export declare function listenWithFilter<TTopics extends {
21
22
  [topic: string]: any;
22
- }, TTopic extends keyof TTopics, TPayloadStep extends Step, TFilterInput>(pubsubOrPlan: Step<GrafastSubscriber<TTopics> | null> | GrafastSubscriber<TTopics> | null, topicOrPlan: Step<TTopic> | string, itemPlan: ((itemPlan: __ItemStep<TTopics[TTopic]>) => TPayloadStep) | undefined, filterInputPlan: Step<TFilterInput>, filterFn: (item: unknown, filterInput: TFilterInput) => boolean): ListenWithFilterStep<TTopics, TTopic, TPayloadStep, TFilterInput>;
23
+ }, TTopic extends keyof TTopics, TPayloadStep extends Step, TFilterInput>(pubsubOrPlan: Step<GrafastSubscriber<TTopics> | null> | GrafastSubscriber<TTopics> | null, topicOrPlan: Step<TTopic> | string, itemPlan: ((itemPlan: __ItemStep<TTopics[TTopic]>) => TPayloadStep) | undefined, filterInputPlan: Step<TFilterInput>, filterFn: (item: unknown, filterInput: TFilterInput) => boolean, $initialEvent?: Step<TTopics[TTopic]>): ListenWithFilterStep<TTopics, TTopic, TPayloadStep, TFilterInput>;
@@ -1,9 +1,9 @@
1
- import { constant, isDev, isExecutableStep, SafeError, } from "postgraphile/grafast";
1
+ import { constant, isDev, isStep, SafeError, } from "postgraphile/grafast";
2
2
  import { Step } from "postgraphile/grafast";
3
3
  export class ListenWithFilterStep extends Step {
4
4
  itemPlan;
5
5
  filterFn;
6
- static $$export = {
6
+ static $export = {
7
7
  moduleName: "grafast",
8
8
  exportName: "ListenWithFilterStep",
9
9
  };
@@ -11,17 +11,21 @@ export class ListenWithFilterStep extends Step {
11
11
  pubsubDep;
12
12
  topicDep;
13
13
  filterInputDep;
14
- constructor(pubsubOrPlan, topicOrPlan, itemPlan = ($item) => $item, filterInputPlan, filterFn) {
14
+ initialEventDep = null;
15
+ constructor(pubsubOrPlan, topicOrPlan, itemPlan = ($item) => $item, filterInputPlan, filterFn, $initialEvent) {
15
16
  super();
16
17
  this.itemPlan = itemPlan;
17
18
  this.filterFn = filterFn;
18
19
  const $topic = typeof topicOrPlan === "string" ? constant(topicOrPlan) : topicOrPlan;
19
- const $pubsub = isExecutableStep(pubsubOrPlan)
20
+ const $pubsub = isStep(pubsubOrPlan)
20
21
  ? pubsubOrPlan
21
22
  : constant(pubsubOrPlan, false);
22
23
  this.pubsubDep = this.addDependency($pubsub);
23
24
  this.topicDep = this.addDependency($topic);
24
25
  this.filterInputDep = this.addDependency(filterInputPlan);
26
+ if ($initialEvent) {
27
+ this.initialEventDep = this.addDependency($initialEvent);
28
+ }
25
29
  }
26
30
  execute({ indexMap, values, stream, }) {
27
31
  if (!stream) {
@@ -30,20 +34,22 @@ export class ListenWithFilterStep extends Step {
30
34
  const pubsubValue = values[this.pubsubDep];
31
35
  const topicValue = values[this.topicDep];
32
36
  const filterInputValue = values[this.filterInputDep];
37
+ const initialEventValue = this.initialEventDep !== null ? values[this.initialEventDep] : null;
33
38
  return indexMap((i) => {
34
39
  const pubsub = pubsubValue.at(i);
35
40
  if (!pubsub) {
36
41
  throw new SafeError("Subscription not supported", isDev
37
42
  ? {
38
- hint: `${this.dependencies[this.pubsubDep]} did not provide a GrafastSubscriber; perhaps you forgot to add the relevant property to context?`,
43
+ hint: `Pubsub did not provide a GrafastSubscriber; perhaps you forgot to add the relevant property to context?`,
39
44
  }
40
45
  : {});
41
46
  }
42
47
  const topic = topicValue.at(i);
43
48
  const filterInput = filterInputValue.at(i);
44
49
  const origStream = pubsub.subscribe(topic);
50
+ const initialEvent = initialEventValue?.at(i);
45
51
  const filterFn = this.filterFn;
46
- return {
52
+ const filteredStream = {
47
53
  [Symbol.asyncIterator]: async function* () {
48
54
  const iterator = await origStream;
49
55
  for await (const item of iterator) {
@@ -53,9 +59,60 @@ export class ListenWithFilterStep extends Step {
53
59
  }
54
60
  },
55
61
  };
62
+ if (initialEvent === undefined) {
63
+ return filteredStream;
64
+ }
65
+ else {
66
+ if (filterFn(initialEvent, filterInput)) {
67
+ return withInitialValue(initialEvent, filteredStream);
68
+ }
69
+ else {
70
+ return filteredStream;
71
+ }
72
+ }
56
73
  });
57
74
  }
58
75
  }
59
- export function listenWithFilter(pubsubOrPlan, topicOrPlan, itemPlan = ($item) => $item, filterInputPlan, filterFn) {
60
- return new ListenWithFilterStep(pubsubOrPlan, topicOrPlan, itemPlan, filterInputPlan, filterFn);
76
+ export function listenWithFilter(pubsubOrPlan, topicOrPlan, itemPlan = ($item) => $item, filterInputPlan, filterFn, $initialEvent) {
77
+ return new ListenWithFilterStep(pubsubOrPlan, topicOrPlan, itemPlan, filterInputPlan, filterFn, $initialEvent);
61
78
  }
79
+ const DONE = Object.freeze({ value: undefined, done: true });
80
+ const withInitialValue = (initialVal, source) => ({
81
+ [Symbol.asyncIterator]() {
82
+ const sourceIterator = source[Symbol.asyncIterator]();
83
+ let first = true;
84
+ let done = null;
85
+ return {
86
+ async next() {
87
+ if (done)
88
+ return done;
89
+ if (first) {
90
+ first = false;
91
+ return { value: initialVal, done: false };
92
+ }
93
+ const res = await sourceIterator.next();
94
+ if (res.done)
95
+ done = res;
96
+ return res;
97
+ },
98
+ async return(value) {
99
+ done ??= { value: value, done: true };
100
+ if (typeof sourceIterator.return === "function") {
101
+ try {
102
+ await sourceIterator.return();
103
+ }
104
+ catch {
105
+ }
106
+ }
107
+ return done;
108
+ },
109
+ async throw(err) {
110
+ done ??= DONE;
111
+ if (typeof sourceIterator.throw === "function") {
112
+ return sourceIterator.throw(err);
113
+ }
114
+ throw err;
115
+ },
116
+ };
117
+ },
118
+ });
@@ -1,15 +1,16 @@
1
1
  import { sideEffect } from "postgraphile/grafast";
2
2
  import { GraphQLError } from "postgraphile/graphql";
3
- import { makeWrapPlansPlugin } from "postgraphile/utils";
4
- import * as v from "../validate.js";
5
- import * as Yup from "yup";
3
+ import { wrapPlans } from "postgraphile/utils";
4
+ import * as v from "../validate-zod.js";
5
+ import z, { prettifyError } from "zod/v4";
6
6
  function validate(value, schema) {
7
7
  try {
8
- schema.validateSync(value, { strict: true, abortEarly: true });
8
+ schema.parse(value);
9
9
  }
10
10
  catch (e) {
11
- if (e instanceof Yup.ValidationError) {
12
- const errorMessage = e.path ? `${e.path}: ${e.errors[0]}` : e.errors[0];
11
+ if (e instanceof z.ZodError) {
12
+ const fieldPath = e.issues.map((issue) => issue.path.join(".")).join(".");
13
+ const errorMessage = `Field "${fieldPath}": ${prettifyError(e)}`;
13
14
  throw new GraphQLError(errorMessage);
14
15
  }
15
16
  else {
@@ -17,45 +18,36 @@ function validate(value, schema) {
17
18
  }
18
19
  }
19
20
  }
20
- const casinoPatchSchema = Yup.object()
21
- .shape({
22
- name: Yup.string().trim().min(1),
23
- graphqlUrl: v.graphqlUrl(),
21
+ const CasinoPatchSchema = z.strictObject({
22
+ name: z.string().trim().min(1).optional(),
23
+ graphqlUrl: v.graphqlUrl().optional(),
24
+ baseUrl: v.baseUrl().optional(),
25
+ });
26
+ const AddCasinoSchema = z.strictObject({
27
+ name: z.string().trim().min(1),
24
28
  baseUrl: v.baseUrl(),
25
- })
26
- .strict()
27
- .noUnknown();
28
- const addCasinoSchema = Yup.object()
29
- .shape({
30
- name: Yup.string().trim().min(1).required(),
31
- baseUrl: v.baseUrl().required(),
32
- graphqlUrl: v.graphqlUrl().required(),
33
- apiKey: v.uuid().required(),
34
- })
35
- .strict()
36
- .noUnknown();
37
- const updateBankrollSchema = Yup.object()
38
- .shape({
39
- amount: Yup.number().integer().min(0).required(),
40
- })
41
- .strict()
42
- .noUnknown();
43
- export const ValidateCasinoFieldsPlugin = makeWrapPlansPlugin({
29
+ graphqlUrl: v.graphqlUrl(),
30
+ apiKey: z.uuid(),
31
+ });
32
+ const UpdateBankrollSchema = z.strictObject({
33
+ amount: z.number().int().min(0),
34
+ });
35
+ export const ValidateCasinoFieldsPlugin = wrapPlans(() => ({
44
36
  Mutation: {
45
37
  updateHubCasinoById: (plan, $source, fieldArgs) => {
46
38
  const $patch = fieldArgs.getRaw(["input", "hubCasinoPatch"]);
47
- sideEffect($patch, (patch) => validate(patch, casinoPatchSchema));
39
+ sideEffect($patch, (patch) => validate(patch, CasinoPatchSchema));
48
40
  return plan();
49
41
  },
50
42
  hubAddCasino: (plan, $source, fieldArgs) => {
51
43
  const $input = fieldArgs.getRaw(["input"]);
52
- sideEffect($input, (input) => validate(input, addCasinoSchema));
44
+ sideEffect($input, (input) => validate(input, AddCasinoSchema));
53
45
  return plan();
54
46
  },
55
47
  updateHubBankrollById: (plan, $source, fieldArgs) => {
56
48
  const $patch = fieldArgs.getRaw(["input", "hubBankrollPatch"]);
57
- sideEffect($patch, (patch) => validate(patch, updateBankrollSchema));
49
+ sideEffect($patch, (patch) => validate(patch, UpdateBankrollSchema));
58
50
  return plan();
59
51
  },
60
52
  },
61
- });
53
+ }));
@@ -6,7 +6,7 @@ import { logger } from "../logger.js";
6
6
  import * as config from "../config.js";
7
7
  import * as db from "../db/index.js";
8
8
  import * as pg from "pg";
9
- import { z } from "zod";
9
+ import { z } from "zod/v4";
10
10
  const activeCasinos = new Set();
11
11
  export async function startCasinoTransferProcessor({ casinoId, signal, pool, }) {
12
12
  if (activeCasinos.has(casinoId)) {
@@ -1,6 +1,6 @@
1
1
  import { formatCurrency } from "./format-currency.js";
2
2
  import { logger } from "./logger.js";
3
- import { z } from "zod";
3
+ import { z } from "zod/v4";
4
4
  const RiskLimitsSchema = z
5
5
  .object({
6
6
  maxWager: z.number().positive().optional(),
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from "zod/v4";
2
2
  import * as jose from "jose";
3
3
  import { gql } from "../__generated__/gql.js";
4
4
  import { maybeOneRow } from "../db/util.js";
@@ -0,0 +1,3 @@
1
+ import z from "zod/v4";
2
+ export declare const baseUrl: () => z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<URL, string>>, z.ZodTransform<string, URL>>;
3
+ export declare const graphqlUrl: () => z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<URL, string>>, z.ZodTransform<string, URL>>;
@@ -0,0 +1,19 @@
1
+ import z from "zod/v4";
2
+ export const baseUrl = () => z
3
+ .string()
4
+ .trim()
5
+ .min(1, "Base URL is required")
6
+ .refine((val) => URL.canParse(val), "Base URL is not a valid URL")
7
+ .transform((val) => new URL(val))
8
+ .refine((val) => val.protocol === "http:" || val.protocol === "https:", "Base URL must use http or https protocol")
9
+ .refine((val) => val.pathname === "/" && val.search === "" && val.hash === "", "Base URL must have no path, query, nor hash")
10
+ .transform((val) => val.toString().replace(/\/$/, ""));
11
+ export const graphqlUrl = () => z
12
+ .string()
13
+ .trim()
14
+ .min(1, "GraphQL URL is required")
15
+ .refine((val) => URL.canParse(val), "GraphQL URL is not a valid URL")
16
+ .transform((val) => new URL(val))
17
+ .refine((val) => val.protocol === "http:" || val.protocol === "https:", "GraphQL URL must use http or https protocol")
18
+ .refine((val) => val.pathname === "/graphql", "GraphQL URL must have /graphql path")
19
+ .transform((val) => val.toString().replace(/\/$/, ""));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneypot/hub",
3
- "version": "1.16.1",
3
+ "version": "1.16.3",
4
4
  "author": "moneypot.com",
5
5
  "homepage": "https://moneypot.com/hub",
6
6
  "keywords": [
@@ -59,10 +59,9 @@
59
59
  "pg": "^8.12.0",
60
60
  "pg-connection-string": "^2.6.4",
61
61
  "pino": "^9.7.0",
62
- "postgraphile": "^5.0.0-beta.48",
62
+ "postgraphile": "^5.0.0-beta.49",
63
63
  "tsafe": "^1.6.6",
64
- "yup": "^1.6.1",
65
- "zod": "^3.23.5"
64
+ "zod": "^4.1.12"
66
65
  },
67
66
  "devDependencies": {
68
67
  "@eslint/js": "^9.8.0",