@koloseum/utils 0.1.17 → 0.2.0

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 (3) hide show
  1. package/dist/utils.d.ts +43 -44
  2. package/dist/utils.js +375 -81
  3. package/package.json +13 -13
package/dist/utils.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import type { Database } from "@koloseum/types/database";
2
- import type { CustomError, Microservice, MicroserviceGroup, MicroserviceObject, UserWithAppMetadata } from "@koloseum/types/general";
3
- import type { PronounsCheckboxes, SocialMediaPlatform } from "@koloseum/types/public-auth";
2
+ import type { CustomError, Microservice, MicroserviceGroup, MicroserviceObject, UserWithCustomMetadata } from "@koloseum/types/general";
3
+ import type { IdentityType, PronounsCheckboxes, PronounsItem, Sex, SocialMediaPlatform } from "@koloseum/types/public-auth";
4
4
  import type { Page } from "@sveltejs/kit";
5
- import type { SupabaseClient, PostgrestError } from "@supabase/supabase-js";
6
- import type { suprsend } from "@suprsend/node-sdk";
5
+ import type { SupabaseClient, FunctionInvokeOptions, PostgrestError } from "@supabase/supabase-js";
7
6
  import type { IOptions } from "@suprsend/web-components/dist/types/interface.d.ts";
8
7
  import type { MobilePhoneLocale } from "validator";
