@koloseum/utils 0.3.2 → 0.3.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/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { BrowserInfo, Microservice, MicroserviceFeature, MicroserviceGroup, MicroserviceObject, MicroserviceRole, UserWithCustomMetadata } from "@koloseum/types/general";
2
- import type { PronounsCheckboxes } from "@koloseum/types/public-auth";
2
+ import type { PronounsCheckboxes } from "@koloseum/types/public/auth";
3
3
  import type { IOptions } from "@suprsend/web-components/dist/types/interface.d.ts";
4
4
  import type { Page, SubmitFunction } from "@sveltejs/kit";
5
5
  export declare const Browser: {
@@ -128,4 +128,15 @@ export declare const Interface: {
128
128
  * @returns `true` if the page is active, `false` otherwise.
129
129
  */
130
130
  pageIsActive: (page: Page, slug: string | undefined, microservice?: Microservice<MicroserviceGroup>, base?: string) => boolean;
131
+ /**
132
+ * Resolves a path and returns the result.
133
+ * Wraps around the `resolve` function in SvelteKit's `$app/paths` module.
134
+ *
135
+ * https://svelte.dev/docs/kit/$app-paths#resolve
136
+ *
137
+ * @param path - The path to resolve
138
+ * @param params - The parameters to resolve the path with
139
+ * @returns {string} The resolved path without the leading dot
140
+ */
141
+ resolvePath: (path: string, params?: Record<string, string>) => Promise<string>;
131
142
  };
package/dist/client.js CHANGED
@@ -117,7 +117,7 @@ export const Browser = {
117
117
  if (typeof window === "undefined")
118
118
  return {
119
119
  title: "Browser compatibility issue",
120
- message: "Browser detection is not available on theserver.",
120
+ message: "Browser detection is not available on the server.",
121
121
  recommendations: [],
122
122
  critical: false
123
123
  };
@@ -309,18 +309,21 @@ export const Access = {
309
309
  .map(({ roles }) => roles)
310
310
  .flat();
311
311
  let slugs;
312
- // Check if the user's role is a top-level microservice role
312
+ // Check if the user's role is a sub-role of a microservice
313
+ // Check sub-roles before root roles so that when the same slug appears in multiple microservices
314
+ // (e.g. "Manager" in Operations with `root` and in Account with `featureSlugs`), we return
315
+ // the most restrictive definition and avoid granting root where a scoped role was intended.
316
+ const subRoles = roles.filter((role) => role !== null && !("root" in role)).filter((role) => role !== null);
317
+ slugs = subRoles.map((role) => role.slug);
318
+ if (slugs.includes(role))
319
+ return subRoles.find(({ slug }) => slug === role);
320
+ // Check if the user's role is a top-level microservice role (root)
313
321
  const topLevelRoles = roles
314
322
  .filter((role) => role !== null && "root" in role && !("superuser" in role) && role.root === true)
315
323
  .filter((role) => role !== null);
316
324
  slugs = topLevelRoles.map((role) => role.slug);
317
325
  if (slugs.includes(role))
318
326
  return topLevelRoles.find(({ slug }) => slug === role);
319
- // Check if the user's role is a sub-role of a microservice
320
- const subRoles = roles.filter((role) => role !== null && !("root" in role)).filter((role) => role !== null);
321
- slugs = subRoles.map((role) => role.slug);
322
- if (slugs.includes(role))
323
- return subRoles.find(({ slug }) => slug === role);
324
327
  // Return null
325
328
  return null;
326
329
  },
@@ -393,50 +396,34 @@ export const Access = {
393
396
  // Return true if the role is superuser
394
397
  if ("superuser" in role && role.superuser === true)
395
398
  return true;
396
- /* LOUNGES */
399
+ // Define helpers
400
+ const featureMatches = (f) => "slug" in f && f.slug ? f.slug === feature.slug : feature.slug === undefined;
401
+ const isRootFeature = feature.slug === undefined;
402
+ const roleHasFeature = (r) => isRootFeature ||
403
+ ("root" in r && r.root === true) ||
404
+ ("featureSlugs" in r && feature.slug !== undefined && (r.featureSlugs?.includes(feature.slug) ?? false));
405
+ // Check through Lounges microservices
397
406
  if (type === "lounges") {
398
- // Branches
399
- const { features: branchesFeatures, roles: branchesRoles } = Config.microservices.lounges.find(({ slug }) => slug === "branches");
400
- if (branchesFeatures.some((f) => f.slug === feature.slug) && branchesRoles.some((r) => r.slug === role.slug)) {
401
- if ("root" in role && role.root === true)
407
+ const microservices = Config.microservices.lounges;
408
+ for (const ms of microservices) {
409
+ if (!ms.roles)
410
+ continue;
411
+ const featureInMs = ms.features.some(featureMatches);
412
+ const msRole = ms.roles.find((r) => r.slug === role.slug);
413
+ if (featureInMs && msRole && roleHasFeature(msRole))
402
414
  return true;
403
- if ("featureSlugs" in role)
404
- return role.featureSlugs.includes(feature.slug);
405
- }
406
- // Staff
407
- const { features: staffFeatures, roles: staffRoles } = Config.microservices.lounges.find(({ slug }) => slug === "staff");
408
- if (staffFeatures.some((f) => f.slug === feature.slug) && staffRoles.some((r) => r.slug === role.slug)) {
409
- if ("root" in role && role.root === true)
410
- return true;
411
- if ("featureSlugs" in role)
412
- return role.featureSlugs.includes(feature.slug);
413
415
  }
414
416
  }
415
- /* BACKROOM */
417
+ // Check through Backroom microservices
416
418
  if (type === "backroom") {
417
- // Compliance
418
- const { features: complianceFeatures, roles: complianceRoles } = Config.microservices.backroom.find(({ slug }) => slug === "compliance");
419
- if (complianceFeatures.some((f) => f.slug === feature.slug) && complianceRoles.some((r) => r.slug === role.slug)) {
420
- if ("root" in role && role.root === true)
419
+ const microservices = Config.microservices.backroom;
420
+ for (const ms of microservices) {
421
+ if (!ms.roles)
422
+ continue;
423
+ const featureInMs = ms.features.some(featureMatches);
424
+ const msRole = ms.roles.find((r) => r.slug === role.slug);
425
+ if (featureInMs && msRole && roleHasFeature(msRole))
421
426
  return true;
422
- if ("featureSlugs" in role)
423
- return role.featureSlugs.includes(feature.slug);
424
- }
425
- // Commerce
426
- const { features: commerceFeatures, roles: commerceRoles } = Config.microservices.backroom.find(({ slug }) => slug === "commerce");
427
- if (commerceFeatures.some((f) => f.slug === feature.slug) && commerceRoles.some((r) => r.slug === role.slug)) {
428
- if ("root" in role && role.root === true)
429
- return true;
430
- if ("featureSlugs" in role)
431
- return role.featureSlugs.includes(feature.slug);
432
- }
433
- // Staff
434
- const { features: staffFeatures, roles: staffRoles } = Config.microservices.backroom.find(({ slug }) => slug === "staff");
435
- if (staffFeatures.some((f) => f.slug === feature.slug) && staffRoles.some((r) => r.slug === role.slug)) {
436
- if ("root" in role && role.root === true)
437
- return true;
438
- if ("featureSlugs" in role)
439
- return role.featureSlugs.includes(feature.slug);
440
427
  }
441
428
  }
442
429
  // Return false
@@ -467,9 +454,12 @@ export const Interface = {
467
454
  await goto(result.location);
468
455
  return;
469
456
  }
470
- // Update form and restore button if not an OTP button
457
+ // Update form
458
+ // OTP buttons are not restored on failure/error so OTP flows keep the loading state
471
459
  await update();
472
- if (result.type === "failure" || result.type === "error" || !button.classList.contains("otp-button")) {
460
+ const isOtpButton = button.classList.contains("otp-button");
461
+ const isFailureOrError = result.type === "failure" || result.type === "error";
462
+ if (!isOtpButton || !isFailureOrError) {
473
463
  button.disabled = false;
474
464
  button.innerHTML = innerHTML;
475
465
  }
@@ -707,5 +697,20 @@ export const Interface = {
707
697
  // Match exact or subpath otherwise
708
698
  const target = `${cleanBase}/${slug}`;
709
699
  return page.url.pathname === target || page.url.pathname.startsWith(`${target}/`);
700
+ },
701
+ /**
702
+ * Resolves a path and returns the result.
703
+ * Wraps around the `resolve` function in SvelteKit's `$app/paths` module.
704
+ *
705
+ * https://svelte.dev/docs/kit/$app-paths#resolve
706
+ *
707
+ * @param path - The path to resolve
708
+ * @param params - The parameters to resolve the path with
709
+ * @returns {string} The resolved path without the leading dot
710
+ */
711
+ resolvePath: async (path, params = {}) => {
712
+ const { resolve } = await import("$app/paths");
713
+ const resolved = resolve(path, params);
714
+ return resolved.charAt(0) === "." ? resolved.slice(1) : resolved;
710
715
  }
711
716
  };
@@ -1,4 +1,4 @@
1
- import type { IdentityType, PronounsItem, Sex, SocialMediaPlatform, SocialMediaPlatformObject } from "@koloseum/types/public-auth";
1
+ import type { IdentityType, PronounsItem, Sex, SocialMediaPlatform, SocialMediaPlatformObject } from "@koloseum/types/public/auth";
2
2
  import type { MobilePhoneLocale } from "validator";
3
3
  export declare const Transform: {
4
4
  /**
@@ -13,6 +13,12 @@ export declare const Transform: {
13
13
  * @returns {string} The cleaned URL
14
14
  */
15
15
  cleanUrl: (url: string) => string;
16
+ /**
17
+ * Formats a Koloseum Credits balance in Kenyan shillings (e.g. "Ksh 12.34").
18
+ * @param {number | null | undefined} cents - The balance to be formatted
19
+ * @returns {string} The formatted balance string, or "—" if the input is invalid
20
+ */
21
+ formatCreditsBalance: (cents: number | null | undefined) => string;
16
22
  /**
17
23
  * Formats a date in the format DD/MM/YYYY (by default) or YYYY-MM-DD (for client).
18
24
  * @param {string} date - Date to be formatted
@@ -20,6 +26,12 @@ export declare const Transform: {
20
26
  * @returns The formatted date string, or `null` if invalid input or in case of an error
21
27
  */
22
28
  formatDate: (date: string, forClient?: boolean) => string | null;
29
+ /**
30
+ * Formats a Koloseum Experience Points (KXP) balance as an integer (e.g. "1,234 KXP").
31
+ * @param {number | null | undefined} kxp - The balance to be formatted
32
+ * @returns {string} The formatted balance string, or "—" if the input is invalid
33
+ */
34
+ formatKxpBalance: (kxp: number | null | undefined) => string;
23
35
  /**
24
36
  * Formats a social media platform for display.
25
37
  * @param {SocialMediaPlatform} platform - The social media platform to be formatted
@@ -21,6 +21,27 @@ export const Transform = {
21
21
  * @returns {string} The cleaned URL
22
22
  */
23
23
  cleanUrl: (url) => url.replace(/^https?:\/\//, "").replace(/\/+$/, ""),
24
+ /**
25
+ * Formats a Koloseum Credits balance in Kenyan shillings (e.g. "Ksh 12.34").
26
+ * @param {number | null | undefined} cents - The balance to be formatted
27
+ * @returns {string} The formatted balance string, or "—" if the input is invalid
28
+ */
29
+ formatCreditsBalance: (cents) => {
30
+ // Return placeholder if no balance is provided or input is invalid
31
+ if (cents == null || typeof cents !== "number")
32
+ return "—";
33
+ // Format balance
34
+ const value = cents / 100;
35
+ let formatted = new Intl.NumberFormat("en-KE", {
36
+ style: "currency",
37
+ currency: "KES",
38
+ minimumFractionDigits: 2,
39
+ maximumFractionDigits: 2
40
+ }).format(value);
41
+ formatted = formatted.replace(/\u00A0/g, " ");
42
+ // Return formatted string
43
+ return formatted;
44
+ },
24
45
  /**
25
46
  * Formats a date in the format DD/MM/YYYY (by default) or YYYY-MM-DD (for client).
26
47
  * @param {string} date - Date to be formatted
@@ -53,6 +74,17 @@ export const Transform = {
53
74
  return null;
54
75
  }
55
76
  },
77
+ /**
78
+ * Formats a Koloseum Experience Points (KXP) balance as an integer (e.g. "1,234 KXP").
79
+ * @param {number | null | undefined} kxp - The balance to be formatted
80
+ * @returns {string} The formatted balance string, or "—" if the input is invalid
81
+ */
82
+ formatKxpBalance: (kxp) => {
83
+ if (kxp == null || typeof kxp !== "number")
84
+ return "—";
85
+ const formatted = new Intl.NumberFormat("en-KE").format(kxp);
86
+ return `${formatted} KXP`;
87
+ },
56
88
  /**
57
89
  * Formats a social media platform for display.
58
90
  * @param {SocialMediaPlatform} platform - The social media platform to be formatted
@@ -1,5 +1,5 @@
1
1
  import type { CustomError, MicroserviceGroup, MicroserviceObject, UserWithCustomMetadata } from "@koloseum/types/general";
2
- import type { BranchAddressObject, County } from "@koloseum/types/public-auth";
2
+ import type { BranchAddressObject, County } from "@koloseum/types/public/auth";
3
3
  export declare const Config: {
4
4
  /**
5
5
  * A reference of microservices available on the platform, represented as an object with each user group (i.e. `public`, `players`, `lounges`, and `backroom`) having its own array of microservices. Each microservice in a list is an object with the following properties:
package/dist/platform.js CHANGED
@@ -16,427 +16,505 @@ export const Config = {
16
16
  * - `roles`: An array of roles attached to the microservice.
17
17
  */
18
18
  microservices: {
19
- backroom: [
19
+ public: [
20
20
  {
21
- name: "Compliance",
22
- description: "Management of compliance and regulatory adherence for Players and Lounges.",
23
- slug: "compliance",
21
+ name: "Koloseum",
22
+ description: "Level up your gaming experience on Kenya's premier esports platform.",
23
+ slug: "landing",
24
24
  features: [
25
25
  {
26
- name: "Players",
27
- description: "Search, review, and manage Player data.",
28
- slug: "players",
29
- tabs: [
30
- {
31
- name: "Profile",
32
- description: "Review basic Player data.",
33
- root: true
34
- },
35
- {
36
- name: "Documents",
37
- description: "Review Player documents.",
38
- slug: "documents"
39
- },
40
- {
41
- name: "Data updates",
42
- description: "Review Player data updates.",
43
- slug: "data-updates",
44
- tabs: [
45
- {
46
- name: "Requests",
47
- description: "Review Player data update requests.",
48
- root: true
49
- },
50
- {
51
- name: "History",
52
- description: "Review Player data update history.",
53
- slug: "history"
54
- }
55
- ]
56
- },
57
- {
58
- name: "Credits",
59
- description: "Review Koloseum Credits data for the Player.",
60
- slug: "credits",
61
- tabs: [
62
- {
63
- name: "Transactions",
64
- description: "Review Player transactions with Koloseum Credits.",
65
- root: true
66
- },
67
- {
68
- name: "Transfers",
69
- description: "Review Player transfers with Koloseum Credits.",
70
- slug: "transfers"
71
- },
72
- {
73
- name: "Subscriptions",
74
- description: "Review Player subscriptions with Koloseum Credits.",
75
- slug: "subscriptions"
76
- }
77
- ]
78
- },
79
- {
80
- name: "Misconduct",
81
- description: "Review Player reports and sanctions.",
82
- slug: "misconduct",
83
- tabs: [
84
- {
85
- name: "Reports",
86
- description: "Review Player reports.",
87
- root: true
88
- },
89
- {
90
- name: "Sanctions",
91
- description: "Review Player sanctions.",
92
- slug: "sanctions"
93
- }
94
- ]
95
- },
96
- {
97
- name: "Points / KXP",
98
- description: "Review Koloseum Experience Points (KXP) transactions for the Player.",
99
- slug: "points"
100
- }
101
- ]
26
+ name: "Home",
27
+ description: "Discover Koloseum and its offerings for Players and Lounges.",
28
+ root: true
29
+ }
30
+ ],
31
+ roles: null
32
+ },
33
+ {
34
+ name: "Authentication",
35
+ description: "Sign in or create an account as a Player or Lounge.",
36
+ slug: "auth",
37
+ features: [
38
+ {
39
+ name: "Login",
40
+ description: "Sign in with phone and OTP or password.",
41
+ slug: "login"
102
42
  },
103
43
  {
104
- name: "Lounges",
105
- description: "Search, review, and manage Lounge data.",
106
- slug: "lounges",
107
- tabs: [
108
- {
109
- name: "Profile",
110
- description: "Review basic Lounge data.",
111
- root: true
112
- },
113
- {
114
- name: "Documents",
115
- description: "Review Lounge documents.",
116
- slug: "documents"
117
- },
118
- {
119
- name: "Staff",
120
- description: "Search, review, and manage Lounge staff data.",
121
- slug: "staff"
122
- },
123
- {
124
- name: "Branches",
125
- description: "Review Lounge branches.",
126
- slug: "branches",
127
- tabs: [
128
- {
129
- name: "Profile",
130
- description: "Review basic Lounge branch data.",
131
- root: true
132
- },
133
- {
134
- name: "Amenities",
135
- description: "Review Lounge branch amenities.",
136
- slug: "amenities"
137
- },
138
- {
139
- name: "Staff",
140
- description: "Search, review, and manage Lounge branchstaff data.",
141
- slug: "staff"
142
- }
143
- ]
144
- },
145
- {
146
- name: "Data updates",
147
- description: "Review Lounge data updates.",
148
- slug: "data-updates",
149
- tabs: [
150
- {
151
- name: "Requests",
152
- description: "Review Lounge data update requests.",
153
- root: true
154
- },
155
- {
156
- name: "History",
157
- description: "Review Lounge data update history.",
158
- slug: "history"
159
- }
160
- ]
161
- },
162
- {
163
- name: "Credits",
164
- description: "Review Koloseum Credits data for the Lounge.",
165
- slug: "credits",
166
- tabs: [
167
- {
168
- name: "Transactions",
169
- description: "Review Lounge transactions with Koloseum Credits.",
170
- root: true
171
- },
172
- {
173
- name: "Transfers",
174
- description: "Review Lounge transfers with Koloseum Credits.",
175
- slug: "transfers"
176
- },
177
- {
178
- name: "Subscriptions",
179
- description: "Review Lounge subscriptions with Koloseum Credits.",
180
- slug: "subscriptions"
181
- }
182
- ]
183
- },
184
- {
185
- name: "Misconduct",
186
- description: "Review Lounge reports and sanctions.",
187
- slug: "misconduct",
188
- tabs: [
189
- {
190
- name: "Reports",
191
- description: "Review Lounge reports.",
192
- root: true
193
- },
194
- {
195
- name: "Sanctions",
196
- description: "Review Lounge sanctions.",
197
- slug: "sanctions"
198
- }
199
- ]
200
- },
201
- {
202
- name: "Points / KXP",
203
- description: "Review Koloseum Experience Points (KXP) transactions for the Lounge.",
204
- slug: "points"
205
- }
206
- ]
44
+ name: "Registration",
45
+ description: "Register as a Player (individual) or Lounge (business).",
46
+ slug: "registration"
207
47
  }
208
48
  ],
209
- roles: [
49
+ roles: null
50
+ },
51
+ {
52
+ name: "Help",
53
+ description: "Knowledge base and support ticket tracking for authenticated users.",
54
+ slug: "help",
55
+ features: [
210
56
  {
211
- name: "Compliance",
212
- slug: "compliance",
57
+ name: "Knowledge base",
58
+ description: "Browse and read help articles and basic guides.",
59
+ slug: "knowledge-base"
60
+ },
61
+ {
62
+ name: "Support tickets",
63
+ description: "View support ticket list and status. Create a new ticket for assistance.",
64
+ slug: "tickets"
65
+ }
66
+ ],
67
+ roles: null
68
+ },
69
+ {
70
+ name: "Legal",
71
+ description: "Review our legal policy documents.",
72
+ slug: "legal",
73
+ features: [
74
+ {
75
+ name: "Terms of Use",
76
+ description: "Find out how you can use Koloseum and the expectations we have of you.",
77
+ slug: "terms"
78
+ },
79
+ {
80
+ name: "Privacy Policy",
81
+ description: "Learn more about how we collect, use, and protect your data.",
82
+ slug: "privacy"
83
+ },
84
+ {
85
+ name: "Code of Conduct",
86
+ description: "Learn more about how you can help ensure a safe and enjoyable experience for all users.",
87
+ slug: "conduct"
88
+ }
89
+ ],
90
+ roles: null
91
+ }
92
+ ],
93
+ players: [
94
+ {
95
+ name: "Sessions",
96
+ description: "Track your gaming sessions at registered Lounges.",
97
+ slug: "sessions",
98
+ features: [
99
+ {
100
+ name: "Home",
101
+ description: "Start a session, find a Lounge, or view and manage your active session.",
213
102
  root: true
214
103
  },
215
104
  {
216
- name: "Players",
217
- slug: "players",
218
- featureSlugs: ["players"]
105
+ name: "Start Session",
106
+ description: "Search for a Lounge, choose game and station, check in or reserve, and manage your session to checkout.",
107
+ slug: "start-session"
219
108
  },
220
109
  {
221
- name: "Lounges",
222
- slug: "lounges",
223
- featureSlugs: ["lounges"]
110
+ name: "Find a Lounge",
111
+ description: "Browse Lounges, view branches and stations, start a session, or file a report.",
112
+ slug: "find-lounge"
224
113
  }
225
- ]
114
+ ],
115
+ roles: null
226
116
  },
227
117
  {
228
118
  name: "Competitions",
229
- description: "Management of competitions data and live events across Markets.",
119
+ description: "One-stop shop for all your competitive gaming needs.",
230
120
  slug: "competitions",
231
121
  features: [
232
122
  {
233
- name: "Ma Esto",
234
- description: "Manage football esports data for Ma Esto.",
235
- slug: "fbl"
236
- },
237
- {
238
- name: "Savanna FGC",
239
- description: "Manage fighting games esports data for Savanna FGC.",
240
- slug: "fgc",
241
- tabs: [
242
- {
243
- name: "Savanna Circuit",
244
- description: "Manage competition data for the Savanna Circuit.",
245
- slug: "league",
246
- tabs: [
247
- {
248
- name: "Rankings",
249
- description: "Manage rankings for any Savanna Circuit season.",
250
- root: true
251
- },
252
- {
253
- name: "Tournaments",
254
- description: "Manage tournaments for any Savanna Circuit season.",
255
- slug: "tournaments",
256
- tabs: [
257
- {
258
- name: "Events",
259
- description: "Manage events for any Savanna Circuit tournament.",
260
- root: true
261
- },
262
- {
263
- name: "Tickets",
264
- description: "Manage tickets for any Savanna Circuit tournament.",
265
- slug: "tickets"
266
- },
267
- {
268
- name: "Settings",
269
- description: "Manage settings for any Savanna Circuit tournament.",
270
- slug: "settings"
271
- }
272
- ]
273
- },
274
- {
275
- name: "Settings",
276
- description: "Manage settings for any Savanna Circuit season.",
277
- slug: "settings"
278
- }
279
- ]
280
- },
281
- {
282
- name: "Savanna Fight Night",
283
- description: "Manage competition data for Savanna Fight Night.",
284
- slug: "challenges"
285
- }
286
- ]
287
- },
288
- {
289
- name: "Hit List",
290
- description: "Manage battle royale esports data for Hit List.",
291
- slug: "bryl"
292
- }
293
- ],
294
- roles: [
295
- {
296
- name: "Competitions",
297
- slug: "competitions",
123
+ name: "Home",
124
+ description: "Discover and participate in Leagues, Challenges, and Locals. Register as an organiser to host your own events.",
298
125
  root: true
299
126
  },
300
127
  {
301
- name: "Ma Esto",
302
- slug: "fbl",
303
- featureSlugs: ["fbl"]
128
+ name: "Leagues",
129
+ description: "Browse leagues, view rankings and results, purchase tournament tickets.",
130
+ slug: "leagues"
131
+ },
132
+ {
133
+ name: "Challenges",
134
+ description: "Browse challenges, view editions, issue challenges, purchase tickets.",
135
+ slug: "challenges"
304
136
  },
305
137
  {
306
- name: "Savanna FGC",
307
- slug: "fgc",
308
- featureSlugs: ["fgc"]
138
+ name: "Locals",
139
+ description: "Browse one-off tournaments and purchase tickets.",
140
+ slug: "locals"
309
141
  },
310
142
  {
311
- name: "Hit List",
312
- slug: "bryl",
313
- featureSlugs: ["bryl"]
143
+ name: "Organisers",
144
+ description: "Register as an organiser, manage staff, view ratings and reviews.",
145
+ slug: "organisers"
314
146
  }
315
- ]
147
+ ],
148
+ roles: null
316
149
  },
317
150
  {
318
151
  name: "KLSM",
319
- description: "Management of game and console trades between users.",
152
+ description: "Trade used games and consoles with other users at affordable prices.",
320
153
  slug: "commerce",
321
- features: [],
154
+ features: [
155
+ {
156
+ name: "Store",
157
+ description: "Browse and buy Koloseum merchandise.",
158
+ slug: "store"
159
+ },
160
+ {
161
+ name: "Events",
162
+ description: "Buy tickets to register for Competitions events as a participant or spectator.",
163
+ slug: "events"
164
+ },
165
+ {
166
+ name: "Game Exchange",
167
+ description: "Discover and reserve used games and consoles for sale.",
168
+ slug: "game-exchange"
169
+ }
170
+ ],
171
+ roles: null
172
+ },
173
+ {
174
+ name: "Account",
175
+ description: "Review Player account settings and preferences.",
176
+ slug: "account",
177
+ features: [
178
+ {
179
+ name: "General",
180
+ description: "Review and manage your general settings.",
181
+ slug: "general"
182
+ },
183
+ {
184
+ name: "Notifications",
185
+ description: "Review and manage your notification preferences.",
186
+ slug: "notifications"
187
+ },
188
+ {
189
+ name: "Security",
190
+ description: "Review and manage your security preferences.",
191
+ slug: "security"
192
+ },
193
+ {
194
+ name: "Finances",
195
+ description: "Review and manage your Koloseum Credits and prize money balances.",
196
+ slug: "finances"
197
+ },
198
+ {
199
+ name: "Loyalty",
200
+ description: "Review your Koloseum Experience Points (KXP) balance and transaction history.",
201
+ slug: "loyalty"
202
+ },
203
+ {
204
+ name: "Documents",
205
+ description: "Review and manage your ID documents.",
206
+ slug: "documents"
207
+ },
208
+ {
209
+ name: "Personal information",
210
+ description: "Review and manage your personal information.",
211
+ slug: "personal"
212
+ },
213
+ {
214
+ name: "Gaming and socials",
215
+ description: "Review and manage your gaming and social media information.",
216
+ slug: "gaming-socials"
217
+ },
218
+ {
219
+ name: "Lounges",
220
+ description: "Review your access to Lounges microservices.",
221
+ slug: "lounges"
222
+ },
223
+ {
224
+ name: "Backroom",
225
+ description: "Review your access to Backroom microservices.",
226
+ slug: "backroom"
227
+ }
228
+ ],
229
+ roles: null
230
+ }
231
+ ],
232
+ lounges: [
233
+ {
234
+ name: "Operations",
235
+ description: "Review and manage your branch's day-to-day operations.",
236
+ slug: "operations",
237
+ features: [
238
+ {
239
+ name: "Dashboard",
240
+ description: "Review a summary of your branch's recent activity.",
241
+ root: true
242
+ },
243
+ {
244
+ name: "Branches",
245
+ description: "Review and manage your existing branches.",
246
+ slug: "branches"
247
+ },
248
+ {
249
+ name: "Inventory",
250
+ description: "Manage games, stations, and KLSM trading for your branch.",
251
+ slug: "inventory"
252
+ },
253
+ {
254
+ name: "Front Desk",
255
+ description: "Process check-in, station-change, and check-out requests at your branch. Open or close for the day.",
256
+ slug: "front-desk"
257
+ },
258
+ {
259
+ name: "Sessions",
260
+ description: "Review and manage sessions, discounts, and special events at your branch.",
261
+ slug: "sessions"
262
+ },
263
+ {
264
+ name: "Eatery",
265
+ description: "Review and manage menu items and orders at your branch.",
266
+ slug: "eatery"
267
+ },
268
+ {
269
+ name: "Reputation",
270
+ description: "Review and manage reviews, sanctions, and misconduct reports across your branches.",
271
+ slug: "reputation"
272
+ }
273
+ ],
322
274
  roles: [
323
275
  {
324
- name: "Commerce",
325
- slug: "commerce",
276
+ name: "Superuser",
277
+ slug: "superuser",
278
+ root: true
279
+ },
280
+ {
281
+ name: "Manager",
282
+ slug: "manager",
326
283
  root: true
284
+ },
285
+ {
286
+ name: "Branch Manager",
287
+ slug: "branch-manager",
288
+ featureSlugs: ["branches", "inventory", "front-desk", "sessions", "reputation"]
289
+ },
290
+ {
291
+ name: "Front Desk",
292
+ slug: "front-desk",
293
+ featureSlugs: ["front-desk", "sessions", "reputation"]
294
+ },
295
+ {
296
+ name: "Inventory",
297
+ slug: "inventory",
298
+ featureSlugs: ["inventory", "reputation"]
299
+ },
300
+ {
301
+ name: "Kitchen",
302
+ slug: "kitchen",
303
+ featureSlugs: ["eatery", "reputation"]
327
304
  }
328
305
  ]
329
306
  },
330
307
  {
331
308
  name: "Staff",
332
- description: "Management of Backroom staff authorisation.",
309
+ description: "Recruit staff members for your Lounge.",
333
310
  slug: "staff",
334
311
  features: [
312
+ {
313
+ name: "Dashboard",
314
+ description: "Review a summary of your Lounge's staff recruitment.",
315
+ root: true
316
+ },
335
317
  {
336
318
  name: "Roles",
337
- description: "Review and manage Backroom roles.",
319
+ description: "Review the default platform roles and their permissions.",
338
320
  slug: "roles"
339
321
  },
340
322
  {
341
323
  name: "Users",
342
- description: "Review and manage Backroom users.",
324
+ description: "Review and manage your Lounge's staff members and their roles.",
343
325
  slug: "users"
344
326
  }
345
327
  ],
346
328
  roles: [
329
+ {
330
+ name: "Superuser",
331
+ slug: "superuser",
332
+ root: true
333
+ },
347
334
  {
348
335
  name: "Human Resources",
349
336
  slug: "hr",
337
+ featureSlugs: ["roles", "users"]
338
+ }
339
+ ]
340
+ },
341
+ {
342
+ name: "Account",
343
+ description: "Review your Lounge's account settings and preferences.",
344
+ slug: "account",
345
+ features: [
346
+ {
347
+ name: "Company information",
348
+ description: "Review and update your Lounge's company information.",
349
+ slug: "company"
350
+ },
351
+ {
352
+ name: "Finances",
353
+ description: "Review your Lounge's Koloseum Credits balance, transaction history, and service subscriptions.",
354
+ slug: "finances"
355
+ },
356
+ {
357
+ name: "Ownership transfer",
358
+ description: "Request a transfer of ownership for your Lounge account.",
359
+ slug: "transfer"
360
+ }
361
+ ],
362
+ roles: [
363
+ {
364
+ name: "Superuser",
365
+ slug: "superuser",
350
366
  root: true
367
+ },
368
+ {
369
+ name: "Manager",
370
+ slug: "manager",
371
+ featureSlugs: ["finances"]
351
372
  }
352
373
  ]
353
374
  }
354
375
  ],
355
- players: [
376
+ backroom: [
356
377
  {
357
- name: "Sessions",
358
- description: "Track your gaming sessions at registered lounges.",
359
- slug: "sessions",
360
- features: [],
361
- roles: null
378
+ name: "Compliance",
379
+ description: "Review and manage compliance and regulatory adherence for Players and Lounges.",
380
+ slug: "compliance",
381
+ features: [
382
+ {
383
+ name: "Dashboard",
384
+ description: "Review a summary of platform registration and usage.",
385
+ slug: "dashboard"
386
+ },
387
+ {
388
+ name: "Players",
389
+ description: "Search, review, and manage Player data.",
390
+ slug: "players"
391
+ },
392
+ {
393
+ name: "Lounges",
394
+ description: "Search, review, and manage Lounge data.",
395
+ slug: "lounges"
396
+ }
397
+ ],
398
+ roles: [
399
+ {
400
+ name: "Superuser",
401
+ slug: "superuser",
402
+ root: true
403
+ },
404
+ {
405
+ name: "Compliance",
406
+ slug: "compliance",
407
+ root: true
408
+ },
409
+ {
410
+ name: "Players",
411
+ slug: "players",
412
+ featureSlugs: ["dashboard", "players"]
413
+ },
414
+ {
415
+ name: "Lounges",
416
+ slug: "lounges",
417
+ featureSlugs: ["dashboard", "lounges"]
418
+ }
419
+ ]
362
420
  },
363
421
  {
364
422
  name: "KLSM",
365
- description: "Trade used games and consoles with other users at affordable prices.",
423
+ description: "Monitor and regulate commercial activity on the platform.",
366
424
  slug: "commerce",
367
- features: [],
368
- roles: null
369
- },
370
- {
371
- name: "Savanna FGC",
372
- description: "Check out the latest news and competitions from Savanna FGC.",
373
- slug: "fgc",
374
425
  features: [
375
426
  {
376
- name: "Home",
377
- description: "Learn more about the Savanna Circuit.",
378
- root: true
427
+ name: "Dashboard",
428
+ description: "Review a summary of commercial activity on the platform.",
429
+ slug: "dashboard"
379
430
  },
380
431
  {
381
- name: "Rules",
382
- description: "Review the rules and regulations of the Savanna Circuit.",
383
- slug: "rules"
432
+ name: "Storefront",
433
+ description: "Review and manage merchandise listings and discounts. Track orders and revenue.",
434
+ slug: "storefront"
384
435
  },
385
436
  {
386
- name: "Tournaments",
387
- description: "Review and join upcoming tournaments on the Savanna Circuit.",
388
- slug: "tournaments"
437
+ name: "Warehouse",
438
+ description: "Review and manage merchandise inventory. Process orders and track deliveries.",
439
+ slug: "warehouse"
440
+ },
441
+ {
442
+ name: "Events",
443
+ description: "Review and manage competition tickets. Track attendance and revenue.",
444
+ slug: "events"
445
+ },
446
+ {
447
+ name: "Game Exchange",
448
+ description: "Review and manage used game and console listings and track revenue.",
449
+ slug: "game-exchange"
389
450
  }
390
451
  ],
391
- roles: null
392
- },
393
- {
394
- name: "Account",
395
- description: "Review Player account settings and preferences.",
396
- slug: "account",
397
- features: [
452
+ roles: [
398
453
  {
399
- name: "General",
400
- description: "Review and manage your general settings.",
401
- slug: "general"
454
+ name: "Superuser",
455
+ slug: "superuser",
456
+ root: true
402
457
  },
403
458
  {
404
- name: "Documents",
405
- description: "Review and manage your ID documents.",
406
- slug: "documents"
459
+ name: "Commerce",
460
+ slug: "commerce",
461
+ root: true
407
462
  },
408
463
  {
409
- name: "Personal",
410
- description: "Review and manage your personal information.",
411
- slug: "personal"
464
+ name: "Storefront",
465
+ slug: "storefront",
466
+ featureSlugs: ["dashboard", "storefront"]
412
467
  },
413
468
  {
414
- name: "Gaming & Socials",
415
- description: "Review and manage your gaming and social media information.",
416
- slug: "gaming-socials"
469
+ name: "Warehouse",
470
+ slug: "warehouse",
471
+ featureSlugs: ["dashboard", "warehouse"]
417
472
  },
418
473
  {
419
- name: "Notifications",
420
- description: "Review and manage your notification preferences.",
421
- slug: "notifications"
474
+ name: "Box Office",
475
+ slug: "box-office",
476
+ featureSlugs: ["dashboard", "events"]
422
477
  },
423
478
  {
424
- name: "Security",
425
- description: "Review and manage your security preferences.",
426
- slug: "security"
479
+ name: "Game Exchange",
480
+ slug: "game-exchange",
481
+ featureSlugs: ["dashboard", "game-exchange"]
482
+ }
483
+ ]
484
+ },
485
+ {
486
+ name: "Staff",
487
+ description: "Review and manage Koloseum staff and their roles.",
488
+ slug: "staff",
489
+ features: [
490
+ {
491
+ name: "Dashboard",
492
+ description: "Review a summary of staff recruitment for the platform.",
493
+ root: true
427
494
  },
428
495
  {
429
- name: "Lounges",
430
- description: "Review your access to Lounges microservices.",
431
- slug: "lounges"
496
+ name: "Roles",
497
+ description: "Review and manage Backroom roles and their permissions.",
498
+ slug: "roles"
432
499
  },
433
500
  {
434
- name: "Backroom",
435
- description: "Review your access to Backroom microservices.",
436
- slug: "backroom"
501
+ name: "Users",
502
+ description: "Review and manage Backroom users and their roles.",
503
+ slug: "users"
437
504
  }
438
505
  ],
439
- roles: null
506
+ roles: [
507
+ {
508
+ name: "Superuser",
509
+ slug: "superuser",
510
+ root: true
511
+ },
512
+ {
513
+ name: "Human Resources",
514
+ slug: "hr",
515
+ root: true
516
+ }
517
+ ]
440
518
  }
441
519
  ]
442
520
  }
@@ -581,7 +659,7 @@ export const Resource = {
581
659
  }
582
660
  // Handle Landing microservice
583
661
  else if (path.startsWith("landing")) {
584
- port = 5184;
662
+ port = 5186;
585
663
  path = path.replace("landing", "");
586
664
  }
587
665
  // Return redirect URL
package/dist/server.d.ts CHANGED
@@ -16,6 +16,18 @@ export declare const Instance: {
16
16
  error?: string;
17
17
  context?: Record<string, any>;
18
18
  }>;
19
+ /**
20
+ * Gets the current Koloseum Experience Points (KXP) and Koloseum Credits balances for the authenticated Player.
21
+ * @param {SupabaseClient<Database>} supabase - The Supabase client
22
+ * @returns {Promise<{ balances?: { kxp: number; credits: number }; error?: CustomError }>} The Player's current `balances`, or an `error` if any has occurred
23
+ */
24
+ getPlayerBalances: (supabase: SupabaseClient<Database>) => Promise<{
25
+ balances?: {
26
+ kxp: number;
27
+ credits: number;
28
+ };
29
+ error?: CustomError;
30
+ }>;
19
31
  /**
20
32
  * Checks if a user is authorised to access a microservice.
21
33
  * @param {SupabaseClient<Database>} supabase - The Supabase client.
package/dist/server.js CHANGED
@@ -71,6 +71,28 @@ export const Instance = {
71
71
  return { code: 500, error: Status.ERROR };
72
72
  }
73
73
  },
74
+ /**
75
+ * Gets the current Koloseum Experience Points (KXP) and Koloseum Credits balances for the authenticated Player.
76
+ * @param {SupabaseClient<Database>} supabase - The Supabase client
77
+ * @returns {Promise<{ balances?: { kxp: number; credits: number }; error?: CustomError }>} The Player's current `balances`, or an `error` if any has occurred
78
+ */
79
+ getPlayerBalances: async (supabase) => {
80
+ // Initialise balances
81
+ let kxp = 0;
82
+ let credits = 0;
83
+ // Get Player balances
84
+ const { data: balances, error: balancesError } = await supabase.schema("compliance").rpc("get_player_balances");
85
+ if (balancesError)
86
+ return Exception.parsePostgrestError(balancesError);
87
+ // Assign balances
88
+ if (balances && typeof balances === "object" && "kxp" in balances && "credits" in balances) {
89
+ const b = balances;
90
+ kxp = b.kxp ?? 0;
91
+ credits = b.credits ?? 0;
92
+ }
93
+ // Return balances
94
+ return { balances: { kxp, credits } };
95
+ },
74
96
  /**
75
97
  * Checks if a user is authorised to access a microservice.
76
98
  * @param {SupabaseClient<Database>} supabase - The Supabase client.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koloseum/utils",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "author": "Koloseum Technologies Limited",
5
5
  "type": "module",
6
6
  "description": "Utility logic for use across Koloseum web apps (TypeScript)",
@@ -48,7 +48,8 @@
48
48
  "build": "rm -rf dist && npx tsc",
49
49
  "check": "npx tsc --noEmit && prettier --check .",
50
50
  "format": "prettier --write .",
51
- "test": "vitest --run"
51
+ "test": "vitest --run",
52
+ "test:coverage": "vitest run --coverage"
52
53
  },
53
54
  "dependencies": {
54
55
  "@supabase/supabase-js": "^2.57.4",
@@ -61,12 +62,13 @@
61
62
  "validator": "^13.15.15"
62
63
  },
63
64
  "devDependencies": {
64
- "@koloseum/types": "^0.3.0",
65
+ "@koloseum/types": "^0.3.3",
65
66
  "@playwright/test": "^1.55.0",
66
67
  "@suprsend/web-components": "^0.4.0",
67
68
  "@types/sanitize-html": "^2.16.0",
68
69
  "@types/uuid": "^10.0.0",
69
70
  "@types/validator": "^13.15.3",
71
+ "@vitest/coverage-v8": "^3.2.4",
70
72
  "prettier": "^3.6.2",
71
73
  "typescript": "^5.9.2",
72
74
  "vitest": "^3.2.4"