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

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 (167) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/admin-console/plugin.d.ts +2 -0
  3. package/dist/admin-console/plugin.d.ts.map +1 -1
  4. package/dist/analytics/plugin.d.ts +2 -0
  5. package/dist/analytics/plugin.d.ts.map +1 -1
  6. package/dist/audit/plugin.d.ts +2 -0
  7. package/dist/audit/plugin.d.ts.map +1 -1
  8. package/dist/audit/plugin.js +31 -0
  9. package/dist/audit/plugin.js.map +1 -1
  10. package/dist/casino/gaming/__tests__/gaming.service.int.test.js +65 -11
  11. package/dist/casino/gaming/__tests__/gaming.service.int.test.js.map +1 -1
  12. package/dist/casino/gaming/plugin.d.ts +2 -0
  13. package/dist/casino/gaming/plugin.d.ts.map +1 -1
  14. package/dist/casino/gaming/plugin.js +2 -2
  15. package/dist/casino/gaming/plugin.js.map +1 -1
  16. package/dist/casino/gaming/router/index.d.ts.map +1 -1
  17. package/dist/casino/gaming/router/index.js +3 -3
  18. package/dist/casino/gaming/router/index.js.map +1 -1
  19. package/dist/casino/gaming/service/gaming.service.d.ts +13 -3
  20. package/dist/casino/gaming/service/gaming.service.d.ts.map +1 -1
  21. package/dist/casino/gaming/service/gaming.service.js +20 -3
  22. package/dist/casino/gaming/service/gaming.service.js.map +1 -1
  23. package/dist/casino/lobby/__tests__/lobby-contract.test.d.ts +2 -0
  24. package/dist/casino/lobby/__tests__/lobby-contract.test.d.ts.map +1 -0
  25. package/dist/casino/lobby/__tests__/lobby-contract.test.js +39 -0
  26. package/dist/casino/lobby/__tests__/lobby-contract.test.js.map +1 -0
  27. package/dist/casino/lobby/__tests__/lobby-layout.int.test.d.ts +2 -0
  28. package/dist/casino/lobby/__tests__/lobby-layout.int.test.d.ts.map +1 -0
  29. package/dist/casino/lobby/__tests__/lobby-layout.int.test.js +145 -0
  30. package/dist/casino/lobby/__tests__/lobby-layout.int.test.js.map +1 -0
  31. package/dist/casino/lobby/__tests__/lobby.cache.int.test.js +45 -7
  32. package/dist/casino/lobby/__tests__/lobby.cache.int.test.js.map +1 -1
  33. package/dist/casino/lobby/contract/index.d.ts +84 -0
  34. package/dist/casino/lobby/contract/index.d.ts.map +1 -1
  35. package/dist/casino/lobby/contract/index.js +38 -1
  36. package/dist/casino/lobby/contract/index.js.map +1 -1
  37. package/dist/casino/lobby/drizzle/migrations/0001_true_clea.sql +20 -0
  38. package/dist/casino/lobby/drizzle/migrations/meta/0001_snapshot.json +343 -0
  39. package/dist/casino/lobby/drizzle/migrations/meta/_journal.json +7 -0
  40. package/dist/casino/lobby/plugin.d.ts +2 -0
  41. package/dist/casino/lobby/plugin.d.ts.map +1 -1
  42. package/dist/casino/lobby/plugin.js +7 -3
  43. package/dist/casino/lobby/plugin.js.map +1 -1
  44. package/dist/casino/lobby/router/index.d.ts +42 -2
  45. package/dist/casino/lobby/router/index.d.ts.map +1 -1
  46. package/dist/casino/lobby/router/index.js +13 -2
  47. package/dist/casino/lobby/router/index.js.map +1 -1
  48. package/dist/casino/lobby/schema/index.d.ts +222 -0
  49. package/dist/casino/lobby/schema/index.d.ts.map +1 -1
  50. package/dist/casino/lobby/schema/index.js +21 -1
  51. package/dist/casino/lobby/schema/index.js.map +1 -1
  52. package/dist/casino/lobby/service/lobby.service.d.ts +36 -3
  53. package/dist/casino/lobby/service/lobby.service.d.ts.map +1 -1
  54. package/dist/casino/lobby/service/lobby.service.js +253 -13
  55. package/dist/casino/lobby/service/lobby.service.js.map +1 -1
  56. package/dist/cms/plugin.d.ts +2 -0
  57. package/dist/cms/plugin.d.ts.map +1 -1
  58. package/dist/compliance/__tests__/compliance.service.int.test.js +152 -3
  59. package/dist/compliance/__tests__/compliance.service.int.test.js.map +1 -1
  60. package/dist/compliance/contract/index.d.ts +59 -2
  61. package/dist/compliance/contract/index.d.ts.map +1 -1
  62. package/dist/compliance/contract/index.js +28 -2
  63. package/dist/compliance/contract/index.js.map +1 -1
  64. package/dist/compliance/drizzle/migrations/0009_living_felicia_hardy.sql +12 -0
  65. package/dist/compliance/drizzle/migrations/meta/0009_snapshot.json +974 -0
  66. package/dist/compliance/drizzle/migrations/meta/_journal.json +7 -0
  67. package/dist/compliance/plugin.d.ts +2 -0
  68. package/dist/compliance/plugin.d.ts.map +1 -1
  69. package/dist/compliance/plugin.js +2 -1
  70. package/dist/compliance/plugin.js.map +1 -1
  71. package/dist/compliance/router/index.d.ts +34 -1
  72. package/dist/compliance/router/index.d.ts.map +1 -1
  73. package/dist/compliance/router/index.js +13 -1
  74. package/dist/compliance/router/index.js.map +1 -1
  75. package/dist/compliance/schema/index.d.ts +110 -0
  76. package/dist/compliance/schema/index.d.ts.map +1 -1
  77. package/dist/compliance/schema/index.js +16 -0
  78. package/dist/compliance/schema/index.js.map +1 -1
  79. package/dist/compliance/service/compliance.service.d.ts +49 -2
  80. package/dist/compliance/service/compliance.service.d.ts.map +1 -1
  81. package/dist/compliance/service/compliance.service.js +111 -4
  82. package/dist/compliance/service/compliance.service.js.map +1 -1
  83. package/dist/contracts/adapters/__tests__/geo-ip.test.d.ts +2 -0
  84. package/dist/contracts/adapters/__tests__/geo-ip.test.d.ts.map +1 -0
  85. package/dist/contracts/adapters/__tests__/geo-ip.test.js +13 -0
  86. package/dist/contracts/adapters/__tests__/geo-ip.test.js.map +1 -0
  87. package/dist/contracts/adapters/__tests__/lobby-section-catalog.test.d.ts +2 -0
  88. package/dist/contracts/adapters/__tests__/lobby-section-catalog.test.d.ts.map +1 -0
  89. package/dist/contracts/adapters/__tests__/lobby-section-catalog.test.js +25 -0
  90. package/dist/contracts/adapters/__tests__/lobby-section-catalog.test.js.map +1 -0
  91. package/dist/contracts/adapters/game-geo-check.d.ts +33 -0
  92. package/dist/contracts/adapters/game-geo-check.d.ts.map +1 -0
  93. package/dist/contracts/adapters/game-geo-check.js +23 -0
  94. package/dist/contracts/adapters/game-geo-check.js.map +1 -0
  95. package/dist/contracts/adapters/geo-ip.d.ts +2 -4
  96. package/dist/contracts/adapters/geo-ip.d.ts.map +1 -1
  97. package/dist/contracts/adapters/geo-ip.js +9 -0
  98. package/dist/contracts/adapters/geo-ip.js.map +1 -1
  99. package/dist/contracts/adapters/index.d.ts +5 -1
  100. package/dist/contracts/adapters/index.d.ts.map +1 -1
  101. package/dist/contracts/adapters/index.js +3 -1
  102. package/dist/contracts/adapters/index.js.map +1 -1
  103. package/dist/contracts/adapters/lobby-section-catalog.d.ts +39 -0
  104. package/dist/contracts/adapters/lobby-section-catalog.d.ts.map +1 -0
  105. package/dist/contracts/adapters/lobby-section-catalog.js +34 -0
  106. package/dist/contracts/adapters/lobby-section-catalog.js.map +1 -0
  107. package/dist/contracts/schemas/__tests__/events.test.js +39 -0
  108. package/dist/contracts/schemas/__tests__/events.test.js.map +1 -1
  109. package/dist/contracts/schemas/compliance.d.ts +1 -0
  110. package/dist/contracts/schemas/compliance.d.ts.map +1 -1
  111. package/dist/contracts/schemas/compliance.js +1 -0
  112. package/dist/contracts/schemas/compliance.js.map +1 -1
  113. package/dist/contracts/schemas/events.d.ts +68 -0
  114. package/dist/contracts/schemas/events.d.ts.map +1 -1
  115. package/dist/contracts/schemas/events.js +34 -1
  116. package/dist/contracts/schemas/events.js.map +1 -1
  117. package/dist/contracts/schemas/iam.d.ts +1 -1
  118. package/dist/contracts/schemas/iam.js +1 -1
  119. package/dist/contracts/schemas/iam.js.map +1 -1
  120. package/dist/contracts/schemas/index.d.ts +1 -0
  121. package/dist/contracts/schemas/index.d.ts.map +1 -1
  122. package/dist/contracts/schemas/index.js +1 -0
  123. package/dist/contracts/schemas/index.js.map +1 -1
  124. package/dist/contracts/schemas/lobby.d.ts +27 -0
  125. package/dist/contracts/schemas/lobby.d.ts.map +1 -0
  126. package/dist/contracts/schemas/lobby.js +22 -0
  127. package/dist/contracts/schemas/lobby.js.map +1 -0
  128. package/dist/engagement/chat/plugin.d.ts +2 -0
  129. package/dist/engagement/chat/plugin.d.ts.map +1 -1
  130. package/dist/engagement/chat-commands/plugin.d.ts +2 -0
  131. package/dist/engagement/chat-commands/plugin.d.ts.map +1 -1
  132. package/dist/engagement/notifications/plugin.d.ts +2 -0
  133. package/dist/engagement/notifications/plugin.d.ts.map +1 -1
  134. package/dist/engagement/social/plugin.d.ts +2 -0
  135. package/dist/engagement/social/plugin.d.ts.map +1 -1
  136. package/dist/fx/exchange-rate/plugin.d.ts +2 -0
  137. package/dist/fx/exchange-rate/plugin.d.ts.map +1 -1
  138. package/dist/iam/plugin.d.ts +2 -0
  139. package/dist/iam/plugin.d.ts.map +1 -1
  140. package/dist/mail/plugin.d.ts +2 -0
  141. package/dist/mail/plugin.d.ts.map +1 -1
  142. package/dist/pam/identity/plugin.d.ts +2 -0
  143. package/dist/pam/identity/plugin.d.ts.map +1 -1
  144. package/dist/pam/player-management/plugin.d.ts +2 -0
  145. package/dist/pam/player-management/plugin.d.ts.map +1 -1
  146. package/dist/pam/player-note/plugin.d.ts +2 -0
  147. package/dist/pam/player-note/plugin.d.ts.map +1 -1
  148. package/dist/pam/profile/plugin.d.ts +2 -0
  149. package/dist/pam/profile/plugin.d.ts.map +1 -1
  150. package/dist/pam/tag/plugin.d.ts +2 -0
  151. package/dist/pam/tag/plugin.d.ts.map +1 -1
  152. package/dist/server/auth/permissions.d.ts +12 -12
  153. package/dist/server/auth/permissions.js +1 -1
  154. package/dist/server/auth/permissions.js.map +1 -1
  155. package/dist/server/runtime/__tests__/create-app.test.js +8 -5
  156. package/dist/server/runtime/__tests__/create-app.test.js.map +1 -1
  157. package/dist/server/runtime/core-token-catalog.d.ts +2 -0
  158. package/dist/server/runtime/core-token-catalog.d.ts.map +1 -1
  159. package/dist/server/runtime/core-token-catalog.js +3 -1
  160. package/dist/server/runtime/core-token-catalog.js.map +1 -1
  161. package/dist/server/runtime/create-app.d.ts +1 -1
  162. package/dist/server/runtime/create-app.d.ts.map +1 -1
  163. package/dist/server/runtime/create-app.js +2 -8
  164. package/dist/server/runtime/create-app.js.map +1 -1
  165. package/dist/wallet/plugin.d.ts +2 -0
  166. package/dist/wallet/plugin.d.ts.map +1 -1
  167. package/package.json +1 -1
