@equinor/fusion-framework-plugin-context-navigation 1.0.0 → 1.0.2-next.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.
@@ -0,0 +1,31 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { createPathAdapter } from '../adapters/create-path-adapter';
3
+ const CONTEXT_ID = '11111111-1111-1111-1111-111111111111';
4
+ describe('createPathAdapter', () => {
5
+ const adapter = createPathAdapter();
6
+ it('drops the context segment and sub-route when context becomes null', () => {
7
+ const currentURL = new URL(`/apps/my-app/${CONTEXT_ID}/settings/general`, 'https://example.com');
8
+ const result = adapter.encode({ context: null, currentURL });
9
+ expect(result?.pathname).toBe('/apps/my-app');
10
+ });
11
+ it('preserves the sub-route when swapping to a new context', () => {
12
+ const currentURL = new URL(`/apps/my-app/${CONTEXT_ID}/settings/general`, 'https://example.com');
13
+ const result = adapter.encode({
14
+ context: { id: 'new-context-id' },
15
+ currentURL,
16
+ });
17
+ expect(result?.pathname).toBe('/apps/my-app/new-context-id/settings/general');
18
+ });
19
+ it('preserves the full route tail when there was no prior context id to distinguish from a route name', () => {
20
+ // With no active context, the segment after the app key is a route name
21
+ // (e.g. "settings"), not a context id — it must be folded into the tail
22
+ // rather than clobbered by the new context id.
23
+ const currentURL = new URL('/apps/my-app/settings/general', 'https://example.com');
24
+ const result = adapter.encode({
25
+ context: { id: CONTEXT_ID },
26
+ currentURL,
27
+ });
28
+ expect(result?.pathname).toBe(`/apps/my-app/${CONTEXT_ID}/settings/general`);
29
+ });
30
+ });
31
+ //# sourceMappingURL=create-path-adapter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-path-adapter.test.js","sourceRoot":"","sources":["../../../src/__tests__/create-path-adapter.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEpE,MAAM,UAAU,GAAG,sCAAsC,CAAC;AAE1D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IAEpC,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,gBAAgB,UAAU,mBAAmB,EAC7C,qBAAqB,CACtB,CAAC;QACF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAE7D,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,gBAAgB,UAAU,mBAAmB,EAC7C,qBAAqB,CACtB,CAAC;QACF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC5B,OAAO,EAAE,EAAE,EAAE,EAAE,gBAAgB,EAAW;YAC1C,UAAU;SACX,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mGAAmG,EAAE,GAAG,EAAE;QAC3G,wEAAwE;QACxE,wEAAwE;QACxE,+CAA+C;QAC/C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,+BAA+B,EAAE,qBAAqB,CAAC,CAAC;QACnF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC5B,OAAO,EAAE,EAAE,EAAE,EAAE,UAAU,EAAW;YACpC,UAAU;SACX,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,gBAAgB,UAAU,mBAAmB,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -3,6 +3,7 @@ import { stripContextQueryParam } from '../utils/url/strip-context-query-param';
3
3
  import { parseAppRoute } from '../utils/url/parse-app-route';
4
4
  import { UUID_PATTERN } from '../constants';
5
5
  import { buildAppRoute } from '../utils/url/build-app-route';
6
+ import { resolveRouteTail } from '../utils/url/resolve-route-tail';
6
7
  /**
7
8
  * Path adapter — encodes context identity as the first path segment after
8
9
  * the app basename.
@@ -60,18 +61,20 @@ export function createPathAdapter() {
60
61
  /**
61
62
  * Encode context into the URL as a path segment.
62
63
  *
63
- * - **Null context:** removes the context segment to produce a bare app route.
64
- * - **Active context:** places/replaces the context id as the segment after the app key.
65
- *
66
- * Sub-routes after the context segment are intentionally dropped — a context
67
- * change resets the app to its root view to avoid landing in an invalid state.
64
+ * - **Null context:** removes the context segment and drops any sub-route,
65
+ * resetting the app to its root view there's no valid context to resolve
66
+ * the sub-route against.
67
+ * - **Active context:** places/replaces the context id as the segment after
68
+ * the app key, preserving the existing sub-route.
68
69
  */