9
8
  export declare const Data: {
@@ -15,7 +14,7 @@ export declare const Data: {
15
14
  * @param {string} identityId - A default identity ID; defaults to a random UUID
16
15
  * @returns A generic authenticated user.
17
16
  */
18
- authenticatedUser: (id?: string, date?: Date, phone?: string, identityId?: string) => UserWithAppMetadata;
17
+ authenticatedUser: (id?: string, date?: Date, phone?: string, identityId?: string) => UserWithCustomMetadata;
19
18
  };
20
19
  export declare const Status: {
21
20
  /**
@@ -37,10 +36,10 @@ export declare const Utility: {
37
36
  * @param {boolean} browser - Whether the function is being called in the browser
38
37
  * @param {SupabaseClient} supabase - The Supabase client
39
38
  * @param {string} path - The path to the Edge function
40
- * @param {"GET" | "POST"} [method="GET"] - The HTTP method to use
39
+ * @param {FunctionInvokeOptions} [options] - The options to use for the function invocation; defaults to `{ method: "GET" }`
41
40
  * @returns The response from the Edge function
42
41
  */
43
- callEdgeFunction: (browser: boolean, supabase: SupabaseClient<Database>, path: string, method?: "GET" | "POST") => Promise<{
42
+ callEdgeFunction: (browser: boolean, supabase: SupabaseClient<Database>, path: string, options?: FunctionInvokeOptions) => Promise<{
44
43
  code: number;
45
44
  data?: any;
46
45
  error?: string;
@@ -52,6 +51,12 @@ export declare const Utility: {
52
51
  * @returns The capitalised word
53
52
  */
54
53
  capitalise: (word: string) => string;
54
+ /**
55
+ * Cleans a URL by removing the protocol and trailing slashes.
56
+ * @param {string} url - The URL to clean
57
+ * @returns {string} The cleaned URL
58
+ */
59
+ cleanUrl: (url: string) => string;
55
60
  /**
56
61
  * Returns a custom error object.
57
62
  * @param {number} code - The error code; defaults to `500` if not provided or invalid
@@ -85,30 +90,40 @@ export declare const Utility: {
85
90
  */
86
91
  getAge: (dateOfBirth: string) => number;
87
92
  /**
88
- * Generate a SuprSend configuration object for a user.
93
+ * Formats a date as a locale-specific string.
94
+ * @param {Date | string} date - Date to be formatted
95
+ * @param {boolean} withTime - Specify whether the date should include time; defaults to `false`
96
+ * @param {string} timeZone - The time zone to use for formatting; defaults to `Africa/Nairobi`
97
+ * @returns The formatted date string, or `null` if invalid input
98
+ */
99
+ getDateString: (date: Date | string | null, withTime?: boolean, timeZone?: string) => string | null;
100
+ /**
101
+ * Formats an identity type for display.
102
+ * @param {IdentityType} identityType - The identity type to be formatted
103
+ * @returns The formatted identity type string
104
+ */
105
+ getIdentityTypeString: (identityType: IdentityType) => string;
106
+ /**
107
+ * Returns a pronoun given pronouns and a type.
108
+ * @param {PronounsItem | null} pronouns - The pronouns to be formatted
109
+ * @param {"subject" | "object" | "possessive" | "reflexive"} type - The type of pronoun to be returned
110
+ * @param {Sex} sex - The user's sex; defaults to `undefined`
111
+ * @returns The formatted pronoun, or `null` if the input is invalid
112
+ */
113
+ getPronoun: (pronouns: PronounsItem | null, type: "subject" | "object" | "possessive" | "reflexive", sex?: Sex) => string | null;
114
+ /**
115
+ * Generate a SuprSend notification inbox configuration object for a user.
89
116
  * @param {string} userId - The user ID to generate the configuration for.
90
117
  * @param {string} publicApiKey - The public API key to use for SuprSend.
91
- * @returns The SuprSend configuration object.
118
+ * @returns The SuprSend notification inbox configuration object.
92
119
  */
93
- getSuprSendConfig: (userId: string, publicApiKey: string) => IOptions;
120
+ getSuprSendInboxConfig: (userId: string, publicApiKey: string) => IOptions;
94
121
  /**
95
122
  * Handles the click event for pronouns checkboxes.
96
123
  * @param {MouseEvent} e - The click event
97
124
  * @param {PronounsCheckboxes} checkboxes - The pronouns checkboxes
98
125
  */
99
126
  handlePronounsCheckboxClick: (e: MouseEvent, checkboxes: PronounsCheckboxes) => void;
100
- /**
101
- * A regular expression for Koloseum Lounge Branch IDs. It covers the following rules:
102
- * - 9 characters long
103
- * - begins with "KLB" followed by 7 digits
104
- */
105
- loungeBranchIdRegex: RegExp;
106
- /**
107
- * A regular expression for Koloseum Lounge IDs. It covers the following rules:
108
- * - 9 characters long
109
- * - begins with "KL" followed by 7 digits
110
- */
111
- loungeIdRegex: RegExp;
112
127
  /**
113
128
  * 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:
114
129
  * - `name`: The name of the microservice.
@@ -130,13 +145,13 @@ export declare const Utility: {
130
145
  * @param base - The base path of the application; defaults to an empty string.
131
146
  * @returns `true` if the page is active, `false` otherwise.
132
147
  */
133
- pageIsActive: (page: Page, slug: string, microservice?: Microservice<MicroserviceGroup>, base?: string) => boolean;
148
+ pageIsActive: (page: Page, slug: string | undefined, microservice?: Microservice<MicroserviceGroup>, base?: string) => boolean;
134
149
  /**
135
150
  * Parses a `CustomError` object within a page/layout server load function and returns an error to be thrown.
136
151
  * @param {CustomError} error - The error object
137
152
  * @returns A new `Error` object if the error is unexpected, or a Svelte error otherwise
138
153
  */
139
- parseLoadError: (error: CustomError) => Error;
154
+ parseLoadError: (error: CustomError) => Error | never;
140
155
  /**
141
156
  * Parses a `PostgrestError` object and returns a custom error object if any has occurred.
142
157
  * @param {PostgrestError | null} postgrestError - The `PostgrestError` object, or `null` if no error occurred
@@ -155,11 +170,11 @@ export declare const Utility: {
155
170
  */
156
171
  passwordRegex: RegExp;
157
172
  /**
158
- * A regular expression for Koloseum Player IDs. It covers the following rules:
159
- * - 9 characters long
160
- * - begins with "KP" followed by 7 digits
173
+ * Returns a regular expression for a Koloseum platform ID.
174
+ * @param {string} type - The type of platform ID to return
175
+ * @returns A regular expression for the platform ID, or `null` if the type is invalid
161
176
  */
162
- playerIdRegex: RegExp;
177
+ platformIdRegex: (type: "lounge" | "loungeBranch" | "player") => RegExp | null;
163
178
  /**
164
179
  * Sanitises any potential HTML injected into user input.
165
180
  * @param {string} input - The input to be sanitised
@@ -185,20 +200,4 @@ export declare const Utility: {
185
200
  * @returns `true` if the handle is valid; `false` otherwise
186
201
  */
187
202
  validateSocialMediaHandle: (handle: string) => boolean;
188
- /**
189
- * Validates a SuprSend subscriber. If the subscriber does not exist, it will be created with the provided person data.
190
- * @param {suprsend.Suprsend} suprSend - The SuprSend instance.
191
- * @param {string} userId - The user ID.
192
- * @param {Object} personData - The person data; required if the subscriber does not exist.
193
- * @returns {Promise<{ error?: CustomError; success?: true }>} A promise that resolves to an object with an error if the subscriber validation fails, or indicating success otherwise.
194
- */
195
- validateSuprSendSubscriber: (suprSend: suprsend.Suprsend, userId: string, personData?: {
196
- firstName: string;
197
- lastName: string;
198
- phone: string;
199
- pseudonym?: string;
200
- }) => Promise<{
201
- error?: CustomError;
202
- success?: true;
203
- }>;
204
203
  };
package/dist/utils.js CHANGED
@@ -38,7 +38,13 @@ export const Data = {
38
38
  user_metadata: {
39
39
  email_verified: false,
40
40
  phone_verified: false,
41
- sub: id
41
+ sub: id,
42
+ backroom: {
43
+ welcome_notification_sent: true
44
+ },
45
+ players: {
46
+ welcome_notification_sent: true
47
+ }
42
48
  },
43
49
  identities: [
44
50
  {
@@ -84,31 +90,25 @@ export const Utility = {
84
90
  * @param {boolean} browser - Whether the function is being called in the browser
85
91
  * @param {SupabaseClient} supabase - The Supabase client
86
92
  * @param {string} path - The path to the Edge function
87
- * @param {"GET" | "POST"} [method="GET"] - The HTTP method to use
93
+ * @param {FunctionInvokeOptions} [options] - The options to use for the function invocation; defaults to `{ method: "GET" }`
88
94
  * @returns The response from the Edge function
89
95
  */
90
- callEdgeFunction: async (browser, supabase, path, method = "GET") => {
96
+ callEdgeFunction: async (browser, supabase, path, options = { method: "GET" }) => {
91
97
  try {
92
- // Set up headers with safe origin handling for SSR
93
- const headers = {
94
- "Content-Type": "application/json",
95
- Accept: "application/json"
96
- };
97
98
  // Only add Origin header in browser environment
98
99
  if (browser)
99
- headers["Origin"] = window.location.origin;
100
+ options.headers = { ...options.headers, Origin: window.location.origin };
100
101
  // Fetch response with additional options for better cross-origin handling
101
- const { data, error } = await supabase.functions.invoke(path, {
102
- method,
103
- headers
104
- });
102
+ const { data, error } = await supabase.functions.invoke(path, options);
105
103
  // Handle different error types
106
104
  if (error) {
107
105
  // Define context
108
106
  const context = {};
109
107
  // Define error code and message
110
108
  const code = Number(error.context.status) || 500;
111
- let message = Status.ERROR;
109
+ let message = error.context.statusText === "Internal Server Error"
110
+ ? Status.ERROR
111
+ : error.context.statusText || Status.ERROR;
112
112
  // Handle HTTP errors
113
113
  if (error instanceof FunctionsHttpError) {
114
114
  // Get error data
@@ -132,8 +132,8 @@ export const Utility = {
132
132
  if (path.includes("verify-id") && data.attemptId)
133
133
  context.attemptId = data.attemptId;
134
134
  }
135
- catch (error) {
136
- console.error(error);
135
+ catch {
136
+ return { code: 500, error: Status.ERROR };
137
137
  }
138
138
  }
139
139
  // Handle relay and fetch errors
@@ -145,7 +145,7 @@ export const Utility = {
145
145
  // Return response
146
146
  return { code: 200, data };
147
147
  }
148
- catch (_err) {
148
+ catch {
149
149
  return { code: 500, error: Status.ERROR };
150
150
  }
151
151
  },
@@ -160,6 +160,12 @@ export const Utility = {
160
160
  .split("")
161
161
  .map((c) => c.toLowerCase())
162
162
  .join(""),
163
+ /**
164
+ * Cleans a URL by removing the protocol and trailing slashes.
165
+ * @param {string} url - The URL to clean
166
+ * @returns {string} The cleaned URL
167
+ */
168
+ cleanUrl: (url) => url.replace(/^https?:\/\//, "").replace(/\/+$/, ""),
163
169
  /**
164
170
  * Returns a custom error object.
165
171
  * @param {number} code - The error code; defaults to `500` if not provided or invalid
@@ -201,7 +207,7 @@ export const Utility = {
201
207
  return formattedDate;
202
208
  }
203
209
  catch (error) {
204
- console.log(error);
210
+ console.error(error);
205
211
  return null;
206
212
  }
207
213
  },
@@ -238,12 +244,157 @@ export const Utility = {
238
244
  return monthDiff < 0 || (monthDiff === 0 && currentDate.getDate() < birthDate.getDate()) ? --age : age;
239
245
  },
240
246
  /**
241
- * Generate a SuprSend configuration object for a user.
247
+ * Formats a date as a locale-specific string.
248
+ * @param {Date | string} date - Date to be formatted
249
+ * @param {boolean} withTime - Specify whether the date should include time; defaults to `false`
250
+ * @param {string} timeZone - The time zone to use for formatting; defaults to `Africa/Nairobi`
251
+ * @returns The formatted date string, or `null` if invalid input
252
+ */
253
+ getDateString: (date, withTime = false, timeZone = "Africa/Nairobi") => {
254
+ // Return null if no date is provided
255
+ if (!date)
256
+ return null;
257
+ // Validate date to format
258
+ let dateToFormat;
259
+ if (typeof date === "string") {
260
+ if (date === "" || isNaN(Date.parse(date)))
261
+ return null;
262
+ dateToFormat = new Date(date);
263
+ }
264
+ else if (date instanceof Date) {
265
+ if (isNaN(date.getTime()))
266
+ return null;
267
+ dateToFormat = date;
268
+ }
269
+ else
270
+ return null;
271
+ // Format date
272
+ return dateToFormat.toLocaleString("en-KE", {
273
+ timeZone,
274
+ year: "numeric",
275
+ month: "long",
276
+ day: "numeric",
277
+ hour: withTime ? "numeric" : undefined,
278
+ minute: withTime ? "2-digit" : undefined,
279
+ hour12: withTime ? true : undefined
280
+ });
281
+ },
282
+ /**
283
+ * Formats an identity type for display.
284
+ * @param {IdentityType} identityType - The identity type to be formatted
285
+ * @returns The formatted identity type string
286
+ */
287
+ getIdentityTypeString: (identityType) => {
288
+ if (identityType === "national")
289
+ return "National ID";
290
+ if (identityType === "alien")
291
+ return "Alien ID";
292
+ if (identityType === "passport")
293
+ return "Passport";
294
+ if (identityType === "driver_licence")
295
+ return "Driver's licence";
296
+ return "";
297
+ },
298
+ /**
299
+ * Returns a pronoun given pronouns and a type.
300
+ * @param {PronounsItem | null} pronouns - The pronouns to be formatted
301
+ * @param {"subject" | "object" | "possessive" | "reflexive"} type - The type of pronoun to be returned
302
+ * @param {Sex} sex - The user's sex; defaults to `undefined`
303
+ * @returns The formatted pronoun, or `null` if the input is invalid
304
+ */
305
+ getPronoun: (pronouns, type, sex) => {
306
+ // Get pronoun from pronouns item
307
+ if (pronouns) {
308
+ // Return subject pronoun
309
+ if (type === "subject") {
310
+ if (pronouns === "he/him/his/himself")
311
+ return "he";
312
+ if (pronouns === "she/her/hers/herself")
313
+ return "she";
314
+ if (pronouns === "they/them/their/themself")
315
+ return "they";
316
+ if (pronouns === "name_only")
317
+ return "";
318
+ }
319
+ // Return object pronoun
320
+ else if (type === "object") {
321
+ if (pronouns === "he/him/his/himself")
322
+ return "him";
323
+ if (pronouns === "she/her/hers/herself")
324
+ return "her";
325
+ if (pronouns === "they/them/their/themself")
326
+ return "them";
327
+ if (pronouns === "name_only")
328
+ return "";
329
+ }
330
+ // Return possessive pronoun
331
+ else if (type === "possessive") {
332
+ if (pronouns === "he/him/his/himself")
333
+ return "his";
334
+ if (pronouns === "she/her/hers/herself")
335
+ return "her";
336
+ if (pronouns === "they/them/their/themself")
337
+ return "their";
338
+ if (pronouns === "name_only")
339
+ return "";
340
+ }
341
+ // Return reflexive pronoun
342
+ else if (type === "reflexive") {
343
+ if (pronouns === "he/him/his/himself")
344
+ return "himself";
345
+ if (pronouns === "she/her/hers/herself")
346
+ return "herself";
347
+ if (pronouns === "they/them/their/themself")
348
+ return "themself";
349
+ if (pronouns === "name_only")
350
+ return "";
351
+ }
352
+ }
353
+ // Assume pronoun for sex if no pronouns are provided
354
+ else if (sex) {
355
+ // Return subject pronoun
356
+ if (type === "subject") {
357
+ if (sex === "male" || sex === "intersex_man")
358
+ return "he";
359
+ if (sex === "female" || sex === "intersex_woman")
360
+ return "she";
361
+ return "they";
362
+ }
363
+ // Return object pronoun
364
+ else if (type === "object") {
365
+ if (sex === "male" || sex === "intersex_man")
366
+ return "him";
367
+ if (sex === "female" || sex === "intersex_woman")
368
+ return "her";
369
+ return "them";
370
+ }
371
+ // Return possessive pronoun
372
+ else if (type === "possessive") {
373
+ if (sex === "male" || sex === "intersex_man")
374
+ return "his";
375
+ if (sex === "female" || sex === "intersex_woman")
376
+ return "her";
377
+ return "their";
378
+ }
379
+ // Return reflexive pronoun
380
+ else if (type === "reflexive") {
381
+ if (sex === "male" || sex === "intersex_man")
382
+ return "himself";
383
+ if (sex === "female" || sex === "intersex_woman")
384
+ return "herself";
385
+ return "themself";
386
+ }
387
+ }
388
+ // Return null
389
+ return null;
390
+ },
391
+ /**
392
+ * Generate a SuprSend notification inbox configuration object for a user.
242
393
  * @param {string} userId - The user ID to generate the configuration for.
243
394
  * @param {string} publicApiKey - The public API key to use for SuprSend.
244
- * @returns The SuprSend configuration object.
395
+ * @returns The SuprSend notification inbox configuration object.
245
396
  */
246
- getSuprSendConfig: (userId, publicApiKey) => ({
397
+ getSuprSendInboxConfig: (userId, publicApiKey) => ({
247
398
  distinctId: userId,
248
399
  publicApiKey,
249
400
  inbox: {
@@ -430,18 +581,6 @@ export const Utility = {
430
581
  }
431
582
  }
432
583
  },
433
- /**
434
- * A regular expression for Koloseum Lounge Branch IDs. It covers the following rules:
435
- * - 9 characters long
436
- * - begins with "KLB" followed by 7 digits
437
- */
438
- loungeBranchIdRegex: /^KLB\d{7}$/,
439
- /**
440
- * A regular expression for Koloseum Lounge IDs. It covers the following rules:
441
- * - 9 characters long
442
- * - begins with "KL" followed by 7 digits
443
- */
444
- loungeIdRegex: /^KL\d{7}$/,
445
584
  /**
446
585
  * 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:
447
586
  * - `name`: The name of the microservice.
@@ -459,13 +598,186 @@ export const Utility = {
459
598
  features: [
460
599
  {
461
600
  name: "Players",
462
- description: "Manage Player data.",
463
- slug: "players"
601
+ description: "Search, review, and manage Player data.",
602
+ slug: "players",
603
+ tabs: [
604
+ {
605
+ name: "Profile",
606
+ description: "Review basic Player data.",
607
+ root: true
608
+ },
609
+ {
610
+ name: "Documents",
611
+ description: "Review Player documents.",
612
+ slug: "documents"
613
+ },
614
+ {
615
+ name: "Data updates",
616
+ description: "Review Player data updates.",
617
+ slug: "data-updates",
618
+ tabs: [
619
+ {
620
+ name: "Requests",
621
+ description: "Review Player data update requests.",
622
+ root: true
623
+ },
624
+ {
625
+ name: "History",
626
+ description: "Review Player data update history.",
627
+ slug: "history"
628
+ }
629
+ ]
630
+ },
631
+ {
632
+ name: "Credits",
633
+ description: "Review Koloseum Credits data for the Player.",
634
+ slug: "credits",
635
+ tabs: [
636
+ {
637
+ name: "Transactions",
638
+ description: "Review Player transactions with Koloseum Credits.",
639
+ root: true
640
+ },
641
+ {
642
+ name: "Transfers",
643
+ description: "Review Player transfers with Koloseum Credits.",
644
+ slug: "transfers"
645
+ },
646
+ {
647
+ name: "Subscriptions",
648
+ description: "Review Player subscriptions with Koloseum Credits.",
649
+ slug: "subscriptions"
650
+ }
651
+ ]
652
+ },
653
+ {
654
+ name: "Misconduct",
655
+ description: "Review Player reports and sanctions.",
656
+ slug: "misconduct",
657
+ tabs: [
658
+ {
659
+ name: "Reports",
660
+ description: "Review Player reports.",
661
+ root: true
662
+ },
663
+ {
664
+ name: "Sanctions",
665
+ description: "Review Player sanctions.",
666
+ slug: "sanctions"
667
+ }
668
+ ]
669
+ },
670
+ {
671
+ name: "Points / KXP",
672
+ description: "Review Koloseum Experience Points (KXP) transactions for the Player.",
673
+ slug: "points"
674
+ }
675
+ ]
464
676
  },
465
677
  {
466
678
  name: "Lounges",
467
- description: "Manage Lounge data.",
468
- slug: "lounges"
679
+ description: "Search, review, and manage Lounge data.",
680
+ slug: "lounges",
681
+ tabs: [
682
+ {
683
+ name: "Profile",
684
+ description: "Review basic Lounge data.",
685
+ root: true
686
+ },
687
+ {
688
+ name: "Documents",
689
+ description: "Review Lounge documents.",
690
+ slug: "documents"
691
+ },
692
+ {
693
+ name: "Staff",
694
+ description: "Search, review, and manage Lounge staff data.",
695
+ slug: "staff"
696
+ },
697
+ {
698
+ name: "Branches",
699
+ description: "Review Lounge branches.",
700
+ slug: "branches",
701
+ tabs: [
702
+ {
703
+ name: "Profile",
704
+ description: "Review basic Lounge branch data.",
705
+ root: true
706
+ },
707
+ {
708
+ name: "Amenities",
709
+ description: "Review Lounge branch amenities.",
710
+ slug: "amenities"
711
+ },
712
+ {
713
+ name: "Staff",
714
+ description: "Search, review, and manage Lounge branchstaff data.",
715
+ slug: "staff"
716
+ }
717
+ ]
718
+ },
719
+ {
720
+ name: "Data updates",
721
+ description: "Review Lounge data updates.",
722
+ slug: "data-updates",
723
+ tabs: [
724
+ {
725
+ name: "Requests",
726
+ description: "Review Lounge data update requests.",
727
+ root: true
728
+ },
729
+ {
730
+ name: "History",
731
+ description: "Review Lounge data update history.",
732
+ slug: "history"
733
+ }
734
+ ]
735
+ },
736
+ {
737
+ name: "Credits",
738
+ description: "Review Koloseum Credits data for the Lounge.",
739
+ slug: "credits",
740
+ tabs: [
741
+ {
742
+ name: "Transactions",
743
+ description: "Review Lounge transactions with Koloseum Credits.",
744
+ root: true
745
+ },
746
+ {
747
+ name: "Transfers",
748
+ description: "Review Lounge transfers with Koloseum Credits.",
749
+ slug: "transfers"
750
+ },
751
+ {
752
+ name: "Subscriptions",
753
+ description: "Review Lounge subscriptions with Koloseum Credits.",
754
+ slug: "subscriptions"
755
+ }
756
+ ]
757
+ },
758
+ {
759
+ name: "Misconduct",
760
+ description: "Review Lounge reports and sanctions.",
761
+ slug: "misconduct",
762
+ tabs: [
763
+ {
764
+ name: "Reports",
765
+ description: "Review Lounge reports.",
766
+ root: true
767
+ },
768
+ {
769
+ name: "Sanctions",
770
+ description: "Review Lounge sanctions.",
771
+ slug: "sanctions"
772
+ }
773
+ ]
774
+ },
775
+ {
776
+ name: "Points / KXP",
777
+ description: "Review Koloseum Experience Points (KXP) transactions for the Lounge.",
778
+ slug: "points"
779
+ }
780
+ ]
469
781
  }
470
782
  ],
471
783
  roles: [
@@ -642,7 +954,19 @@ export const Utility = {
642
954
  * @param base - The base path of the application; defaults to an empty string.
643
955
  * @returns `true` if the page is active, `false` otherwise.
644
956
  */
645
- pageIsActive: (page, slug, microservice, base = "") => slug === microservice || page.url.pathname.startsWith(`${base === "." ? "" : base}/${slug}`),
957
+ pageIsActive: (page, slug, microservice, base = "") => {
958
+ // Return true if microservice is provided and matches slug
959
+ if (microservice)
960
+ return slug === microservice;
961
+ // Remove trailing slash from base for consistency
962
+ const cleanBase = base.replace(/\/$/, "");
963
+ // Match exactly the base path if slug is falsy (i.e. root microservice feature)
964
+ if (!slug)
965
+ return page.url.pathname === cleanBase || page.url.pathname === `${cleanBase}/`;
966
+ // Match exact or subpath otherwise
967
+ const target = `${cleanBase}/${slug}`;
968
+ return page.url.pathname === target || page.url.pathname.startsWith(`${target}/`);
969
+ },
646
970
  /**
647
971
  * Parses a `CustomError` object within a page/layout server load function and returns an error to be thrown.
648
972
  * @param {CustomError} error - The error object
@@ -717,11 +1041,19 @@ export const Utility = {
717
1041
  */
718
1042
  passwordRegex: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()-_+=|{}[\]:;'<>,.?/~]).{8,}$/,
719
1043
  /**
720
- * A regular expression for Koloseum Player IDs. It covers the following rules:
721
- * - 9 characters long
722
- * - begins with "KP" followed by 7 digits
1044
+ * Returns a regular expression for a Koloseum platform ID.
1045
+ * @param {string} type - The type of platform ID to return
1046
+ * @returns A regular expression for the platform ID, or `null` if the type is invalid
723
1047
  */
724
- playerIdRegex: /^KP\d{7}$/,
1048
+ platformIdRegex: (type) => {
1049
+ if (type === "lounge")
1050
+ return /^KL\d{7}$/;
1051
+ if (type === "loungeBranch")
1052
+ return /^KLB\d{7}$/;
1053
+ if (type === "player")
1054
+ return /^KP\d{7}$/;
1055
+ return null;
1056
+ },
725
1057
  /**
726
1058
  * Sanitises any potential HTML injected into user input.
727
1059
  * @param {string} input - The input to be sanitised
@@ -748,43 +1080,5 @@ export const Utility = {
748
1080
  */
749
1081
  validateSocialMediaHandle: (handle) => !isURL(handle, { require_protocol: false }) &&
750
1082
  !handle.startsWith("@") &&
751
- Boolean(handle.match(Utility.socialMediaHandleRegex)),
752
- /**
753
- * Validates a SuprSend subscriber. If the subscriber does not exist, it will be created with the provided person data.
754
- * @param {suprsend.Suprsend} suprSend - The SuprSend instance.
755
- * @param {string} userId - The user ID.
756
- * @param {Object} personData - The person data; required if the subscriber does not exist.
757
- * @returns {Promise<{ error?: CustomError; success?: true }>} A promise that resolves to an object with an error if the subscriber validation fails, or indicating success otherwise.
758
- */
759
- validateSuprSendSubscriber: async (suprSend, userId, personData) => {
760
- // Check if subscriber exists
761
- try {
762
- await suprSend.users.get(userId);
763
- }
764
- catch (_err) {
765
- // If person data is not provided, return an error
766
- if (!personData)
767
- return { error: Utility.customError(400, "Person data is required to create a SuprSend subscriber.") };
768
- // Destructure person data
769
- const { firstName, lastName, phone, pseudonym } = personData;
770
- // Create subscriber
771
- const subscriber = suprSend.user.get_instance(userId);
772
- if (!subscriber)
773
- return { error: Utility.customError(500, Status.ERROR) };
774
- // Set subscriber details
775
- subscriber.set_preferred_language("en");
776
- subscriber.set_timezone("Africa/Nairobi");
777
- subscriber.set("firstName", firstName);
778
- subscriber.set("lastName", lastName);
779
- subscriber.set("phone", `+${phone}`);
780
- if (pseudonym)
781
- subscriber.set("pseudonym", pseudonym);
782
- // Save subscriber
783
- const response = await subscriber.save();
784
- if (!response.success)
785
- return { error: Utility.customError(500, Status.ERROR) };
786
- }
787
- // Return success
788
- return { success: true };
789
- }
1083
+ Boolean(handle.match(Utility.socialMediaHandleRegex))
790
1084
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koloseum/utils",
3
- "version": "0.1.17",
3
+ "version": "0.2.0",
4
4
  "author": "Koloseum Technologies Limited",
5
5
  "type": "module",
6
6
  "description": "Utility logic for use across Koloseum web apps (TypeScript)",
@@ -29,21 +29,21 @@
29
29
  "test": "vitest --run"
30
30
  },
31
31
  "dependencies": {
32
- "@supabase/supabase-js": "^2.48.1",
33
- "@sveltejs/kit": "^2.17.1",
34
- "sanitize-html": "^2.14.0",
35
- "validator": "^13.12.0"
32
+ "@supabase/supabase-js": "^2.50.1",
33
+ "@sveltejs/kit": "^2.22.0",
34
+ "sanitize-html": "^2.17.0",
35
+ "uuid": "^11.1.0",
36
+ "validator": "^13.15.15"
36
37
  },
37
38
  "devDependencies": {
38
- "@koloseum/types": "^0.1.12",
39
- "@playwright/test": "^1.50.1",
40
- "@suprsend/node-sdk": "^1.13.0",
39
+ "@koloseum/types": "^0.2.0",
40
+ "@playwright/test": "^1.53.1",
41
41
  "@suprsend/web-components": "^0.2.1",
42
- "@types/sanitize-html": "^2.13.0",
42
+ "@types/sanitize-html": "^2.16.0",
43
43
  "@types/uuid": "^10.0.0",
44
- "@types/validator": "^13.12.2",
45
- "prettier": "^3.5.1",
46
- "typescript": "^5.0.0",
47
- "vitest": "^3.0.5"
44
+ "@types/validator": "^13.15.2",
45
+ "prettier": "^3.6.0",
46
+ "typescript": "^5.8.3",
47
+ "vitest": "^3.2.4"
48
48
  }
49
49
  }