@enfyra/sdk-nuxt 0.3.9 → 0.3.11

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.
@@ -1,6 +1,6 @@
1
1
  import { ref, unref, toRaw } from "vue";
2
2
  import { $fetch } from "../utils/http";
3
- import { getAppUrl } from "../utils/url";
3
+ import { getAppUrl, normalizeUrl } from "../utils/url";
4
4
  import { ENFYRA_API_PREFIX } from "../constants/config";
5
5
  import { useRuntimeConfig, useFetch, useRequestHeaders } from "#imports";
6
6
  function handleError(error, context, customHandler) {
@@ -25,7 +25,11 @@ export function useEnfyraApi(path, opts = {}) {
25
25
  const config = useRuntimeConfig().public.enfyraSDK;
26
26
  const basePath = (typeof path === "function" ? path() : path).replace(/^\/?api\/?/, "").replace(/^\/+/, "");
27
27
  const appUrl = getAppUrl();
28
- const finalUrl = appUrl + (config?.apiPrefix || ENFYRA_API_PREFIX) + "/" + basePath;
28
+ const finalUrl = normalizeUrl(
29
+ appUrl,
30
+ config?.apiPrefix || ENFYRA_API_PREFIX,
31
+ basePath
32
+ );
29
33
  const clientHeaders = process.client ? {} : useRequestHeaders([
30
34
  "authorization",
31
35
  "cookie",
@@ -76,7 +80,7 @@ export function useEnfyraApi(path, opts = {}) {
76
80
  const buildPath = (...segments) => {
77
81
  return segments.filter(Boolean).join("/");
78
82
  };
79
- const fullBaseURL = apiUrl + (apiPrefix || ENFYRA_API_PREFIX);
83
+ const fullBaseURL = normalizeUrl(apiUrl, apiPrefix || ENFYRA_API_PREFIX);
80
84
  async function processBatch(items, processor) {
81
85
  const results = [];
82
86
  const progressResults = [];
package/dist/module.cjs CHANGED
@@ -10,11 +10,18 @@ const module$1 = kit.defineNuxtModule({
10
10
  configKey: "enfyraSDK"
11
11
  },
12
12
  defaults: {
13
- apiUrl: ""
13
+ apiUrl: "",
14
+ apiPrefix: config_mjs.ENFYRA_API_PREFIX
14
15
  },
15
16
  setup(options, nuxt) {
17
+ const normalizedOptions = {
18
+ ...options,
19
+ apiUrl: typeof options.apiUrl === "string" ? options.apiUrl.replace(/\/+$/, "") : options.apiUrl,
20
+ apiPrefix: typeof options.apiPrefix === "string" ? options.apiPrefix.trim() ? "/" + options.apiPrefix.replace(/^\/+|\/+$/g, "").replace(/\/+/g, "/") : config_mjs.ENFYRA_API_PREFIX : config_mjs.ENFYRA_API_PREFIX
21
+ };
22
+ const apiPrefix = normalizedOptions.apiPrefix;
16
23
  const { resolve } = kit.createResolver((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('module.cjs', document.baseURI).href)));
17
- if (!options.apiUrl) {
24
+ if (!normalizedOptions.apiUrl) {
18
25
  console.warn(
19
26
  `[Enfyra SDK Nuxt] Missing required configuration:
20
27
  - apiUrl is required
@@ -24,18 +31,18 @@ enfyraSDK: {
24
31
  }`
25
32
  );
26
33
  nuxt.options.runtimeConfig.public.enfyraSDK = {
27
- ...options,
28
- apiPrefix: config_mjs.ENFYRA_API_PREFIX,
34
+ ...normalizedOptions,
35
+ apiPrefix,
29
36
  configError: true,
30
37
  configErrorMessage: "Enfyra SDK: apiUrl is required. Please configure it in nuxt.config.ts"
31
38
  };
32
39
  } else {
33
40
  nuxt.options.runtimeConfig.public.enfyraSDK = {
34
- ...options,
35
- apiPrefix: config_mjs.ENFYRA_API_PREFIX
41
+ ...normalizedOptions,
42
+ apiPrefix
36
43
  };
37
44
  }
38
- if (!options.apiUrl) {
45
+ if (!normalizedOptions.apiUrl) {
39
46
  kit.addPlugin({
40
47
  src: resolve("./runtime/plugin/config-error.client"),
41
48
  mode: "client"
@@ -47,12 +54,12 @@ enfyraSDK: {
47
54
  middleware: true
48
55
  });
49
56
  kit.addServerHandler({
50
- route: `${config_mjs.ENFYRA_API_PREFIX}/login`,
57
+ route: `${apiPrefix}/login`,
51
58
  handler: resolve("./runtime/server/api/login.post"),
52
59
  method: "post"
53
60
  });
54
61
  kit.addServerHandler({
55
- route: `${config_mjs.ENFYRA_API_PREFIX}/logout`,
62
+ route: `${apiPrefix}/logout`,
56
63
  handler: resolve("./runtime/server/api/logout.post"),
57
64
  method: "post"
58
65
  });
@@ -61,7 +68,7 @@ enfyraSDK: {
61
68
  handler: resolve("./runtime/server/api/all")
62
69
  });
63
70
  kit.addServerHandler({
64
- route: `${config_mjs.ENFYRA_API_PREFIX}/**`,
71
+ route: `${apiPrefix}/**`,
65
72
  handler: resolve("./runtime/server/api/all")
66
73
  });
67
74
  }
package/dist/module.d.cts CHANGED
@@ -2,8 +2,10 @@ import * as _nuxt_schema from '@nuxt/schema';
2
2
 
3
3
  declare const _default: _nuxt_schema.NuxtModule<{
4
4
  apiUrl: string;
5
+ apiPrefix: string;
5
6
  }, {
6
7
  apiUrl: string;
8
+ apiPrefix: string;
7
9
  }, false>;
8
10
 
9
11
  export { _default as default };
package/dist/module.d.mts CHANGED
@@ -2,8 +2,10 @@ import * as _nuxt_schema from '@nuxt/schema';
2
2
 
3
3
  declare const _default: _nuxt_schema.NuxtModule<{
4
4
  apiUrl: string;
5
+ apiPrefix: string;
5
6
  }, {
6
7
  apiUrl: string;
8
+ apiPrefix: string;
7
9
  }, false>;
8
10
 
9
11
  export { _default as default };
package/dist/module.d.ts CHANGED
@@ -2,8 +2,10 @@ import * as _nuxt_schema from '@nuxt/schema';
2
2
 
3
3
  declare const _default: _nuxt_schema.NuxtModule<{
4
4
  apiUrl: string;
5
+ apiPrefix: string;
5
6
  }, {
6
7
  apiUrl: string;
8
+ apiPrefix: string;
7
9
  }, false>;
8
10
 
9
11
  export { _default as default };
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enfyra/sdk-nuxt",
3
3
  "configKey": "enfyraSDK",
4
- "version": "0.3.9",
4
+ "version": "0.3.11",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -7,11 +7,18 @@ const module = defineNuxtModule({
7
7
  configKey: "enfyraSDK"
8
8
  },
9
9
  defaults: {
10
- apiUrl: ""
10
+ apiUrl: "",
11
+ apiPrefix: ENFYRA_API_PREFIX
11
12
  },
12
13
  setup(options, nuxt) {
14
+ const normalizedOptions = {
15
+ ...options,
16
+ apiUrl: typeof options.apiUrl === "string" ? options.apiUrl.replace(/\/+$/, "") : options.apiUrl,
17
+ apiPrefix: typeof options.apiPrefix === "string" ? options.apiPrefix.trim() ? "/" + options.apiPrefix.replace(/^\/+|\/+$/g, "").replace(/\/+/g, "/") : ENFYRA_API_PREFIX : ENFYRA_API_PREFIX
18
+ };
19
+ const apiPrefix = normalizedOptions.apiPrefix;
13
20
  const { resolve } = createResolver(import.meta.url);
14
- if (!options.apiUrl) {
21
+ if (!normalizedOptions.apiUrl) {
15
22
  console.warn(
16
23
  `[Enfyra SDK Nuxt] Missing required configuration:
17
24
  - apiUrl is required
@@ -21,18 +28,18 @@ enfyraSDK: {
21
28
  }`
22
29
  );
23
30
  nuxt.options.runtimeConfig.public.enfyraSDK = {
24
- ...options,
25
- apiPrefix: ENFYRA_API_PREFIX,
31
+ ...normalizedOptions,
32
+ apiPrefix,
26
33
  configError: true,
27
34
  configErrorMessage: "Enfyra SDK: apiUrl is required. Please configure it in nuxt.config.ts"
28
35
  };
29
36
  } else {
30
37
  nuxt.options.runtimeConfig.public.enfyraSDK = {
31
- ...options,
32
- apiPrefix: ENFYRA_API_PREFIX
38
+ ...normalizedOptions,
39
+ apiPrefix
33
40
  };
34
41
  }
35
- if (!options.apiUrl) {
42
+ if (!normalizedOptions.apiUrl) {
36
43
  addPlugin({
37
44
  src: resolve("./runtime/plugin/config-error.client"),
38
45
  mode: "client"
@@ -44,12 +51,12 @@ enfyraSDK: {
44
51
  middleware: true
45
52
  });
46
53
  addServerHandler({
47
- route: `${ENFYRA_API_PREFIX}/login`,
54
+ route: `${apiPrefix}/login`,
48
55
  handler: resolve("./runtime/server/api/login.post"),
49
56
  method: "post"
50
57
  });
51
58
  addServerHandler({
52
- route: `${ENFYRA_API_PREFIX}/logout`,
59
+ route: `${apiPrefix}/logout`,
53
60
  handler: resolve("./runtime/server/api/logout.post"),
54
61
  method: "post"
55
62
  });
@@ -58,7 +65,7 @@ enfyraSDK: {
58
65
  handler: resolve("./runtime/server/api/all")
59
66
  });
60
67
  addServerHandler({
61
- route: `${ENFYRA_API_PREFIX}/**`,
68
+ route: `${apiPrefix}/**`,
62
69
  handler: resolve("./runtime/server/api/all")
63
70
  });
64
71
  }
@@ -13,12 +13,13 @@ import {
13
13
  REFRESH_TOKEN_KEY,
14
14
  EXP_TIME_KEY
15
15
  } from "../../../constants/auth";
16
+ import { normalizeUrl } from "../../../utils/url";
16
17
  export default defineEventHandler(async (event) => {
17
18
  const config = useRuntimeConfig();
18
19
  const apiUrl = config.public.enfyraSDK?.apiUrl;
19
20
  try {
20
21
  const body = await readBody(event);
21
- const response = await $fetch(`${apiUrl}/auth/login`, {
22
+ const response = await $fetch(normalizeUrl(apiUrl, "/auth/login"), {
22
23
  method: "POST",
23
24
  body,
24
25
  headers: {
@@ -13,12 +13,13 @@ import {
13
13
  REFRESH_TOKEN_KEY,
14
14
  EXP_TIME_KEY
15
15
  } from "../../../constants/auth";
16
+ import { normalizeUrl } from "../../../utils/url";
16
17
  export default defineEventHandler(async (event) => {
17
18
  const config = useRuntimeConfig();
18
19
  const apiUrl = config.public.enfyraSDK?.apiUrl;
19
20
  try {
20
21
  const body = await readBody(event);
21
- const response = await $fetch(`${apiUrl}/auth/login`, {
22
+ const response = await $fetch(normalizeUrl(apiUrl, "/auth/login"), {
22
23
  method: "POST",
23
24
  body,
24
25
  headers: {
@@ -6,12 +6,13 @@ import {
6
6
  REFRESH_TOKEN_KEY,
7
7
  EXP_TIME_KEY
8
8
  } from "../../../constants/auth";
9
+ import { normalizeUrl } from "../../../utils/url";
9
10
  export default defineEventHandler(async (event) => {
10
11
  const config = useRuntimeConfig();
11
12
  const apiUrl = config.public?.enfyraSDK?.apiUrl;
12
13
  const refreshToken = getCookie(event, REFRESH_TOKEN_KEY);
13
14
  try {
14
- const result = await $fetch(`${apiUrl}/auth/logout`, {
15
+ const result = await $fetch(normalizeUrl(apiUrl, "/auth/logout"), {
15
16
  method: "POST",
16
17
  headers: {
17
18
  cookie: getHeader(event, "cookie") || "",
@@ -6,12 +6,13 @@ import {
6
6
  REFRESH_TOKEN_KEY,
7
7
  EXP_TIME_KEY
8
8
  } from "../../../constants/auth";
9
+ import { normalizeUrl } from "../../../utils/url";
9
10
  export default defineEventHandler(async (event) => {
10
11
  const config = useRuntimeConfig();
11
12
  const apiUrl = config.public?.enfyraSDK?.apiUrl;
12
13
  const refreshToken = getCookie(event, REFRESH_TOKEN_KEY);
13
14
  try {
14
- const result = await $fetch(`${apiUrl}/auth/logout`, {
15
+ const result = await $fetch(normalizeUrl(apiUrl, "/auth/logout"), {
15
16
  method: "POST",
16
17
  headers: {
17
18
  cookie: getHeader(event, "cookie") || "",
@@ -5,11 +5,7 @@ const config = ref({
5
5
  });
6
6
  export function useEnfyraConfig() {
7
7
  const setConfig = (newConfig) => {
8
- const normalizedConfig = { ...newConfig };
9
- if (typeof normalizedConfig.apiUrl === "string") {
10
- normalizedConfig.apiUrl = normalizedConfig.apiUrl.replace(/\/+$/, "");
11
- }
12
- config.value = { ...config.value, ...normalizedConfig };
8
+ config.value = { ...config.value, ...newConfig };
13
9
  };
14
10
  const getConfig = () => config.value;
15
11
  return {
@@ -1,11 +1,12 @@
1
1
  import { proxyRequest } from "h3";
2
2
  import { useRuntimeConfig } from "#imports";
3
3
  import { ENFYRA_API_PREFIX } from "../../constants/config";
4
+ import { normalizeUrl } from "../../utils/url";
4
5
  export function proxyToAPI(event, customPath) {
5
6
  const config = useRuntimeConfig();
6
7
  const apiPrefix = config.public?.enfyraSDK?.apiPrefix || ENFYRA_API_PREFIX;
7
8
  const rawPath = customPath || event.path.replace(new RegExp(`^${apiPrefix}`), "");
8
- const targetUrl = `${config.public?.enfyraSDK?.apiUrl}${rawPath}`;
9
+ const targetUrl = normalizeUrl(config.public?.enfyraSDK?.apiUrl, rawPath);
9
10
  const headers = event.context.proxyHeaders || {};
10
11
  return proxyRequest(event, targetUrl, {
11
12
  headers
@@ -5,6 +5,7 @@ import {
5
5
  REFRESH_TOKEN_KEY,
6
6
  EXP_TIME_KEY
7
7
  } from "../../constants/auth";
8
+ import { normalizeUrl } from "../../utils/url";
8
9
  export function decodeJWT(token) {
9
10
  try {
10
11
  const parts = token.split(".");
@@ -39,7 +40,7 @@ export function validateTokens(event) {
39
40
  }
40
41
  export async function refreshAccessToken(event, refreshToken, apiUrl) {
41
42
  try {
42
- const response = await $fetch(`${apiUrl}/auth/refresh-token`, {
43
+ const response = await $fetch(normalizeUrl(apiUrl, "/auth/refresh-token"), {
43
44
  method: "POST",
44
45
  body: { refreshToken }
45
46
  });
@@ -1,3 +1,20 @@
1
+ export function normalizeUrl(...segments) {
2
+ const validSegments = segments.filter((s) => Boolean(s));
3
+ if (validSegments.length === 0) return "";
4
+ let result = validSegments[0].replace(/\/+$/, "");
5
+ for (let i = 1; i < validSegments.length; i++) {
6
+ const segment = validSegments[i].replace(/^\/+/, "").replace(/\/+$/, "").replace(/\/+/g, "/");
7
+ if (segment) {
8
+ result += "/" + segment;
9
+ }
10
+ }
11
+ return result;
12
+ }
13
+ export function joinUrlPath(...paths) {
14
+ const validPaths = paths.filter((p) => Boolean(p));
15
+ if (validPaths.length === 0) return "";
16
+ return validPaths.map((path) => path.replace(/^\/+/, "").replace(/\/+$/, "")).filter(Boolean).join("/");
17
+ }
1
18
  export function getAppUrl() {
2
19
  if (process.client && typeof window !== "undefined") {
3
20
  return window.location.origin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enfyra/sdk-nuxt",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Nuxt SDK for Enfyra CMS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -47,20 +47,15 @@
47
47
  "vue": "^3.0.0"
48
48
  },
49
49
  "dependencies": {
50
- "@vitejs/plugin-vue": "^5.2.0",
51
- "cookie": "^0.6.0",
52
- "glob": "^8.1.0",
53
50
  "h3": "^1.15.4",
54
- "jwt-decode": "^4.0.0",
55
- "nuxt": "^3.18.1",
56
- "ofetch": "^1.3.3",
57
- "vite": "^6.0.7"
51
+ "ofetch": "^1.3.3"
58
52
  },
59
53
  "devDependencies": {
60
54
  "@nuxt/module-builder": "^0.8.4",
61
- "@types/cookie": "^0.6.0",
62
55
  "@vitest/ui": "^3.2.4",
56
+ "nuxt": "^3.18.1",
63
57
  "typescript": "^5.0.0",
58
+ "vite": "^6.0.7",
64
59
  "vite-plugin-dts": "^4.3.0",
65
60
  "vitest": "^3.2.4"
66
61
  }
@@ -8,7 +8,7 @@ import type {
8
8
  BatchProgress,
9
9
  } from "../types";
10
10
  import { $fetch } from "../utils/http";
11
- import { getAppUrl } from "../utils/url";
11
+ import { getAppUrl, normalizeUrl } from "../utils/url";
12
12
  import { ENFYRA_API_PREFIX } from "../constants/config";
13
13
 
14
14
  import { useRuntimeConfig, useFetch, useRequestHeaders } from "#imports";
@@ -59,8 +59,11 @@ export function useEnfyraApi<T = any>(
59
59
  .replace(/^\/+/, "");
60
60
 
61
61
  const appUrl = getAppUrl();
62
- const finalUrl =
63
- appUrl + (config?.apiPrefix || ENFYRA_API_PREFIX) + "/" + basePath;
62
+ const finalUrl = normalizeUrl(
63
+ appUrl,
64
+ config?.apiPrefix || ENFYRA_API_PREFIX,
65
+ basePath
66
+ );
64
67
 
65
68
  const clientHeaders = process.client
66
69
  ? {}
@@ -141,7 +144,7 @@ export function useEnfyraApi<T = any>(
141
144
  return segments.filter(Boolean).join("/");
142
145
  };
143
146
 
144
- const fullBaseURL = apiUrl + (apiPrefix || ENFYRA_API_PREFIX);
147
+ const fullBaseURL = normalizeUrl(apiUrl, apiPrefix || ENFYRA_API_PREFIX);
145
148
 
146
149
  async function processBatch<T>(
147
150
  items: any[],
@@ -1,5 +1 @@
1
- /**
2
- * Hardcoded API prefix for all Enfyra SDK routes
3
- * This ensures no conflicts with application routes
4
- */
5
1
  export const ENFYRA_API_PREFIX = "/enfyra/api";
package/src/module.ts CHANGED
@@ -14,11 +14,27 @@ export default defineNuxtModule({
14
14
  },
15
15
  defaults: {
16
16
  apiUrl: "",
17
+ apiPrefix: ENFYRA_API_PREFIX,
17
18
  },
18
19
  setup(options, nuxt) {
20
+ const normalizedOptions = {
21
+ ...options,
22
+ apiUrl:
23
+ typeof options.apiUrl === "string"
24
+ ? options.apiUrl.replace(/\/+$/, "")
25
+ : options.apiUrl,
26
+ apiPrefix:
27
+ typeof options.apiPrefix === "string"
28
+ ? (options.apiPrefix.trim()
29
+ ? "/" + options.apiPrefix.replace(/^\/+|\/+$/g, "").replace(/\/+/g, "/")
30
+ : ENFYRA_API_PREFIX)
31
+ : ENFYRA_API_PREFIX,
32
+ };
33
+
34
+ const apiPrefix = normalizedOptions.apiPrefix;
19
35
  const { resolve } = createResolver(import.meta.url);
20
36
 
21
- if (!options.apiUrl) {
37
+ if (!normalizedOptions.apiUrl) {
22
38
  console.warn(
23
39
  `[Enfyra SDK Nuxt] Missing required configuration:\n` +
24
40
  `- apiUrl is required\n` +
@@ -29,20 +45,20 @@ export default defineNuxtModule({
29
45
  );
30
46
 
31
47
  nuxt.options.runtimeConfig.public.enfyraSDK = {
32
- ...options,
33
- apiPrefix: ENFYRA_API_PREFIX,
48
+ ...normalizedOptions,
49
+ apiPrefix: apiPrefix,
34
50
  configError: true,
35
51
  configErrorMessage: 'Enfyra SDK: apiUrl is required. Please configure it in nuxt.config.ts'
36
52
  };
37
53
  } else {
38
54
  nuxt.options.runtimeConfig.public.enfyraSDK = {
39
- ...options,
40
- apiPrefix: ENFYRA_API_PREFIX,
55
+ ...normalizedOptions,
56
+ apiPrefix: apiPrefix,
41
57
  };
42
58
  }
43
59
 
44
60
 
45
- if (!options.apiUrl) {
61
+ if (!normalizedOptions.apiUrl) {
46
62
  addPlugin({
47
63
  src: resolve("./runtime/plugin/config-error.client"),
48
64
  mode: 'client'
@@ -57,13 +73,13 @@ export default defineNuxtModule({
57
73
  });
58
74
 
59
75
  addServerHandler({
60
- route: `${ENFYRA_API_PREFIX}/login`,
76
+ route: `${apiPrefix}/login`,
61
77
  handler: resolve("./runtime/server/api/login.post"),
62
78
  method: "post",
63
79
  });
64
80
 
65
81
  addServerHandler({
66
- route: `${ENFYRA_API_PREFIX}/logout`,
82
+ route: `${apiPrefix}/logout`,
67
83
  handler: resolve("./runtime/server/api/logout.post"),
68
84
  method: "post",
69
85
  });
@@ -75,7 +91,7 @@ export default defineNuxtModule({
75
91
  });
76
92
 
77
93
  addServerHandler({
78
- route: `${ENFYRA_API_PREFIX}/**`,
94
+ route: `${apiPrefix}/**`,
79
95
  handler: resolve("./runtime/server/api/all"),
80
96
  });
81
97
  },
@@ -13,6 +13,7 @@ import {
13
13
  REFRESH_TOKEN_KEY,
14
14
  EXP_TIME_KEY,
15
15
  } from "../../../constants/auth";
16
+ import { normalizeUrl } from "../../../utils/url";
16
17
 
17
18
  export default defineEventHandler(async (event) => {
18
19
  const config = useRuntimeConfig();
@@ -20,7 +21,7 @@ export default defineEventHandler(async (event) => {
20
21
 
21
22
  try {
22
23
  const body = await readBody(event);
23
- const response = await $fetch<any>(`${apiUrl}/auth/login`, {
24
+ const response = await $fetch<any>(normalizeUrl(apiUrl, "/auth/login"), {
24
25
  method: "POST",
25
26
  body,
26
27
  headers: {
@@ -6,6 +6,7 @@ import {
6
6
  REFRESH_TOKEN_KEY,
7
7
  EXP_TIME_KEY,
8
8
  } from "../../../constants/auth";
9
+ import { normalizeUrl } from "../../../utils/url";
9
10
 
10
11
  export default defineEventHandler(async (event) => {
11
12
  const config = useRuntimeConfig();
@@ -14,7 +15,7 @@ export default defineEventHandler(async (event) => {
14
15
  const refreshToken = getCookie(event, REFRESH_TOKEN_KEY);
15
16
 
16
17
  try {
17
- const result = await $fetch(`${apiUrl}/auth/logout`, {
18
+ const result = await $fetch(normalizeUrl(apiUrl, "/auth/logout"), {
18
19
  method: "POST",
19
20
  headers: {
20
21
  cookie: getHeader(event, "cookie") || "",
@@ -8,13 +8,7 @@ const config = ref<EnfyraConfig>({
8
8
 
9
9
  export function useEnfyraConfig() {
10
10
  const setConfig = (newConfig: Partial<EnfyraConfig>) => {
11
- const normalizedConfig = { ...newConfig };
12
-
13
- if (typeof normalizedConfig.apiUrl === 'string') {
14
- normalizedConfig.apiUrl = normalizedConfig.apiUrl.replace(/\/+$/, '');
15
- }
16
-
17
- config.value = { ...config.value, ...normalizedConfig };
11
+ config.value = { ...config.value, ...newConfig };
18
12
  };
19
13
 
20
14
  const getConfig = () => config.value;
@@ -1,13 +1,14 @@
1
1
  import { H3Event, proxyRequest } from "h3";
2
2
  import { useRuntimeConfig } from "#imports";
3
3
  import { ENFYRA_API_PREFIX } from "../../constants/config";
4
+ import { normalizeUrl } from "../../utils/url";
4
5
 
5
6
  export function proxyToAPI(event: H3Event, customPath?: string) {
6
7
  const config = useRuntimeConfig();
7
8
  const apiPrefix = config.public?.enfyraSDK?.apiPrefix || ENFYRA_API_PREFIX;
8
9
  const rawPath =
9
10
  customPath || event.path.replace(new RegExp(`^${apiPrefix}`), "");
10
- const targetUrl = `${config.public?.enfyraSDK?.apiUrl}${rawPath}`;
11
+ const targetUrl = normalizeUrl(config.public?.enfyraSDK?.apiUrl, rawPath);
11
12
 
12
13
  const headers = event.context.proxyHeaders || {};
13
14
 
@@ -5,6 +5,7 @@ import {
5
5
  REFRESH_TOKEN_KEY,
6
6
  EXP_TIME_KEY,
7
7
  } from "../../constants/auth";
8
+ import { normalizeUrl } from "../../utils/url";
8
9
 
9
10
  interface TokenValidationResult {
10
11
  accessToken: string | null;
@@ -58,7 +59,7 @@ export async function refreshAccessToken(
58
59
  apiUrl: string
59
60
  ): Promise<string> {
60
61
  try {
61
- const response = await $fetch(`${apiUrl}/auth/refresh-token`, {
62
+ const response = await $fetch(normalizeUrl(apiUrl, "/auth/refresh-token"), {
62
63
  method: "POST",
63
64
  body: { refreshToken },
64
65
  });
package/src/utils/url.ts CHANGED
@@ -1,3 +1,42 @@
1
+ /**
2
+ * Normalizes URL segments to avoid double slashes when concatenating
3
+ * Removes trailing slashes from base URL and leading slashes from path segments
4
+ * Also normalizes multiple consecutive slashes within segments
5
+ */
6
+ export function normalizeUrl(...segments: (string | undefined | null)[]): string {
7
+ const validSegments = segments.filter((s): s is string => Boolean(s));
8
+ if (validSegments.length === 0) return '';
9
+
10
+ // First segment is the base URL - remove trailing slashes
11
+ let result = validSegments[0].replace(/\/+$/, '');
12
+
13
+ // For remaining segments, remove leading and trailing slashes, normalize internal slashes, then join
14
+ for (let i = 1; i < validSegments.length; i++) {
15
+ const segment = validSegments[i]
16
+ .replace(/^\/+/, '') // Remove leading slashes
17
+ .replace(/\/+$/, '') // Remove trailing slashes
18
+ .replace(/\/+/g, '/'); // Normalize multiple consecutive slashes to single slash
19
+ if (segment) {
20
+ result += '/' + segment;
21
+ }
22
+ }
23
+
24
+ return result;
25
+ }
26
+
27
+ /**
28
+ * Joins URL paths safely, avoiding double slashes
29
+ */
30
+ export function joinUrlPath(...paths: (string | undefined | null)[]): string {
31
+ const validPaths = paths.filter((p): p is string => Boolean(p));
32
+ if (validPaths.length === 0) return '';
33
+
34
+ return validPaths
35
+ .map(path => path.replace(/^\/+/, '').replace(/\/+$/, ''))
36
+ .filter(Boolean)
37
+ .join('/');
38
+ }
39
+
1
40
  export function getAppUrl(): string {
2
41
  if (process.client && typeof window !== 'undefined') {
3
42
  return window.location.origin;