@equinor/fusion-framework-plugin-context-navigation 0.1.0 → 0.1.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.
Files changed (218) hide show
  1. package/dist/esm/ContextNavigationConfigurator.js +178 -0
  2. package/dist/esm/ContextNavigationConfigurator.js.map +1 -0
  3. package/dist/esm/__tests__/active-app-navigation-events.test.js +115 -0
  4. package/dist/esm/__tests__/active-app-navigation-events.test.js.map +1 -0
  5. package/dist/esm/__tests__/apply-navigation.test.js +152 -0
  6. package/dist/esm/__tests__/apply-navigation.test.js.map +1 -0
  7. package/dist/esm/__tests__/enable.test.js +91 -0
  8. package/dist/esm/__tests__/enable.test.js.map +1 -0
  9. package/dist/esm/__tests__/guard-handlers.test.js +82 -0
  10. package/dist/esm/__tests__/guard-handlers.test.js.map +1 -0
  11. package/dist/esm/__tests__/plugin.test.js +483 -0
  12. package/dist/esm/__tests__/plugin.test.js.map +1 -0
  13. package/dist/esm/__tests__/reconcile.test.js +164 -0
  14. package/dist/esm/__tests__/reconcile.test.js.map +1 -0
  15. package/dist/esm/adapters/create-path-adapter.js +92 -0
  16. package/dist/esm/adapters/create-path-adapter.js.map +1 -0
  17. package/dist/esm/adapters/create-query-adapter.js +83 -0
  18. package/dist/esm/adapters/create-query-adapter.js.map +1 -0
  19. package/dist/esm/adapters/custom-adapter/create-custom-adapter.js +116 -0
  20. package/dist/esm/adapters/custom-adapter/create-custom-adapter.js.map +1 -0
  21. package/dist/esm/adapters/custom-adapter/index.js +5 -0
  22. package/dist/esm/adapters/custom-adapter/index.js.map +1 -0
  23. package/dist/esm/adapters/custom-adapter/normalize-string-result.js +37 -0
  24. package/dist/esm/adapters/custom-adapter/normalize-string-result.js.map +1 -0
  25. package/dist/esm/adapters/custom-adapter/to-app-relative.js +34 -0
  26. package/dist/esm/adapters/custom-adapter/to-app-relative.js.map +1 -0
  27. package/dist/esm/adapters/custom-adapter/to-full-path.js +28 -0
  28. package/dist/esm/adapters/custom-adapter/to-full-path.js.map +1 -0
  29. package/dist/esm/adapters/index.js +4 -0
  30. package/dist/esm/adapters/index.js.map +1 -0
  31. package/dist/esm/adapters/types.js +2 -0
  32. package/dist/esm/adapters/types.js.map +1 -0
  33. package/dist/esm/apply-navigation.js +102 -0
  34. package/dist/esm/apply-navigation.js.map +1 -0
  35. package/dist/esm/constants/context-query-param-key.js +8 -0
  36. package/dist/esm/constants/context-query-param-key.js.map +1 -0
  37. package/dist/esm/constants/index.js +3 -0
  38. package/dist/esm/constants/index.js.map +1 -0
  39. package/dist/esm/constants/uuid-pattern.js +8 -0
  40. package/dist/esm/constants/uuid-pattern.js.map +1 -0
  41. package/dist/esm/create-context-navigation-plugin.js +124 -0
  42. package/dist/esm/create-context-navigation-plugin.js.map +1 -0
  43. package/dist/esm/enable-context-navigation.js +40 -0
  44. package/dist/esm/enable-context-navigation.js.map +1 -0
  45. package/dist/esm/events.js +2 -0
  46. package/dist/esm/events.js.map +1 -0
  47. package/dist/esm/guard-handlers/consume-own-nav-token.js +29 -0
  48. package/dist/esm/guard-handlers/consume-own-nav-token.js.map +1 -0
  49. package/dist/esm/guard-handlers/guard-tick-deps.js +2 -0
  50. package/dist/esm/guard-handlers/guard-tick-deps.js.map +1 -0
  51. package/dist/esm/guard-handlers/guard-tick-payload.js +2 -0
  52. package/dist/esm/guard-handlers/guard-tick-payload.js.map +1 -0
  53. package/dist/esm/guard-handlers/handle-push-mode-guard.js +44 -0
  54. package/dist/esm/guard-handlers/handle-push-mode-guard.js.map +1 -0
  55. package/dist/esm/guard-handlers/handle-replace-mode-guard.js +40 -0
  56. package/dist/esm/guard-handlers/handle-replace-mode-guard.js.map +1 -0
  57. package/dist/esm/guard-handlers/index.js +5 -0
  58. package/dist/esm/guard-handlers/index.js.map +1 -0
  59. package/dist/esm/guard-handlers/is-in-app-scope.js +19 -0
  60. package/dist/esm/guard-handlers/is-in-app-scope.js.map +1 -0
  61. package/dist/esm/helpers/get-current-url.js +11 -0
  62. package/dist/esm/helpers/get-current-url.js.map +1 -0
  63. package/dist/esm/helpers/index.js +6 -0
  64. package/dist/esm/helpers/index.js.map +1 -0
  65. package/dist/esm/helpers/normalize-path-from-url.js +14 -0
  66. package/dist/esm/helpers/normalize-path-from-url.js.map +1 -0
  67. package/dist/esm/helpers/normalize-path.js +30 -0
  68. package/dist/esm/helpers/normalize-path.js.map +1 -0
  69. package/dist/esm/helpers/resolve-adapter.js +23 -0
  70. package/dist/esm/helpers/resolve-adapter.js.map +1 -0
  71. package/dist/esm/helpers/strip-query-params.js +14 -0
  72. package/dist/esm/helpers/strip-query-params.js.map +1 -0
  73. package/dist/esm/index.js +13 -0
  74. package/dist/esm/index.js.map +1 -0
  75. package/dist/esm/operators/active-app-navigation-events.js +52 -0
  76. package/dist/esm/operators/active-app-navigation-events.js.map +1 -0
  77. package/dist/esm/reconcile.js +76 -0
  78. package/dist/esm/reconcile.js.map +1 -0
  79. package/dist/esm/sources/app-first-source.js +41 -0
  80. package/dist/esm/sources/app-first-source.js.map +1 -0
  81. package/dist/esm/sources/context-first-source.js +47 -0
  82. package/dist/esm/sources/context-first-source.js.map +1 -0
  83. package/dist/esm/sources/index.js +4 -0
  84. package/dist/esm/sources/index.js.map +1 -0
  85. package/dist/esm/sources/types.js +16 -0
  86. package/dist/esm/sources/types.js.map +1 -0
  87. package/dist/esm/types.js +2 -0
  88. package/dist/esm/types.js.map +1 -0
  89. package/dist/esm/utils/has-custom-context-generators.js +14 -0
  90. package/dist/esm/utils/has-custom-context-generators.js.map +1 -0
  91. package/dist/esm/utils/legacy-app-navigation-fix.js +45 -0
  92. package/dist/esm/utils/legacy-app-navigation-fix.js.map +1 -0
  93. package/dist/esm/utils/resolve-context-from-url.js +37 -0
  94. package/dist/esm/utils/resolve-context-from-url.js.map +1 -0
  95. package/dist/esm/utils/url/build-app-route.js +20 -0
  96. package/dist/esm/utils/url/build-app-route.js.map +1 -0
  97. package/dist/esm/utils/url/build-context-url-for-strategy.js +43 -0
  98. package/dist/esm/utils/url/build-context-url-for-strategy.js.map +1 -0
  99. package/dist/esm/utils/url/index.js +7 -0
  100. package/dist/esm/utils/url/index.js.map +1 -0
  101. package/dist/esm/utils/url/parse-app-route.js +47 -0
  102. package/dist/esm/utils/url/parse-app-route.js.map +1 -0
  103. package/dist/esm/utils/url/resolve-context-id-from-url.js +29 -0
  104. package/dist/esm/utils/url/resolve-context-id-from-url.js.map +1 -0
  105. package/dist/esm/utils/url/strip-context-query-param.js +20 -0
  106. package/dist/esm/utils/url/strip-context-query-param.js.map +1 -0
  107. package/dist/esm/version.js +3 -0
  108. package/dist/esm/version.js.map +1 -0
  109. package/dist/tsconfig.tsbuildinfo +1 -0
  110. package/dist/types/ContextNavigationConfigurator.d.ts +125 -0
  111. package/dist/types/ContextNavigationConfigurator.d.ts.map +1 -0
  112. package/dist/types/__tests__/active-app-navigation-events.test.d.ts +2 -0
  113. package/dist/types/__tests__/active-app-navigation-events.test.d.ts.map +1 -0
  114. package/dist/types/__tests__/apply-navigation.test.d.ts +2 -0
  115. package/dist/types/__tests__/apply-navigation.test.d.ts.map +1 -0
  116. package/dist/types/__tests__/enable.test.d.ts +2 -0
  117. package/dist/types/__tests__/enable.test.d.ts.map +1 -0
  118. package/dist/types/__tests__/guard-handlers.test.d.ts +2 -0
  119. package/dist/types/__tests__/guard-handlers.test.d.ts.map +1 -0
  120. package/dist/types/__tests__/plugin.test.d.ts +2 -0
  121. package/dist/types/__tests__/plugin.test.d.ts.map +1 -0
  122. package/dist/types/__tests__/reconcile.test.d.ts +2 -0
  123. package/dist/types/__tests__/reconcile.test.d.ts.map +1 -0
  124. package/dist/types/adapters/create-path-adapter.d.ts +27 -0
  125. package/dist/types/adapters/create-path-adapter.d.ts.map +1 -0
  126. package/dist/types/adapters/create-query-adapter.d.ts +34 -0
  127. package/dist/types/adapters/create-query-adapter.d.ts.map +1 -0
  128. package/dist/types/adapters/custom-adapter/create-custom-adapter.d.ts +29 -0
  129. package/dist/types/adapters/custom-adapter/create-custom-adapter.d.ts.map +1 -0
  130. package/dist/types/adapters/custom-adapter/index.d.ts +5 -0
  131. package/dist/types/adapters/custom-adapter/index.d.ts.map +1 -0
  132. package/dist/types/adapters/custom-adapter/normalize-string-result.d.ts +21 -0
  133. package/dist/types/adapters/custom-adapter/normalize-string-result.d.ts.map +1 -0
  134. package/dist/types/adapters/custom-adapter/to-app-relative.d.ts +25 -0
  135. package/dist/types/adapters/custom-adapter/to-app-relative.d.ts.map +1 -0
  136. package/dist/types/adapters/custom-adapter/to-full-path.d.ts +22 -0
  137. package/dist/types/adapters/custom-adapter/to-full-path.d.ts.map +1 -0
  138. package/dist/types/adapters/index.d.ts +5 -0
  139. package/dist/types/adapters/index.d.ts.map +1 -0
  140. package/dist/types/adapters/types.d.ts +73 -0
  141. package/dist/types/adapters/types.d.ts.map +1 -0
  142. package/dist/types/apply-navigation.d.ts +88 -0
  143. package/dist/types/apply-navigation.d.ts.map +1 -0
  144. package/dist/types/constants/context-query-param-key.d.ts +8 -0
  145. package/dist/types/constants/context-query-param-key.d.ts.map +1 -0
  146. package/dist/types/constants/index.d.ts +3 -0
  147. package/dist/types/constants/index.d.ts.map +1 -0
  148. package/dist/types/constants/uuid-pattern.d.ts +8 -0
  149. package/dist/types/constants/uuid-pattern.d.ts.map +1 -0
  150. package/dist/types/create-context-navigation-plugin.d.ts +58 -0
  151. package/dist/types/create-context-navigation-plugin.d.ts.map +1 -0
  152. package/dist/types/enable-context-navigation.d.ts +21 -0
  153. package/dist/types/enable-context-navigation.d.ts.map +1 -0
  154. package/dist/types/events.d.ts +16 -0
  155. package/dist/types/events.d.ts.map +1 -0
  156. package/dist/types/guard-handlers/consume-own-nav-token.d.ts +21 -0
  157. package/dist/types/guard-handlers/consume-own-nav-token.d.ts.map +1 -0
  158. package/dist/types/guard-handlers/guard-tick-deps.d.ts +20 -0
  159. package/dist/types/guard-handlers/guard-tick-deps.d.ts.map +1 -0
  160. package/dist/types/guard-handlers/guard-tick-payload.d.ts +15 -0
  161. package/dist/types/guard-handlers/guard-tick-payload.d.ts.map +1 -0
  162. package/dist/types/guard-handlers/handle-push-mode-guard.d.ts +24 -0
  163. package/dist/types/guard-handlers/handle-push-mode-guard.d.ts.map +1 -0
  164. package/dist/types/guard-handlers/handle-replace-mode-guard.d.ts +19 -0
  165. package/dist/types/guard-handlers/handle-replace-mode-guard.d.ts.map +1 -0
  166. package/dist/types/guard-handlers/index.d.ts +7 -0
  167. package/dist/types/guard-handlers/index.d.ts.map +1 -0
  168. package/dist/types/guard-handlers/is-in-app-scope.d.ts +15 -0
  169. package/dist/types/guard-handlers/is-in-app-scope.d.ts.map +1 -0
  170. package/dist/types/helpers/get-current-url.d.ts +10 -0
  171. package/dist/types/helpers/get-current-url.d.ts.map +1 -0
  172. package/dist/types/helpers/index.d.ts +6 -0
  173. package/dist/types/helpers/index.d.ts.map +1 -0
  174. package/dist/types/helpers/normalize-path-from-url.d.ts +11 -0
  175. package/dist/types/helpers/normalize-path-from-url.d.ts.map +1 -0
  176. package/dist/types/helpers/normalize-path.d.ts +14 -0
  177. package/dist/types/helpers/normalize-path.d.ts.map +1 -0
  178. package/dist/types/helpers/resolve-adapter.d.ts +14 -0
  179. package/dist/types/helpers/resolve-adapter.d.ts.map +1 -0
  180. package/dist/types/helpers/strip-query-params.d.ts +12 -0
  181. package/dist/types/helpers/strip-query-params.d.ts.map +1 -0
  182. package/dist/types/index.d.ts +9 -0
  183. package/dist/types/index.d.ts.map +1 -0
  184. package/dist/types/operators/active-app-navigation-events.d.ts +47 -0
  185. package/dist/types/operators/active-app-navigation-events.d.ts.map +1 -0
  186. package/dist/types/reconcile.d.ts +43 -0
  187. package/dist/types/reconcile.d.ts.map +1 -0
  188. package/dist/types/sources/app-first-source.d.ts +14 -0
  189. package/dist/types/sources/app-first-source.d.ts.map +1 -0
  190. package/dist/types/sources/context-first-source.d.ts +15 -0
  191. package/dist/types/sources/context-first-source.d.ts.map +1 -0
  192. package/dist/types/sources/index.d.ts +4 -0
  193. package/dist/types/sources/index.d.ts.map +1 -0
  194. package/dist/types/sources/types.d.ts +61 -0
  195. package/dist/types/sources/types.d.ts.map +1 -0
  196. package/dist/types/types.d.ts +126 -0
  197. package/dist/types/types.d.ts.map +1 -0
  198. package/dist/types/utils/has-custom-context-generators.d.ts +13 -0
  199. package/dist/types/utils/has-custom-context-generators.d.ts.map +1 -0
  200. package/dist/types/utils/legacy-app-navigation-fix.d.ts +33 -0
  201. package/dist/types/utils/legacy-app-navigation-fix.d.ts.map +1 -0
  202. package/dist/types/utils/resolve-context-from-url.d.ts +20 -0
  203. package/dist/types/utils/resolve-context-from-url.d.ts.map +1 -0
  204. package/dist/types/utils/url/build-app-route.d.ts +11 -0
  205. package/dist/types/utils/url/build-app-route.d.ts.map +1 -0
  206. package/dist/types/utils/url/build-context-url-for-strategy.d.ts +14 -0
  207. package/dist/types/utils/url/build-context-url-for-strategy.d.ts.map +1 -0
  208. package/dist/types/utils/url/index.d.ts +8 -0
  209. package/dist/types/utils/url/index.d.ts.map +1 -0
  210. package/dist/types/utils/url/parse-app-route.d.ts +20 -0
  211. package/dist/types/utils/url/parse-app-route.d.ts.map +1 -0
  212. package/dist/types/utils/url/resolve-context-id-from-url.d.ts +8 -0
  213. package/dist/types/utils/url/resolve-context-id-from-url.d.ts.map +1 -0
  214. package/dist/types/utils/url/strip-context-query-param.d.ts +12 -0
  215. package/dist/types/utils/url/strip-context-query-param.d.ts.map +1 -0
  216. package/dist/types/version.d.ts +2 -0
  217. package/dist/types/version.d.ts.map +1 -0
  218. package/package.json +4 -4
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Strip all query parameters from a URL.
3
+ *
4
+ * Used on app switches to give the adapter a clean URL. The adapter's
5
+ * `encode()` will add back whatever params it needs (e.g. `$contextId`
6
+ * for query-strategy apps). On context-only changes within the same app,
7
+ * this function is not called — query params survive naturally.
8
+ *
9
+ * @param url - The URL to strip (mutated in-place).
10
+ */
11
+ export function stripQueryParams(url) {
12
+ url.search = '';
13
+ }
14
+ //# sourceMappingURL=strip-query-params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-query-params.js","sourceRoot":"","sources":["../../../src/helpers/strip-query-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAQ;IACvC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;AAClB,CAAC"}
@@ -0,0 +1,13 @@
1
+ // Plugin
2
+ export { createContextNavigationPlugin } from './create-context-navigation-plugin';
3
+ // Configurator
4
+ export { ContextNavigationConfigurator } from './ContextNavigationConfigurator';
5
+ // Enable helper
6
+ export { enableContextNavigation } from './enable-context-navigation';
7
+ // Legacy compat
8
+ export { legacyAppNavigationFix } from './utils/legacy-app-navigation-fix';
9
+ // Events (side-effect: augments FrameworkEventMap)
10
+ import './events';
11
+ // Version
12
+ export { version } from './version';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,SAAS;AACT,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAMnF,eAAe;AACf,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAEhF,gBAAgB;AAChB,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAWtE,gBAAgB;AAChB,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAE3E,mDAAmD;AACnD,OAAO,UAAU,CAAC;AAElB,UAAU;AACV,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { EMPTY, combineLatestWith, map, switchMap } from 'rxjs';
2
+ /**
3
+ * RxJS operator factory that produces an emission each time the browser
4
+ * navigation state changes while a fully resolved app is present.
5
+ *
6
+ * Intent: decouple the URL guard from the mechanics of observing app readiness.
7
+ * The guard only needs to know "an app is active and a navigation just happened" —
8
+ * this operator encapsulates the four-level observable plumbing required to
9
+ * produce that signal:
10
+ *
11
+ * 1. **App switch** — outer `switchMap` on `app.current$`. If the portal switches
12
+ * to a different app (or clears the app), inner subscriptions auto-cancel.
13
+ * 2. **Module + manifest resolution** — middle `switchMap` on
14
+ * `currentApp.instance$.pipe(combineLatestWith(currentApp.manifest$))`.
15
+ * Waits until both the app's lazy modules and its manifest have resolved.
16
+ * Emits EMPTY if modules are null.
17
+ * 3. **Routing strategy extraction** — `manifest.build.options.contextRouting`
18
+ * is projected into the downstream payload so the guard can pass it to
19
+ * adapter resolution without a separate manifest lookup.
20
+ * 4. **Navigation event** — inner `map` on `navigation.state$`. Each state change
21
+ * (push, replace, popstate) triggers a downstream emission carrying the
22
+ * resolved app key, modules, and routing strategy.
23
+ *
24
+ * @param app - The app module provider (source of `current$`).
25
+ * @param navigation - The navigation provider (source of `state$`).
26
+ * @returns An observable that emits `{ appKey, appModules }` on every navigation
27
+ * event while an active app with resolved modules is present.
28
+ */
29
+ export function activeAppNavigationEvents(app, navigation) {
30
+ // Emit once per navigation event while the active app is fully resolved
31
+ return app.current$.pipe(
32
+ // Cancel inner subscriptions when the active app changes or is cleared.
33
+ switchMap((currentApp) => !currentApp
34
+ ? EMPTY
35
+ : currentApp.instance$
36
+ // Pair the app instance with its manifest; both must resolve before emitting
37
+ .pipe(combineLatestWith(currentApp.manifest$),
38
+ // Wait for modules to resolve; bail if they're null (loading/error).
39
+ switchMap(([appModules, manifest]) => !appModules
40
+ ? EMPTY
41
+ : navigation.state$
42
+ // Each state$ emission represents a navigation event; project app identity into payload
43
+ .pipe(
44
+ // Each state$ emission means a navigation happened — project
45
+ // the resolved app identity into the downstream payload.
46
+ map(() => ({
47
+ appKey: currentApp.appKey,
48
+ appModules: appModules,
49
+ routingStrategy: manifest?.build?.options?.contextRouting,
50
+ })))))));
51
+ }
52
+ //# sourceMappingURL=active-app-navigation-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"active-app-navigation-events.js","sourceRoot":"","sources":["../../../src/operators/active-app-navigation-events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAmB,iBAAiB,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAyBjF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAsB,EACtB,UAA+B;IAE/B,wEAAwE;IACxE,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI;IACtB,wEAAwE;IACxE,SAAS,CAAC,CAAC,UAAU,EAAE,EAAE,CACvB,CAAC,UAAU;QACT,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,UAAU,CAAC,SAAS;YAClB,6EAA6E;aAC5E,IAAI,CACH,iBAAiB,CAAC,UAAU,CAAC,SAAS,CAAC;QACvC,qEAAqE;QACrE,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,CACnC,CAAC,UAAU;YACT,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,UAAU,CAAC,MAAM;gBACf,wFAAwF;iBACvF,IAAI;YACH,6DAA6D;YAC7D,yDAAyD;YACzD,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;gBACT,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,UAAU,EAAE,UAAiD;gBAC7D,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc;aAC1D,CAAC,CAAC,CACJ,CACR,CACF,CACR,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,76 @@
1
+ import { getCurrentURL, normalizePathFromURL, resolveAdapter, stripQueryParams } from './helpers';
2
+ import { applyNavigation } from './apply-navigation';
3
+ /**
4
+ * Reconcile a source entry: resolve an adapter and apply navigation
5
+ * for the given app/context combination.
6
+ *
7
+ * Handles three context states:
8
+ * - `undefined` — context not yet resolved, skip.
9
+ * - `null` — context cleared, navigate to `nullContextUrl` if configured.
10
+ * - `ContextItem` — active context, encode via adapter and navigate.
11
+ *
12
+ * @param entry - The reconciler source entry with app key, modules, and context state.
13
+ * @param deps - Shared plugin dependencies.
14
+ * @param options - Optional trigger context (e.g. whether this is an app switch).
15
+ */
16
+ export function reconcile(entry, deps, options = {}) {
17
+ const { appKey, appModules, contextState, routingStrategy } = entry;
18
+ const { event, navigation, config, eventSource, ownNavTokens, log } = deps;
19
+ // Context not yet resolved, skip reconciliation and wait for context to be available
20
+ if (contextState === undefined) {
21
+ event.dispatchEvent('onContextNavigationSkipped', {
22
+ detail: { appKey, reason: 'no-context' },
23
+ source: eventSource,
24
+ });
25
+ log(`Context undefined for [${appKey}] — idle, waiting.`);
26
+ return;
27
+ }
28
+ // Context cleared, navigate to the configured null context URL
29
+ if (contextState === null && config.nullContextUrl) {
30
+ const currentURL = getCurrentURL(navigation, config.origin);
31
+ const targetPath = config.nullContextUrl({ appKey, currentURL });
32
+ const targetURL = new URL(targetPath, config.origin);
33
+ // Only navigate if the target path differs from current path
34
+ if (normalizePathFromURL(targetURL) !== normalizePathFromURL(currentURL)) {
35
+ ownNavTokens.add(normalizePathFromURL(targetURL));
36
+ navigation.navigate(targetURL, config.navigationOptions);
37
+ log(`Null context → navigated to [${targetPath}] for [${appKey}]`);
38
+ }
39
+ return;
40
+ }
41
+ const appContext = appModules.context;
42
+ // App context not available, skip reconciliation
43
+ if (!appContext) {
44
+ return;
45
+ }
46
+ const currentURL = getCurrentURL(navigation, config.origin);
47
+ // On app switch, strip all query params so nothing leaks from the
48
+ // previous app. The adapter will add back what it needs.
49
+ // This ensures a clean slate for the new app's context encoding.
50
+ if (options.isAppSwitch) {
51
+ stripQueryParams(currentURL);
52
+ log(`App switch detected → stripped query params for [${appKey}]`);
53
+ }
54
+ // When switching apps, always replace the history entry rather than pushing.
55
+ // The portal already pushed a new entry for the app navigation; adding another
56
+ // push here would create a double entry that traps the user when pressing Back.
57
+ // `config.navigationOptions.replace` only applies to in-app context changes.
58
+ const navOptionsOverride = options.isAppSwitch ? { replace: true } : undefined;
59
+ const adapter = resolveAdapter({ appKey, appContext, routingStrategy, currentURL }, config.adapters);
60
+ // No matching adapter found for this app, skip navigation
61
+ if (!adapter) {
62
+ event.dispatchEvent('onContextNavigationSkipped', {
63
+ detail: { appKey, reason: 'no-adapter' },
64
+ source: eventSource,
65
+ });
66
+ log(`No adapter matched for [${appKey}] — skipping.`);
67
+ return;
68
+ }
69
+ event.dispatchEvent('onContextNavigationAdapterResolved', {
70
+ detail: { appKey, adapterId: adapter.id },
71
+ source: eventSource,
72
+ });
73
+ log(`Resolved adapter [${adapter.id}] for [${appKey}]`);
74
+ applyNavigation({ appKey, appModules, adapter, context: contextState, currentURL }, deps, navOptionsOverride);
75
+ }
76
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/reconcile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { switchMap, EMPTY, of, combineLatestWith, distinctUntilChanged } from 'rxjs';
2
+ import { contextStateChanged } from './types';
3
+ /**
4
+ * App-first source factory — app switches lead, context follows.
5
+ *
6
+ * Stream composition:
7
+ * ```
8
+ * app.current$ → combineLatest(instance$, manifest$) → combineLatestWith(context.currentContext$)
9
+ * ```
10
+ *
11
+ * Use this for **app-portal** where the active app determines the navigation
12
+ * context. Context changes are reconciled within the scope of the current app.
13
+ */
14
+ export const appFirstSource = () => {
15
+ return ({ app, context }) => {
16
+ return (app.current$
17
+ // Track the active app; each switch abandons the previous app's streams and starts fresh
18
+ .pipe(switchMap((currentApp) => {
19
+ // No active app yet (e.g. initial load or app unmounted) — nothing to reconcile
20
+ if (!currentApp) {
21
+ return EMPTY;
22
+ }
23
+ return (currentApp.instance$
24
+ // Wait for both the module instance and the manifest to resolve before projecting downstream
25
+ .pipe(combineLatestWith(currentApp.manifest$), switchMap(([appModules, manifest]) => {
26
+ // App is registered but its module bundle hasn't resolved yet — skip until it's ready
27
+ if (!appModules) {
28
+ return EMPTY;
29
+ }
30
+ return of({
31
+ appModules: appModules,
32
+ appKey: currentApp.appKey,
33
+ routingStrategy: manifest?.build?.options?.contextRouting,
34
+ });
35
+ })));
36
+ }), combineLatestWith(context.currentContext$
37
+ // Suppress redundant reconciler runs when context identity hasn't actually changed
38
+ .pipe(distinctUntilChanged(contextStateChanged))), switchMap(([{ appModules, appKey, routingStrategy }, contextState]) => of({ appKey, appModules, contextState, routingStrategy }))));
39
+ };
40
+ };
41
+ //# sourceMappingURL=app-first-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-first-source.js","sourceRoot":"","sources":["../../../src/sources/app-first-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,MAAM,CAAC;AAKrF,OAAO,EAAE,mBAAmB,EAAgC,MAAM,SAAS,CAAC;AAE5E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAA4B,EAAE;IAC1D,OAAO,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1B,OAAO,CACL,GAAG,CAAC,QAAQ;YACV,yFAAyF;aACxF,IAAI,CACH,SAAS,CAAC,CAAC,UAAU,EAAE,EAAE;YACvB,gFAAgF;YAChF,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,CACL,UAAU,CAAC,SAAS;gBAClB,6FAA6F;iBAC5F,IAAI,CACH,iBAAiB,CAAC,UAAU,CAAC,SAAS,CAAC,EACvC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE;gBACnC,sFAAsF;gBACtF,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,OAAO,KAAK,CAAC;gBACf,CAAC;gBACD,OAAO,EAAE,CAAC;oBACR,UAAU,EAAE,UAAiD;oBAC7D,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc;iBAC1D,CAAC,CAAC;YACL,CAAC,CAAC,CACH,CACJ,CAAC;QACJ,CAAC,CAAC,EACF,iBAAiB,CACf,OAAO,CAAC,eAAe;YACrB,mFAAmF;aAClF,IAAI,CAAC,oBAAoB,CAAC,mBAAmB,CAAC,CAAC,CACnD,EACD,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,YAAY,CAAC,EAAE,EAAE,CACpE,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAC1D,CACF,CACJ,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { switchMap, EMPTY, of, combineLatestWith, distinctUntilChanged } from 'rxjs';
2
+ import { contextStateChanged } from './types';
3
+ /**
4
+ * Context-first source factory — context changes lead, app follows.
5
+ *
6
+ * Stream composition:
7
+ * ```
8
+ * context.currentContext$ → combineLatestWith(app.current$ → combineLatest(instance$, manifest$))
9
+ * ```
10
+ *
11
+ * Use this for **context-portal** where the selected context drives all
12
+ * navigation. App switches are reconciled within the scope of the current
13
+ * context.
14
+ */
15
+ export const contextFirstSource = () => {
16
+ return ({ app, context }) => {
17
+ return (context.currentContext$
18
+ // Context is the primary signal — downstream app state reacts to each change
19
+ // Deduplicate context changes and combine with the active app's instance and manifest
20
+ .pipe(distinctUntilChanged(contextStateChanged), combineLatestWith(app.current$
21
+ // Reactively pair context state with the active app's resolved instance and manifest
22
+ // Resolve the active app to its instance and manifest — emits nothing until both are ready
23
+ .pipe(switchMap((currentApp) => {
24
+ // No active app yet — nothing to reconcile against
25
+ if (!currentApp) {
26
+ return EMPTY;
27
+ }
28
+ return (currentApp.instance$
29
+ // Both instance and manifest must be resolved — manifest provides the routing strategy
30
+ // Combine instance and manifest into a single resolved app shape
31
+ .pipe(combineLatestWith(currentApp.manifest$), switchMap(([appModules, manifest]) => {
32
+ // App is loading or failed to initialize — defer until instance is available
33
+ if (!appModules) {
34
+ return EMPTY;
35
+ }
36
+ return of({
37
+ appModules: appModules,
38
+ appKey: currentApp.appKey,
39
+ routingStrategy: manifest?.build?.options?.contextRouting,
40
+ });
41
+ })));
42
+ }))))
43
+ // Flatten the combined tuple into a unified reconciler input object
44
+ .pipe(switchMap(([contextState, { appModules, appKey, routingStrategy }]) => of({ appKey, appModules, contextState, routingStrategy }))));
45
+ };
46
+ };
47
+ //# sourceMappingURL=context-first-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-first-source.js","sourceRoot":"","sources":["../../../src/sources/context-first-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,MAAM,CAAC;AAKrF,OAAO,EAAE,mBAAmB,EAAgC,MAAM,SAAS,CAAC;AAE5E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAA4B,EAAE;IAC9D,OAAO,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1B,OAAO,CACL,OAAO,CAAC,eAAe;YACrB,6EAA6E;YAC7E,sFAAsF;aACrF,IAAI,CACH,oBAAoB,CAAC,mBAAmB,CAAC,EACzC,iBAAiB,CACf,GAAG,CAAC,QAAQ;YACV,qFAAqF;YACrF,2FAA2F;aAC1F,IAAI,CACH,SAAS,CAAC,CAAC,UAAU,EAAE,EAAE;YACvB,mDAAmD;YACnD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,CACL,UAAU,CAAC,SAAS;gBAClB,uFAAuF;gBACvF,iEAAiE;iBAChE,IAAI,CACH,iBAAiB,CAAC,UAAU,CAAC,SAAS,CAAC,EACvC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE;gBACnC,6EAA6E;gBAC7E,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,OAAO,KAAK,CAAC;gBACf,CAAC;gBACD,OAAO,EAAE,CAAC;oBACR,UAAU,EAAE,UAAiD;oBAC7D,MAAM,EAAE,UAAU,CAAC,MAAM;oBACzB,eAAe,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,cAAc;iBAC1D,CAAC,CAAC;YACL,CAAC,CAAC,CACH,CACJ,CAAC;QACJ,CAAC,CAAC,CACH,CACJ,CACF;YACD,oEAAoE;aACnE,IAAI,CACH,SAAS,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,CACpE,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC,CAC1D,CACF,CACJ,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { appFirstSource } from './app-first-source';
2
+ export { contextFirstSource } from './context-first-source';
3
+ export { contextStateChanged, } from './types';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sources/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EACL,mBAAmB,GAMpB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,16 @@
1
+ // ─── Shared Comparator ──────────────────────────────────────────────
2
+ /**
3
+ * Equality comparator for `ContextState` values in `distinctUntilChanged`.
4
+ *
5
+ * Returns `true` when the two states represent the same context identity,
6
+ * preventing redundant reconciler emissions. Both `null` (cleared) and
7
+ * `undefined` (initializing) are compared by reference; active contexts
8
+ * are compared by `id`.
9
+ */
10
+ export const contextStateChanged = (a, b) => {
11
+ // Both are active contexts — compare by ID
12
+ if (a && b)
13
+ return a.id === b.id;
14
+ return a === b;
15
+ };
16
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/sources/types.ts"],"names":[],"mappings":"AA0EA,uEAAuE;AAEvE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAe,EAAE,CAAe,EAAW,EAAE;IAC/E,2CAA2C;IAC3C,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;IACjC,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Check whether an app's context provider has custom path generators registered.
3
+ *
4
+ * Returns `true` when both `extractContextIdFromPath` and
5
+ * `generatePathFromContext` are present, indicating the app owns its own
6
+ * URL shape and should be handled by the custom adapter.
7
+ *
8
+ * @param appContext - The app's context provider instance.
9
+ * @returns `true` if the app has custom context generators, `false` otherwise.
10
+ */
11
+ export function hasCustomContextGenerators(appContext) {
12
+ return !!appContext.extractContextIdFromPath && !!appContext.generatePathFromContext;
13
+ }
14
+ //# sourceMappingURL=has-custom-context-generators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has-custom-context-generators.js","sourceRoot":"","sources":["../../../src/utils/has-custom-context-generators.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,UAA4B;IACrE,OAAO,CAAC,CAAC,UAAU,CAAC,wBAAwB,IAAI,CAAC,CAAC,UAAU,CAAC,uBAAuB,CAAC;AACvF,CAAC"}
@@ -0,0 +1,45 @@
1
+ // Import event type augmentations so the event name is recognized
2
+ import '../events';
3
+ const LEGACY_NAVIGATION_VERSION = 7;
4
+ /**
5
+ * Register a legacy app navigation fix that resets the app's internal
6
+ * router when context navigation occurs and the app uses a navigation
7
+ * module older than v7.
8
+ *
9
+ * Apps with navigation <v7 don't respond to portal navigation events
10
+ * correctly. This listener detects them and calls `appNavigation.replace('/')`
11
+ * after each context navigation to keep the app router in sync.
12
+ *
13
+ * Call this during framework configuration (e.g. in `postInitialize`)
14
+ * and store the returned teardown function to clean up when needed.
15
+ *
16
+ * @param deps - The required framework module instances.
17
+ * @returns A teardown function that removes the listener.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * // In portal framework configuration:
22
+ * const teardown = legacyAppNavigationFix({ event });
23
+ * ```
24
+ */
25
+ export function legacyAppNavigationFix(deps) {
26
+ const { event } = deps;
27
+ return event.addEventListener('onContextNavigationNavigated', (e) => {
28
+ // Only act when context is cleared — regular navigation doesn't need fixing
29
+ if (e.detail.context !== null) {
30
+ return;
31
+ }
32
+ const { appKey, appModules } = e.detail;
33
+ const appNavigation = appModules.navigation;
34
+ // Apps without a navigation module have no router to reset — nothing to do
35
+ if (!appNavigation) {
36
+ return;
37
+ }
38
+ // Legacy nav modules don't respond to portal context changes — force a router reset
39
+ if (appNavigation.version.major < LEGACY_NAVIGATION_VERSION) {
40
+ console.warn(`[ContextNavigation] App [${appKey}] uses navigation v${appNavigation.version.major} (< ${LEGACY_NAVIGATION_VERSION}). Resetting app router to keep it in sync.`);
41
+ appNavigation.replace('/');
42
+ }
43
+ });
44
+ }
45
+ //# sourceMappingURL=legacy-app-navigation-fix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-app-navigation-fix.js","sourceRoot":"","sources":["../../../src/utils/legacy-app-navigation-fix.ts"],"names":[],"mappings":"AAGA,kEAAkE;AAClE,OAAO,WAAW,CAAC;AAEnB,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAUpC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAgC;IACrE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IAEvB,OAAO,KAAK,CAAC,gBAAgB,CAAC,8BAA8B,EAAE,CAAC,CAAC,EAAE,EAAE;QAClE,4EAA4E;QAC5E,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC;QAExC,MAAM,aAAa,GAAI,UAAmD,CAAC,UAAU,CAAC;QAEtF,2EAA2E;QAC3E,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,oFAAoF;QACpF,IAAI,aAAa,CAAC,OAAO,CAAC,KAAK,GAAG,yBAAyB,EAAE,CAAC;YAC5D,OAAO,CAAC,IAAI,CACV,4BAA4B,MAAM,sBAAsB,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,yBAAyB,6CAA6C,CACjK,CAAC;YACF,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Creates a resolver that decodes a context ID from the current URL using
3
+ * the provided adapters and sets it on the context provider.
4
+ *
5
+ * Iterates adapters in priority order — the first static adapter whose
6
+ * `decode()` returns a non-null value wins. Factory adapters are skipped
7
+ * since no app context is available at initial resolution time.
8
+ *
9
+ * @param adapters - Registered adapters, evaluated in order.
10
+ * @param origin - The origin to use when constructing the URL (defaults to `window.location.origin`).
11
+ * @returns An async function compatible with `ContextNavigationConfig.resolveInitialContext`.
12
+ */
13
+ export const resolveContextFromUrl = (adapters, origin) => async ({ context, navigation, }) => {
14
+ const resolvedOrigin = origin ?? window.location.origin;
15
+ const currentURL = new URL(`${navigation.path.pathname}${navigation.path.search ?? ''}`, resolvedOrigin);
16
+ // Adapters are evaluated in registration order — first successful decode wins
17
+ for (const entry of adapters) {
18
+ // Factory adapters need app context to decode — skip until an app is active
19
+ if (typeof entry === 'function') {
20
+ // No app is mounted yet — factory adapters cannot produce a valid decode
21
+ continue;
22
+ }
23
+ const contextId = entry.decode(currentURL);
24
+ // First adapter to decode a valid ID wins — apply context and stop processing
25
+ if (contextId) {
26
+ try {
27
+ await context.setCurrentContextByIdAsync(contextId);
28
+ }
29
+ catch (err) {
30
+ // Log the error but do not throw — initial context resolution is best-effort
31
+ console.warn('ContextNavigation: failed to resolve initial context from URL', contextId, err);
32
+ }
33
+ return;
34
+ }
35
+ }
36
+ };
37
+ //# sourceMappingURL=resolve-context-from-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-context-from-url.js","sourceRoot":"","sources":["../../../src/utils/resolve-context-from-url.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,CAAC,QAAyC,EAAE,MAAe,EAAE,EAAE,CAC/D,KAAK,EAAE,EACL,OAAO,EACP,UAAU,GAIX,EAAiB,EAAE;IAClB,MAAM,cAAc,GAAG,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxD,MAAM,UAAU,GAAG,IAAI,GAAG,CACxB,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,EAC5D,cAAc,CACf,CAAC;IAEF,8EAA8E;IAC9E,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,4EAA4E;QAC5E,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;YAChC,yEAAyE;YACzE,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC3C,8EAA8E;QAC9E,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,0BAA0B,CAAC,SAAS,CAAC,CAAC;YACtD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,6EAA6E;gBAC7E,OAAO,CAAC,IAAI,CACV,+DAA+D,EAC/D,SAAS,EACT,GAAG,CACJ,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;IACH,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Builds `/apps/:appKey[/:contextId][/:rest]` from named segments.
3
+ * Inverse of `parseAppRoute` — used by all write/upsert helpers.
4
+ *
5
+ * @param appKey - The application key (e.g. `'my-app'`).
6
+ * @param contextId - Optional context id to embed as the third path segment.
7
+ * @param rest - Optional sub-route tail to append after the context segment.
8
+ * @returns The constructed app route path string.
9
+ */
10
+ export const buildAppRoute = (appKey, contextId, rest) => {
11
+ const path = [`/apps/${appKey}`];
12
+ // Context segment is optional — omitted when no context is actively selected
13
+ if (contextId)
14
+ path.push(`${contextId}`);
15
+ // Preserve any sub-route depth that exists beyond the context segment
16
+ if (rest)
17
+ path.push(`${rest}`);
18
+ return path.join('/');
19
+ };
20
+ //# sourceMappingURL=build-app-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-app-route.js","sourceRoot":"","sources":["../../../../src/utils/url/build-app-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,SAAkB,EAAE,IAAa,EAAU,EAAE;IACzF,MAAM,IAAI,GAAG,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAEjC,6EAA6E;IAC7E,IAAI,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,SAAS,EAAE,CAAC,CAAC;IAEzC,sEAAsE;IACtE,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IAE/B,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { CONTEXT_QUERY_PARAM_KEY } from '../../constants';
2
+ import { parseAppRoute } from './parse-app-route';
3
+ import { buildAppRoute } from './build-app-route';
4
+ /**
5
+ * Builds the correct context URL for the given routing strategy.
6
+ *
7
+ * Designed for `setContextPathGenerator` — the adapter layer handles
8
+ * runtime URL encoding; this is only for the context module's own hooks.
9
+ *
10
+ * @param contextId - The context id to embed in the URL, or `undefined` to clear it.
11
+ * @param path - The current URL path to modify.
12
+ * @param routingStrategy - The routing strategy (`'query'` or `'path'`).
13
+ * @returns The modified URL path with the context id applied.
14
+ */
15
+ export const buildContextUrlForStrategy = (contextId, path, routingStrategy) => {
16
+ // Query routing keeps context out of the path — safer for apps that parse their own routes
17
+ if (routingStrategy === 'query') {
18
+ try {
19
+ const url = new URL(path, 'http://localhost');
20
+ // Keep the query param in sync with context lifecycle — cleared when context is removed
21
+ if (contextId) {
22
+ url.searchParams.set(CONTEXT_QUERY_PARAM_KEY, contextId);
23
+ }
24
+ else {
25
+ url.searchParams.delete(CONTEXT_QUERY_PARAM_KEY);
26
+ }
27
+ // Restore $ from %24 encoding
28
+ url.search = url.search.replace(/%24/gi, '$');
29
+ return `${url.pathname}${url.search}${url.hash}`;
30
+ }
31
+ catch {
32
+ return path;
33
+ }
34
+ }
35
+ // Path routing embeds the context id directly in the URL structure
36
+ const match = parseAppRoute(path);
37
+ // Path is not a recognised app route — return it unchanged
38
+ if (!match) {
39
+ return path;
40
+ }
41
+ return buildAppRoute(match.appKey, contextId ?? undefined);
42
+ };
43
+ //# sourceMappingURL=build-context-url-for-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-context-url-for-strategy.js","sourceRoot":"","sources":["../../../../src/utils/url/build-context-url-for-strategy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,SAA6B,EAC7B,IAAY,EACZ,eAAoD,EAC5C,EAAE;IACV,2FAA2F;IAC3F,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;YAC9C,wFAAwF;YACxF,IAAI,SAAS,EAAE,CAAC;gBACd,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,SAAS,CAAC,CAAC;YAC3D,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;YACnD,CAAC;YACD,8BAA8B;YAC9B,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC9C,OAAO,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAElC,2DAA2D;IAC3D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,SAAS,CAAC,CAAC;AAC7D,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { parseAppRoute } from './parse-app-route';
2
+ export { buildAppRoute } from './build-app-route';
3
+ export { CONTEXT_QUERY_PARAM_KEY, UUID_PATTERN } from '../../constants';
4
+ export { stripContextQueryParam } from './strip-context-query-param';
5
+ export { resolveContextIdFromUrl } from './resolve-context-id-from-url';
6
+ export { buildContextUrlForStrategy } from './build-context-url-for-strategy';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,47 @@
1
+ import { normalizePath } from '../../helpers';
2
+ /**
3
+ * URLPattern for the standard Fusion portal app route:
4
+ *
5
+ * /apps/:appKey — bare app route
6
+ * /apps/:appKey/:contextId — app with context
7
+ * /apps/:appKey/:contextId/… — app with context + sub-routes
8
+ *
9
+ * Named groups give direct access to path segments.
10
+ * Instantiated once at module scope — pattern compilation is O(1) thereafter.
11
+ */
12
+ const APP_ROUTE_PATTERN = new URLPattern({
13
+ pathname: '/apps/:appKey/:contextId?/:rest*',
14
+ });
15
+ /**
16
+ * Parses `/apps/:appKey/:contextId?/…rest` into named segments.
17
+ *
18
+ * Trailing slashes are stripped before matching because URLPattern's
19
+ * pathname matching does not treat `/apps/my-app/` and `/apps/my-app`
20
+ * as equivalent.
21
+ *
22
+ * @param pathname - The URL pathname to parse.
23
+ * @returns Named segments, or undefined if the pathname is not an app route.
24
+ */
25
+ export const parseAppRoute = (pathname) => {
26
+ // URLPattern requires an absolute path — prepend slash if missing
27
+ if (!pathname[0] || pathname[0] !== '/') {
28
+ pathname = `/${pathname}`;
29
+ }
30
+ const normalized = normalizePath(pathname);
31
+ const result = APP_ROUTE_PATTERN.exec({ pathname: normalized });
32
+ // Pathname does not match the /apps/:appKey pattern
33
+ if (!result) {
34
+ return undefined;
35
+ }
36
+ const { appKey, contextId, rest } = result.pathname.groups;
37
+ // Guard against an empty appKey capture group (malformed pattern match)
38
+ if (!appKey) {
39
+ return undefined;
40
+ }
41
+ return {
42
+ appKey,
43
+ contextId: contextId || undefined,
44
+ rest: rest || undefined,
45
+ };
46
+ };
47
+ //# sourceMappingURL=parse-app-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-app-route.js","sourceRoot":"","sources":["../../../../src/utils/url/parse-app-route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C;;;;;;;;;GASG;AACH,MAAM,iBAAiB,GAAG,IAAI,UAAU,CAAC;IACvC,QAAQ,EAAE,kCAAkC;CAC7C,CAAC,CAAC;AAWH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAgB,EAA6B,EAAE;IAC3E,kEAAkE;IAClE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;QACxC,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IAEhE,oDAAoD;IACpD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;IAE3D,wEAAwE;IACxE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;QACL,MAAM;QACN,SAAS,EAAE,SAAS,IAAI,SAAS;QACjC,IAAI,EAAE,IAAI,IAAI,SAAS;KACxB,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { UUID_PATTERN, CONTEXT_QUERY_PARAM_KEY } from '../../constants';
2
+ import { parseAppRoute } from './parse-app-route';
3
+ /**
4
+ * Extracts a context id from a URL — checks query param first, falls back to path segment.
5
+ * Designed for use with `setContextPathExtractor`.
6
+ * @param path - The URL path to parse.
7
+ * @returns The context id, or `undefined` if none was found.
8
+ */
9
+ export const resolveContextIdFromUrl = (path) => {
10
+ try {
11
+ const url = new URL(path, 'http://localhost');
12
+ const fromQuery = url.searchParams.get(CONTEXT_QUERY_PARAM_KEY) ?? undefined;
13
+ // Query params are the explicit/authoritative way to set context — path is a fallback
14
+ if (fromQuery) {
15
+ return fromQuery;
16
+ }
17
+ }
18
+ catch {
19
+ // path may not be a valid URL — continue to path parsing
20
+ }
21
+ // Context may be embedded in the path when no query param is present
22
+ const match = parseAppRoute(path);
23
+ // Guard against non-context path segments — parseAppRoute can match non-UUID routes
24
+ if (match?.contextId && UUID_PATTERN.test(match.contextId)) {
25
+ return match.contextId;
26
+ }
27
+ return undefined;
28
+ };
29
+ //# sourceMappingURL=resolve-context-id-from-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-context-id-from-url.js","sourceRoot":"","sources":["../../../../src/utils/url/resolve-context-id-from-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,IAAY,EAAsB,EAAE;IAC1E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,SAAS,CAAC;QAE7E,sFAAsF;QACtF,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;IAED,qEAAqE;IACrE,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAElC,oFAAoF;IACpF,IAAI,KAAK,EAAE,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3D,OAAO,KAAK,CAAC,SAAS,CAAC;IACzB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { CONTEXT_QUERY_PARAM_KEY } from '../../constants';
2
+ /**
3
+ * Removes the `$contextId` query parameter from a URL in-place.
4
+ *
5
+ * Path and custom adapters encode context in the URL path, not the query
6
+ * string. If a previous adapter (or an external link) left `$contextId`
7
+ * in the query, carrying it forward creates a confusing duplicate signal.
8
+ * This utility strips it so the URL only contains the path-based encoding.
9
+ *
10
+ * @param url - The URL object to mutate (parameter is deleted in-place).
11
+ */
12
+ export function stripContextQueryParam(url) {
13
+ // Only mutate the URL if the param is actually present
14
+ if (url.searchParams.has(CONTEXT_QUERY_PARAM_KEY)) {
15
+ url.searchParams.delete(CONTEXT_QUERY_PARAM_KEY);
16
+ // Restore $ from %24 encoding in remaining params
17
+ url.search = url.search.replace(/%24/gi, '$');
18
+ }
19
+ }
20
+ //# sourceMappingURL=strip-context-query-param.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-context-query-param.js","sourceRoot":"","sources":["../../../../src/utils/url/strip-context-query-param.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAQ;IAC7C,uDAAuD;IACvD,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;QACjD,kDAAkD;QAClD,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}