@koloseum/utils 0.3.0 → 0.3.1

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,7 +1,7 @@
1
1
  import type { BrowserInfo, Microservice, MicroserviceFeature, MicroserviceGroup, MicroserviceObject, MicroserviceRole, UserWithCustomMetadata } from "@koloseum/types/general";
2
2
  import type { PronounsCheckboxes } from "@koloseum/types/public-auth";
3
3
  import type { IOptions } from "@suprsend/web-components/dist/types/interface.d.ts";
4
- import type { Page } from "@sveltejs/kit";
4
+ import type { Page, SubmitFunction } from "@sveltejs/kit";
5
5
  export declare const Browser: {
6
6
  /**
7
7
  * Checks if a specific feature is supported by the browser.
@@ -93,6 +93,12 @@ export declare const Access: {
93
93
  roleHasAccessToFeature: (role: MicroserviceRole, feature: MicroserviceFeature, type: "backroom" | "lounges") => boolean;
94
94
  };
95
95
  export declare const Interface: {
96
+ /**
97
+ * Progressive-enhancement submit function for SvelteKit.
98
+ * Disables the submit button and shows a loading spinner; navigates via `goto` on redirect, otherwise calls `update` and restores the button.
99
+ * Buttons with class `otp-button` are not restored on failure/error so OTP flows can keep the loading state.
100
+ */
101
+ formEnhance: SubmitFunction;
96
102
  /**
97
103
  * Returns the URL for a menu item based on the slug.
98
104
  * @param {string} base - The base URL
package/dist/client.js CHANGED
@@ -445,6 +445,37 @@ export const Access = {
445
445
  };
446
446
  /* INTERFACE HELPERS */
447
447
  export const Interface = {
448
+ /**
449
+ * Progressive-enhancement submit function for SvelteKit.
450
+ * Disables the submit button and shows a loading spinner; navigates via `goto` on redirect, otherwise calls `update` and restores the button.
451
+ * Buttons with class `otp-button` are not restored on failure/error so OTP flows can keep the loading state.
452
+ */
453
+ formEnhance: (({ submitter }) => {
454
+ if (submitter) {
455
+ const button = submitter;
456
+ const innerHTML = button.innerHTML;
457
+ // Disable button and show loading spinner if not an OTP button
458
+ if (!button.classList.contains("otp-button")) {
459
+ button.disabled = true;
460
+ button.innerHTML = `<span class="loading loading-bars loading-sm"></span>`;
461
+ }
462
+ // Handle form submission
463
+ return async ({ update, result }) => {
464
+ // Navigate via `goto` on redirect
465
+ if (result.type === "redirect" && result.location) {
466
+ const { goto } = await import("$app/navigation");
467
+ await goto(result.location);
468
+ return;
469
+ }
470
+ // Update form and restore button if not an OTP button
471
+ await update();
472
+ if (result.type === "failure" || result.type === "error" || !button.classList.contains("otp-button")) {
473
+ button.disabled = false;
474
+ button.innerHTML = innerHTML;
475
+ }
476
+ };
477
+ }
478
+ }),
448
479
  /**
449
480
  * Returns the URL for a menu item based on the slug.
450
481
  * @param {string} base - The base URL
@@ -99,6 +99,12 @@ export declare const Validate: {
99
99
  * - at least one symbol
100
100
  */
101
101
  passwordRegex: RegExp;
102
+ /**
103
+ * Returns a regular expression for a Koloseum platform ID.
104
+ * @param {string} type - The type of platform ID to return
105
+ * @returns A regular expression for the platform ID, or `null` if the type is invalid
106
+ */
107
+ platformIdRegex: (type: "lounge" | "loungeBranch" | "player") => RegExp | null;
102
108
  /**
103
109
  * A regular expression for social media handles, without a leading slash or @ character.
104
110
  *
@@ -333,6 +333,20 @@ export const Validate = {
333
333
  * - at least one symbol
334
334
  */
335
335
  passwordRegex: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()\-_+=|{}\[\]:;'<>,.?/~]).{8,}$/,
336
+ /**
337
+ * Returns a regular expression for a Koloseum platform ID.
338
+ * @param {string} type - The type of platform ID to return
339
+ * @returns A regular expression for the platform ID, or `null` if the type is invalid
340
+ */
341
+ platformIdRegex: (type) => {
342
+ if (type === "lounge")
343
+ return /^KL\d{7}$/;
344
+ if (type === "loungeBranch")
345
+ return /^KLB\d{7}$/;
346
+ if (type === "player")
347
+ return /^KP\d{7}$/;
348
+ return null;
349
+ },
336
350
  /**
337
351
  * A regular expression for social media handles, without a leading slash or @ character.
338
352
  *
package/dist/general.d.ts CHANGED
@@ -11,6 +11,10 @@ export declare const Status: {
11
11
  * A generic password reset request message.
12
12
  */
13
13
  PASSWORD_RESET_REQUESTED: string;
14
+ /**
15
+ * A generic password reset confirmation message.
16
+ */
17
+ PASSWORD_RESET_COMPLETED: string;
14
18
  };
15
19
  export declare const Cache: {
16
20
  /**
package/dist/general.js CHANGED
@@ -11,7 +11,11 @@ export const Status = {
11
11
  /**
12
12
  * A generic password reset request message.
13
13
  */
14
- PASSWORD_RESET_REQUESTED: "If the provided email address is registered, you will receive a password reset link shortly."
14
+ PASSWORD_RESET_REQUESTED: "If the provided email address is registered, you will receive a password reset link shortly.",
15
+ /**
16
+ * A generic password reset confirmation message.
17
+ */
18
+ PASSWORD_RESET_COMPLETED: "Your password has been reset successfully. You can now log in with your new password."
15
19
  };
16
20
  /* CACHE HELPERS */
17
21
  export const Cache = {
@@ -1,4 +1,4 @@
1
- import type { MicroserviceGroup, MicroserviceObject, UserWithCustomMetadata } from "@koloseum/types/general";
1
+ import type { CustomError, MicroserviceGroup, MicroserviceObject, UserWithCustomMetadata } from "@koloseum/types/general";
2
2
  import type { BranchAddressObject, County } from "@koloseum/types/public-auth";
3
3
  export declare const Config: {
4
4
  /**
@@ -45,6 +45,23 @@ export declare const Resource: {
45
45
  url?: string;
46
46
  error?: any;
47
47
  };
48
+ /**
49
+ * Processes a redirect URI and returns a validated, safe redirect URL. Callers in SvelteKit can pass `dev` from `$app/environment`.
50
+ * @param uri - The URI to process (e.g. `players:competitions/leagues`)
51
+ * @param dev - Whether the environment is development/test; defaults to `false`
52
+ * @returns An object with the safe redirect `url`, or an `error` if the URI or URL is invalid or unsafe
53
+ */
54
+ getSafeRedirectUrl: (uri: string, dev?: boolean) => {
55
+ url?: string;
56
+ error?: CustomError;
57
+ };
58
+ /**
59
+ * Returns whether the given URL points to a local Supabase instance (localhost or 127.0.0.1, any port).
60
+ * Use for cookie domain, redirects, and MSW so non-default local ports (e.g. 54621) behave like default (54321).
61
+ * @param url - Supabase API URL (e.g. PUBLIC_SUPABASE_URL)
62
+ * @returns `true` if the URL host is localhost or 127.0.0.1, `false` otherwise
63
+ */
64
+ isLocalSupabase: (url: string | undefined) => boolean;
48
65
  /**
49
66
  * Parses a resource request and returns the URL and path.
50
67
  * @param request - The request to parse.
@@ -63,4 +80,11 @@ export declare const Resource: {
63
80
  address?: BranchAddressObject;
64
81
  error?: any;
65
82
  }>;
83
+ /**
84
+ * Validates that a redirect URL is safe and allowed. Callers in SvelteKit can pass `dev` from `$app/environment`.
85
+ * @param url - The URL to validate
86
+ * @param dev - Whether the environment is development/test. Defaults to `false`
87
+ * @returns `true` if the URL is safe, `false` otherwise
88
+ */
89
+ validateRedirectUrl: (url: string, dev?: boolean) => boolean;
66
90
  };
package/dist/platform.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Transform } from "./formatting.js";
2
+ import { Exception } from "./server.js";
2
3
  import { v4 as uuidv4 } from "uuid";
3
4
  import validator from "validator";
4
5
  /* HELPERS */
@@ -597,26 +598,36 @@ export const Resource = {
597
598
  if (env === "development")
598
599
  path = path.replace("account", "");
599
600
  }
600
- if (path.startsWith("fgc")) {
601
+ if (path.startsWith("sessions")) {
601
602
  port = 5178;
602
603
  if (env === "development")
603
- path = path.replace("fgc", "");
604
+ path = path.replace("sessions", "");
604
605
  }
605
- if (path.startsWith("commerce")) {
606
+ if (path.startsWith("competitions")) {
606
607
  port = 5179;
608
+ if (env === "development")
609
+ path = path.replace("competitions", "");
610
+ }
611
+ if (path.startsWith("commerce")) {
612
+ port = 5180;
607
613
  if (env === "development")
608
614
  path = path.replace("commerce", "");
609
615
  }
610
616
  }
611
617
  // Handle Lounges microservices
612
618
  if (microserviceGroup === "lounges") {
613
- if (path.startsWith("branches")) {
619
+ if (path.startsWith("account")) {
614
620
  port = 5181;
615
621
  if (env === "development")
616
- path = path.replace("branches", "");
622
+ path = path.replace("account", "");
623
+ }
624
+ if (path.startsWith("operations")) {
625
+ port = 5182;
626
+ if (env === "development")
627
+ path = path.replace("operations", "");
617
628
  }
618
629
  if (path.startsWith("staff")) {
619
- port = 5182;
630
+ port = 5183;
620
631
  if (env === "development")
621
632
  path = path.replace("staff", "");
622
633
  }
@@ -628,18 +639,13 @@ export const Resource = {
628
639
  if (env === "development")
629
640
  path = path.replace("compliance", "");
630
641
  }
631
- if (path.startsWith("competitions")) {
632
- port = 5177;
633
- if (env === "development")
634
- path = path.replace("competitions", "");
635
- }
636
642
  if (path.startsWith("commerce")) {
637
- port = 5180;
643
+ port = 5184;
638
644
  if (env === "development")
639
645
  path = path.replace("commerce", "");
640
646
  }
641
647
  if (path.startsWith("staff")) {
642
- port = 5183;
648
+ port = 5185;
643
649
  if (env === "development")
644
650
  path = path.replace("staff", "");
645
651
  }
@@ -651,6 +657,39 @@ export const Resource = {
651
657
  : `http://127.0.0.1:${port}${path || ""}`
652
658
  };
653
659
  },
660
+ /**
661
+ * Processes a redirect URI and returns a validated, safe redirect URL. Callers in SvelteKit can pass `dev` from `$app/environment`.
662
+ * @param uri - The URI to process (e.g. `players:competitions/leagues`)
663
+ * @param dev - Whether the environment is development/test; defaults to `false`
664
+ * @returns An object with the safe redirect `url`, or an `error` if the URI or URL is invalid or unsafe
665
+ */
666
+ getSafeRedirectUrl: (uri, dev = false) => {
667
+ const { url, error } = Resource.getRedirectUrl(uri, dev ? "development" : "production");
668
+ if (error)
669
+ return {
670
+ error: Exception.customError(error.code ?? 400, error.message ?? "Redirect URI is invalid.")
671
+ };
672
+ if (!url || !Resource.validateRedirectUrl(url, dev))
673
+ return { error: Exception.customError(400, "Redirect URL is invalid or unsafe.") };
674
+ return { url };
675
+ },
676
+ /**
677
+ * Returns whether the given URL points to a local Supabase instance (localhost or 127.0.0.1, any port).
678
+ * Use for cookie domain, redirects, and MSW so non-default local ports (e.g. 54621) behave like default (54321).
679
+ * @param url - Supabase API URL (e.g. PUBLIC_SUPABASE_URL)
680
+ * @returns `true` if the URL host is localhost or 127.0.0.1, `false` otherwise
681
+ */
682
+ isLocalSupabase: (url) => {
683
+ if (!url || typeof url !== "string")
684
+ return false;
685
+ try {
686
+ const host = new URL(url).hostname;
687
+ return host === "localhost" || host === "127.0.0.1";
688
+ }
689
+ catch {
690
+ return false;
691
+ }
692
+ },
654
693
  /**
655
694
  * Parses a resource request and returns the URL and path.
656
695
  * @param request - The request to parse.
@@ -760,5 +799,26 @@ export const Resource = {
760
799
  };
761
800
  // Return data
762
801
  return { address };
802
+ },
803
+ /**
804
+ * Validates that a redirect URL is safe and allowed. Callers in SvelteKit can pass `dev` from `$app/environment`.
805
+ * @param url - The URL to validate
806
+ * @param dev - Whether the environment is development/test. Defaults to `false`
807
+ * @returns `true` if the URL is safe, `false` otherwise
808
+ */
809
+ validateRedirectUrl: (url, dev = false) => {
810
+ try {
811
+ const parsedUrl = new URL(url);
812
+ if (!dev && parsedUrl.protocol !== "https:")
813
+ return false;
814
+ if (dev && parsedUrl.hostname === "127.0.0.1")
815
+ return true;
816
+ if (!parsedUrl.hostname.endsWith(".koloseum.ke") && parsedUrl.hostname !== "koloseum.ke")
817
+ return false;
818
+ return true;
819
+ }
820
+ catch {
821
+ return false;
822
+ }
763
823
  }
764
824
  };
package/dist/server.js CHANGED
@@ -36,7 +36,7 @@ export const Instance = {
36
36
  const errorData = await error.context.json();
37
37
  message = errorData.message || message;
38
38
  // Assign attempt ID to context if present
39
- if (path.includes("verify-id") && errorData.attemptId)
39
+ if ((path.includes("verify-id") || path.includes("smile-id")) && errorData.attemptId)
40
40
  context.attemptId = errorData.attemptId;
41
41
  }
42
42
  catch (jsonError) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koloseum/utils",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "author": "Koloseum Technologies Limited",
5
5
  "type": "module",
6
6
  "description": "Utility logic for use across Koloseum web apps (TypeScript)",