69
70
  encode({ context, currentURL }) {
70
71
  const match = parseAppRoute(currentURL.pathname);
71
72
  // Pathname doesn't match the /apps/{key}/... shape — not a Fusion app route, bail out
72
73
  if (!match)
73
74
  return null;
74
- const targetPath = context === null ? buildAppRoute(match.appKey) : buildAppRoute(match.appKey, context.id);
75
+ const targetPath = context === null
76
+ ? buildAppRoute(match.appKey)
77
+ : buildAppRoute(match.appKey, context.id, resolveRouteTail(match));
75
78
  const url = new URL(targetPath, currentURL.origin);
76
79
  url.search = currentURL.search;
77
80
  stripContextQueryParam(url);
@@ -1 +1 @@
1
- {"version":3,"file":"create-path-adapter.js","sourceRoot":"","sources":["../../../src/adapters/create-path-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAC;AACpF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,EAAE,EAAE,MAAM;QAEV;;;;;;WAMG;QACH,SAAS,CAAC,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAA4B;YAC7E,4FAA4F;YAC5F,IAAI,0BAA0B,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3C,OAAO,KAAK,CAAC;YACf,CAAC;YAED,0FAA0F;YAC1F,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACxC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,QAAQ,GAAG,eAAe,CAAC;YACjC,kEAAkE;YAClE,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,iFAAiF;YACjF,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBAChD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAED;;;;;;;;WAQG;QACH,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAoD;YAC9E,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACjD,sFAAsF;YACtF,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YAExB,MAAM,UAAU,GACd,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;YAE3F,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YACnD,GAAG,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;YAC/B,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAC5B,+EAA+E;YAC/E,OAAO,GAAG,CAAC;QACb,CAAC;QAED;;;;WAIG;QACH,MAAM,CAAC,GAAQ;YACb,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1C,qGAAqG;YACrG,IAAI,KAAK,EAAE,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3D,OAAO,KAAK,CAAC,SAAS,CAAC;YACzB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"create-path-adapter.js","sourceRoot":"","sources":["../../../src/adapters/create-path-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAC;AACpF,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,EAAE,EAAE,MAAM;QAEV;;;;;;WAMG;QACH,SAAS,CAAC,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAA4B;YAC7E,4FAA4F;YAC5F,IAAI,0BAA0B,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3C,OAAO,KAAK,CAAC;YACf,CAAC;YAED,0FAA0F;YAC1F,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACxC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,QAAQ,GAAG,eAAe,CAAC;YACjC,kEAAkE;YAClE,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,iFAAiF;YACjF,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBAChD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAED;;;;;;;;WAQG;QACH,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAoD;YAC9E,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACjD,sFAAsF;YACtF,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YAExB,MAAM,UAAU,GACd,OAAO,KAAK,IAAI;gBACd,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC;gBAC7B,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;YAEvE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YACnD,GAAG,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;YAC/B,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAC5B,+EAA+E;YAC/E,OAAO,GAAG,CAAC;QACb,CAAC;QAED;;;;WAIG;QACH,MAAM,CAAC,GAAQ;YACb,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1C,qGAAqG;YACrG,IAAI,KAAK,EAAE,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3D,OAAO,KAAK,CAAC,SAAS,CAAC;YACzB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -3,5 +3,6 @@ export { buildAppRoute } from './build-app-route';
3
3
  export { CONTEXT_QUERY_PARAM_KEY, UUID_PATTERN } from '../../constants';
4
4
  export { stripContextQueryParam } from './strip-context-query-param';
5
5
  export { resolveContextIdFromUrl } from './resolve-context-id-from-url';
6
+ export { resolveRouteTail } from './resolve-route-tail';
6
7
  export { buildContextUrlForStrategy } from './build-context-url-for-strategy';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/utils/url/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/utils/url/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { UUID_PATTERN } from '../../constants';
2
+ /**
3
+ * Reconstructs the full sub-route tail from a parsed app route.
4
+ *
5
+ * `contextId` is a positional capture, not a verified context — when the URL has
6
+ * no active context that slot actually holds a route name (e.g. "settings" in
7
+ * `/apps/app/settings/general`). Folding it back into the tail prevents it from
8
+ * being silently discarded when a real context id is substituted into that slot.
9
+ *
10
+ * @param match - The `contextId`/`rest` segments from `parseAppRoute`.
11
+ * @returns The full sub-route tail, or `undefined` when there is none.
12
+ */
13
+ export const resolveRouteTail = (match) => {
14
+ // A UUID in this slot is a real context id — the tail is just whatever follows it
15
+ if (match.contextId && UUID_PATTERN.test(match.contextId)) {
16
+ return match.rest;
17
+ }
18
+ // Otherwise the slot is a route name, not a context — rejoin it with the rest to keep the full tail
19
+ return [match.contextId, match.rest].filter(Boolean).join('/') || undefined;
20
+ };
21
+ //# sourceMappingURL=resolve-route-tail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-route-tail.js","sourceRoot":"","sources":["../../../../src/utils/url/resolve-route-tail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,KAAgD,EAC5B,EAAE;IACtB,kFAAkF;IAClF,IAAI,KAAK,CAAC,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IACD,oGAAoG;IACpG,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;AAC9E,CAAC,CAAC"}
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '1.0.0';
2
+ export const version = '1.0.2-next.0';
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,cAAc,CAAC"}