@@ -291,6 +291,228 @@ export declare const featuredSlot: import("drizzle-orm/pg-core").PgTableWithColu
291
291
  };
292
292
  dialect: "pg";
293
293
  }>;
294
+ export declare const lobbyLayout: import("drizzle-orm/pg-core").PgTableWithColumns<{
295
+ name: "lobby_layout";
296
+ schema: undefined;
297
+ columns: {
298
+ id: import("drizzle-orm/pg-core").PgColumn<{
299
+ name: "id";
300
+ tableName: "lobby_layout";
301
+ dataType: "string";
302
+ columnType: "PgUUID";
303
+ data: string;
304
+ driverParam: string;
305
+ notNull: true;
306
+ hasDefault: true;
307
+ isPrimaryKey: true;
308
+ isAutoincrement: false;
309
+ hasRuntimeDefault: false;
310
+ enumValues: undefined;
311
+ baseColumn: never;
312
+ identity: undefined;
313
+ generated: undefined;
314
+ }, {}, {}>;
315
+ layoutKey: import("drizzle-orm/pg-core").PgColumn<{
316
+ name: "layoutKey";
317
+ tableName: "lobby_layout";
318
+ dataType: "string";
319
+ columnType: "PgText";
320
+ data: string;
321
+ driverParam: string;
322
+ notNull: true;
323
+ hasDefault: true;
324
+ isPrimaryKey: false;
325
+ isAutoincrement: false;
326
+ hasRuntimeDefault: false;
327
+ enumValues: [string, ...string[]];
328
+ baseColumn: never;
329
+ identity: undefined;
330
+ generated: undefined;
331
+ }, {}, {}>;
332
+ version: import("drizzle-orm/pg-core").PgColumn<{
333
+ name: "version";
334
+ tableName: "lobby_layout";
335
+ dataType: "number";
336
+ columnType: "PgInteger";
337
+ data: number;
338
+ driverParam: string | number;
339
+ notNull: true;
340
+ hasDefault: true;
341
+ isPrimaryKey: false;
342
+ isAutoincrement: false;
343
+ hasRuntimeDefault: false;
344
+ enumValues: undefined;
345
+ baseColumn: never;
346
+ identity: undefined;
347
+ generated: undefined;
348
+ }, {}, {}>;
349
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
350
+ name: "updatedAt";
351
+ tableName: "lobby_layout";
352
+ dataType: "date";
353
+ columnType: "PgTimestamp";
354
+ data: Date;
355
+ driverParam: string;
356
+ notNull: true;
357
+ hasDefault: true;
358
+ isPrimaryKey: false;
359
+ isAutoincrement: false;
360
+ hasRuntimeDefault: false;
361
+ enumValues: undefined;
362
+ baseColumn: never;
363
+ identity: undefined;
364
+ generated: undefined;
365
+ }, {}, {}>;
366
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
367
+ name: "createdAt";
368
+ tableName: "lobby_layout";
369
+ dataType: "date";
370
+ columnType: "PgTimestamp";
371
+ data: Date;
372
+ driverParam: string;
373
+ notNull: true;
374
+ hasDefault: true;
375
+ isPrimaryKey: false;
376
+ isAutoincrement: false;
377
+ hasRuntimeDefault: false;
378
+ enumValues: undefined;
379
+ baseColumn: never;
380
+ identity: undefined;
381
+ generated: undefined;
382
+ }, {}, {}>;
383
+ };
384
+ dialect: "pg";
385
+ }>;
386
+ export declare const lobbySection: import("drizzle-orm/pg-core").PgTableWithColumns<{
387
+ name: "lobby_section";
388
+ schema: undefined;
389
+ columns: {
390
+ id: import("drizzle-orm/pg-core").PgColumn<{
391
+ name: "id";
392
+ tableName: "lobby_section";
393
+ dataType: "string";
394
+ columnType: "PgUUID";
395
+ data: string;
396
+ driverParam: string;
397
+ notNull: true;
398
+ hasDefault: true;
399
+ isPrimaryKey: true;
400
+ isAutoincrement: false;
401
+ hasRuntimeDefault: false;
402
+ enumValues: undefined;
403
+ baseColumn: never;
404
+ identity: undefined;
405
+ generated: undefined;
406
+ }, {}, {}>;
407
+ type: import("drizzle-orm/pg-core").PgColumn<{
408
+ name: "type";
409
+ tableName: "lobby_section";
410
+ dataType: "string";
411
+ columnType: "PgText";
412
+ data: string;
413
+ driverParam: string;
414
+ notNull: true;
415
+ hasDefault: false;
416
+ isPrimaryKey: false;
417
+ isAutoincrement: false;
418
+ hasRuntimeDefault: false;
419
+ enumValues: [string, ...string[]];
420
+ baseColumn: never;
421
+ identity: undefined;
422
+ generated: undefined;
423
+ }, {}, {}>;
424
+ config: import("drizzle-orm/pg-core").PgColumn<{
425
+ name: "config";
426
+ tableName: "lobby_section";
427
+ dataType: "json";
428
+ columnType: "PgJsonb";
429
+ data: Record<string, import("zod").JSONType>;
430
+ driverParam: unknown;
431
+ notNull: true;
432
+ hasDefault: false;
433
+ isPrimaryKey: false;
434
+ isAutoincrement: false;
435
+ hasRuntimeDefault: false;
436
+ enumValues: undefined;
437
+ baseColumn: never;
438
+ identity: undefined;
439
+ generated: undefined;
440
+ }, {}, {
441
+ $type: Record<string, import("zod").JSONType>;
442
+ }>;
443
+ sortOrder: import("drizzle-orm/pg-core").PgColumn<{
444
+ name: "sortOrder";
445
+ tableName: "lobby_section";
446
+ dataType: "number";
447
+ columnType: "PgInteger";
448
+ data: number;
449
+ driverParam: string | number;
450
+ notNull: true;
451
+ hasDefault: true;
452
+ isPrimaryKey: false;
453
+ isAutoincrement: false;
454
+ hasRuntimeDefault: false;
455
+ enumValues: undefined;
456
+ baseColumn: never;
457
+ identity: undefined;
458
+ generated: undefined;
459
+ }, {}, {}>;
460
+ isEnabled: import("drizzle-orm/pg-core").PgColumn<{
461
+ name: "isEnabled";
462
+ tableName: "lobby_section";
463
+ dataType: "boolean";
464
+ columnType: "PgBoolean";
465
+ data: boolean;
466
+ driverParam: boolean;
467
+ notNull: true;
468
+ hasDefault: true;
469
+ isPrimaryKey: false;
470
+ isAutoincrement: false;
471
+ hasRuntimeDefault: false;
472
+ enumValues: undefined;
473
+ baseColumn: never;
474
+ identity: undefined;
475
+ generated: undefined;
476
+ }, {}, {}>;
477
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
478
+ name: "updatedAt";
479
+ tableName: "lobby_section";
480
+ dataType: "date";
481
+ columnType: "PgTimestamp";
482
+ data: Date;
483
+ driverParam: string;
484
+ notNull: true;
485
+ hasDefault: true;
486
+ isPrimaryKey: false;
487
+ isAutoincrement: false;
488
+ hasRuntimeDefault: false;
489
+ enumValues: undefined;
490
+ baseColumn: never;
491
+ identity: undefined;
492
+ generated: undefined;
493
+ }, {}, {}>;
494
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
495
+ name: "createdAt";
496
+ tableName: "lobby_section";
497
+ dataType: "date";
498
+ columnType: "PgTimestamp";
499
+ data: Date;
500
+ driverParam: string;
501
+ notNull: true;
502
+ hasDefault: true;
503
+ isPrimaryKey: false;
504
+ isAutoincrement: false;
505
+ hasRuntimeDefault: false;
506
+ enumValues: undefined;
507
+ baseColumn: never;
508
+ identity: undefined;
509
+ generated: undefined;
510
+ }, {}, {}>;
511
+ };
512
+ dialect: "pg";
513
+ }>;
514
+ export type LobbySection = typeof lobbySection.$inferSelect;
515
+ export type LobbyLayout = typeof lobbyLayout.$inferSelect;
294
516
  export type LobbyCategory = typeof lobbyCategory.$inferSelect;
