@openora/core 0.4.1-canary.152 → 0.4.1-canary.154

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.
Files changed (163) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/admin-console/__tests__/backoffice.service.int.test.js +20 -3
  3. package/dist/admin-console/__tests__/backoffice.service.int.test.js.map +1 -1
  4. package/dist/audit/__tests__/map-event.test.js +46 -0
  5. package/dist/audit/__tests__/map-event.test.js.map +1 -1
  6. package/dist/audit/plugin.d.ts.map +1 -1
  7. package/dist/audit/plugin.js +74 -1
  8. package/dist/audit/plugin.js.map +1 -1
  9. package/dist/audit/service/audit.service.d.ts.map +1 -1
  10. package/dist/audit/service/audit.service.js +1 -6
  11. package/dist/audit/service/audit.service.js.map +1 -1
  12. package/dist/casino/gaming/__tests__/admin-reporting.int.test.js +20 -3
  13. package/dist/casino/gaming/__tests__/admin-reporting.int.test.js.map +1 -1
  14. package/dist/casino/gaming/__tests__/game-category.service.int.test.d.ts +2 -0
  15. package/dist/casino/gaming/__tests__/game-category.service.int.test.d.ts.map +1 -0
  16. package/dist/casino/gaming/__tests__/game-category.service.int.test.js +134 -0
  17. package/dist/casino/gaming/__tests__/game-category.service.int.test.js.map +1 -0
  18. package/dist/casino/gaming/__tests__/game-provider.service.int.test.d.ts +2 -0
  19. package/dist/casino/gaming/__tests__/game-provider.service.int.test.d.ts.map +1 -0
  20. package/dist/casino/gaming/__tests__/game-provider.service.int.test.js +274 -0
  21. package/dist/casino/gaming/__tests__/game-provider.service.int.test.js.map +1 -0
  22. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.d.ts +2 -0
  23. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.d.ts.map +1 -0
  24. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.js +174 -0
  25. package/dist/casino/gaming/__tests__/gaming-catalog-admin.router.int.test.js.map +1 -0
  26. package/dist/casino/gaming/__tests__/gaming-migration.int.test.d.ts +2 -0
  27. package/dist/casino/gaming/__tests__/gaming-migration.int.test.d.ts.map +1 -0
  28. package/dist/casino/gaming/__tests__/gaming-migration.int.test.js +171 -0
  29. package/dist/casino/gaming/__tests__/gaming-migration.int.test.js.map +1 -0
  30. package/dist/casino/gaming/__tests__/gaming.service.int.test.js +274 -13
  31. package/dist/casino/gaming/__tests__/gaming.service.int.test.js.map +1 -1
  32. package/dist/casino/gaming/contract/index.d.ts +520 -18
  33. package/dist/casino/gaming/contract/index.d.ts.map +1 -1
  34. package/dist/casino/gaming/contract/index.js +167 -4
  35. package/dist/casino/gaming/contract/index.js.map +1 -1
  36. package/dist/casino/gaming/drizzle/migrations/0005_natural_thunderbolt_ross.sql +223 -0
  37. package/dist/casino/gaming/drizzle/migrations/meta/0005_snapshot.json +680 -0
  38. package/dist/casino/gaming/drizzle/migrations/meta/_journal.json +7 -0
  39. package/dist/casino/gaming/plugin.d.ts.map +1 -1
  40. package/dist/casino/gaming/plugin.js +9 -2
  41. package/dist/casino/gaming/plugin.js.map +1 -1
  42. package/dist/casino/gaming/router/index.d.ts +372 -18
  43. package/dist/casino/gaming/router/index.d.ts.map +1 -1
  44. package/dist/casino/gaming/router/index.js +62 -6
  45. package/dist/casino/gaming/router/index.js.map +1 -1
  46. package/dist/casino/gaming/schema/index.d.ts +499 -4
  47. package/dist/casino/gaming/schema/index.d.ts.map +1 -1
  48. package/dist/casino/gaming/schema/index.js +72 -6
  49. package/dist/casino/gaming/schema/index.js.map +1 -1
  50. package/dist/casino/gaming/service/game-category.service.d.ts +109 -0
  51. package/dist/casino/gaming/service/game-category.service.d.ts.map +1 -0
  52. package/dist/casino/gaming/service/game-category.service.js +176 -0
  53. package/dist/casino/gaming/service/game-category.service.js.map +1 -0
  54. package/dist/casino/gaming/service/game-provider.service.d.ts +118 -0
  55. package/dist/casino/gaming/service/game-provider.service.d.ts.map +1 -0
  56. package/dist/casino/gaming/service/game-provider.service.js +251 -0
  57. package/dist/casino/gaming/service/game-provider.service.js.map +1 -0
  58. package/dist/casino/gaming/service/gaming.service.d.ts +122 -14
  59. package/dist/casino/gaming/service/gaming.service.d.ts.map +1 -1
  60. package/dist/casino/gaming/service/gaming.service.js +189 -23
  61. package/dist/casino/gaming/service/gaming.service.js.map +1 -1
  62. package/dist/casino/lobby/__tests__/lobby.cache.int.test.js +137 -4
  63. package/dist/casino/lobby/__tests__/lobby.cache.int.test.js.map +1 -1
  64. package/dist/casino/lobby/contract/index.d.ts +68 -8
  65. package/dist/casino/lobby/contract/index.d.ts.map +1 -1
  66. package/dist/casino/lobby/contract/index.js +4 -3
  67. package/dist/casino/lobby/contract/index.js.map +1 -1
  68. package/dist/casino/lobby/router/index.d.ts +34 -4
  69. package/dist/casino/lobby/router/index.d.ts.map +1 -1
  70. package/dist/casino/lobby/service/lobby.service.d.ts +34 -4
  71. package/dist/casino/lobby/service/lobby.service.d.ts.map +1 -1
  72. package/dist/casino/lobby/service/lobby.service.js +60 -26
  73. package/dist/casino/lobby/service/lobby.service.js.map +1 -1
  74. package/dist/casino/shared/game-catalog.d.ts +32 -0
  75. package/dist/casino/shared/game-catalog.d.ts.map +1 -0
  76. package/dist/casino/shared/game-catalog.js +67 -0
  77. package/dist/casino/shared/game-catalog.js.map +1 -0
  78. package/dist/compliance/__tests__/compliance.service.int.test.js +248 -25
  79. package/dist/compliance/__tests__/compliance.service.int.test.js.map +1 -1
  80. package/dist/compliance/__tests__/regulatory-overview.router.int.test.d.ts +2 -0
  81. package/dist/compliance/__tests__/regulatory-overview.router.int.test.d.ts.map +1 -0
  82. package/dist/compliance/__tests__/regulatory-overview.router.int.test.js +102 -0
  83. package/dist/compliance/__tests__/regulatory-overview.router.int.test.js.map +1 -0
  84. package/dist/compliance/__tests__/rg-monitoring.service.int.test.js +17 -3
  85. package/dist/compliance/__tests__/rg-monitoring.service.int.test.js.map +1 -1
  86. package/dist/compliance/__tests__/rg-self-service.int.test.js +2 -2
  87. package/dist/compliance/__tests__/rg-self-service.int.test.js.map +1 -1
  88. package/dist/compliance/contract/index.d.ts +77 -2
  89. package/dist/compliance/contract/index.d.ts.map +1 -1
  90. package/dist/compliance/contract/index.js +50 -1
  91. package/dist/compliance/contract/index.js.map +1 -1
  92. package/dist/compliance/drizzle/migrations/0008_omniscient_northstar.sql +14 -0
  93. package/dist/compliance/drizzle/migrations/meta/0008_snapshot.json +870 -0
  94. package/dist/compliance/drizzle/migrations/meta/_journal.json +7 -0
  95. package/dist/compliance/plugin.d.ts.map +1 -1
  96. package/dist/compliance/plugin.js +2 -2
  97. package/dist/compliance/plugin.js.map +1 -1
  98. package/dist/compliance/router/index.d.ts +43 -1
  99. package/dist/compliance/router/index.d.ts.map +1 -1
  100. package/dist/compliance/router/index.js +32 -4
  101. package/dist/compliance/router/index.js.map +1 -1
  102. package/dist/compliance/schema/index.d.ts +181 -2
  103. package/dist/compliance/schema/index.d.ts.map +1 -1
  104. package/dist/compliance/schema/index.js +15 -2
  105. package/dist/compliance/schema/index.js.map +1 -1
  106. package/dist/compliance/server.d.ts +1 -1
  107. package/dist/compliance/server.d.ts.map +1 -1
  108. package/dist/compliance/server.js +1 -1
  109. package/dist/compliance/server.js.map +1 -1
  110. package/dist/compliance/service/compliance.service.d.ts +61 -14
  111. package/dist/compliance/service/compliance.service.d.ts.map +1 -1
  112. package/dist/compliance/service/compliance.service.js +226 -30
  113. package/dist/compliance/service/compliance.service.js.map +1 -1
  114. package/dist/contracts/__tests__/kit.test.js +21 -1
  115. package/dist/contracts/__tests__/kit.test.js.map +1 -1
  116. package/dist/contracts/adapters/audit.d.ts +1 -1
  117. package/dist/contracts/adapters/audit.d.ts.map +1 -1
  118. package/dist/contracts/adapters/audit.js.map +1 -1
  119. package/dist/contracts/kit.d.ts +3 -0
  120. package/dist/contracts/kit.d.ts.map +1 -1
  121. package/dist/contracts/kit.js +8 -0
  122. package/dist/contracts/kit.js.map +1 -1
  123. package/dist/contracts/schemas/__tests__/events.test.js +7 -0
  124. package/dist/contracts/schemas/__tests__/events.test.js.map +1 -1
  125. package/dist/contracts/schemas/__tests__/game-schemas.test.d.ts +2 -0
  126. package/dist/contracts/schemas/__tests__/game-schemas.test.d.ts.map +1 -0
  127. package/dist/contracts/schemas/__tests__/game-schemas.test.js +43 -0
  128. package/dist/contracts/schemas/__tests__/game-schemas.test.js.map +1 -0
  129. package/dist/contracts/schemas/events.d.ts +109 -0
  130. package/dist/contracts/schemas/events.d.ts.map +1 -1
  131. package/dist/contracts/schemas/events.js +87 -3
  132. package/dist/contracts/schemas/events.js.map +1 -1
  133. package/dist/contracts/schemas/game.d.ts +39 -0
  134. package/dist/contracts/schemas/game.d.ts.map +1 -1
  135. package/dist/contracts/schemas/game.js +32 -0
  136. package/dist/contracts/schemas/game.js.map +1 -1
  137. package/dist/contracts/schemas/iam.d.ts +2 -1
  138. package/dist/contracts/schemas/iam.d.ts.map +1 -1
  139. package/dist/contracts/schemas/iam.js +2 -1
  140. package/dist/contracts/schemas/iam.js.map +1 -1
  141. package/dist/iam/seed/data/default-admin-roles.d.ts.map +1 -1
  142. package/dist/iam/seed/data/default-admin-roles.js +1 -0
  143. package/dist/iam/seed/data/default-admin-roles.js.map +1 -1
  144. package/dist/iam/service/iam.service.d.ts +1 -1
  145. package/dist/react/client.d.ts +2 -1
  146. package/dist/react/client.d.ts.map +1 -1
  147. package/dist/react/client.js +2 -1
  148. package/dist/react/client.js.map +1 -1
  149. package/dist/server/auth/permissions.d.ts +24 -12
  150. package/dist/server/auth/permissions.d.ts.map +1 -1
  151. package/dist/server/auth/permissions.js +2 -1
  152. package/dist/server/auth/permissions.js.map +1 -1
  153. package/dist/server/db/__tests__/query-helpers.test.js +47 -1
  154. package/dist/server/db/__tests__/query-helpers.test.js.map +1 -1
  155. package/dist/server/db/index.d.ts +1 -1
  156. package/dist/server/db/index.d.ts.map +1 -1
  157. package/dist/server/db/index.js +1 -1
  158. package/dist/server/db/index.js.map +1 -1
  159. package/dist/server/db/query-helpers.d.ts +5 -0
  160. package/dist/server/db/query-helpers.d.ts.map +1 -1
  161. package/dist/server/db/query-helpers.js +37 -0
  162. package/dist/server/db/query-helpers.js.map +1 -1
  163. package/package.json +1 -1
