@jsenv/navi 0.3.5 → 0.4.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.
@@ -11735,6 +11735,7 @@ const openCallout = (
11735
11735
  level = "warning",
11736
11736
  onClose,
11737
11737
  closeOnClickOutside = level === "info",
11738
+ hideErrorStack,
11738
11739
  debug = false,
11739
11740
  } = {},
11740
11741
  ) => {
@@ -11802,8 +11803,8 @@ const openCallout = (
11802
11803
  if (Error.isError(newMessage)) {
11803
11804
  const error = newMessage;
11804
11805
  newMessage = error.message;
11805
- if (error.stack) {
11806
- newMessage += `<pre class="navi_callout_error_stack">${escapeHtml(error.stack)}</pre>`;
11806
+ if (!hideErrorStack && error.stack) {
11807
+ newMessage += `<pre class="navi_callout_error_stack">${escapeHtml(String(error.stack))}</pre>`;
11807
11808
  }
11808
11809
  }
11809
11810
 
@@ -16758,6 +16759,14 @@ let baseUrl = window.location.origin;
16758
16759
  const setBaseUrl = (value) => {
16759
16760
  baseUrl = new URL(value, window.location).href;
16760
16761
  };
16762
+
16763
+ const rawUrlPartSymbol = Symbol("raw_url_part");
16764
+ const rawUrlPart = (value) => {
16765
+ return {
16766
+ [rawUrlPartSymbol]: true,
16767
+ value,
16768
+ };
16769
+ };
16761
16770
  const NO_PARAMS = { [SYMBOL_IDENTITY]: Symbol("no_params") };
16762
16771
  // Controls what happens to actions when their route becomes inactive:
16763
16772
  // 'abort' - Cancel the action immediately when route deactivates
@@ -17032,11 +17041,24 @@ const createRoute = (urlPatternInput) => {
17032
17041
 
17033
17042
  const buildRelativeUrl = (params = {}) => {
17034
17043
  let relativeUrl = urlPatternInput;
17044
+ let hasRawUrlPartWithInvalidChars = false;
17045
+
17046
+ const encode = (value) => {
17047
+ if (value && value[rawUrlPartSymbol]) {
17048
+ const rawValue = value.value;
17049
+ // Check if raw value contains invalid URL characters
17050
+ if (/[\s<>{}|\\^`]/.test(rawValue)) {
17051
+ hasRawUrlPartWithInvalidChars = true;
17052
+ }
17053
+ return rawValue;
17054
+ }
17055
+ return encodeURIComponent(value);
17056
+ };
17035
17057
 
17036
17058
  // Replace named parameters (:param and {param})
17037
17059
  for (const key of Object.keys(params)) {
17038
17060
  const value = params[key];
17039
- const encodedValue = encodeURIComponent(value);
17061
+ const encodedValue = encode(value);
17040
17062
  relativeUrl = relativeUrl.replace(`:${key}`, encodedValue);
17041
17063
  relativeUrl = relativeUrl.replace(`{${key}}`, encodedValue);
17042
17064
  }
@@ -17051,22 +17073,36 @@ const createRoute = (urlPatternInput) => {
17051
17073
  let wildcardIndex = 0;
17052
17074
  relativeUrl = relativeUrl.replace(/\*/g, () => {
17053
17075
  const paramKey = wildcardIndex.toString();
17054
- const replacement = params[paramKey]
17055
- ? encodeURIComponent(params[paramKey])
17056
- : "*";
17076
+ const paramValue = params[paramKey];
17077
+ const replacement = paramValue ? encode(paramValue) : "*";
17057
17078
  wildcardIndex++;
17058
17079
  return replacement;
17059
17080
  });
17060
17081
  }
17061
17082
 
17062
- return relativeUrl;
17083
+ return {
17084
+ relativeUrl,
17085
+ hasRawUrlPartWithInvalidChars,
17086
+ };
17063
17087
  };
17064
17088
  const buildUrl = (params = {}) => {
17065
- let relativeUrl = buildRelativeUrl(params);
17066
- if (relativeUrl[0] === "/") {
17067
- relativeUrl = relativeUrl.slice(1);
17089
+ const { relativeUrl, hasRawUrlPartWithInvalidChars } =
17090
+ buildRelativeUrl(params);
17091
+ let processedRelativeUrl = relativeUrl;
17092
+ if (processedRelativeUrl[0] === "/") {
17093
+ processedRelativeUrl = processedRelativeUrl.slice(1);
17068
17094
  }
17069
- const url = new URL(relativeUrl, baseUrl).href;
17095
+
17096
+ if (hasRawUrlPartWithInvalidChars) {
17097
+ // Manually construct URL to avoid automatic encoding by URL constructor
17098
+ const baseUrlObj = new URL(baseUrl);
17099
+ if (baseUrlObj.pathname.endsWith("/")) {
17100
+ return `${baseUrl}${processedRelativeUrl}`;
17101
+ }
17102
+ return `${baseUrl}/${processedRelativeUrl}`;
17103
+ }
17104
+
17105
+ const url = new URL(processedRelativeUrl, baseUrl).href;
17070
17106
  return url;
17071
17107
  };
17072
17108
  route.buildUrl = buildUrl;
@@ -17076,7 +17112,7 @@ const createRoute = (urlPatternInput) => {
17076
17112
  const visitedSignal = signal(false);
17077
17113
  const relativeUrlSignal = computed(() => {
17078
17114
  const params = paramsSignal.value;
17079
- const relativeUrl = buildRelativeUrl(params);
17115
+ const { relativeUrl } = buildRelativeUrl(params);
17080
17116
  return relativeUrl;
17081
17117
  });
17082
17118
  const disposeRelativeUrlEffect = effect(() => {
@@ -27883,4 +27919,4 @@ const useDependenciesDiff = (inputs) => {
27883
27919
  return diffRef.current;
27884
27920
  };
27885
27921
 
27886
- export { ActionRenderer, ActiveKeyboardShortcuts, Button, Checkbox, CheckboxList, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FontSizedSvg, Form, IconAndText, Input, Label, Link, LinkWithIcon, Overflow, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, SummaryMarker, Tab, TabList, Table, TableCell, Tbody, TextAndCount, Thead, Tr, UITransition, actionIntegratedVia, addCustomMessage, createAction, createSelectionKeyboardShortcuts, createUniqueValueConstraint, defineRoutes, enableDebugActions, enableDebugOnDocumentLoading, goBack, goForward, goTo, isCellSelected, isColumnSelected, isRowSelected, openCallout, reload, removeCustomMessage, rerunActions, resource, setBaseUrl, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useCellsAndColumns, useDependenciesDiff, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, valueInLocalStorage };
27922
+ export { ActionRenderer, ActiveKeyboardShortcuts, Button, Checkbox, CheckboxList, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FontSizedSvg, Form, IconAndText, Input, Label, Link, LinkWithIcon, Overflow, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, SummaryMarker, Tab, TabList, Table, TableCell, Tbody, TextAndCount, Thead, Tr, UITransition, actionIntegratedVia, addCustomMessage, createAction, createSelectionKeyboardShortcuts, createUniqueValueConstraint, defineRoutes, enableDebugActions, enableDebugOnDocumentLoading, goBack, goForward, goTo, isCellSelected, isColumnSelected, isRowSelected, openCallout, rawUrlPart, reload, removeCustomMessage, rerunActions, resource, setBaseUrl, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useCellsAndColumns, useDependenciesDiff, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, valueInLocalStorage };
package/index.js CHANGED
@@ -12,7 +12,7 @@ export { useStateArray } from "./src/components/use_state_array.js";
12
12
  export { resource } from "./src/store/resource_graph.js";
13
13
  export { valueInLocalStorage } from "./src/store/value_in_local_storage.js";
14
14
 
15
- // integration with browser (and routing)
15
+ // routing
16
16
  export {
17
17
  actionIntegratedVia,
18
18
  goBack,
@@ -24,7 +24,12 @@ export {
24
24
  } from "./src/browser_integration/browser_integration.js";
25
25
  export { useDocumentState } from "./src/browser_integration/document_state_signal.js";
26
26
  export { useDocumentUrl } from "./src/browser_integration/document_url_signal.js";
27
- export { defineRoutes, setBaseUrl, useRouteStatus } from "./src/route/route.js";
27
+ export {
28
+ defineRoutes,
29
+ rawUrlPart,
30
+ setBaseUrl,
31
+ useRouteStatus,
32
+ } from "./src/route/route.js";
28
33
 
29
34
  // Components
30
35
  export { ActionRenderer } from "./src/components/action_renderer.jsx";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.3.5",
3
+ "version": "0.4.1",
4
4
  "description": "Library of components including navigation to create frontend applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,6 +48,7 @@ export const openCallout = (
48
48
  level = "warning",
49
49
  onClose,
50
50
  closeOnClickOutside = level === "info",
51
+ hideErrorStack,
51
52
  debug = false,
52
53
  } = {},
53
54
  ) => {
@@ -115,8 +116,8 @@ export const openCallout = (
115
116
  if (Error.isError(newMessage)) {
116
117
  const error = newMessage;
117
118
  newMessage = error.message;
118
- if (error.stack) {
119
- newMessage += `<pre class="navi_callout_error_stack">${escapeHtml(error.stack)}</pre>`;
119
+ if (!hideErrorStack && error.stack) {
120
+ newMessage += `<pre class="navi_callout_error_stack">${escapeHtml(String(error.stack))}</pre>`;
120
121
  }
121
122
  }
122
123
 
@@ -13,6 +13,14 @@ export const setBaseUrl = (value) => {
13
13
  baseUrl = new URL(value, window.location).href;
14
14
  };
15
15
 
16
+ const rawUrlPartSymbol = Symbol("raw_url_part");
17
+ export const rawUrlPart = (value) => {
18
+ return {
19
+ [rawUrlPartSymbol]: true,
20
+ value,
21
+ };
22
+ };
23
+
16
24
  const DEBUG = false;
17
25
  const NO_PARAMS = { [SYMBOL_IDENTITY]: Symbol("no_params") };
18
26
  // Controls what happens to actions when their route becomes inactive:
@@ -300,11 +308,24 @@ const createRoute = (urlPatternInput) => {
300
308
 
301
309
  const buildRelativeUrl = (params = {}) => {
302
310
  let relativeUrl = urlPatternInput;
311
+ let hasRawUrlPartWithInvalidChars = false;
312
+
313
+ const encode = (value) => {
314
+ if (value && value[rawUrlPartSymbol]) {
315
+ const rawValue = value.value;
316
+ // Check if raw value contains invalid URL characters
317
+ if (/[\s<>{}|\\^`]/.test(rawValue)) {
318
+ hasRawUrlPartWithInvalidChars = true;
319
+ }
320
+ return rawValue;
321
+ }
322
+ return encodeURIComponent(value);
323
+ };
303
324
 
304
325
  // Replace named parameters (:param and {param})
305
326
  for (const key of Object.keys(params)) {
306
327
  const value = params[key];
307
- const encodedValue = encodeURIComponent(value);
328
+ const encodedValue = encode(value);
308
329
  relativeUrl = relativeUrl.replace(`:${key}`, encodedValue);
309
330
  relativeUrl = relativeUrl.replace(`{${key}}`, encodedValue);
310
331
  }
@@ -319,22 +340,36 @@ const createRoute = (urlPatternInput) => {
319
340
  let wildcardIndex = 0;
320
341
  relativeUrl = relativeUrl.replace(/\*/g, () => {
321
342
  const paramKey = wildcardIndex.toString();
322
- const replacement = params[paramKey]
323
- ? encodeURIComponent(params[paramKey])
324
- : "*";
343
+ const paramValue = params[paramKey];
344
+ const replacement = paramValue ? encode(paramValue) : "*";
325
345
  wildcardIndex++;
326
346
  return replacement;
327
347
  });
328
348
  }
329
349
 
330
- return relativeUrl;
350
+ return {
351
+ relativeUrl,
352
+ hasRawUrlPartWithInvalidChars,
353
+ };
331
354
  };
332
355
  const buildUrl = (params = {}) => {
333
- let relativeUrl = buildRelativeUrl(params);
334
- if (relativeUrl[0] === "/") {
335
- relativeUrl = relativeUrl.slice(1);
356
+ const { relativeUrl, hasRawUrlPartWithInvalidChars } =
357
+ buildRelativeUrl(params);
358
+ let processedRelativeUrl = relativeUrl;
359
+ if (processedRelativeUrl[0] === "/") {
360
+ processedRelativeUrl = processedRelativeUrl.slice(1);
336
361
  }
337
- const url = new URL(relativeUrl, baseUrl).href;
362
+
363
+ if (hasRawUrlPartWithInvalidChars) {
364
+ // Manually construct URL to avoid automatic encoding by URL constructor
365
+ const baseUrlObj = new URL(baseUrl);
366
+ if (baseUrlObj.pathname.endsWith("/")) {
367
+ return `${baseUrl}${processedRelativeUrl}`;
368
+ }
369
+ return `${baseUrl}/${processedRelativeUrl}`;
370
+ }
371
+
372
+ const url = new URL(processedRelativeUrl, baseUrl).href;
338
373
  return url;
339
374
  };
340
375
  route.buildUrl = buildUrl;
@@ -344,7 +379,7 @@ const createRoute = (urlPatternInput) => {
344
379
  const visitedSignal = signal(false);
345
380
  const relativeUrlSignal = computed(() => {
346
381
  const params = paramsSignal.value;
347
- const relativeUrl = buildRelativeUrl(params);
382
+ const { relativeUrl } = buildRelativeUrl(params);
348
383
  return relativeUrl;
349
384
  });
350
385
  const disposeRelativeUrlEffect = effect(() => {