295
517
  export type LobbyCategoryGame = typeof lobbyCategoryGame.$inferSelect;
296
518
  export type FeaturedSlot = typeof featuredSlot.$inferSelect;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/casino/lobby/schema/index.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUzB,CAAC;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW7B,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQvB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,YAAY,CAAC;AAC9D,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/casino/lobby/schema/index.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUzB,CAAC;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW7B,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQvB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYvB,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,YAAY,CAAC;AAC5D,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,YAAY,CAAC;AAC1D,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,YAAY,CAAC;AAC9D,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,YAAY,CAAC"}
@@ -1,4 +1,4 @@
1
- import { pgTable, uuid, text, integer, boolean, timestamp, uniqueIndex, index, } from 'drizzle-orm/pg-core';
1
+ import { pgTable, uuid, text, integer, boolean, timestamp, index, uniqueIndex, jsonb, } from 'drizzle-orm/pg-core';
2
2
  export const lobbyCategory = pgTable('lobby_category', {
3
3
  id: uuid().primaryKey().defaultRandom(),
4
4
  name: text().notNull(),
@@ -23,4 +23,24 @@ export const featuredSlot = pgTable('featured_slot', {
23
23
  isActive: boolean().notNull().default(true),
24
24
  createdAt: timestamp({ withTimezone: true }).notNull().defaultNow(),
25
25
  });
26
+ export const lobbyLayout = pgTable('lobby_layout', {
27
+ id: uuid().primaryKey().defaultRandom(),
28
+ layoutKey: text().notNull().default('global'),
29
+ version: integer().notNull().default(0),
30
+ updatedAt: timestamp({ withTimezone: true })
31
+ .notNull()
32
+ .$onUpdateFn(() => new Date()),
33
+ createdAt: timestamp({ withTimezone: true }).notNull().defaultNow(),
34
+ }, (t) => [uniqueIndex('lobby_layout_layout_key_key').on(t.layoutKey)]);
35
+ export const lobbySection = pgTable('lobby_section', {
36
+ id: uuid().primaryKey().defaultRandom(),
37
+ type: text().notNull(),
38
+ config: jsonb().$type().notNull(),
39
+ sortOrder: integer().notNull().default(0),
40
+ isEnabled: boolean().notNull().default(true),
41
+ updatedAt: timestamp({ withTimezone: true })
42
+ .notNull()
43
+ .$onUpdateFn(() => new Date()),
44
+ createdAt: timestamp({ withTimezone: true }).notNull().defaultNow(),
45
+ }, (t) => [index('lobby_section_sort_order_idx').on(t.sortOrder)]);
26
46
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/casino/lobby/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,OAAO,EACP,SAAS,EACT,WAAW,EACX,KAAK,GACN,MAAM,qBAAqB,CAAC;AAE7B,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAClC,gBAAgB,EAChB;IACE,EAAE,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE;IACvC,IAAI,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IACtB,IAAI,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IACtB,SAAS,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,SAAS,EAAE,SAAS,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;CACpE,EACD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CACtD,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CACtC,qBAAqB,EACrB;IACE,EAAE,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE;IACvC,UAAU,EAAE,IAAI,EAAE;SACf,OAAO,EAAE;SACT,UAAU,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAC9D,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IACxB,SAAS,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;CAC1C,EACD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAClE,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,EAAE;IACnD,EAAE,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC,aAAa,EAAE;IACvC,MAAM,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IACvB,SAAS,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;IAC3B,SAAS,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACzC,QAAQ,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3C,SAAS,EAAE,SAAS,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;CACpE,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/casino/lobby/schema/index.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,32 @@
1
- import { DrizzleService } from '@openora/core/server';
2
- import type { CacheAdapter } from '@openora/core/contracts';
1
+ import { type DrizzleService, type EventBus } from '@openora/core/server';
2
+ import type { CacheAdapter, ClientMeta, LobbySectionCatalog, User } from '@openora/core/contracts';
3
+ import type { LobbyAdminLayout, ReplaceLobbyLayoutInput } from '../contract/index.js';
4
+ export declare const LobbySectionNotFoundError: new (id: string) => Error & {
5
+ data?: Record<string, string>;
6
+ };
3
7
  export declare const LobbyCategoryNotFoundError: new (slug: string) => Error & {
4
8
  data?: Record<string, string>;
5
9
  };
10
+ export declare const LobbySectionFieldError: new (message: string) => Error & {
11
+ data?: Record<string, string>;
12
+ };
13
+ export declare const LobbyLayoutVersionConflictError: new (expectedVersion: number, actualVersion: number) => Error & {
14
+ data?: Record<string, string>;
15
+ };
16
+ type Actor = {
17
+ actorId?: User['id'];
18
+ } & ClientMeta;
6
19
  export declare class LobbyService {
7
20
  private readonly drizzle;
21
+ private readonly events;
22
+ private readonly sectionCatalog;
8
23
  private readonly cache?;
9
- constructor(drizzle: DrizzleService, cache?: CacheAdapter | undefined);
24
+ constructor(drizzle: DrizzleService, events: EventBus, sectionCatalog: LobbySectionCatalog, cache?: CacheAdapter | undefined);
25
+ getLayout(): Promise<{
26
+ id: string;
27
+ type: string;
28
+ data: import("zod").JSONType;
29
+ }[]>;
10
30
  listCategories(): Promise<{
11
31
  id: string;
12
32
  name: string;
@@ -72,5 +92,18 @@ export declare class LobbyService {
72
92
  }[];
73
93
  thumbnailUrl: string | null;
74
94
  }[]>;
95
+ getAdminLayout(): Promise<LobbyAdminLayout>;
96
+ replaceLayout(input: ReplaceLobbyLayoutInput & Actor): Promise<LobbyAdminLayout>;
97
+ private loadLayout;
98
+ private resolveSectionGroup;
99
+ private validateSections;
100
+ private parseConfig;
101
+ private groupSections;
102
+ private requireDefinition;
103
+ private loadAdminSections;
104
+ private assertUniqueSectionIds;
105
+ private assertExistingSectionIds;
106
+ private replaceSections;
75
107
  }
108
+ export {};
76
109
  //# sourceMappingURL=lobby.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lobby.service.d.ts","sourceRoot":"","sources":["../../../../src/casino/lobby/service/lobby.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EAIf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAU5D,eAAO,MAAM,0BAA0B;QAVoB,CAAC;CAa3D,CAAC;AA2BF,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBADN,OAAO,EAAE,cAAc,EACvB,KAAK,CAAC,EAAE,YAAY,YAAA;IAGjC,cAAc;;;;;;;IA0Bd,gBAAgB,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8C7B,WAAW;;;;;;;;;IAyCX,MAAM,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;CAmB3B"}
1
+ {"version":3,"file":"lobby.service.d.ts","sourceRoot":"","sources":["../../../../src/casino/lobby/service/lobby.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAWL,KAAK,cAAc,EAEnB,KAAK,QAAQ,EACd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,mBAAmB,EAOnB,IAAI,EACL,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,gBAAgB,EAGhB,uBAAuB,EACxB,MAAM,sBAAsB,CAAC;AAgB9B,eAAO,MAAM,yBAAyB;QA7CpC,CAAD;CA6CyE,CAAC;AAC3E,eAAO,MAAM,0BAA0B;QA9CrC,CAAD;CAiDA,CAAC;AACF,eAAO,MAAM,sBAAsB;QAlDjC,CAAD;CAqDA,CAAC;AACF,eAAO,MAAM,+BAA+B;QAtD1C,CAAD;CA4DA,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;CACtB,GAAG,UAAU,CAAC;AAyCf,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAHN,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,QAAQ,EAChB,cAAc,EAAE,mBAAmB,EACnC,KAAK,CAAC,EAAE,YAAY,YAAA;IAGjC,SAAS;;;;;IAIT,cAAc;;;;;;;IA0Bd,gBAAgB,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;IA4C7B,WAAW;;;;;;;;;IAuCX,MAAM,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;IAoBpB,cAAc,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAiB3C,aAAa,CAAC,KAAK,EAAE,uBAAuB,GAAG,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAmExE,UAAU;YAsBV,mBAAmB;YAuEnB,gBAAgB;IA4B9B,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,iBAAiB;YAUX,iBAAiB;IAW/B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,wBAAwB;YAyBlB,eAAe;CA8B9B"}
@@ -1,11 +1,20 @@
1
- import { createDomainError, findOneOrThrow, cached, likeContains, } from '@openora/core/server';
2
- import { eq, and, ilike, count, asc, inArray } from 'drizzle-orm';
3
- import { lobbyCategory, lobbyCategoryGame, featuredSlot } from '../schema/index.js';
1
+ import { randomUUID } from 'node:crypto';
2
+ import { and, asc, count, eq, ilike, inArray, notInArray, sql } from 'drizzle-orm';
3
+ import { cached, createDomainError, createLogger, findOneOrThrow, invalidate, mapConcurrent, likeContains, makeNotFoundError, serializeRow, withAdvisoryXactLock, } from '@openora/core/server';
4
+ import { LobbySectionDataSchema } from '@openora/core/contracts';
5
+ import { featuredSlot, lobbyCategory, lobbyCategoryGame, lobbyLayout, lobbySection, } from '../schema/index.js';
4
6
  import { game, gameProvider } from '@openora/core/casino/schema/gaming';
5
7
  import { categoriesByGameIds, playableGameCondition, toCategorySummary, } from '../../shared/game-catalog.js';
8
+ export const LobbySectionNotFoundError = makeNotFoundError('LobbySection');
6
9
  export const LobbyCategoryNotFoundError = createDomainError('LobbyCategoryNotFoundError', (slug) => `Lobby category not found: ${slug}`);
7
- // Feeds tolerate this much staleness; no invalidation wiring - TTL-only expiry.
10
+ export const LobbySectionFieldError = createDomainError('LobbySectionFieldError', (message) => message);
11
+ export const LobbyLayoutVersionConflictError = createDomainError('LobbyLayoutVersionConflictError', (expectedVersion, actualVersion) => `Lobby layout version ${expectedVersion} is stale; current version is ${actualVersion}`);
12
+ const logger = createLogger('lobby');
13
+ const GLOBAL_LAYOUT_KEY = 'global';
14
+ const LAYOUT_LOCK_KEY = 'lobby:global-layout';
8
15
  const LOBBY_CACHE_TTL_MS = 30_000;
16
+ const LAYOUT_CACHE_KEY = 'lobby:layout';
17
+ const SECTION_OPERATION_CONCURRENCY = 5;
9
18
  const CATEGORIES_CACHE_KEY = 'lobby:categories';
10
19
  const FEATURED_CACHE_KEY = 'lobby:featured';
11
20
  function toGameSummary(row) {
@@ -25,11 +34,18 @@ function toGameSummary(row) {
25
34
  }
26
35
  export class LobbyService {
27
36
  drizzle;
37
+ events;
38
+ sectionCatalog;
28
39
  cache;
29
- constructor(drizzle, cache) {
40
+ constructor(drizzle, events, sectionCatalog, cache) {
30
41
  this.drizzle = drizzle;
42
+ this.events = events;
43
+ this.sectionCatalog = sectionCatalog;
31
44
  this.cache = cache;
32
45
  }
46
+ async getLayout() {
47
+ return cached(this.cache, LAYOUT_CACHE_KEY, LOBBY_CACHE_TTL_MS, () => this.loadLayout());
48
+ }
33
49
  async listCategories() {
34
50
  return cached(this.cache, CATEGORIES_CACHE_KEY, LOBBY_CACHE_TTL_MS, async () => {
35
51
  const db = this.drizzle.db;
@@ -43,13 +59,13 @@ export class LobbyService {
43
59
  .where(playableGameCondition())
44
60
  .groupBy(lobbyCategoryGame.categoryId),
45
61
  ]);
46
- const countMap = new Map(counts.map((r) => [r.categoryId, Number(r.n)]));
47
- return categories.map((cat) => ({
48
- id: cat.id,
49
- name: cat.name,
50
- slug: cat.slug,
51
- sortOrder: cat.sortOrder,
52
- gameCount: countMap.get(cat.id) ?? 0,
62
+ const countMap = new Map(counts.map((row) => [row.categoryId, Number(row.n)]));
63
+ return categories.map((category) => ({
64
+ id: category.id,
65
+ name: category.name,
66
+ slug: category.slug,
67
+ sortOrder: category.sortOrder,
68
+ gameCount: countMap.get(category.id) ?? 0,
53
69
  }));
54
70
  });
55
71
  }
@@ -89,7 +105,7 @@ export class LobbyService {
89
105
  .from(featuredSlot)
90
106
  .where(eq(featuredSlot.isActive, true))
91
107
  .orderBy(asc(featuredSlot.placement), asc(featuredSlot.sortOrder));
92
- const gameIds = [...new Set(slots.map((s) => s.gameId))];
108
+ const gameIds = [...new Set(slots.map((slot) => slot.gameId))];
93
109
  const games = gameIds.length > 0
94
110
  ? await db
95
111
  .select({ game })
@@ -129,5 +145,229 @@ export class LobbyService {
129
145
  const categories = await categoriesByGameIds(db, rows.map((r) => r.game.id), true);
130
146
  return rows.map((r) => toGameSummary({ ...r, categories: categories.get(r.game.id) ?? [] }));
131
147
  }
148
+ async getAdminLayout() {
149
+ return this.drizzle.db.transaction(async (tx) => {
150
+ const [layout] = await tx
151
+ .select({ version: lobbyLayout.version })
152
+ .from(lobbyLayout)
153
+ .where(eq(lobbyLayout.layoutKey, GLOBAL_LAYOUT_KEY));
154
+ return {
155
+ version: layout?.version ?? 0,
156
+ sections: await this.loadAdminSections(tx),
157
+ };
158
+ }, { isolationLevel: 'repeatable read', accessMode: 'read only' });
159
+ }
160
+ async replaceLayout(input) {
161
+ const { actorId, ip, userAgent } = input;
162
+ this.assertUniqueSectionIds(input.sections.flatMap((section) => section.id ?? []));
163
+ const preparedSections = input.sections.map((section, sortOrder) => {
164
+ const definition = this.requireDefinition(section.type);
165
+ return {
166
+ id: section.id ?? randomUUID(),
167
+ isNew: section.id === undefined,
168
+ type: section.type,
169
+ config: this.parseConfig(definition, section.config),
170
+ isEnabled: section.isEnabled,
171
+ sortOrder,
172
+ };
173
+ });
174
+ await this.validateSections(preparedSections);
175
+ const { before, after } = await this.drizzle.db.transaction((tx) => withAdvisoryXactLock(tx, LAYOUT_LOCK_KEY, async () => {
176
+ await tx
177
+ .insert(lobbyLayout)
178
+ .values({ layoutKey: GLOBAL_LAYOUT_KEY })
179
+ .onConflictDoNothing({ target: lobbyLayout.layoutKey });
180
+ const [layout] = await tx
181
+ .select()
182
+ .from(lobbyLayout)
183
+ .where(eq(lobbyLayout.layoutKey, GLOBAL_LAYOUT_KEY))
184
+ .for('update');
185
+ if (!layout) {
186
+ throw new Error('Lobby layout row missing after upsert');
187
+ }
188
+ if (layout.version !== input.version) {
189
+ throw new LobbyLayoutVersionConflictError(input.version, layout.version);
190
+ }
191
+ const before = { version: layout.version, sections: await this.loadAdminSections(tx) };
192
+ this.assertExistingSectionIds(before.sections, preparedSections);
193
+ await this.replaceSections(tx, preparedSections);
194
+ const nextVersion = layout.version + 1;
195
+ await tx
196
+ .update(lobbyLayout)
197
+ .set({ version: nextVersion })
198
+ .where(eq(lobbyLayout.id, layout.id));
199
+ const after = { version: nextVersion, sections: await this.loadAdminSections(tx) };
200
+ return { before, after };
201
+ }));
202
+ await invalidate(this.cache, LAYOUT_CACHE_KEY);
203
+ this.events.emit('lobby.layout.updated', {
204
+ actorId,
205
+ ip: ip ?? null,
206
+ userAgent: userAgent ?? null,
207
+ before,
208
+ after,
209
+ });
210
+ return after;
211
+ }
212
+ async loadLayout() {
213
+ const sections = await this.drizzle.db
214
+ .select()
215
+ .from(lobbySection)
216
+ .where(eq(lobbySection.isEnabled, true))
217
+ .orderBy(asc(lobbySection.sortOrder), asc(lobbySection.createdAt));
218
+ const groups = this.groupSections(sections);
219
+ const resolvedGroups = await mapConcurrent([...groups.entries()], SECTION_OPERATION_CONCURRENCY, ([type, group]) => this.resolveSectionGroup(type, group));
220
+ const byId = new Map(resolvedGroups.flatMap((group) => [...group]));
221
+ return sections.flatMap((section) => {
222
+ const resolved = byId.get(section.id);
223
+ return resolved ? [resolved] : [];
224
+ });
225
+ }
226
+ async resolveSectionGroup(type, group) {
227
+ const definition = this.sectionCatalog.get(type);
228
+ if (!definition) {
229
+ logger.warn({ type, sectionIds: group.map((section) => section.id) }, 'Skipping lobby sections with unknown type');
230
+ return new Map();
231
+ }
232
+ const validSections = [];
233
+ for (const section of group) {
234
+ try {
235
+ validSections.push({
236
+ id: section.id,
237
+ config: definition.parseConfig(section.config),
238
+ });
239
+ }
240
+ catch (err) {
241
+ logger.warn({ err, type, sectionId: section.id }, 'Skipping lobby section with invalid config');
242
+ }
243
+ }
244
+ if (validSections.length === 0) {
245
+ return new Map();
246
+ }
247
+ let data;
248
+ try {
249
+ data = await definition.resolve(validSections);
250
+ }
251
+ catch (err) {
252
+ logger.warn({ err, type }, 'Skipping lobby sections that failed to resolve');
253
+ return new Map();
254
+ }
255
+ const resolved = new Map();
256
+ for (const section of group) {
257
+ const sectionData = data.get(section.id);
258
+ if (sectionData === undefined) {
259
+ logger.warn({ type, sectionId: section.id }, 'Skipping lobby section that was not resolved');
260
+ continue;
261
+ }
262
+ const parsedData = LobbySectionDataSchema.safeParse(sectionData);
263
+ if (!parsedData.success) {
264
+ logger.warn({ issues: parsedData.error.issues, type, sectionId: section.id }, 'Skipping lobby section with invalid resolved data');
265
+ continue;
266
+ }
267
+ resolved.set(section.id, { id: section.id, type, data: parsedData.data });
268
+ }
269
+ return resolved;
270
+ }
271
+ async validateSections(sections) {
272
+ const groups = this.groupSections(sections);
273
+ await mapConcurrent([...groups.entries()], SECTION_OPERATION_CONCURRENCY, async ([type, group]) => {
274
+ const definition = this.requireDefinition(type);
275
+ let validation;
276
+ try {
277
+ validation = await definition.validate?.(group.map((section) => ({ id: section.id, config: section.config })));
278
+ }
279
+ catch (error) {
280
+ logger.error({ err: error, type }, 'Lobby section validation failed');
281
+ throw error;
282
+ }
283
+ if (validation && !validation.valid) {
284
+ throw new LobbySectionFieldError(`Invalid '${type}' section configuration: ${validation.message}`);
285
+ }
286
+ });
287
+ }
288
+ parseConfig(definition, config) {
289
+ try {
290
+ return definition.parseConfig(config);
291
+ }
292
+ catch (error) {
293
+ throw new LobbySectionFieldError(`Invalid '${definition.type}' section configuration: ${errorMessage(error)}`);
294
+ }
295
+ }
296
+ groupSections(sections) {
297
+ const groups = new Map();
298
+ for (const section of sections) {
299
+ const group = groups.get(section.type);
300
+ if (group) {
301
+ group.push(section);
302
+ }
303
+ else {
304
+ groups.set(section.type, [section]);
305
+ }
306
+ }
307
+ return groups;
308
+ }
309
+ requireDefinition(type) {
310
+ const definition = this.sectionCatalog.get(type);
311
+ if (!definition) {
312
+ throw new LobbySectionFieldError(`Unknown lobby section type: ${type}`);
313
+ }
314
+ return definition;
315
+ }
316
+ async loadAdminSections(tx) {
317
+ const sections = await tx
318
+ .select()
319
+ .from(lobbySection)
320
+ .orderBy(asc(lobbySection.sortOrder), asc(lobbySection.createdAt));
321
+ return sections.map((section) => serializeRow(section, { dateFields: ['createdAt', 'updatedAt'] }));
322
+ }
323
+ assertUniqueSectionIds(sectionIds) {
324
+ if (new Set(sectionIds).size !== sectionIds.length) {
325
+ throw new LobbySectionFieldError('Section ids must be unique');
326
+ }
327
+ }
328
+ assertExistingSectionIds(existingSections, preparedSections) {
329
+ const existingById = new Map(existingSections.map((section) => [section.id, section]));
330
+ for (const section of preparedSections) {
331
+ if (section.isNew) {
332
+ continue;
333
+ }
334
+ const existing = existingById.get(section.id);
335
+ if (!existing) {
336
+ throw new LobbySectionNotFoundError(section.id);
337
+ }
338
+ if (existing.type !== section.type) {
339
+ throw new LobbySectionFieldError(`Section type is '${existing.type}', not '${section.type}'`);
340
+ }
341
+ }
342
+ }
343
+ async replaceSections(tx, sections) {
344
+ const sectionIds = sections.map((section) => section.id);
345
+ if (sectionIds.length === 0) {
346
+ await tx.delete(lobbySection);
347
+ return;
348
+ }
349
+ await tx.delete(lobbySection).where(notInArray(lobbySection.id, sectionIds));
350
+ await tx
351
+ .insert(lobbySection)
352
+ .values(sections.map((section) => ({
353
+ id: section.id,
354
+ type: section.type,
355
+ config: section.config,
356
+ sortOrder: section.sortOrder,
357
+ isEnabled: section.isEnabled,
358
+ })))
359
+ .onConflictDoUpdate({
360
+ target: lobbySection.id,
361
+ set: {
362
+ config: sql `excluded.config`,
363
+ sortOrder: sql `excluded.sort_order`,
364
+ isEnabled: sql `excluded.is_enabled`,
365
+ updatedAt: sql `now()`,
366
+ },
367
+ });
368
+ }
369
+ }
370
+ function errorMessage(error) {
371
+ return error instanceof Error ? error.message : 'validation failed';
132
372
  }
133
373
  //# sourceMappingURL=lobby.service.js.map