@@ -0,0 +1,109 @@
1
+ import { type EventBus, DrizzleService } from '@openora/core/server';
2
+ import type { ClientMeta, User } from '@openora/core/contracts';
3
+ import type { CreateCategoryInput, UpdateCategoryInput } from '../contract/index.js';
4
+ export declare const GameCategoryNotFoundError: new (id: string) => Error & {
5
+ data?: Record<string, string>;
6
+ };
7
+ export declare const GameCategorySlugTakenError: new () => Error & {
8
+ data?: Record<string, string>;
9
+ };
10
+ type Actor = {
11
+ actorId: User['id'];
12
+ } & ClientMeta;
13
+ export declare class GameCategoryService {
14
+ private readonly drizzle;
15
+ private readonly events;
16
+ constructor(drizzle: DrizzleService, events: EventBus);
17
+ listActiveCategories({ page, limit }: {
18
+ page: number;
19
+ limit: number;
20
+ }): Promise<{
21
+ items: {
22
+ translations: Record<string, {
23
+ name: string;
24
+ }>;
25
+ id: string;
26
+ slug: string;
27
+ name: string;
28
+ icon: string | null;
29
+ sortOrder: number;
30
+ }[];
31
+ total: number;
32
+ page: number;
33
+ limit: number;
34
+ }>;
35
+ listCategoriesAdmin({ page, limit, q, isActive, }: {
36
+ page: number;
37
+ limit: number;
38
+ q?: string;
39
+ isActive?: boolean;
40
+ }): Promise<{
41
+ items: {
42
+ isActive: boolean;
43
+ createdAt: string;
44
+ updatedAt: string;
45
+ id: string;
46
+ slug: string;
47
+ name: string;
48
+ translations: Record<string, {
49
+ name: string;
50
+ }>;
51
+ icon: string | null;
52
+ sortOrder: number;
53
+ }[];
54
+ total: number;
55
+ page: number;
56
+ limit: number;
57
+ }>;
58
+ getCategory(id: string): Promise<{
59
+ isActive: boolean;
60
+ createdAt: string;
61
+ updatedAt: string;
62
+ id: string;
63
+ slug: string;
64
+ name: string;
65
+ translations: Record<string, {
66
+ name: string;
67
+ }>;
68
+ icon: string | null;
69
+ sortOrder: number;
70
+ }>;
71
+ getActiveCategoryBySlug(slug: string): Promise<{
72
+ id: string;
73
+ slug: string;
74
+ name: string;
75
+ translations: Record<string, {
76
+ name: string;
77
+ }>;
78
+ icon: string | null;
79
+ sortOrder: number;
80
+ }>;
81
+ createCategory({ slug, name, translations, icon, sortOrder, actorId, ip, userAgent, }: CreateCategoryInput & Actor): Promise<{
82
+ isActive: boolean;
83
+ createdAt: string;
84
+ updatedAt: string;
85
+ id: string;
86
+ slug: string;
87
+ name: string;
88
+ translations: Record<string, {
89
+ name: string;
90
+ }>;
91
+ icon: string | null;
92
+ sortOrder: number;
93
+ }>;
94
+ updateCategory({ id, actorId, ip, userAgent, ...patchInput }: UpdateCategoryInput & Actor): Promise<{
95
+ isActive: boolean;
96
+ createdAt: string;
97
+ updatedAt: string;
98
+ id: string;
99
+ slug: string;
100
+ name: string;
101
+ translations: Record<string, {
102
+ name: string;
103
+ }>;
104
+ icon: string | null;
105
+ sortOrder: number;
106
+ }>;
107
+ }
108
+ export {};
109
+ //# sourceMappingURL=game-category.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-category.service.d.ts","sourceRoot":"","sources":["../../../../src/casino/gaming/service/game-category.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAGb,cAAc,EAMf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGrF,eAAO,MAAM,yBAAyB;QAPtC,CAAA;CAO0E,CAAC;AAC3E,eAAO,MAAM,0BAA0B;QARvC,CAAA;CAWC,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;CACrB,GAAG,UAAU,CAAC;AAuBf,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,QAAQ;IAG7B,oBAAoB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;;;IA2BrE,mBAAmB,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,CAAC,EACD,QAAQ,GACT,EAAE;QACD,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,CAAC,CAAC,EAAE,MAAM,CAAC;QACX,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;;;;;;;;;;;;;;;;;;IAoBK,WAAW,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;IAQtB,uBAAuB,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;IAYpC,cAAc,CAAC,EACnB,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,OAAO,EACP,EAAE,EACF,SAAS,GACV,EAAE,mBAAmB,GAAG,KAAK;;;;;;;;;;;;;IAwCxB,cAAc,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,EAAE,mBAAmB,GAAG,KAAK;;;;;;;;;;;;;CAsDhG"}
@@ -0,0 +1,176 @@
1
+ import { makeNotFoundError, makeConflictError, findOneOrThrow, isUniqueConstraintViolation, likeContains, serializeRow, pageToOffset, } from '@openora/core/server';
2
+ import { eq, and, asc, count, ilike, ne, or } from 'drizzle-orm';
3
+ import { gameCategory } from '../schema/index.js';
4
+ import { toCategorySummary } from '../../shared/game-catalog.js';
5
+ export const GameCategoryNotFoundError = makeNotFoundError('GameCategory');
6
+ export const GameCategorySlugTakenError = makeConflictError('GameCategorySlugTakenError', 'A category with this slug already exists');
7
+ function categorySnapshot(record) {
8
+ return {
9
+ slug: record.slug,
10
+ name: record.name,
11
+ translations: record.translations ?? {},
12
+ icon: record.icon,
13
+ sortOrder: record.sortOrder,
14
+ isActive: record.isActive,
15
+ };
16
+ }
17
+ function toCategoryDetail(record) {
18
+ const dates = serializeRow(record, { dateFields: ['createdAt', 'updatedAt'] });
19
+ return {
20
+ ...toCategorySummary(record),
21
+ isActive: record.isActive,
22
+ createdAt: dates.createdAt,
23
+ updatedAt: dates.updatedAt,
24
+ };
25
+ }
26
+ export class GameCategoryService {
27
+ drizzle;
28
+ events;
29
+ constructor(drizzle, events) {
30
+ this.drizzle = drizzle;
31
+ this.events = events;
32
+ }
33
+ async listActiveCategories({ page, limit }) {
34
+ const where = eq(gameCategory.isActive, true);
35
+ const [rows, [{ n }]] = await Promise.all([
36
+ this.drizzle.db
37
+ .select({
38
+ id: gameCategory.id,
39
+ slug: gameCategory.slug,
40
+ name: gameCategory.name,
41
+ translations: gameCategory.translations,
42
+ icon: gameCategory.icon,
43
+ sortOrder: gameCategory.sortOrder,
44
+ })
45
+ .from(gameCategory)
46
+ .where(where)
47
+ .orderBy(asc(gameCategory.sortOrder), asc(gameCategory.name), asc(gameCategory.slug))
48
+ .limit(limit)
49
+ .offset(pageToOffset(page, limit)),
50
+ this.drizzle.db.select({ n: count() }).from(gameCategory).where(where),
51
+ ]);
52
+ return {
53
+ items: rows.map((row) => ({ ...row, translations: row.translations ?? {} })),
54
+ total: Number(n),
55
+ page,
56
+ limit,
57
+ };
58
+ }
59
+ async listCategoriesAdmin({ page, limit, q, isActive, }) {
60
+ const where = and(q
61
+ ? or(ilike(gameCategory.name, likeContains(q)), ilike(gameCategory.slug, likeContains(q)))
62
+ : undefined, isActive === undefined ? undefined : eq(gameCategory.isActive, isActive));
63
+ const [rows, [{ n }]] = await Promise.all([
64
+ this.drizzle.db
65
+ .select()
66
+ .from(gameCategory)
67
+ .where(where)
68
+ .orderBy(asc(gameCategory.sortOrder), asc(gameCategory.name))
69
+ .limit(limit)
70
+ .offset(pageToOffset(page, limit)),
71
+ this.drizzle.db.select({ n: count() }).from(gameCategory).where(where),
72
+ ]);
73
+ return { items: rows.map(toCategoryDetail), total: Number(n), page, limit };
74
+ }
75
+ async getCategory(id) {
76
+ const record = findOneOrThrow(await this.drizzle.db.select().from(gameCategory).where(eq(gameCategory.id, id)).limit(1), new GameCategoryNotFoundError(id));
77
+ return toCategoryDetail(record);
78
+ }
79
+ async getActiveCategoryBySlug(slug) {
80
+ const record = findOneOrThrow(await this.drizzle.db
81
+ .select()
82
+ .from(gameCategory)
83
+ .where(and(eq(gameCategory.slug, slug), eq(gameCategory.isActive, true)))
84
+ .limit(1), new GameCategoryNotFoundError(slug));
85
+ return toCategorySummary(record);
86
+ }
87
+ async createCategory({ slug, name, translations, icon, sortOrder, actorId, ip, userAgent, }) {
88
+ let record;
89
+ try {
90
+ record = await this.drizzle.db.transaction(async (tx) => {
91
+ const [existing] = await tx
92
+ .select({ id: gameCategory.id })
93
+ .from(gameCategory)
94
+ .where(eq(gameCategory.slug, slug))
95
+ .limit(1);
96
+ if (existing) {
97
+ throw new GameCategorySlugTakenError();
98
+ }
99
+ const [created] = await tx
100
+ .insert(gameCategory)
101
+ .values({
102
+ slug,
103
+ name,
104
+ translations: translations ?? {},
105
+ icon: icon ?? null,
106
+ sortOrder: sortOrder ?? 0,
107
+ })
108
+ .returning();
109
+ return created;
110
+ });
111
+ }
112
+ catch (error) {
113
+ if (isUniqueConstraintViolation(error)) {
114
+ throw new GameCategorySlugTakenError();
115
+ }
116
+ throw error;
117
+ }
118
+ this.events.emit('gaming.category.created', {
119
+ categoryId: record.id,
120
+ ...categorySnapshot(record),
121
+ actorId,
122
+ ip: ip ?? null,
123
+ userAgent: userAgent ?? null,
124
+ });
125
+ return toCategoryDetail(record);
126
+ }
127
+ async updateCategory({ id, actorId, ip, userAgent, ...patchInput }) {
128
+ const patch = { ...patchInput };
129
+ const hasChanges = Object.values(patch).some((value) => value !== undefined);
130
+ const outcome = await this.drizzle.db
131
+ .transaction(async (tx) => {
132
+ // The row lock serializes concurrent PATCHes, so the audited `before` is the
133
+ // state this write replaced, never a snapshot another request already changed.
134
+ const existing = findOneOrThrow(await tx
135
+ .select()
136
+ .from(gameCategory)
137
+ .where(eq(gameCategory.id, id))
138
+ .limit(1)
139
+ .for('update'), new GameCategoryNotFoundError(id));
140
+ if (patchInput.slug !== undefined && patchInput.slug !== existing.slug) {
141
+ const [clash] = await tx
142
+ .select({ id: gameCategory.id })
143
+ .from(gameCategory)
144
+ .where(and(eq(gameCategory.slug, patchInput.slug), ne(gameCategory.id, id)))
145
+ .limit(1);
146
+ if (clash) {
147
+ throw new GameCategorySlugTakenError();
148
+ }
149
+ }
150
+ if (!hasChanges) {
151
+ return { changed: false, before: existing, after: existing };
152
+ }
153
+ const updated = findOneOrThrow(await tx.update(gameCategory).set(patch).where(eq(gameCategory.id, id)).returning(), new GameCategoryNotFoundError(id));
154
+ return { changed: true, before: existing, after: updated };
155
+ })
156
+ .catch((error) => {
157
+ if (isUniqueConstraintViolation(error)) {
158
+ throw new GameCategorySlugTakenError();
159
+ }
160
+ throw error;
161
+ });
162
+ if (!outcome.changed) {
163
+ return toCategoryDetail(outcome.after);
164
+ }
165
+ this.events.emit('gaming.category.updated', {
166
+ categoryId: id,
167
+ actorId,
168
+ before: categorySnapshot(outcome.before),
169
+ after: categorySnapshot(outcome.after),
170
+ ip: ip ?? null,
171
+ userAgent: userAgent ?? null,
172
+ });
173
+ return toCategoryDetail(outcome.after);
174
+ }
175
+ }
176
+ //# sourceMappingURL=game-category.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-category.service.js","sourceRoot":"","sources":["../../../../src/casino/gaming/service/game-category.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import { type EventBus, DrizzleService } from '@openora/core/server';
2
+ import type { ClientMeta, User } from '@openora/core/contracts';
3
+ import { gameProvider } from '../schema/index.js';
4
+ import type { CreateProviderInput, UpdateProviderInput } from '../contract/index.js';
5
+ export declare const GameProviderNotFoundError: new (id: string) => Error & {
6
+ data?: Record<string, string>;
7
+ };
8
+ export declare const GameProviderSlugTakenError: new () => Error & {
9
+ data?: Record<string, string>;
10
+ };
11
+ export declare const GameProviderVendorIdTakenError: new () => Error & {
12
+ data?: Record<string, string>;
13
+ };
14
+ export declare const GameProviderMappingInUseError: new (providerId: string, aggregator: string) => Error & {
15
+ data?: Record<string, string>;
16
+ };
17
+ type Actor = {
18
+ actorId: User['id'];
19
+ } & ClientMeta;
20
+ export declare function toProviderSummary(record: typeof gameProvider.$inferSelect): {
21
+ id: string;
22
+ slug: string;
23
+ name: string;
24
+ logoUrl: string | null;
25
+ };
26
+ export declare class GameProviderService {
27
+ private readonly drizzle;
28
+ private readonly events;
29
+ constructor(drizzle: DrizzleService, events: EventBus);
30
+ listActiveProviders({ page, limit }: {
31
+ page: number;
32
+ limit: number;
33
+ }): Promise<{
34
+ items: {
35
+ id: string;
36
+ slug: string;
37
+ name: string;
38
+ logoUrl: string | null;
39
+ }[];
40
+ total: number;
41
+ page: number;
42
+ limit: number;
43
+ }>;
44
+ listProvidersAdmin({ page, limit, q, isActive, }: {
45
+ page: number;
46
+ limit: number;
47
+ q?: string;
48
+ isActive?: boolean;
49
+ }): Promise<{
50
+ items: {
51
+ aggregatorMappings: {
52
+ aggregator: string;
53
+ vendorId: string;
54
+ }[];
55
+ metadata: {} | null;
56
+ isActive: boolean;
57
+ createdAt: string;
58
+ updatedAt: string;
59
+ id: string;
60
+ slug: string;
61
+ name: string;
62
+ logoUrl: string | null;
63
+ }[];
64
+ total: number;
65
+ page: number;
66
+ limit: number;
67
+ }>;
68
+ getProvider(id: string): Promise<{
69
+ aggregatorMappings: {
70
+ aggregator: string;
71
+ vendorId: string;
72
+ }[];
73
+ metadata: {} | null;
74
+ isActive: boolean;
75
+ createdAt: string;
76
+ updatedAt: string;
77
+ id: string;
78
+ slug: string;
79
+ name: string;
80
+ logoUrl: string | null;
81
+ }>;
82
+ getActiveProviderBySlug(slug: string): Promise<{
83
+ id: string;
84
+ slug: string;
85
+ name: string;
86
+ logoUrl: string | null;
87
+ }>;
88
+ createProvider({ slug, name, aggregatorMappings, logoUrl, metadata, actorId, ip, userAgent, }: CreateProviderInput & Actor): Promise<{
89
+ aggregatorMappings: {
90
+ aggregator: string;
91
+ vendorId: string;
92
+ }[];
93
+ metadata: {} | null;
94
+ isActive: boolean;
95
+ createdAt: string;
96
+ updatedAt: string;
97
+ id: string;
98
+ slug: string;
99
+ name: string;
100
+ logoUrl: string | null;
101
+ }>;
102
+ updateProvider({ id, actorId, ip, userAgent, aggregatorMappings, ...patchInput }: UpdateProviderInput & Actor): Promise<{
103
+ aggregatorMappings: {
104
+ aggregator: string;
105
+ vendorId: string;
106
+ }[];
107
+ metadata: {} | null;
108
+ isActive: boolean;
109
+ createdAt: string;
110
+ updatedAt: string;
111
+ id: string;
112
+ slug: string;
113
+ name: string;
114
+ logoUrl: string | null;
115
+ }>;
116
+ }
117
+ export {};
118
+ //# sourceMappingURL=game-provider.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game-provider.service.d.ts","sourceRoot":"","sources":["../../../../src/casino/gaming/service/game-provider.service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAIb,cAAc,EAMf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAiC,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/F,OAAO,EAAQ,YAAY,EAAiC,MAAM,oBAAoB,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGrF,eAAO,MAAM,yBAAyB;QAR7B,CAAC;CAQgE,CAAC;AAC3E,eAAO,MAAM,0BAA0B;QAT9B,CAAC;CAYT,CAAC;AACF,eAAO,MAAM,8BAA8B;QAblC,CAAC;CAgBT,CAAC;AACF,eAAO,MAAM,6BAA6B;QAjBjC,CAAC;CAuBT,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;CACrB,GAAG,UAAU,CAAC;AAEf,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC,YAAY;;;;;EAOzE;AAsCD,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,QAAQ;IAG7B,mBAAmB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;IAoBpE,kBAAkB,CAAC,EACvB,IAAI,EACJ,KAAK,EACL,CAAC,EACD,QAAQ,GACT,EAAE;QACD,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,CAAC,CAAC,EAAE,MAAM,CAAC;QACX,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB;;;;;;;;;;;;;;;;;;;IA6BK,WAAW,CAAC,EAAE,EAAE,MAAM;;;;;;;;;;;;;;IAStB,uBAAuB,CAAC,IAAI,EAAE,MAAM;;;;;;IAYpC,cAAc,CAAC,EACnB,IAAI,EACJ,IAAI,EACJ,kBAAuB,EACvB,OAAO,EACP,QAAQ,EACR,OAAO,EACP,EAAE,EACF,SAAS,GACV,EAAE,mBAAmB,GAAG,KAAK;;;;;;;;;;;;;;IAsDxB,cAAc,CAAC,EACnB,EAAE,EACF,OAAO,EACP,EAAE,EACF,SAAS,EACT,kBAAkB,EAClB,GAAG,UAAU,EACd,EAAE,mBAAmB,GAAG,KAAK;;;;;;;;;;;;;;CA6G/B"}
@@ -0,0 +1,251 @@
1
+ import { createDomainError, makeNotFoundError, makeConflictError, findOneOrThrow, likeContains, serializeRow, pageToOffset, uniqueConstraintName, } from '@openora/core/server';
2
+ import { eq, and, asc, count, ilike, inArray, ne, or } from 'drizzle-orm';
3
+ import { game, gameProvider, gameProviderAggregatorMapping } from '../schema/index.js';
4
+ import { mappingsByProviderIds } from '../../shared/game-catalog.js';
5
+ export const GameProviderNotFoundError = makeNotFoundError('GameProvider');
6
+ export const GameProviderSlugTakenError = makeConflictError('GameProviderSlugTakenError', 'A provider with this slug already exists');
7
+ export const GameProviderVendorIdTakenError = makeConflictError('GameProviderVendorIdTakenError', 'A provider with this vendor ID already exists for the aggregator');
8
+ export const GameProviderMappingInUseError = createDomainError('GameProviderMappingInUseError', (providerId, aggregator) => `Provider ${providerId} still has games on aggregator ${aggregator}; move them before removing the mapping`);
9
+ export function toProviderSummary(record) {
10
+ return {
11
+ id: record.id,
12
+ slug: record.slug,
13
+ name: record.name,
14
+ logoUrl: record.logoUrl,
15
+ };
16
+ }
17
+ function providerSnapshot(record, aggregatorMappings) {
18
+ return {
19
+ slug: record.slug,
20
+ name: record.name,
21
+ aggregatorMappings: [...aggregatorMappings],
22
+ logoUrl: record.logoUrl,
23
+ metadata: record.metadata ?? null,
24
+ isActive: record.isActive,
25
+ };
26
+ }
27
+ function toProviderDetail(record, aggregatorMappings) {
28
+ const dates = serializeRow(record, { dateFields: ['createdAt', 'updatedAt'] });
29
+ return {
30
+ ...toProviderSummary(record),
31
+ aggregatorMappings: [...aggregatorMappings],
32
+ metadata: record.metadata ?? null,
33
+ isActive: record.isActive,
34
+ createdAt: dates.createdAt,
35
+ updatedAt: dates.updatedAt,
36
+ };
37
+ }
38
+ function isMappingConstraint(name) {
39
+ return (name === 'game_provider_aggregator_mapping_provider_aggregator_key' ||
40
+ name === 'game_provider_aggregator_mapping_aggregator_vendor_id_key');
41
+ }
42
+ export class GameProviderService {
43
+ drizzle;
44
+ events;
45
+ constructor(drizzle, events) {
46
+ this.drizzle = drizzle;
47
+ this.events = events;
48
+ }
49
+ async listActiveProviders({ page, limit }) {
50
+ const where = eq(gameProvider.isActive, true);
51
+ const [items, [{ n }]] = await Promise.all([
52
+ this.drizzle.db
53
+ .select({
54
+ id: gameProvider.id,
55
+ slug: gameProvider.slug,
56
+ name: gameProvider.name,
57
+ logoUrl: gameProvider.logoUrl,
58
+ })
59
+ .from(gameProvider)
60
+ .where(where)
61
+ .orderBy(asc(gameProvider.name), asc(gameProvider.slug))
62
+ .limit(limit)
63
+ .offset(pageToOffset(page, limit)),
64
+ this.drizzle.db.select({ n: count() }).from(gameProvider).where(where),
65
+ ]);
66
+ return { items, total: Number(n), page, limit };
67
+ }
68
+ async listProvidersAdmin({ page, limit, q, isActive, }) {
69
+ const where = and(q
70
+ ? or(ilike(gameProvider.name, likeContains(q)), ilike(gameProvider.slug, likeContains(q)))
71
+ : undefined, isActive === undefined ? undefined : eq(gameProvider.isActive, isActive));
72
+ const [rows, [{ n }]] = await Promise.all([
73
+ this.drizzle.db
74
+ .select()
75
+ .from(gameProvider)
76
+ .where(where)
77
+ .orderBy(asc(gameProvider.name))
78
+ .limit(limit)
79
+ .offset(pageToOffset(page, limit)),
80
+ this.drizzle.db.select({ n: count() }).from(gameProvider).where(where),
81
+ ]);
82
+ const mappings = await mappingsByProviderIds(this.drizzle.db, rows.map((row) => row.id));
83
+ return {
84
+ items: rows.map((row) => toProviderDetail(row, mappings.get(row.id) ?? [])),
85
+ total: Number(n),
86
+ page,
87
+ limit,
88
+ };
89
+ }
90
+ async getProvider(id) {
91
+ const record = findOneOrThrow(await this.drizzle.db.select().from(gameProvider).where(eq(gameProvider.id, id)).limit(1), new GameProviderNotFoundError(id));
92
+ const mappings = await mappingsByProviderIds(this.drizzle.db, [record.id]);
93
+ return toProviderDetail(record, mappings.get(record.id) ?? []);
94
+ }
95
+ async getActiveProviderBySlug(slug) {
96
+ const record = findOneOrThrow(await this.drizzle.db
97
+ .select()
98
+ .from(gameProvider)
99
+ .where(and(eq(gameProvider.slug, slug), eq(gameProvider.isActive, true)))
100
+ .limit(1), new GameProviderNotFoundError(slug));
101
+ return toProviderSummary(record);
102
+ }
103
+ async createProvider({ slug, name, aggregatorMappings = [], logoUrl, metadata, actorId, ip, userAgent, }) {
104
+ let outcome;
105
+ try {
106
+ outcome = await this.drizzle.db.transaction(async (tx) => {
107
+ const [existing] = await tx
108
+ .select({ id: gameProvider.id })
109
+ .from(gameProvider)
110
+ .where(eq(gameProvider.slug, slug))
111
+ .limit(1);
112
+ if (existing) {
113
+ throw new GameProviderSlugTakenError();
114
+ }
115
+ const created = findOneOrThrow(await tx
116
+ .insert(gameProvider)
117
+ .values({ slug, name, logoUrl: logoUrl ?? null, metadata: metadata ?? null })
118
+ .returning(), new GameProviderNotFoundError(slug));
119
+ if (aggregatorMappings.length > 0) {
120
+ await tx.insert(gameProviderAggregatorMapping).values(aggregatorMappings.map((mapping) => ({
121
+ providerId: created.id,
122
+ ...mapping,
123
+ })));
124
+ }
125
+ const mappings = (await mappingsByProviderIds(tx, [created.id])).get(created.id) ?? [];
126
+ return { record: created, mappings };
127
+ });
128
+ }
129
+ catch (error) {
130
+ const constraint = uniqueConstraintName(error);
131
+ if (constraint === 'game_provider_slug_key') {
132
+ throw new GameProviderSlugTakenError();
133
+ }
134
+ if (isMappingConstraint(constraint)) {
135
+ throw new GameProviderVendorIdTakenError();
136
+ }
137
+ throw error;
138
+ }
139
+ const snapshot = providerSnapshot(outcome.record, outcome.mappings);
140
+ this.events.emit('gaming.provider.created', {
141
+ providerId: outcome.record.id,
142
+ ...snapshot,
143
+ actorId,
144
+ ip: ip ?? null,
145
+ userAgent: userAgent ?? null,
146
+ });
147
+ return toProviderDetail(outcome.record, outcome.mappings);
148
+ }
149
+ async updateProvider({ id, actorId, ip, userAgent, aggregatorMappings, ...patchInput }) {
150
+ const outcome = await this.drizzle.db
151
+ .transaction(async (tx) => {
152
+ const existing = findOneOrThrow(await tx
153
+ .select()
154
+ .from(gameProvider)
155
+ .where(eq(gameProvider.id, id))
156
+ .limit(1)
157
+ .for('update'), new GameProviderNotFoundError(id));
158
+ const existingMappings = (await mappingsByProviderIds(tx, [id])).get(existing.id) ?? [];
159
+ if (patchInput.slug !== undefined && patchInput.slug !== existing.slug) {
160
+ const [clash] = await tx
161
+ .select({ id: gameProvider.id })
162
+ .from(gameProvider)
163
+ .where(and(eq(gameProvider.slug, patchInput.slug), ne(gameProvider.id, id)))
164
+ .limit(1);
165
+ if (clash) {
166
+ throw new GameProviderSlugTakenError();
167
+ }
168
+ }
169
+ const patch = { ...patchInput };
170
+ const hasProviderChanges = Object.values(patch).some((value) => value !== undefined);
171
+ const replaceMappings = aggregatorMappings !== undefined;
172
+ if (!hasProviderChanges && !replaceMappings) {
173
+ const snapshot = providerSnapshot(existing, existingMappings);
174
+ return {
175
+ changed: false,
176
+ before: snapshot,
177
+ after: snapshot,
178
+ result: toProviderDetail(existing, existingMappings),
179
+ };
180
+ }
181
+ if (replaceMappings) {
182
+ // A game on a dropped aggregator would stay playable yet sit in the unmapped
183
+ // state updateGame rejects. The provider row lock above conflicts with the
184
+ // FOR SHARE updateGame takes, so no game can move onto the pair mid-check.
185
+ const retained = new Set(aggregatorMappings.map((mapping) => mapping.aggregator));
186
+ const removed = existingMappings
187
+ .map((mapping) => mapping.aggregator)
188
+ .filter((aggregator) => !retained.has(aggregator));
189
+ if (removed.length > 0) {
190
+ const [inUse] = await tx
191
+ .select({ aggregator: game.aggregator })
192
+ .from(game)
193
+ .where(and(eq(game.providerId, id), inArray(game.aggregator, removed)))
194
+ .limit(1);
195
+ if (inUse) {
196
+ throw new GameProviderMappingInUseError(id, inUse.aggregator);
197
+ }
198
+ }
199
+ }
200
+ const next = findOneOrThrow(await tx
201
+ .update(gameProvider)
202
+ .set(replaceMappings ? { ...patch, updatedAt: new Date() } : patch)
203
+ .where(eq(gameProvider.id, id))
204
+ .returning(), new GameProviderNotFoundError(id));
205
+ if (replaceMappings) {
206
+ await tx
207
+ .delete(gameProviderAggregatorMapping)
208
+ .where(eq(gameProviderAggregatorMapping.providerId, id));
209
+ if (aggregatorMappings.length > 0) {
210
+ await tx.insert(gameProviderAggregatorMapping).values(aggregatorMappings.map((mapping) => ({
211
+ providerId: id,
212
+ ...mapping,
213
+ })));
214
+ }
215
+ }
216
+ const persistedMappings = replaceMappings
217
+ ? ((await mappingsByProviderIds(tx, [id])).get(id) ?? [])
218
+ : existingMappings;
219
+ return {
220
+ changed: true,
221
+ before: providerSnapshot(existing, existingMappings),
222
+ after: providerSnapshot(next, persistedMappings),
223
+ result: toProviderDetail(next, persistedMappings),
224
+ };
225
+ })
226
+ .catch((error) => {
227
+ // Translate only the index that actually fired: a vendor-id collision must
228
+ // never surface as a slug conflict, and an unknown 23505 must not lie at all.
229
+ if (isMappingConstraint(uniqueConstraintName(error))) {
230
+ throw new GameProviderVendorIdTakenError();
231
+ }
232
+ if (uniqueConstraintName(error) === 'game_provider_slug_key') {
233
+ throw new GameProviderSlugTakenError();
234
+ }
235
+ throw error;
236
+ });
237
+ if (!outcome.changed) {
238
+ return outcome.result;
239
+ }
240
+ this.events.emit('gaming.provider.updated', {
241
+ providerId: id,
242
+ actorId,
243
+ before: outcome.before,
244
+ after: outcome.after,
245
+ ip: ip ?? null,
246
+ userAgent: userAgent ?? null,
247
+ });
248
+ return outcome.result;
249
+ }
250
+ }
251
+ //# sourceMappingURL=game-provider.service.js.map