@magmonium/one 0.3.5 → 0.3.6

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,13 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, inject, Injectable, signal, computed, DestroyRef, DOCUMENT, ApplicationRef, RendererFactory2, createComponent, EnvironmentInjector, Pipe, ChangeDetectorRef, untracked, ElementRef, afterNextRender, input, ChangeDetectionStrategy, Component, effect, Injector, reflectComponentType, model, HostListener, Renderer2, Directive, output, viewChild, ViewEncapsulation, forwardRef, contentChild, TemplateRef, ViewContainerRef, inputBinding, outputBinding, linkedSignal, afterRenderEffect, runInInjectionContext, Input, booleanAttribute, HostAttributeToken, viewChildren, afterEveryRender, EventEmitter, Output, ViewChild, ContentChild, provideZonelessChangeDetection, makeEnvironmentProviders, HostBinding, provideAppInitializer, provideBrowserGlobalErrorListeners, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
3
- import { HttpClient, HttpParams, provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
4
- import { firstValueFrom, of, tap, catchError, throwError, shareReplay, finalize, map, forkJoin, Observable, pipe, interval, fromEvent, switchMap as switchMap$1, filter, EMPTY, Subject, combineLatest, race, take, from, startWith } from 'rxjs';
3
+ import { HttpClient, HttpParams, HttpErrorResponse, provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
4
+ import { firstValueFrom, of, tap, catchError, throwError, shareReplay, finalize, forkJoin, map, Observable, pipe, interval, fromEvent, switchMap as switchMap$1, filter, EMPTY, Subject, combineLatest, race, take, from, startWith } from 'rxjs';
5
5
  import { rxResource, toObservable, toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
6
  import { signalStore, withState, withMethods, patchState, withHooks, withComputed, withProps } from '@ngrx/signals';
7
7
  import { rxMethod } from '@ngrx/signals/rxjs-interop';
8
8
  import { map as map$1, switchMap, catchError as catchError$1 } from 'rxjs/operators';
9
9
  import { io } from 'socket.io-client';
10
- import { DefaultUrlSerializer, UrlSerializer, Router, NavigationEnd, RouterModule, RouterLink, provideRouter, withComponentInputBinding, withRouterConfig, RouterOutlet } from '@angular/router';
10
+ import { DefaultUrlSerializer, UrlSerializer, PRIMARY_OUTLET, Router, NavigationEnd, RouterModule, RouterLink, UrlSegment, UrlSegmentGroup, UrlTree, provideRouter, withComponentInputBinding, withRouterConfig, RouterOutlet } from '@angular/router';
11
11
  import * as i1 from '@angular/common';
12
12
  import { APP_BASE_HREF, Location, NgOptimizedImage, IMAGE_LOADER, NgTemplateOutlet, CommonModule, NgComponentOutlet, DecimalPipe, NgClass, DatePipe, NgStyle } from '@angular/common';
13
13
  import { FormField, email, required, validate, minLength, maxLength, min, max, apply, form } from '@angular/forms/signals';
@@ -88,6 +88,11 @@ var AppRelationType;
88
88
  })(AppRelationType || (AppRelationType = {}));
89
89
  const LOGIN_COMPONENT = new InjectionToken('LOGIN_COMPONENT');
90
90
  const LOGIN_STORE = new InjectionToken('LOGIN_STORE');
91
+ /**
92
+ * True inside a Remote's own bundle (`provideMagWcConfig`). Where it is mounted
93
+ * is the host's MountPattern, so the base href no longer tells a Remote apart.
94
+ */
95
+ const WC_BUNDLE = new InjectionToken('WC_BUNDLE');
91
96
  const ASSET_BASE_URL = new InjectionToken('ASSET_BASE_URL');
92
97
  const ONE_ASSET_BASE_URL = new InjectionToken('ONE_ASSET_BASE_URL');
93
98
  const SEARCH_QUERY = new InjectionToken('SEARCH_QUERY');
@@ -165,15 +170,44 @@ function isUrlLocalhost(url) {
165
170
  }
166
171
  }
167
172
 
173
+ const tierWindow = () => typeof window !== 'undefined' ? window : undefined;
174
+ /** Pins an AppLink to a tier, or back to its status folder with `undefined`. */
175
+ function setWcTier(remoteId, tier) {
176
+ const win = tierWindow();
177
+ if (!win)
178
+ return;
179
+ const tiers = { ...(win.__MAGMONIUM_WC_TIERS__ ?? {}) };
180
+ if (tier)
181
+ tiers[remoteId] = tier;
182
+ else
183
+ delete tiers[remoteId];
184
+ win.__MAGMONIUM_WC_TIERS__ = tiers;
185
+ }
186
+ function wcTierOf(remoteId) {
187
+ return tierWindow()?.__MAGMONIUM_WC_TIERS__?.[remoteId];
188
+ }
189
+ /** Which tier's bundle defined a tag — `status` for the app's status folder. */
190
+ function definedWcTierOf(selector) {
191
+ return tierWindow()?.__MAGMONIUM_WC_DEFINED_TIERS__?.[selector];
192
+ }
193
+ function recordDefinedWcTier(selector, tier) {
194
+ const win = tierWindow();
195
+ if (!win)
196
+ return;
197
+ win.__MAGMONIUM_WC_DEFINED_TIERS__ = {
198
+ ...(win.__MAGMONIUM_WC_DEFINED_TIERS__ ?? {}),
199
+ [selector]: tier,
200
+ };
201
+ }
168
202
  function buildWcBaseUrl(apiUrl, remoteId) {
169
- if (typeof window !== 'undefined' &&
170
- window.__MAGMONIUM_STANDALONE__) {
203
+ if (tierWindow()?.__MAGMONIUM_STANDALONE__) {
171
204
  return '/assets';
172
205
  }
173
206
  if (isLocalhost) {
174
207
  return `/local-dist/m-${remoteId}-wc`;
175
208
  }
176
- return `${apiUrl}/store/v1/wc/${remoteId}`;
209
+ const tier = wcTierOf(remoteId);
210
+ return `${apiUrl}/store/v1/wc/${remoteId}${tier ? `/${tier}` : ''}`;
177
211
  }
178
212
 
179
213
  /** Access domains — routed to Auth URL when it differs from Backend URL (ADR 0012). */
@@ -268,7 +302,8 @@ class HttpService {
268
302
  if (isLocalhost) {
269
303
  return this.http.get(`/local-dist/m-${link}-wc/${filePath.join('/')}`);
270
304
  }
271
- return this.get('store', ['wc', link, ...filePath]);
305
+ const tier = wcTierOf(link);
306
+ return this.get('store', ['wc', link, ...(tier ? [tier] : []), ...filePath]);
272
307
  }
273
308
  buildWcUrl(link) {
274
309
  return buildWcBaseUrl(this.url, link);
@@ -834,15 +869,27 @@ class TranslateService {
834
869
  remoteAppId = signal(undefined, ...(ngDevMode ? [{ debugName: "remoteAppId" }] : /* istanbul ignore next */ []));
835
870
  httpService = inject(HttpService);
836
871
  assetStore = inject(AssetStore);
872
+ /**
873
+ * Every Remote mounted beside m-ui's single slot, counted per link so one
874
+ * app mounted twice stays loaded until both mounts leave (ADR 0036).
875
+ */
876
+ mountedApps = signal({}, ...(ngDevMode ? [{ debugName: "mountedApps" }] : /* istanbul ignore next */ []));
877
+ mountRemoteApp = (link) => this.mountedApps.update((apps) => ({ ...apps, [link]: (apps[link] ?? 0) + 1 }));
878
+ unmountRemoteApp = (link) => this.mountedApps.update(({ [link]: count = 0, ...rest }) => count > 1 ? { ...rest, [link]: count - 1 } : rest);
879
+ remoteAppIds = computed(() => [
880
+ ...new Set([
881
+ ...(this.remoteAppId() ? [this.remoteAppId()] : []),
882
+ ...Object.keys(this.mountedApps()),
883
+ ]),
884
+ ], { ...(ngDevMode ? { debugName: "remoteAppIds" } : /* istanbul ignore next */ {}), equal: (a, b) => a.length === b.length && a.every((v, i) => v === b[i]) });
837
885
  remoteTranslation = rxResource({
838
- params: () => ({ lang: this.lang(), appId: this.remoteAppId() }),
839
- stream: ({ params: { lang, appId } }) => {
840
- if (!appId)
886
+ params: () => ({ lang: this.lang(), appIds: this.remoteAppIds() }),
887
+ stream: ({ params: { lang, appIds } }) => {
888
+ if (!appIds.length)
841
889
  return of({});
842
- const baseUrl = this.httpService.buildWcAssetBaseUrl(appId);
843
- return this.httpService
844
- .asset(['i18n', lang], Assets.JSON, undefined, baseUrl)
845
- .pipe(catchError(() => of({})));
890
+ return forkJoin(appIds.map((appId) => this.httpService
891
+ .asset(['i18n', lang], Assets.JSON, undefined, this.httpService.buildWcAssetBaseUrl(appId))
892
+ .pipe(catchError(() => of({}))))).pipe(map((bundles) => Object.assign({}, ...bundles)));
846
893
  },
847
894
  });
848
895
  langs = rxResource({
@@ -4124,6 +4171,19 @@ function deriveBreadcrumb(params) {
4124
4171
  };
4125
4172
  }
4126
4173
 
4174
+ /**
4175
+ * The mount a host-scoped NavId belongs to — the deepest one, so a Remote
4176
+ * mounted inside another Remote answers for its own subtree.
4177
+ */
4178
+ const mountFor = (navId, mounts) => mounts
4179
+ .filter((mount) => toLocalNavId(navId, mount.appNavId) !== undefined)
4180
+ .sort((a, b) => b.appNavId.length - a.appNavId.length)[0];
4181
+
4182
+ /** One chain can cross the host and several Remotes, so the mount is per NavId. */
4183
+ const mountIn = (navId, ctx) => mountFor(navId, ctx.mounts ??
4184
+ (ctx.appNavId && ctx.appLink
4185
+ ? [{ name: ctx.appLink, link: ctx.appLink, appNavId: ctx.appNavId }]
4186
+ : []));
4127
4187
  class GetNavService {
4128
4188
  httpService = inject(HttpService);
4129
4189
  assetStore = inject(AssetStore);
@@ -4168,11 +4228,11 @@ class GetNavService {
4168
4228
  ? forkJoin(navIds.map((id) => this.fetchNav(id, ctx))).pipe(map((pairs) => Object.fromEntries(pairs.filter(Boolean))))
4169
4229
  : of({});
4170
4230
  fetchNav = (navId, ctx) => {
4171
- const { appNavId, appLink } = ctx;
4172
- const localId = appNavId && appLink ? toLocalNavId(navId, appNavId) : undefined;
4173
- const nav$ = localId && appLink
4231
+ const mount = mountIn(navId, ctx);
4232
+ const localId = mount ? toLocalNavId(navId, mount.appNavId) : undefined;
4233
+ const nav$ = localId && mount
4174
4234
  ? this.httpService
4175
- .asset(['navs', localId], Assets.JSON, undefined, this.httpService.buildWcUrl(appLink))
4235
+ .asset(['navs', localId], Assets.JSON, undefined, this.httpService.buildWcUrl(mount.link))
4176
4236
  .pipe(catchError(() => of(undefined)))
4177
4237
  : this.assetStore
4178
4238
  .resolve('navs', navId)
@@ -4185,7 +4245,7 @@ class GetNavService {
4185
4245
  const seed = unfetchedPlatformNav(navId);
4186
4246
  return seed ? [navId, seed] : undefined;
4187
4247
  }
4188
- return [navId, this.rebase(nav, navId, ctx)];
4248
+ return [navId, this.rebase(nav, navId, mount?.appNavId)];
4189
4249
  }));
4190
4250
  };
4191
4251
  /**
@@ -4198,10 +4258,11 @@ class GetNavService {
4198
4258
  * panel the library ships (ADR 0016). A Remote's root is not the host's root
4199
4259
  * and takes no platform children.
4200
4260
  */
4201
- rebase = (nav, navId, { appNavId }) => {
4202
- if (!appNavId || navId === appNavId) {
4261
+ rebase = (nav, navId, appNavId) => {
4262
+ // A mount's own root is rebased too: its children arrive root-relative
4263
+ // (`root_book`), and left alone they named host nodes nothing holds.
4264
+ if (!appNavId)
4203
4265
  return mergePlatformNav({ ...nav, navId }, navId);
4204
- }
4205
4266
  return {
4206
4267
  ...nav,
4207
4268
  navId,
@@ -4224,6 +4285,8 @@ const initialState$3 = {
4224
4285
  murl: [],
4225
4286
  appLink: undefined,
4226
4287
  appNavId: undefined,
4288
+ mounts: {},
4289
+ outlets: [],
4227
4290
  navOpenedByUser: false,
4228
4291
  menuNavId: undefined,
4229
4292
  keepPanelOnPathChange: false,
@@ -4292,6 +4355,16 @@ const visibilityKey = (navId, params) => `${navId}|${Object.keys(params)
4292
4355
  .map((name) => `${name}=${params[name]}`)
4293
4356
  .join('&')}`;
4294
4357
  const isPromise$1 = (value) => typeof value?.then === 'function';
4358
+ const urlSerializer = new DefaultUrlSerializer();
4359
+ /** The auxiliary segment names at the URL's root, the primary path aside. */
4360
+ const outletsOf = (url) => {
4361
+ try {
4362
+ return Object.keys(urlSerializer.parse(url).root.children).filter((name) => name !== PRIMARY_OUTLET);
4363
+ }
4364
+ catch {
4365
+ return [];
4366
+ }
4367
+ };
4295
4368
  const murlOf = (root) => {
4296
4369
  const raw = root.queryParams?.[MURL_PARAM];
4297
4370
  return typeof raw === 'string' ? raw.split(MURL_SEP).filter(Boolean) : [];
@@ -4330,6 +4403,64 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4330
4403
  const rooted = navIdFor(ROOT_NAV$1, murl);
4331
4404
  return anchored === rooted ? [anchored] : [anchored, rooted];
4332
4405
  }, ...(ngDevMode ? [{ debugName: "widgetNavIds" }] : /* istanbul ignore next */ []));
4406
+ /** The m-ui slot first, then every named mount (ADR 0036). */
4407
+ const allMounts = computed(() => {
4408
+ const appLink = store.appLink();
4409
+ const appNavId = store.appNavId();
4410
+ const mounts = Object.values(store.mounts());
4411
+ return appLink && appNavId && !store.mounts()[appLink]
4412
+ ? [{ name: appLink, link: appLink, appNavId }, ...mounts]
4413
+ : mounts;
4414
+ }, ...(ngDevMode ? [{ debugName: "allMounts" }] : /* istanbul ignore next */ []));
4415
+ /**
4416
+ * The side apps the menu lists, in mount order: a registered mount whose
4417
+ * segment the URL holds, then any segment no mount has claimed yet —
4418
+ * drawn under its name until one does (ADR 0038).
4419
+ */
4420
+ const menuMounts = computed(() => {
4421
+ const outlets = store.outlets();
4422
+ const mounts = Object.values(store.mounts());
4423
+ const listed = mounts
4424
+ .filter((m) => m.createMenu &&
4425
+ (m.urlMode === 'tail' ||
4426
+ (m.urlMode === 'outlet' && outlets.includes(m.name))))
4427
+ .map((m) => ({ name: m.name, appNavId: m.appNavId, link: m.link }));
4428
+ const unclaimed = outlets
4429
+ .filter((name) => !store.mounts()[name])
4430
+ .map((name) => ({ name, appNavId: `root_app_${name}`, link: undefined }));
4431
+ return [...listed, ...unclaimed];
4432
+ }, ...(ngDevMode ? [{ debugName: "menuMounts" }] : /* istanbul ignore next */ []));
4433
+ /** The map the menu reads: the tree, with each listed side app under root. */
4434
+ const menuNavMap = computed(() => {
4435
+ const navMap = store.navMap();
4436
+ const listed = menuMounts();
4437
+ if (!listed.length)
4438
+ return navMap;
4439
+ const configs = store.wcConfigMap();
4440
+ const root = navMap[ROOT_NAV$1];
4441
+ const extra = listed.map(({ name, appNavId, link }) => {
4442
+ const fetched = navMap[appNavId];
4443
+ const config = link ? configs[link] : undefined;
4444
+ const node = {
4445
+ ...(fetched ?? { kind: 'url', presentation: 'static' }),
4446
+ navId: appNavId,
4447
+ title: config?.app ?? fetched?.title ?? name,
4448
+ ...(config?.logo ? { logo: config.logo } : {}),
4449
+ };
4450
+ return node;
4451
+ });
4452
+ return {
4453
+ ...navMap,
4454
+ ...Object.fromEntries(extra.map((node) => [node.navId, node])),
4455
+ [ROOT_NAV$1]: {
4456
+ ...root,
4457
+ children: [
4458
+ ...(root?.children ?? []).filter((id) => !extra.some((node) => node.navId === id)),
4459
+ ...extra.map((node) => node.navId),
4460
+ ],
4461
+ },
4462
+ };
4463
+ }, ...(ngDevMode ? [{ debugName: "menuNavMap" }] : /* istanbul ignore next */ []));
4333
4464
  const hasMurl = computed(() => store.murl().length > 0, ...(ngDevMode ? [{ debugName: "hasMurl" }] : /* istanbul ignore next */ []));
4334
4465
  const isNavOpened = computed(() => hasMurl() || store.navOpenedByUser(), ...(ngDevMode ? [{ debugName: "isNavOpened" }] : /* istanbul ignore next */ []));
4335
4466
  const isMNavOpened = hasMurl;
@@ -4356,18 +4487,17 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4356
4487
  * panel, so the candidate list the open panel walks would never reach them.
4357
4488
  */
4358
4489
  const entryAt = (candidate) => {
4359
- const appNavId = store.appNavId();
4360
- const appLink = store.appLink();
4361
4490
  const maps = navWidgetMaps ?? [];
4362
4491
  const navId = candidate;
4363
- if (appLink && appNavId) {
4364
- const registered = store.remoteWidgets()[appLink];
4365
- const localId = toLocalNavId(navId, appNavId);
4366
- if (registered && localId) {
4367
- const entry = registered.widgetMap()[localId];
4368
- if (entry)
4369
- return { entry, remote: true };
4370
- }
4492
+ // Registrations are per app, not per mount: two mounts of one link share
4493
+ // one widget map, each reading it through its own prefix.
4494
+ const mount = mountFor(navId, allMounts());
4495
+ if (mount) {
4496
+ const registered = store.remoteWidgets()[mount.link];
4497
+ const localId = toLocalNavId(navId, mount.appNavId);
4498
+ const entry = registered?.widgetMap()[localId];
4499
+ if (entry)
4500
+ return { entry, remote: true };
4371
4501
  }
4372
4502
  for (let i = maps.length - 1; i >= 0; i--) {
4373
4503
  const entry = maps[i]()[navId];
@@ -4607,7 +4737,7 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4607
4737
  const emittedMenu = menuConfig?.navMenu();
4608
4738
  return deriveBreadcrumb({
4609
4739
  navId: panelNavId(),
4610
- navMap: store.navMap(),
4740
+ navMap: menuNavMap(),
4611
4741
  anchor: { navId: store.routeNavId() ?? ROOT_NAV$1, path: store.path() },
4612
4742
  dynamicRows: dynamicRows(),
4613
4743
  instances: dynamicInstances(),
@@ -4647,6 +4777,8 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4647
4777
  }, ...(ngDevMode ? [{ debugName: "navHasContent" }] : /* istanbul ignore next */ []));
4648
4778
  return {
4649
4779
  id,
4780
+ allMounts,
4781
+ menuMounts,
4650
4782
  panelNavId,
4651
4783
  hasMurl,
4652
4784
  isNavOpened,
@@ -4667,7 +4799,9 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4667
4799
  const localDefaultNavId = inject(NAV_DEFAULT_MURL, { optional: true });
4668
4800
  const appBaseHref = inject(APP_BASE_HREF, { optional: true }) ?? '/';
4669
4801
  const location = inject(Location);
4802
+ const translateService = inject(TranslateService);
4670
4803
  const router = store.router;
4804
+ const mountOf = (navId) => mountFor(navId, untracked(() => store.allMounts()));
4671
4805
  /** Address for a NavId, anchored to where we stand when it applies. */
4672
4806
  const addressFor = (navId, params) => renderAddress(navId, store.navMap(), { navId: store.routeNavId() ?? ROOT_NAV$1, path: store.path() }, params);
4673
4807
  /**
@@ -4679,6 +4813,16 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4679
4813
  * must not clear `navOpenedByUser`, so the panel stays up on root.
4680
4814
  */
4681
4815
  const goToNav = (navId, params, options) => {
4816
+ // A side app's own Nav moves that app; it reports, and its m-remote
4817
+ // writes the URL through `out` — one write path, not two.
4818
+ const mount = mountOf(navId);
4819
+ if (mount?.navigate) {
4820
+ const map = untracked(() => store.navMap());
4821
+ const own = navIdToRoutePath(mount.appNavId, map);
4822
+ const full = navIdToRoutePath(navId, map);
4823
+ mount.navigate(full.slice(own === URL_SEP ? 1 : own.length + 1));
4824
+ return;
4825
+ }
4682
4826
  const wasOpen = untracked(() => store.hasMurl());
4683
4827
  patchState(store, { menuNavId: undefined });
4684
4828
  const address = addressFor(navId, params);
@@ -4701,11 +4845,15 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4701
4845
  * 0015); where the map holds the host-scoped key instead, that is the one.
4702
4846
  */
4703
4847
  const knownNavId = (navId) => {
4704
- const appNavId = untracked(() => store.appNavId());
4705
- if (untracked(() => store.navMap())[navId] || !appNavId)
4848
+ const map = untracked(() => store.navMap());
4849
+ if (map[navId])
4706
4850
  return navId;
4707
- const hosted = toHostNavId(navId, appNavId);
4708
- return untracked(() => store.navMap())[hosted] ? hosted : navId;
4851
+ for (const { appNavId } of untracked(() => store.allMounts())) {
4852
+ const hosted = toHostNavId(navId, appNavId);
4853
+ if (map[hosted])
4854
+ return hosted;
4855
+ }
4856
+ return navId;
4709
4857
  };
4710
4858
  /** The route parameter one Nav contributes, read off the map. */
4711
4859
  const paramNameOf = (navId) => navParamOf(untracked(() => store.navMap())[knownNavId(navId)]);
@@ -4781,11 +4929,13 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4781
4929
  const routeChain = routeChainOf(snapshot.root);
4782
4930
  const murl = murlOf(snapshot.root);
4783
4931
  const path = router.url.split(MURL_SEP)[0].split('?')[0];
4932
+ const outlets = outletsOf(router.url);
4784
4933
  untracked(() => {
4785
4934
  const pathChanged = path !== store.path();
4786
4935
  const keepPanel = store.keepPanelOnPathChange();
4787
4936
  patchState(store, {
4788
4937
  path,
4938
+ outlets,
4789
4939
  routeNavId,
4790
4940
  routeParams,
4791
4941
  routeChain,
@@ -4841,6 +4991,40 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4841
4991
  });
4842
4992
  loadWcConfig(appLink);
4843
4993
  };
4994
+ const registerMount = (mount) => {
4995
+ const previous = untracked(() => store.mounts())[mount.name];
4996
+ if (previous)
4997
+ translateService.unmountRemoteApp(previous.link);
4998
+ patchState(store, (state) => ({
4999
+ mounts: { ...state.mounts, [mount.name]: mount },
5000
+ }));
5001
+ translateService.mountRemoteApp(mount.link);
5002
+ loadWcConfig(mount.link);
5003
+ if (mount.createMenu)
5004
+ loadMountNav(mount);
5005
+ };
5006
+ /**
5007
+ * A listed side app's root and its children, fetched beside whatever the
5008
+ * page is loading rather than through `loadNav`, whose switchMap would
5009
+ * cancel the page's own fetch.
5010
+ */
5011
+ const loadMountNav = (mount) => getNavService
5012
+ .getNav(mount.appNavId, untracked(() => store.navMap()), {
5013
+ mounts: untracked(() => store.allMounts()),
5014
+ })
5015
+ .subscribe((delta) => patchState(store, (state) => ({
5016
+ navMap: { ...state.navMap, ...delta },
5017
+ })));
5018
+ /** The node leaves with the mount; the fetched navs and config stay cached. */
5019
+ const unregisterMount = (name) => {
5020
+ const mount = untracked(() => store.mounts())[name];
5021
+ if (!mount)
5022
+ return;
5023
+ translateService.unmountRemoteApp(mount.link);
5024
+ patchState(store, (state) => ({
5025
+ mounts: Object.fromEntries(Object.entries(state.mounts).filter(([key]) => key !== name)),
5026
+ }));
5027
+ };
4844
5028
  const registerWidgets = (appLink, entry) => {
4845
5029
  patchState(store, (state) => ({
4846
5030
  remoteWidgets: { ...state.remoteWidgets, [appLink]: entry },
@@ -4891,6 +5075,7 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4891
5075
  .getNav(navId, untracked(() => store.navMap()), {
4892
5076
  appNavId,
4893
5077
  appLink: untracked(() => store.appLink()),
5078
+ mounts: untracked(() => store.allMounts()),
4894
5079
  })
4895
5080
  .pipe(tap((delta) => {
4896
5081
  if (!Object.keys(delta).length)
@@ -4922,6 +5107,9 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
4922
5107
  toggleMenu,
4923
5108
  setSelected,
4924
5109
  setAppLink,
5110
+ registerMount,
5111
+ unregisterMount,
5112
+ mountOf,
4925
5113
  setWcConfig,
4926
5114
  loadWcConfig,
4927
5115
  loadNav,
@@ -5015,6 +5203,28 @@ const NavStore = signalStore({ providedIn: 'root' }, withState(initialState$3),
5015
5203
  }
5016
5204
  });
5017
5205
  });
5206
+ // A remote segment nobody mounted — a stale link, a closed side app
5207
+ // whose clear never ran — leaves once the app has settled, so the URL
5208
+ // says only what is on screen. The app's own named outlets are not ours.
5209
+ const appRef = inject(ApplicationRef);
5210
+ effect(() => {
5211
+ const outlets = store.outlets();
5212
+ if (!outlets.length)
5213
+ return;
5214
+ untracked(() => appRef.whenStable().then(() => {
5215
+ const mounts = store.mounts();
5216
+ const config = store.router.config ?? [];
5217
+ for (const name of store.outlets()) {
5218
+ if (mounts[name])
5219
+ continue;
5220
+ const ours = config.some((route) => route.outlet === name && route.data?.['remoteOutlet']);
5221
+ if (ours)
5222
+ store.router.navigate([{ outlets: { [name]: null } }], {
5223
+ replaceUrl: true,
5224
+ });
5225
+ }
5226
+ }));
5227
+ });
5018
5228
  store.loadNav(computed(() => ({
5019
5229
  navId: store.id(),
5020
5230
  appNavId: store.appNavId(),
@@ -5201,6 +5411,7 @@ const SHARED_ICONS = [
5201
5411
  'layers',
5202
5412
  'layouts',
5203
5413
  'link',
5414
+ 'link-broken',
5204
5415
  'list',
5205
5416
  'loading',
5206
5417
  'loading-bars',
@@ -7464,7 +7675,7 @@ class SectionFormItemComponent extends ConfigComponent {
7464
7675
  break;
7465
7676
  }
7466
7677
  case InputType.TOGGLE: {
7467
- const { ToggleInputComponent } = await import('./magmonium-one-toggle-aWv0EPWO.mjs');
7678
+ const { ToggleInputComponent } = await import('./magmonium-one-toggle-DUaDJWDm.mjs');
7468
7679
  this.createDynamicComponent(seq, ToggleInputComponent, [], true);
7469
7680
  break;
7470
7681
  }
@@ -7476,12 +7687,12 @@ class SectionFormItemComponent extends ConfigComponent {
7476
7687
  break;
7477
7688
  }
7478
7689
  case InputType.PASSWORD: {
7479
- const { PasswordInputComponent } = await import('./magmonium-one-password-BzC17fOd.mjs');
7690
+ const { PasswordInputComponent } = await import('./magmonium-one-password-BAO_KJTn.mjs');
7480
7691
  this.createDynamicComponent(seq, PasswordInputComponent);
7481
7692
  break;
7482
7693
  }
7483
7694
  case InputType.OTP: {
7484
- const { OtpInputComponent } = await import('./magmonium-one-otp-BsBQwXEa.mjs');
7695
+ const { OtpInputComponent } = await import('./magmonium-one-otp-DWxgKMy4.mjs');
7485
7696
  this.createDynamicComponent(seq, OtpInputComponent);
7486
7697
  break;
7487
7698
  }
@@ -18009,7 +18220,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
18009
18220
  }]
18010
18221
  }], ctorParameters: () => [] });
18011
18222
 
18223
+ /**
18224
+ * A Remote reporting its own route, dispatched on its element and bubbling —
18225
+ * two mounts of one app share an appId, their elements they do not.
18226
+ */
18012
18227
  const WC_ROUTE_CHANGED_EVENT = 'mag-wc-route-changed';
18228
+ /** The host moving a Remote to one of its own paths, dispatched on its element. */
18229
+ const WC_NAVIGATE_EVENT = 'mag-wc-navigate';
18013
18230
  const WcRouterStore = signalStore({ providedIn: 'root' }, withState({ url: [] }), withMethods((state, router = inject(Router)) => ({
18014
18231
  setExtra: (path) => {
18015
18232
  if (path === undefined)
@@ -18036,6 +18253,10 @@ const WcRouterStore = signalStore({ providedIn: 'root' }, withState({ url: [] })
18036
18253
  class BaseRootWebComponent extends BaseWebComponent {
18037
18254
  router = inject(Router);
18038
18255
  extra = input(...(ngDevMode ? [undefined, { debugName: "extra" }] : /* istanbul ignore next */ []));
18256
+ /** Handed down by the mounting m-remote: where it is mounted is the host's to say. */
18257
+ appLink = input(...(ngDevMode ? [undefined, { debugName: "appLink" }] : /* istanbul ignore next */ []));
18258
+ /** The host path it is mounted under, when the host synced it through a MountPattern. */
18259
+ base = input(...(ngDevMode ? [undefined, { debugName: "base" }] : /* istanbul ignore next */ []));
18039
18260
  lang = input(...(ngDevMode ? [undefined, { debugName: "lang" }] : /* istanbul ignore next */ []));
18040
18261
  query = input(...(ngDevMode ? [undefined, { debugName: "query" }] : /* istanbul ignore next */ []));
18041
18262
  navWidgetMaps = inject(NAV_WIDGET_MAP, { optional: true });
@@ -18048,10 +18269,11 @@ class BaseRootWebComponent extends BaseWebComponent {
18048
18269
  assetStore = inject(AssetStore);
18049
18270
  searchQuery = inject(SEARCH_QUERY, { optional: true });
18050
18271
  searchGroups = inject(WC_SEARCH_GROUPS, { optional: true });
18272
+ /** The custom element itself: the one channel two mounts of this app do not share. */
18273
+ host = inject(ElementRef).nativeElement;
18051
18274
  constructor() {
18052
18275
  super();
18053
18276
  effect(() => {
18054
- // eslint-disable-next-line @angular-eslint/no-uncalled-signals
18055
18277
  if (this.searchQuery) {
18056
18278
  const q = this.query();
18057
18279
  if (q !== undefined) {
@@ -18081,28 +18303,41 @@ class BaseRootWebComponent extends BaseWebComponent {
18081
18303
  }
18082
18304
  });
18083
18305
  });
18084
- const appIdMatch = this.appBaseHref?.match(/^\/app\/([^/]+)/);
18085
- if (appIdMatch) {
18086
- this.assetStore.setAppLink(appIdMatch[1]);
18087
- }
18088
- if (this.navWidgetMaps?.length && appIdMatch) {
18089
- this.registerCustomElements();
18090
- const maps = this.navWidgetMaps;
18091
- const mergedValue = maps.reduce((acc, m) => ({ ...acc, ...m() }), {});
18092
- registerNavWidgets(appIdMatch[1], signal(mergedValue), this.injector, this.defaultNavId ?? undefined);
18093
- }
18094
- if (this.userTabMap && appIdMatch) {
18095
- this.registerUserTabElements(appIdMatch[1]);
18096
- }
18097
- // eslint-disable-next-line @angular-eslint/no-uncalled-signals
18098
- if (this.searchGroups && appIdMatch) {
18099
- const appId = appIdMatch[1];
18100
- const searchGroups = this.searchGroups;
18101
- effect(() => {
18102
- const groups = searchGroups();
18103
- window.dispatchEvent(new CustomEvent(SEARCH_RESULTS_EVENT, { detail: { appId, groups } }));
18104
- });
18105
- }
18306
+ // The AppLink arrives as the `appLink` input from m-remote, which knows the
18307
+ // host's MountPattern. The `/app/<link>` base href stays as the fallback for
18308
+ // a host that passes none, and is read now, before any input lands.
18309
+ let appId;
18310
+ const bindApp = (id) => {
18311
+ appId = id;
18312
+ this.assetStore.setAppLink(id);
18313
+ if (this.navWidgetMaps?.length) {
18314
+ this.registerCustomElements();
18315
+ const maps = this.navWidgetMaps;
18316
+ const mergedValue = maps.reduce((acc, m) => ({ ...acc, ...m() }), {});
18317
+ registerNavWidgets(id, signal(mergedValue), this.injector, this.defaultNavId ?? undefined);
18318
+ }
18319
+ if (this.userTabMap) {
18320
+ this.registerUserTabElements(id);
18321
+ }
18322
+ if (this.searchGroups) {
18323
+ const searchGroups = this.searchGroups;
18324
+ effect(() => {
18325
+ const groups = searchGroups();
18326
+ const base = this.base();
18327
+ window.dispatchEvent(new CustomEvent(SEARCH_RESULTS_EVENT, {
18328
+ detail: { appId: id, groups, ...(base ? { base } : {}) },
18329
+ }));
18330
+ }, { injector: this.injector });
18331
+ }
18332
+ };
18333
+ const fromBaseHref = this.appBaseHref?.match(/^\/app\/([^/]+)/)?.[1];
18334
+ if (fromBaseHref)
18335
+ bindApp(fromBaseHref);
18336
+ effect(() => {
18337
+ const id = this.appLink();
18338
+ if (id && !appId)
18339
+ untracked(() => bindApp(id));
18340
+ });
18106
18341
  // Run initial sync when router is ready
18107
18342
  this.router.initialNavigation();
18108
18343
  const initialPath = this.extra();
@@ -18111,19 +18346,29 @@ class BaseRootWebComponent extends BaseWebComponent {
18111
18346
  skipLocationChange: true,
18112
18347
  });
18113
18348
  }
18114
- // Notify host NavStore when WC navigates internally
18115
- if (typeof window !== 'undefined' && appIdMatch) {
18116
- const appId = appIdMatch[1];
18117
- const destroyRef = inject(DestroyRef);
18118
- const sub = this.router.events
18119
- .pipe(filter((e) => e instanceof NavigationEnd))
18120
- .subscribe(({ urlAfterRedirects }) => {
18121
- window.dispatchEvent(new CustomEvent(WC_ROUTE_CHANGED_EVENT, {
18122
- detail: { appId, path: urlAfterRedirects },
18123
- }));
18124
- });
18125
- destroyRef.onDestroy(() => sub.unsubscribe());
18126
- }
18349
+ // Report every settled route on this element, bubbling: the m-remote that
18350
+ // mounted it listens there (ADR 0038), anything above still hears it.
18351
+ const destroyRef = inject(DestroyRef);
18352
+ const sub = this.router.events
18353
+ .pipe(filter((e) => e instanceof NavigationEnd))
18354
+ .subscribe(({ urlAfterRedirects }) => {
18355
+ this.host.dispatchEvent(new CustomEvent(WC_ROUTE_CHANGED_EVENT, {
18356
+ bubbles: true,
18357
+ composed: true,
18358
+ detail: { appId, path: urlAfterRedirects },
18359
+ }));
18360
+ });
18361
+ // The host moving this remote — a path binding or a menu click.
18362
+ const onNavigate = (event) => {
18363
+ const path = event.detail?.path;
18364
+ if (path !== undefined)
18365
+ this.router.navigateByUrl(path, { skipLocationChange: true });
18366
+ };
18367
+ this.host.addEventListener(WC_NAVIGATE_EVENT, onNavigate);
18368
+ destroyRef.onDestroy(() => {
18369
+ sub.unsubscribe();
18370
+ this.host.removeEventListener(WC_NAVIGATE_EVENT, onNavigate);
18371
+ });
18127
18372
  }
18128
18373
  registerCustomElements() {
18129
18374
  for (const component of this.wcComponents ?? []) {
@@ -18163,7 +18408,7 @@ class BaseRootWebComponent extends BaseWebComponent {
18163
18408
  }
18164
18409
  }
18165
18410
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: BaseRootWebComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18166
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: BaseRootWebComponent, isStandalone: true, selector: "ng-component", inputs: { extra: { classPropertyName: "extra", publicName: "extra", isSignal: true, isRequired: false, transformFunction: null }, lang: { classPropertyName: "lang", publicName: "lang", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
18411
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: BaseRootWebComponent, isStandalone: true, selector: "ng-component", inputs: { extra: { classPropertyName: "extra", publicName: "extra", isSignal: true, isRequired: false, transformFunction: null }, appLink: { classPropertyName: "appLink", publicName: "appLink", isSignal: true, isRequired: false, transformFunction: null }, base: { classPropertyName: "base", publicName: "base", isSignal: true, isRequired: false, transformFunction: null }, lang: { classPropertyName: "lang", publicName: "lang", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
18167
18412
  }
18168
18413
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: BaseRootWebComponent, decorators: [{
18169
18414
  type: Component,
@@ -18171,7 +18416,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
18171
18416
  template: '',
18172
18417
  changeDetection: ChangeDetectionStrategy.OnPush,
18173
18418
  }]
18174
- }], ctorParameters: () => [], propDecorators: { extra: [{ type: i0.Input, args: [{ isSignal: true, alias: "extra", required: false }] }], lang: [{ type: i0.Input, args: [{ isSignal: true, alias: "lang", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }] } });
18419
+ }], ctorParameters: () => [], propDecorators: { extra: [{ type: i0.Input, args: [{ isSignal: true, alias: "extra", required: false }] }], appLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "appLink", required: false }] }], base: [{ type: i0.Input, args: [{ isSignal: true, alias: "base", required: false }] }], lang: [{ type: i0.Input, args: [{ isSignal: true, alias: "lang", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }] } });
18175
18420
 
18176
18421
  const getRootRoute = (snapshot) => {
18177
18422
  let root = snapshot.root;
@@ -22107,7 +22352,7 @@ class WrapperInputComponent extends ConfigComponent {
22107
22352
  break;
22108
22353
  }
22109
22354
  case InputType.TOGGLE: {
22110
- const { ToggleInputComponent } = await import('./magmonium-one-toggle-aWv0EPWO.mjs');
22355
+ const { ToggleInputComponent } = await import('./magmonium-one-toggle-DUaDJWDm.mjs');
22111
22356
  this.createDynamicComponent(seq, ToggleInputComponent, [], true);
22112
22357
  break;
22113
22358
  }
@@ -22119,12 +22364,12 @@ class WrapperInputComponent extends ConfigComponent {
22119
22364
  break;
22120
22365
  }
22121
22366
  case InputType.PASSWORD: {
22122
- const { PasswordInputComponent } = await import('./magmonium-one-password-BzC17fOd.mjs');
22367
+ const { PasswordInputComponent } = await import('./magmonium-one-password-BAO_KJTn.mjs');
22123
22368
  this.createDynamicComponent(seq, PasswordInputComponent);
22124
22369
  break;
22125
22370
  }
22126
22371
  case InputType.OTP: {
22127
- const { OtpInputComponent } = await import('./magmonium-one-otp-BsBQwXEa.mjs');
22372
+ const { OtpInputComponent } = await import('./magmonium-one-otp-DWxgKMy4.mjs');
22128
22373
  this.createDynamicComponent(seq, OtpInputComponent);
22129
22374
  break;
22130
22375
  }
@@ -23831,7 +24076,7 @@ class FileUploadInputComponent extends BaseInputComponent {
23831
24076
  const modalStore = this.modalStore;
23832
24077
  if (!modalStore)
23833
24078
  return undefined;
23834
- const { ImageEditorComponent } = await import('./magmonium-one-image-editor-Bc5gaTcA.mjs');
24079
+ const { ImageEditorComponent } = await import('./magmonium-one-image-editor-DOHQLwOd.mjs');
23835
24080
  const ratio = String(this.config()?.ratio ?? '');
23836
24081
  return new Promise((resolve) => {
23837
24082
  const modalRef = new ModalRef();
@@ -26302,6 +26547,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
26302
26547
  }, styles: [":host{display:block;width:100%}.m-section-carousel{display:flex;flex-direction:column;gap:.75rem;width:100%}.m-section-carousel__viewport{position:relative;flex:1;min-width:0;overflow:hidden;max-height:var(--m-section-carousel-max-height, none);touch-action:pan-y}.m-section-carousel__track{display:flex;width:100%;transition:transform .35s cubic-bezier(.4,0,.2,1)}.m-section-carousel__track--dragging{transition:none}.m-section-carousel__slide{flex:0 0 100%;min-width:0;max-height:var(--m-section-carousel-max-height, none);overflow:hidden}.m-section-carousel__indicators{display:flex;align-items:center;justify-content:center;gap:.5rem;flex-wrap:wrap}.m-section-carousel__indicator{appearance:none;border:none;padding:0;cursor:pointer;width:.5rem;height:.5rem;border-radius:50%;background:var(--m-section-carousel-indicator, currentColor);opacity:.35;transition:opacity .2s ease,width .2s ease,background .2s ease}.m-section-carousel__indicator--capsule{width:1.25rem;height:.375rem;border-radius:999px}.m-section-carousel__indicator--active{opacity:1}.m-section-carousel__indicator--capsule.m-section-carousel__indicator--active{width:2rem}.m-section-carousel--indicator-top{flex-direction:column-reverse}.m-section-carousel--indicator-left{flex-direction:row-reverse;align-items:center}.m-section-carousel--indicator-right{flex-direction:row;align-items:center}.m-section-carousel--indicator-left .m-section-carousel__indicators,.m-section-carousel--indicator-right .m-section-carousel__indicators{flex-direction:column}\n"] }]
26303
26548
  }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], element: [{ type: i0.Input, args: [{ isSignal: true, alias: "element", required: false }] }], template: [{ type: i0.Input, args: [{ isSignal: true, alias: "template", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], indicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "indicator", required: false }] }], indicatorShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "indicatorShape", required: false }] }], autoPlay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoPlay", required: false }] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], pauseOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseOnHover", required: false }] }], drag: [{ type: i0.Input, args: [{ isSignal: true, alias: "drag", required: false }] }], slideChange: [{ type: i0.Output, args: ["slideChange"] }], itemChange: [{ type: i0.Output, args: ["itemChange"] }], projectedTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
26304
26549
 
26550
+ /** The Store's own link rule (m-api `LINK_SLUG_PATTERN`) — ADR 0037. */
26551
+ const LINK_SLUG = /^[a-z0-9-]+$/;
26552
+ const ELEMENT_TIMEOUT_MS = 15_000;
26305
26553
  class RemoteLoaderService {
26306
26554
  httpService = inject(HttpService);
26307
26555
  loginStore = inject(LOGIN_STORE, { optional: true });
@@ -26361,28 +26609,67 @@ class RemoteLoaderService {
26361
26609
  assetBaseUrl: wcBase,
26362
26610
  };
26363
26611
  }
26612
+ /**
26613
+ * Loads a Remote by its Store link: the slug is checked before anything is
26614
+ * fetched, because the link is concatenated into a module-script URL.
26615
+ */
26616
+ loadFromLink = async (link, useShadow = false, props, children) => {
26617
+ if (!LINK_SLUG.test(link))
26618
+ return { reason: 'invalid-link' };
26619
+ let config;
26620
+ try {
26621
+ config = await firstValueFrom(this.httpService.wc(link, ['remote', 'app-wc.json']));
26622
+ }
26623
+ catch (error) {
26624
+ const status = error instanceof HttpErrorResponse ? error.status : 0;
26625
+ return status
26626
+ ? { reason: 'config-not-found', status }
26627
+ : { reason: 'offline' };
26628
+ }
26629
+ // The validated link, never the answer's own: the script URL is built from it.
26630
+ return this.mount({ ...config, link }, useShadow, props, children);
26631
+ };
26364
26632
  loadComponentFromConfig = async (config, useShadow = false, props, children) => {
26633
+ const { element, assetBaseUrl } = await this.mount(config, useShadow, props, children);
26634
+ return element ? { element, assetBaseUrl: assetBaseUrl ?? '' } : undefined;
26635
+ };
26636
+ /** Logged out answers nothing at all — not a failure, the login effect reloads. */
26637
+ mount = async (config, useShadow, props, children) => {
26638
+ if (this.loginStore && !this.loginStore.isLoggedIn()) {
26639
+ console.warn(`Attempted to load component ${config.selector} without authentication`);
26640
+ return {};
26641
+ }
26642
+ const { scriptUrl, assetBaseUrl } = this.resolveUrls(config);
26643
+ if (!scriptUrl)
26644
+ return { reason: 'script-failed' };
26645
+ // A tag already defined by another tier's bundle would hand back that
26646
+ // tier's element under this one's name — refused, never mounted silently.
26647
+ const tier = (config.link && wcTierOf(config.link)) || 'status';
26648
+ const wasDefined = !!customElements.get(config.selector);
26649
+ const definedAs = definedWcTierOf(config.selector);
26650
+ if (wasDefined && definedAs && definedAs !== tier) {
26651
+ return { reason: 'tier-conflict' };
26652
+ }
26365
26653
  try {
26366
- if (this.loginStore && !this.loginStore.isLoggedIn()) {
26367
- console.warn(`Attempted to load component ${config.selector} without authentication`);
26368
- return undefined;
26369
- }
26370
- const { scriptUrl, assetBaseUrl } = this.resolveUrls(config);
26371
- if (!scriptUrl)
26372
- return undefined;
26373
26654
  await this.loadScript(scriptUrl);
26374
- await Promise.race([
26375
- customElements.whenDefined(config.selector),
26376
- new Promise((_, reject) => setTimeout(() => reject(new Error(`${config.selector}: registration timeout`)), 15_000)),
26377
- ]);
26378
- const element = this.create(config.selector, props || {}, children || [], useShadow);
26379
- if (!element)
26380
- return undefined;
26381
- return { element, assetBaseUrl };
26382
26655
  }
26383
26656
  catch {
26384
- return undefined;
26657
+ return { reason: 'script-failed' };
26385
26658
  }
26659
+ let timer;
26660
+ const defined = await Promise.race([
26661
+ customElements.whenDefined(config.selector).then(() => true),
26662
+ new Promise((resolve) => {
26663
+ timer = setTimeout(() => resolve(false), ELEMENT_TIMEOUT_MS);
26664
+ }),
26665
+ ]);
26666
+ clearTimeout(timer);
26667
+ if (!defined)
26668
+ return { reason: 'element-timeout' };
26669
+ if (!wasDefined)
26670
+ recordDefinedWcTier(config.selector, tier);
26671
+ const element = this.create(config.selector, props || {}, children || [], useShadow);
26672
+ return element ? { element, assetBaseUrl, config } : { reason: 'element-timeout' };
26386
26673
  };
26387
26674
  async loadScript(url) {
26388
26675
  if (document.querySelector(`script[data-wc-src="${url}"]`)) {
@@ -26427,23 +26714,242 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
26427
26714
  }]
26428
26715
  }] });
26429
26716
 
26717
+ /**
26718
+ * The token each non-static kind is written as — in the `mount` attribute
26719
+ * codegen emits and in the editor's stored list alike. None is a URL segment a
26720
+ * static could hold: braces and `*` are refused there.
26721
+ */
26722
+ const MOUNT_TOKEN = {
26723
+ dynamic: '*',
26724
+ appLink: '{appLink}',
26725
+ navUrl: '{navUrl}',
26726
+ };
26727
+ /** What a `static` segment may hold: one plain URL segment. */
26728
+ const MOUNT_STATIC_SEGMENT = /^[A-Za-z0-9._~-]+$/;
26729
+ const mountSegmentOf = (token) => {
26730
+ const kind = Object.keys(MOUNT_TOKEN).find((k) => MOUNT_TOKEN[k] === token);
26731
+ return kind ? { kind } : { kind: 'static', value: token };
26732
+ };
26733
+ const mountTokenOf = (segment) => segment.kind === 'static' ? (segment.value ?? '').trim() : MOUNT_TOKEN[segment.kind];
26734
+ /** `store/app/{appLink}/manage/beta/{navUrl}` → its segments. */
26735
+ const parseMountPattern = (text) => text.split('/').map((t) => t.trim()).filter(Boolean).map(mountSegmentOf);
26736
+ const mountPatternText = (pattern) => pattern.map(mountTokenOf).join('/');
26737
+ const mountPatternProblem = (pattern) => {
26738
+ const navUrls = pattern.filter((s) => s.kind === 'navUrl').length;
26739
+ if (navUrls === 0)
26740
+ return 'no-nav-url';
26741
+ if (navUrls > 1)
26742
+ return 'many-nav-urls';
26743
+ if (pattern[pattern.length - 1].kind !== 'navUrl')
26744
+ return 'nav-url-not-last';
26745
+ if (pattern.filter((s) => s.kind === 'appLink').length > 1)
26746
+ return 'many-app-links';
26747
+ if (pattern.some((s) => s.kind === 'static' && !MOUNT_STATIC_SEGMENT.test(s.value?.trim() ?? '')))
26748
+ return 'bad-static';
26749
+ if (pattern.length < 2)
26750
+ return 'no-prefix';
26751
+ return undefined;
26752
+ };
26753
+ /**
26754
+ * Where the host's primary path stands against a pattern — `null` when it does
26755
+ * not reach the `navUrl`, so a Remote never reads a navUrl off a stranger's URL.
26756
+ */
26757
+ const matchMount = (segments, pattern) => {
26758
+ if (mountPatternProblem(pattern))
26759
+ return null;
26760
+ const prefix = pattern.slice(0, -1);
26761
+ if (segments.length < prefix.length)
26762
+ return null;
26763
+ let appLink;
26764
+ for (let i = 0; i < prefix.length; i++) {
26765
+ const { kind, value } = prefix[i];
26766
+ if (kind === 'static' && segments[i] !== value?.trim())
26767
+ return null;
26768
+ if (kind === 'appLink')
26769
+ appLink = segments[i];
26770
+ }
26771
+ return {
26772
+ ...(appLink !== undefined ? { appLink } : {}),
26773
+ navUrl: segments.slice(prefix.length).join('/'),
26774
+ base: '/' + segments.slice(0, prefix.length).join('/'),
26775
+ };
26776
+ };
26777
+
26778
+ /** `coins/7?sort=desc` → its path segments and its query. */
26779
+ const splitPath = (path) => {
26780
+ const [rawPath, rawQuery = ''] = path.replace(/^\//, '').split('?');
26781
+ return {
26782
+ segments: rawPath.split('/').filter(Boolean),
26783
+ query: Object.fromEntries(new URLSearchParams(rawQuery)),
26784
+ };
26785
+ };
26786
+ /**
26787
+ * Where one mounted Remote's route lives in the host URL, read and written
26788
+ * through the Router — the primary path below a NavTail, or an auxiliary
26789
+ * segment named after the mount (ADR 0038).
26790
+ */
26791
+ class RemoteUrl {
26792
+ router;
26793
+ mode;
26794
+ name;
26795
+ tail;
26796
+ constructor(router, mode, name,
26797
+ /** The NavTail's current value — what `tail` mode owns of the primary path. */
26798
+ tail) {
26799
+ this.router = router;
26800
+ this.mode = mode;
26801
+ this.name = name;
26802
+ this.tail = tail;
26803
+ }
26804
+ tree = () => this.router.parseUrl(this.router.url);
26805
+ segmentsNow = (tree) => this.mode === 'outlet'
26806
+ ? (tree.root.children[this.name]?.segments ?? []).map((s) => s.path)
26807
+ : this.tail.split('/').filter(Boolean);
26808
+ /** Whether the host URL already says `path` — its segments, and every query key it names. */
26809
+ holds = (path) => {
26810
+ const tree = this.tree();
26811
+ const { segments, query } = splitPath(path);
26812
+ return (this.segmentsNow(tree).join('/') === segments.join('/') &&
26813
+ Object.entries(query).every(([k, v]) => tree.queryParams[k] === v));
26814
+ };
26815
+ write = (path, replaceUrl) => {
26816
+ if (this.mode === 'none')
26817
+ return;
26818
+ const tree = this.tree();
26819
+ const { segments, query } = splitPath(path);
26820
+ const next = segments.map((s) => new UrlSegment(s, {}));
26821
+ const children = { ...tree.root.children };
26822
+ if (this.mode === 'outlet') {
26823
+ if (next.length)
26824
+ children[this.name] = new UrlSegmentGroup(next, {});
26825
+ else
26826
+ delete children[this.name];
26827
+ }
26828
+ else {
26829
+ const primary = children['primary'];
26830
+ const own = this.segmentsNow(tree).length;
26831
+ const base = (primary?.segments ?? []).slice(0, (primary?.segments.length ?? 0) - own);
26832
+ children['primary'] = new UrlSegmentGroup([...base, ...next], primary?.children ?? {});
26833
+ }
26834
+ const url = new UrlTree(new UrlSegmentGroup([], children), { ...tree.queryParams, ...query }, tree.fragment);
26835
+ this.router.navigateByUrl(url, { replaceUrl });
26836
+ };
26837
+ /** An outlet leaving takes its segment with it; a tail leaves with its route. */
26838
+ clear = () => {
26839
+ if (this.mode === 'outlet' && this.tree().root.children[this.name]) {
26840
+ this.write('', true);
26841
+ }
26842
+ };
26843
+ }
26844
+ /**
26845
+ * The route one `outlet` RemoteControl's segment needs: it renders nothing —
26846
+ * the control reads the segment itself — and `children: []` is the least
26847
+ * Angular accepts. `remoteOutlet` is what lets NavStore clear it when no
26848
+ * mount claims it, and leave an app's own named outlets alone (ADR 0038).
26849
+ */
26850
+ const remoteOutletRoute = (name) => ({
26851
+ outlet: name,
26852
+ matcher: (segments) => ({ consumed: segments }),
26853
+ children: [],
26854
+ data: { remoteOutlet: true },
26855
+ });
26856
+
26857
+ /**
26858
+ * Reloads the page — what a tier conflict needs, a page having one element
26859
+ * registry. A token so a spec can watch it instead of navigating jsdom.
26860
+ */
26861
+ const REMOTE_PAGE_RELOAD = new InjectionToken('REMOTE_PAGE_RELOAD', { providedIn: 'root', factory: () => () => window.location.reload() });
26862
+ /** Set while one reload for a tier conflict is spent on this path. */
26863
+ const tierReloadKey = () => `m-remote-tier-reload:${window.location.pathname}`;
26430
26864
  class RemoteComponent {
26431
- config = input.required(...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
26865
+ /** A resolved config, as m-ui hands it — or a Store link `m-remote` resolves itself. */
26866
+ config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : /* istanbul ignore next */ []));
26867
+ link = input(...(ngDevMode ? [undefined, { debugName: "link" }] : /* istanbul ignore next */ []));
26432
26868
  useShadow = input(false, ...(ngDevMode ? [{ debugName: "useShadow" }] : /* istanbul ignore next */ []));
26433
26869
  props = input({}, ...(ngDevMode ? [{ debugName: "props" }] : /* istanbul ignore next */ []));
26434
26870
  children = input([], ...(ngDevMode ? [{ debugName: "children" }] : /* istanbul ignore next */ []));
26871
+ /** The mount key and outlet name — two mounts of one app differ by it (ADR 0036). */
26872
+ name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
26873
+ urlMode = input('none', ...(ngDevMode ? [{ debugName: "urlMode" }] : /* istanbul ignore next */ []));
26874
+ /** RemotePathMap `in`: where the remote should stand, already mapped. */
26875
+ path = input(...(ngDevMode ? [undefined, { debugName: "path" }] : /* istanbul ignore next */ []));
26876
+ /** The NavTail's raw value, which `tail` mode rewrites below. */
26877
+ tail = input('', ...(ngDevMode ? [{ debugName: "tail" }] : /* istanbul ignore next */ []));
26878
+ /** RemotePathMap `out`: the remote's reported path as the host URL holds it. */
26879
+ out = input(...(ngDevMode ? [undefined, { debugName: "out" }] : /* istanbul ignore next */ []));
26880
+ /** Listed in the host menu while its segment is in the URL. */
26881
+ createMenu = input(false, ...(ngDevMode ? [{ debugName: "createMenu" }] : /* istanbul ignore next */ []));
26882
+ /**
26883
+ * The host path a `tail` Remote stands under, segment by segment. Set, it is
26884
+ * the sync: the AppLink, the path handed in and the tail written back are all
26885
+ * read off the host URL through it, and `link`/`path`/`tail` only fill gaps.
26886
+ */
26887
+ mount = input(...(ngDevMode ? [undefined, { debugName: "mount" }] : /* istanbul ignore next */ []));
26888
+ /** The pattern as segments, whichever form it arrived in. */
26889
+ pattern = computed(() => {
26890
+ const mount = this.mount();
26891
+ return typeof mount === 'string' ? parseMountPattern(mount) : mount;
26892
+ }, ...(ngDevMode ? [{ debugName: "pattern" }] : /* istanbul ignore next */ []));
26893
+ /** Loads the Remote's beta tier rather than its status folder. */
26894
+ beta = input(false, ...(ngDevMode ? [{ debugName: "beta" }] : /* istanbul ignore next */ []));
26895
+ /** The config of the app now mounted — m-ui titles its page after it. */
26896
+ loaded = output();
26897
+ /** Why a load drew the broken state — m-ui's Beta gate reads `config-not-found`. */
26898
+ failed = output();
26435
26899
  loader = inject(RemoteLoaderService);
26436
26900
  themeStore = inject(ThemeStore);
26437
26901
  translateService = inject(TranslateService);
26438
26902
  loginStore = inject(LOGIN_STORE, { optional: true });
26439
26903
  elementRef = inject(ElementRef);
26904
+ router = inject(Router, { optional: true });
26905
+ navRef = inject(NAV_STORE_REF, { optional: true });
26906
+ reloadPage = inject(REMOTE_PAGE_RELOAD);
26440
26907
  loadedElement;
26908
+ /** Bumped per load, so an answer for a link since replaced is dropped. */
26909
+ loadId = 0;
26910
+ /** The link whose segment the URL holds now, cleared when another replaces it. */
26911
+ mountedLink;
26912
+ /** The first report after each mount replaces: boot redirects leave no entry. */
26913
+ reported = false;
26914
+ /** The remote's own last word on where it stands, never echoed back to it. */
26915
+ lastReported;
26916
+ lastSent;
26917
+ registeredName;
26918
+ state = signal('idle', ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
26919
+ failure = signal({}, ...(ngDevMode ? [{ debugName: "failure" }] : /* istanbul ignore next */ []));
26920
+ /** The host's primary path, segment by segment, as of the last navigation. */
26921
+ hostSegments = this.router
26922
+ ? toSignal(this.router.events.pipe(filter((e) => e instanceof NavigationEnd), map(() => this.primarySegments())), { initialValue: this.primarySegments() })
26923
+ : signal([]);
26924
+ /**
26925
+ * Where the host URL stands against the MountPattern. A URL the pattern no
26926
+ * longer matches keeps the last match: the page is leaving, and reading that
26927
+ * as "no app" would reset the segment of a URL that is not this mount's.
26928
+ */
26929
+ mountMatch = linkedSignal({ ...(ngDevMode ? { debugName: "mountMatch" } : /* istanbul ignore next */ {}), source: () => {
26930
+ const pattern = this.pattern();
26931
+ return pattern?.length && this.urlMode() === 'tail'
26932
+ ? matchMount(this.hostSegments(), pattern)
26933
+ : null;
26934
+ },
26935
+ computation: (next, previous) => next ?? (this.pattern()?.length ? (previous?.value ?? null) : null),
26936
+ equal: (a, b) => a?.appLink === b?.appLink && a?.navUrl === b?.navUrl && a?.base === b?.base });
26937
+ /** The AppLink loaded: the bound one, else the one the URL names. */
26938
+ appLink = computed(() => this.link() || this.mountMatch()?.appLink, ...(ngDevMode ? [{ debugName: "appLink" }] : /* istanbul ignore next */ []));
26939
+ remotePath = computed(() => this.path() ?? this.mountMatch()?.navUrl, ...(ngDevMode ? [{ debugName: "remotePath" }] : /* istanbul ignore next */ []));
26940
+ ownTail = computed(() => this.mountMatch()?.navUrl ?? this.tail(), ...(ngDevMode ? [{ debugName: "ownTail" }] : /* istanbul ignore next */ []));
26441
26941
  constructor() {
26442
26942
  // Reload when config or login state changes
26443
26943
  effect(() => {
26444
26944
  this.loginStore?.isLoggedIn();
26445
26945
  const config = this.config();
26446
- untracked(() => void this.loadRemoteComponent(config));
26946
+ const link = this.appLink();
26947
+ const beta = this.beta();
26948
+ untracked(() => {
26949
+ if (link)
26950
+ setWcTier(link, beta ? 'beta' : undefined);
26951
+ void this.loadRemoteComponent(config, link);
26952
+ });
26447
26953
  });
26448
26954
  // Sync props into the WC element without reloading
26449
26955
  effect(() => {
@@ -26456,6 +26962,35 @@ class RemoteComponent {
26456
26962
  el[key] = value;
26457
26963
  });
26458
26964
  });
26965
+ // Hand the remote a path it did not just report itself — the loop guard.
26966
+ effect(() => {
26967
+ const path = this.remotePath();
26968
+ if (path === undefined)
26969
+ return;
26970
+ untracked(() => this.sendPath(path));
26971
+ });
26972
+ effect(() => {
26973
+ const link = this.appLink();
26974
+ const name = this.mountName();
26975
+ const urlMode = this.urlMode();
26976
+ const out = this.out();
26977
+ const createMenu = this.createMenu();
26978
+ untracked(() => {
26979
+ this.unregister();
26980
+ if (!link || !name)
26981
+ return;
26982
+ this.navRef?.registerMount?.({
26983
+ name,
26984
+ link,
26985
+ appNavId: `root_app_${name}`,
26986
+ urlMode,
26987
+ out,
26988
+ createMenu,
26989
+ navigate: this.move,
26990
+ });
26991
+ this.registeredName = name;
26992
+ });
26993
+ });
26459
26994
  // Push host language changes into the WC element
26460
26995
  effect(() => {
26461
26996
  const lang = this.translateService.lang();
@@ -26467,35 +27002,200 @@ class RemoteComponent {
26467
27002
  }
26468
27003
  ngOnDestroy() {
26469
27004
  this.dismountElement();
26470
- }
26471
- loadRemoteComponent = async (config) => {
27005
+ this.url()?.clear();
27006
+ this.unregister();
27007
+ }
27008
+ mountName = () => this.name() ?? this.appLink();
27009
+ /** A method, not an arrow field: `hostSegments` calls it during field init. */
27010
+ primarySegments() {
27011
+ return this.router
27012
+ ? (this.router.parseUrl(this.router.url).root.children['primary']
27013
+ ?.segments ?? []).map((s) => s.path)
27014
+ : [];
27015
+ }
27016
+ url = () => {
27017
+ const name = untracked(() => this.mountName());
27018
+ return this.router && name
27019
+ ? new RemoteUrl(this.router, untracked(() => this.urlMode()), name, untracked(() => this.ownTail()))
27020
+ : undefined;
27021
+ };
27022
+ unregister = () => {
27023
+ if (this.registeredName)
27024
+ this.navRef?.unregisterMount?.(this.registeredName);
27025
+ this.registeredName = undefined;
27026
+ };
27027
+ sendPath = (path) => {
27028
+ const el = this.loadedElement;
27029
+ if (!el || path === this.lastReported || path === this.lastSent)
27030
+ return;
27031
+ this.lastSent = path;
27032
+ el.dispatchEvent(new CustomEvent(WC_NAVIGATE_EVENT, { detail: { path } }));
27033
+ };
27034
+ /**
27035
+ * A menu click: unlike a path binding the host URL does not hold it yet, so
27036
+ * the report that follows is a move to write, not an echo to drop.
27037
+ */
27038
+ move = (path) => {
27039
+ this.loadedElement?.dispatchEvent(new CustomEvent(WC_NAVIGATE_EVENT, { detail: { path } }));
27040
+ };
27041
+ onReport = (event) => {
27042
+ const raw = event.detail?.path;
27043
+ if (raw === undefined)
27044
+ return;
27045
+ const path = raw.replace(/^\//, '');
27046
+ // The remote arriving where the host sent it is an echo, not a move: writing
27047
+ // it back through `out` is what spins a pair that does not round-trip.
27048
+ const echo = path === this.lastSent;
27049
+ this.lastReported = path;
27050
+ this.lastSent = undefined;
27051
+ if (echo && this.reported)
27052
+ return;
27053
+ const link = untracked(() => this.appLink()) ?? '';
27054
+ const out = untracked(() => this.out());
27055
+ const target = out ? out(path, link) : path;
27056
+ const url = this.url();
27057
+ const replace = !this.reported;
27058
+ this.reported = true;
27059
+ if (!url || url.holds(target))
27060
+ return;
27061
+ url.write(target, replace);
27062
+ };
27063
+ loadRemoteComponent = async (config, link) => {
26472
27064
  const host = this.elementRef.nativeElement;
27065
+ const id = ++this.loadId;
26473
27066
  this.dismountElement();
26474
- const result = await this.loader.loadComponentFromConfig(config, untracked(() => this.useShadow()), {
27067
+ // Another app's path means nothing to this one: its segment starts over.
27068
+ if (this.mountedLink !== undefined && this.mountedLink !== link) {
27069
+ const url = this.url();
27070
+ if (url && !url.holds(''))
27071
+ url.write('', true);
27072
+ }
27073
+ this.mountedLink = link;
27074
+ this.reported = false;
27075
+ this.lastReported = undefined;
27076
+ if (!config && link === undefined)
27077
+ return this.state.set('idle');
27078
+ this.state.set('loading');
27079
+ const useShadow = untracked(() => this.useShadow());
27080
+ const path = untracked(() => this.remotePath());
27081
+ const base = untracked(() => this.mountMatch())?.base;
27082
+ this.lastSent = path;
27083
+ // The Remote is told its AppLink and the host path it stands under rather
27084
+ // than reading them off the address: where it is mounted is the host's
27085
+ // MountPattern, not a fixed `/app/<link>`.
27086
+ const props = {
26475
27087
  lang: untracked(() => this.translateService.lang()),
26476
27088
  ...untracked(() => this.props()),
26477
- }, untracked(() => this.children()));
26478
- if (!result)
27089
+ ...(path !== undefined ? { extra: path } : {}),
27090
+ ...(link ? { appLink: link } : {}),
27091
+ ...(base ? { base } : {}),
27092
+ };
27093
+ const children = untracked(() => this.children());
27094
+ const result = config
27095
+ ? ((await this.loader.loadComponentFromConfig(config, useShadow, props, children)) ?? {})
27096
+ : await this.loader.loadFromLink(link ?? '', useShadow, props, children);
27097
+ if (id !== this.loadId)
27098
+ return;
27099
+ if (result.reason === 'tier-conflict' && this.reloadForTier())
26479
27100
  return;
27101
+ if (!result.element) {
27102
+ this.failure.set(result);
27103
+ if (result.reason)
27104
+ this.failed.emit(result);
27105
+ return this.state.set(result.reason ? 'failed' : 'idle');
27106
+ }
26480
27107
  result.element.setAttribute('style', 'display: block; width: 100%; min-height: 100%;');
26481
27108
  this.loadedElement = result.element;
27109
+ try {
27110
+ sessionStorage.removeItem(tierReloadKey());
27111
+ }
27112
+ catch {
27113
+ // Storage refused: the next conflict simply draws its broken state.
27114
+ }
27115
+ result.element.addEventListener(WC_ROUTE_CHANGED_EVENT, this.onReport);
26482
27116
  host.appendChild(result.element);
27117
+ this.state.set('loaded');
27118
+ const loadedConfig = result.config ?? config;
27119
+ if (loadedConfig)
27120
+ this.loaded.emit(loadedConfig);
26483
27121
  // Theme vars on document.documentElement inherit automatically into shadow DOM.
26484
27122
  };
27123
+ /**
27124
+ * Another tier of this app already defined its tag, and a tag defines once
27125
+ * per page: a fresh page loads the tier the URL now asks for. Once per path —
27126
+ * a second conflict means two tiers on one page, drawn as broken, not looped.
27127
+ */
27128
+ reloadForTier = () => {
27129
+ try {
27130
+ if (sessionStorage.getItem(tierReloadKey()))
27131
+ return false;
27132
+ sessionStorage.setItem(tierReloadKey(), '1');
27133
+ }
27134
+ catch {
27135
+ return false;
27136
+ }
27137
+ this.reloadPage();
27138
+ return true;
27139
+ };
26485
27140
  dismountElement = () => {
26486
27141
  const host = this.elementRef.nativeElement;
27142
+ this.loadedElement?.removeEventListener(WC_ROUTE_CHANGED_EVENT, this.onReport);
26487
27143
  if (this.loadedElement?.parentNode === host) {
26488
27144
  host.removeChild(this.loadedElement);
26489
27145
  }
26490
27146
  this.loadedElement = undefined;
26491
27147
  };
26492
27148
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RemoteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
26493
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: RemoteComponent, isStandalone: true, selector: "m-remote", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, useShadow: { classPropertyName: "useShadow", publicName: "useShadow", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, children: { classPropertyName: "children", publicName: "children", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ``, isInline: true, styles: [".m-remote{display:block;width:100%;min-height:100%;flex:1;isolation:isolate}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
27149
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: RemoteComponent, isStandalone: true, selector: "m-remote", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null }, useShadow: { classPropertyName: "useShadow", publicName: "useShadow", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, children: { classPropertyName: "children", publicName: "children", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, urlMode: { classPropertyName: "urlMode", publicName: "urlMode", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: false, transformFunction: null }, tail: { classPropertyName: "tail", publicName: "tail", isSignal: true, isRequired: false, transformFunction: null }, out: { classPropertyName: "out", publicName: "out", isSignal: true, isRequired: false, transformFunction: null }, createMenu: { classPropertyName: "createMenu", publicName: "createMenu", isSignal: true, isRequired: false, transformFunction: null }, mount: { classPropertyName: "mount", publicName: "mount", isSignal: true, isRequired: false, transformFunction: null }, beta: { classPropertyName: "beta", publicName: "beta", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", failed: "failed" }, host: { classAttribute: "m-remote" }, ngImport: i0, template: `
27150
+ @if (state() === 'loading') {
27151
+ <div class="m-remote__loading"><div class="m-remote__spinner"></div></div>
27152
+ } @else if (state() === 'failed') {
27153
+ <div class="m-remote__broken">
27154
+ <m-icon name="link-broken" color="currentColor" />
27155
+ <span class="m-remote__reason">
27156
+ @switch (failure().reason) {
27157
+ @case ('invalid-link') { {{ 'remote-invalid-link' | translate }} }
27158
+ @case ('config-not-found') { {{ 'remote-config-not-found' | translate }} }
27159
+ @case ('script-failed') { {{ 'remote-script-failed' | translate }} }
27160
+ @case ('element-timeout') { {{ 'remote-element-timeout' | translate }} }
27161
+ @case ('offline') { {{ 'remote-offline' | translate }} }
27162
+ @case ('tier-conflict') { {{ 'remote-tier-conflict' | translate }} }
27163
+ }
27164
+ </span>
27165
+ <span class="m-remote__detail">{{ appLink() }}</span>
27166
+ @if (failure().status) {
27167
+ <span class="m-remote__detail">{{ failure().status }}</span>
27168
+ }
27169
+ </div>
27170
+ }
27171
+ `, isInline: true, styles: ["@keyframes toggle-pulse{0%,to{transform:scale(1)}50%{transform:scale(1.02)}}@keyframes thumb-slide{0%{transform:translate(0) scale(1)}50%{transform:translate(.3em) scale(.9)}to{transform:translate(.6em) scale(1)}}.m-remote{display:block;width:100%;min-height:100%;flex:1;isolation:isolate}.m-remote__loading,.m-remote__broken{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:24px}.m-remote__spinner{width:20px;height:20px;border:2px solid var(--m-backdrop-border);border-top-color:var(--m-mm);border-radius:50%;animation:m-remote-spin .8s linear infinite}.m-remote__broken{color:var(--m-text-secondary)}.m-remote__detail{font-size:.85em}@keyframes m-remote-spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "m-icon, m-one-icon", inputs: ["config", "name", "color", "size", "one", "baseUrl", "remote"], outputs: ["configChange"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
26494
27172
  }
26495
27173
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RemoteComponent, decorators: [{
26496
27174
  type: Component,
26497
- args: [{ selector: 'm-remote', template: ``, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, styles: [".m-remote{display:block;width:100%;min-height:100%;flex:1;isolation:isolate}\n"] }]
26498
- }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], useShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "useShadow", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], children: [{ type: i0.Input, args: [{ isSignal: true, alias: "children", required: false }] }] } });
27175
+ args: [{ selector: 'm-remote', imports: [TranslatePipe, IconComponent], host: { class: 'm-remote' }, template: `
27176
+ @if (state() === 'loading') {
27177
+ <div class="m-remote__loading"><div class="m-remote__spinner"></div></div>
27178
+ } @else if (state() === 'failed') {
27179
+ <div class="m-remote__broken">
27180
+ <m-icon name="link-broken" color="currentColor" />
27181
+ <span class="m-remote__reason">
27182
+ @switch (failure().reason) {
27183
+ @case ('invalid-link') { {{ 'remote-invalid-link' | translate }} }
27184
+ @case ('config-not-found') { {{ 'remote-config-not-found' | translate }} }
27185
+ @case ('script-failed') { {{ 'remote-script-failed' | translate }} }
27186
+ @case ('element-timeout') { {{ 'remote-element-timeout' | translate }} }
27187
+ @case ('offline') { {{ 'remote-offline' | translate }} }
27188
+ @case ('tier-conflict') { {{ 'remote-tier-conflict' | translate }} }
27189
+ }
27190
+ </span>
27191
+ <span class="m-remote__detail">{{ appLink() }}</span>
27192
+ @if (failure().status) {
27193
+ <span class="m-remote__detail">{{ failure().status }}</span>
27194
+ }
27195
+ </div>
27196
+ }
27197
+ `, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, styles: ["@keyframes toggle-pulse{0%,to{transform:scale(1)}50%{transform:scale(1.02)}}@keyframes thumb-slide{0%{transform:translate(0) scale(1)}50%{transform:translate(.3em) scale(.9)}to{transform:translate(.6em) scale(1)}}.m-remote{display:block;width:100%;min-height:100%;flex:1;isolation:isolate}.m-remote__loading,.m-remote__broken{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:24px}.m-remote__spinner{width:20px;height:20px;border:2px solid var(--m-backdrop-border);border-top-color:var(--m-mm);border-radius:50%;animation:m-remote-spin .8s linear infinite}.m-remote__broken{color:var(--m-text-secondary)}.m-remote__detail{font-size:.85em}@keyframes m-remote-spin{to{transform:rotate(360deg)}}\n"] }]
27198
+ }], ctorParameters: () => [], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }], useShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "useShadow", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], children: [{ type: i0.Input, args: [{ isSignal: true, alias: "children", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], urlMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "urlMode", required: false }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: false }] }], tail: [{ type: i0.Input, args: [{ isSignal: true, alias: "tail", required: false }] }], out: [{ type: i0.Input, args: [{ isSignal: true, alias: "out", required: false }] }], createMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "createMenu", required: false }] }], mount: [{ type: i0.Input, args: [{ isSignal: true, alias: "mount", required: false }] }], beta: [{ type: i0.Input, args: [{ isSignal: true, alias: "beta", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], failed: [{ type: i0.Output, args: ["failed"] }] } });
26499
27199
 
26500
27200
  class TooltipComponent {
26501
27201
  text = input(...(ngDevMode ? [undefined, { debugName: "text" }] : /* istanbul ignore next */ []));
@@ -32279,7 +32979,6 @@ const SearchStore = signalStore({ providedIn: 'root' }, withState(initialState$1
32279
32979
  return {
32280
32980
  onInit() {
32281
32981
  const sq = injector.get(SEARCH_QUERY, null);
32282
- // eslint-disable-next-line @angular-eslint/no-uncalled-signals
32283
32982
  if (sq) {
32284
32983
  effect(() => {
32285
32984
  const q = sq();
@@ -32290,11 +32989,13 @@ const SearchStore = signalStore({ providedIn: 'root' }, withState(initialState$1
32290
32989
  });
32291
32990
  }
32292
32991
  const baseHref = injector.get(APP_BASE_HREF, null) ?? '/';
32293
- const isWebComponent = /^\/app\//.test(baseHref);
32992
+ const isWebComponent = injector.get(WC_BUNDLE, false) || /^\/app\//.test(baseHref);
32294
32993
  if (!isWebComponent && typeof window !== 'undefined') {
32295
32994
  const handler = (e) => {
32296
- const { appId, groups } = e.detail;
32297
- const prefix = `/app/${appId}`;
32995
+ const { appId, groups, base } = e.detail;
32996
+ // Where the host mounted the app, as m-remote told it; the old
32997
+ // `/app/<link>` for a Remote mounted without a MountPattern.
32998
+ const prefix = base ?? `/app/${appId}`;
32298
32999
  patchState(store, (state) => {
32299
33000
  const updated = Object.fromEntries(Object.entries(state.sourceResults).filter(([key]) => !key.startsWith(`${appId}:`)));
32300
33001
  for (const group of groups) {
@@ -32658,6 +33359,29 @@ const navVisibilityGuard = (route, state) => {
32658
33359
  const isThenable = (value) => typeof value?.then === 'function';
32659
33360
  const isPromise = (value) => typeof value?.then === 'function';
32660
33361
 
33362
+ /**
33363
+ * The route a NavTail emits (m-one-ui CONTEXT.md NavTail): its own prefix, then
33364
+ * every segment below it joined into one `name` parameter. Angular's `**`
33365
+ * binds to no input, which is why this is a matcher and not a path. A `:x`
33366
+ * segment of the prefix is filled the way a path would fill it.
33367
+ */
33368
+ const navTailMatcher = (prefix, name) => (segments) => {
33369
+ const own = prefix.split('/').filter(Boolean);
33370
+ if (segments.length < own.length)
33371
+ return null;
33372
+ const posParams = {};
33373
+ for (let i = 0; i < own.length; i++) {
33374
+ const part = own[i];
33375
+ if (part.startsWith(':'))
33376
+ posParams[part.slice(1)] = segments[i];
33377
+ else if (part !== segments[i].path)
33378
+ return null;
33379
+ }
33380
+ const rest = segments.slice(own.length).map((s) => s.path).join('/');
33381
+ posParams[name] = new UrlSegment(rest, {});
33382
+ return { consumed: segments, posParams };
33383
+ };
33384
+
32661
33385
  class NavTrailComponent {
32662
33386
  config = input.required(...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
32663
33387
  Size = Size;
@@ -35804,6 +36528,7 @@ function provideAppContext() {
35804
36528
  function provideMagWcConfig(options) {
35805
36529
  return {
35806
36530
  providers: [
36531
+ { provide: WC_BUNDLE, useValue: true },
35807
36532
  {
35808
36533
  provide: APP_BASE_HREF,
35809
36534
  useFactory: () => {
@@ -38598,5 +39323,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
38598
39323
  * Generated bundle index. Do not edit.
38599
39324
  */
38600
39325
 
38601
- export { ClipboardService as $, ACCESS_DOMAINS as A, ButtonComponent as B, Assets as C, AuthActivityPageComponent as D, AuthApiService as E, AuthStore as F, AutosizeDirective as G, HeaderComponent$1 as H, InputType as I, BadgeComponent as J, BandingComponent as K, LabelComponent as L, MODAL_REF as M, BaseArrayInputComponent as N, BaseRootWebComponent as O, BaseWebComponent as P, ButtonGroupComponent as Q, RangeInputComponent as R, COMPONENT_INPUT_REGISTRY as S, TranslatePipe as T, CardComponent as U, CardWrapperComponent as V, CarouselComponent as W, ChartComponent as X, ChatBubbleComponent as Y, CheckboxInputComponent as Z, ClearableInputComponent as _, MIN_ZOOM as a, ImgComponent as a$, ColComponent as a0, ColorPickerInputComponent as a1, CommentItemComponent as a2, CommentsApiService as a3, CommentsComponent as a4, CommentsStore as a5, CompactNumberPipe as a6, ComponentInputComponent as a7, ComponentStepperComponent as a8, ConfigComponent as a9, Domain as aA, DotGridComponent as aB, DragListDirective as aC, DragListItemDirective as aD, DraggableDirective as aE, DropdownInputComponent as aF, FILTER_GROUP_CONTEXT as aG, FILTER_RANGE_MODES as aH, FILTER_VARIANTS as aI, FLEX_VARIANTS as aJ, FOLDER_PICK_LISTENER as aK, FORM_ASSET_FOLDER as aL, FileService as aM, FileUploadDirective as aN, FileUploadInputComponent as aO, FlexComponent as aP, FlexItemComponent as aQ, FormGroupComponent as aR, FrameComponent as aS, FreezeService as aT, GRID_BREAKPOINTS as aU, GetNavService as aV, HighlightDirective as aW, HttpService as aX, ICON_SOURCE as aY, IS_SIDE_PANEL as aZ, IconComponent as a_, ConfirmComponent as aa, ContextMenuComponent as ab, CustomIconClass as ac, CustomIconEditComponent as ad, DEFAULT_DENIAL as ae, DEFAULT_DOC_DENSITY as af, DEFAULT_DOC_VARIANT as ag, DEFAULT_FILTER_RANGE_MODE as ah, DEFAULT_FILTER_VARIANT as ai, DEFAULT_NAV_PARAM as aj, DEFAULT_NAV_SEGMENT as ak, DEFAULT_SIZE as al, DOCK_MIN_PAGE_REMAINDER as am, DOCS_FOLDER as an, DOC_ACTION_ICONS as ao, DOC_DENSITIES as ap, DOC_EDIT_ACTIONS as aq, DOC_TOOLBAR_GROUPS as ar, DOC_VARIANTS as as, DashboardCardComponent as at, DateInputComponent as au, DatePickerComponent as av, DeviceService as aw, DocComponent as ax, DocEditorComponent as ay, DomService as az, MAX_ZOOM as b, PositionDirective as b$, InstrumentScoreComponent as b0, InterceptorObservables as b1, JumbotronComponent as b2, KeyValueComponent as b3, LAYOUT_ASSET_FOLDER as b4, LOGIN_COMPONENT as b5, LOGIN_STORE as b6, LanguageComponent as b7, ListComponent as b8, LogoComponent as b9, NavMenuComponent as bA, NavStore as bB, NavTrailComponent as bC, NothingComponent as bD, NotificationElementComponent as bE, NotificationGroupComponent as bF, NotificationPopupComponent as bG, NotificationService as bH, NotificationStore as bI, NotificationType as bJ, NotificationWidgetComponent as bK, ONE_ASSET_BASE_URL as bL, OPTIONS_SOURCE as bM, OVERLAY_WIDGETS as bN, OneApp as bO, OptionsSourceDirective as bP, OverlayBodyComponent as bQ, OverlayRef as bR, OverlayService as bS, PLATFORM_BUTTON_NAV_IDS as bT, PLATFORM_EXTENSIBLE_NAV_IDS as bU, PLATFORM_NAV_MAP as bV, PLATFORM_ROOT_CHILDREN as bW, PaginationComponent as bX, PanelComponent as bY, PercentagePipe as bZ, PlaygroundComponent as b_, MAG_SOCKET_EVENT as ba, MHeroColorDirective as bb, MHeroComponent as bc, MODAL_STORE_REF as bd, MRefDirective as be, MStepComponent as bf, MURL_PARAM as bg, MURL_SEP as bh, ManifestEnrichmentService as bi, MenuComponent as bj, ModalDirective as bk, ModalRef as bl, ModalStore as bm, MoneyPipe as bn, MultiRangeInputComponent as bo, MurlUrlSerializer as bp, NAV_DEFAULT_MURL as bq, NAV_ID_SEP as br, NAV_MAIN_BUTTONS as bs, NAV_SEGMENT_RE as bt, NAV_STORE_REF as bu, NAV_WC_COMPONENTS as bv, NAV_WIDGET_MAP as bw, NavComponent as bx, NavDetailsComponent as by, NavHeaderComponent as bz, cropRect as c, StepComponent as c$, PwaInstallComponent as c0, ROOT_NAV$1 as c1, RadioGroupComponent as c2, RadioInputComponent as c3, RatingInputComponent as c4, ReactiveElementComponent as c5, RemoteComponent as c6, RemoteLoaderService as c7, ResizeElementComponent as c8, RouteContainer as c9, SectionFilterRangePanelComponent as cA, SectionFooterComponent as cB, SectionFormComponent as cC, SectionFormItemComponent as cD, SectionHeaderComponent as cE, SectionHeroComponent as cF, SectionPaginationComponent as cG, SectionSearchComponent as cH, SectionStepperComponent as cI, SectionTableCellComponent as cJ, SectionTableComponent as cK, SectionTableRowComponent as cL, SectionTableSortPipe as cM, SectionTabsComponent as cN, SectionToggleComponent as cO, SectionToggleItemDirective as cP, SelectableCardInputComponent as cQ, SelectorDirective as cR, SettingsSearchBarComponent as cS, SettingsSearchService as cT, ShapeComponent as cU, SharedStoreRegistry as cV, SidePanelDirective as cW, Size as cX, SocketStore as cY, SortComponent as cZ, StatComponent as c_, RowComponent as ca, SEARCH_QUERY as cb, SEARCH_RESULTS_EVENT as cc, SECTION_ACCORDION_GROUP as cd, SECTION_FORM_CONTEXT as ce, SECTION_TABLE_VARIANTS as cf, SHARED_ICONS as cg, SIZE_CONTEXT as ch, ScoreComponent as ci, ScrollComponent as cj, ScrollService as ck, SearchPanelComponent as cl, SearchStore as cm, SearchUserPanelComponent as cn, SectionAccordionDirective as co, SectionAccordionGroupDirective as cp, SectionBackComponent as cq, SectionBadgesComponent as cr, SectionButtonGroupComponent as cs, SectionCardComponent as ct, SectionCarouselComponent as cu, SectionComponent as cv, SectionFilterComponent as cw, SectionFilterGroupComponent as cx, SectionFilterMenuComponent as cy, SectionFilterPanelComponent as cz, drawFramed as d, cornerSide as d$, StepperComponent as d0, StepsComponent as d1, StorageService as d2, StrokeLinecap as d3, StrokeLinejoin as d4, SummaryComponent as d5, SvgGeneratorComponent as d6, SvgGeneratorService as d7, SvgService as d8, TOTAL_COLUMNS as d9, UserNavComponent as dA, UserSettingsComponent as dB, UserStore as dC, WC_ROUTE_CHANGED_EVENT as dD, WC_SEARCH_GROUPS as dE, WIN_USER_TAB_HOOK as dF, WIN_USER_TAB_KEY as dG, WatermarkComponent as dH, WcRouterStore as dI, WrapperInputComponent as dJ, anchorNavId as dK, applyColorsToElement as dL, applyDocEdit as dM, assetOptions as dN, bootstrapMagApp as dO, bootstrapPwaInstall as dP, buildWcBaseUrl as dQ, calculateLuminance as dR, calculateRanks as dS, cellText as dT, checkDoc as dU, checkFilterCondition as dV, childNavId as dW, claimDenial as dX, classListSignal as dY, coerceSize as dZ, cornerEdge as d_, TRANSLATION_SOURCE as da, TableComponent as db, TechnicalMeterComponent as dc, TextInputComponent as dd, TextareaInputComponent as de, ThemeComponent as df, ThemeDataService as dg, ThemeService as dh, ThemeStore as di, TimeAgoPipe as dj, TimelineComponent as dk, ToggleButtonComponent as dl, ToggleInputComponent as dm, ToggleRadioInputComponent as dn, ToolTipDirective as dp, TooltipComponent as dq, TranslateService as dr, TreeGridComponent as ds, URL_SEP as dt, USER_STORE_REF as du, USER_TAB_MAP as dv, UniverseComponent as dw, UserApiService as dx, UserAvatarComponent as dy, UserComponent as dz, encodeFramed as e, isNavVisibilityConfig as e$, createMap as e0, createPlatformNavMap as e1, deriveAvatarGradient as e2, deriveContrastColor as e3, deriveOppositeColor as e4, derivePropertyName as e5, docActionKey as e6, docAssetCandidates as e7, docProblemKey as e8, docRemotePath as e9, getProperty as eA, getScrollParent as eB, getTierFromPreviewPath as eC, getTreeGridRow as eD, getUniqueId as eE, getValue as eF, hasErrorComputed as eG, hexToRgb as eH, hslToRgb$1 as eI, initMagmoniumApp as eJ, initialNotificationState as eK, initialState$2 as eL, initials as eM, injectAuthenticate as eN, injectDefaultDenial as eO, injectInstallApp as eP, injectParentSize as eQ, injectScrollSticky as eR, isButtonName as eS, isCancelledComputed as eT, isExtensiblePlatformNavId as eU, isJson as eV, isLoadingComputed as eW, isLocalhost as eX, isNavInstanceConfig as eY, isNavMenuConfig as eZ, isNavRowsConfig as e_, emailValidation as ea, evaluate as eb, evaluateBool as ec, fieldBoolean as ed, fieldNumber as ee, fieldString as ef, filterHoldsList as eg, filterHoldsOneBound as eh, filterHoldsOptions as ei, filterHoldsRange as ej, filterList as ek, filterNumber as el, filterNumberList as em, filterOne as en, filterPanelOf as eo, filterPanelWidth as ep, filterRange as eq, filterTreeGridRows as er, filterValueList as es, filterValues as et, flattenTreeGridRows as eu, formatBadgeCount as ev, fullName as ew, generateClipPath as ex, generateTransform as ey, getClassList as ez, clampFraming as f, resolvePallet as f$, isPlatformNavId as f0, isSize as f1, isTierPreview as f2, isUrlLocalhost as f3, isValidNavId as f4, isValidNavSegment as f5, isWebComponent as f6, linkToId as f7, linkToNav as f8, loadingActions as f9, parseColor as fA, parsePatternNames as fB, patternValidation as fC, patternsValidation as fD, permissionRefusal as fE, platformNavWidgets as fF, privateGuard as fG, processImageToSvg as fH, provideAppContext as fI, provideMagAppConfig as fJ, provideMagWcConfig as fK, provideMagWcRoutes as fL, provideModalComponents as fM, provideMurlUrlSerializer as fN, provideNavWidgets as fO, provideOverlayWidgets as fP, providePlatformNavWidgets as fQ, provideSearch as fR, provideSizeContext as fS, provideUserTabs as fT, publicGuard as fU, readFieldPatterns as fV, renderAddress as fW, renderDocument as fX, requiredValidation as fY, resolveConfigAsset as fZ, resolveIconSize as f_, mInterceptor as fa, manualValidation as fb, matchFieldValidation as fc, maxLengthValidation as fd, maxValidation as fe, mergePlatformNav as ff, mergeUnique as fg, mergeUniqueBy as fh, mergeUniqueWith as fi, minAgeValidation as fj, minLengthValidation as fk, minValidation as fl, miniMarkToHtml as fm, navIdChain as fn, navIdFor as fo, navIdSegment as fp, navIdToRoutePath as fq, navIdToSegments as fr, navParamOf as fs, navToId as ft, navVisibilityGuard as fu, nextSectionTableSort as fv, normalizeAssetOptions as fw, offsetOfLine as fx, parentNavId as fy, parseAddress as fz, BaseInputComponent as g, resolvePatternRules as g0, resolveSize as g1, rgbToHex as g2, rgbToHsl as g3, rowHasChildren as g4, samePatterns as g5, sectionTableColumnId as g6, sectionTableColumnWidths as g7, sectionTableFrozenCount as g8, segmentsToNavId as g9, setProperty as ga, setTreeGridChildren as gb, settingsWidgets as gc, shouldShowBadge as gd, sortSectionTableRows as ge, splitNavId as gf, splitOnMatch as gg, stringToColor as gh, toAttrBool as gi, toAttrNumber as gj, toCssLength as gk, toHostNavId as gl, toLength$1 as gm, toLocalNavId as gn, toggleTreeGridRow as go, unfetchedPlatformNav as gp, ungroupedDocActions as gq, urlValidation as gr, BaseTextInputComponent as h, initialFraming as i, TextOutputComponent as j, IS_DESIGN_MODE as k, APP_CONTEXT_REF as l, ASSET_BASE_URL as m, AccordionBodyDirective as n, outputSize as o, parseRatio as p, AccordionComponent as q, renameForType as r, AccordionGroupComponent as s, ActionComponent as t, AnimatedGraphsComponent as u, AppCardComponent as v, AppRelationType as w, AppTileComponent as x, AssetStore as y, AssetUrlPipe as z };
38602
- //# sourceMappingURL=magmonium-one-magmonium-one-CY3DM850.mjs.map
39326
+ export { ClipboardService as $, ACCESS_DOMAINS as A, ButtonComponent as B, Assets as C, AuthActivityPageComponent as D, AuthApiService as E, AuthStore as F, AutosizeDirective as G, HeaderComponent$1 as H, InputType as I, BadgeComponent as J, BandingComponent as K, LabelComponent as L, MODAL_REF as M, BaseArrayInputComponent as N, BaseRootWebComponent as O, BaseWebComponent as P, ButtonGroupComponent as Q, RangeInputComponent as R, COMPONENT_INPUT_REGISTRY as S, TranslatePipe as T, CardComponent as U, CardWrapperComponent as V, CarouselComponent as W, ChartComponent as X, ChatBubbleComponent as Y, CheckboxInputComponent as Z, ClearableInputComponent as _, MIN_ZOOM as a, ImgComponent as a$, ColComponent as a0, ColorPickerInputComponent as a1, CommentItemComponent as a2, CommentsApiService as a3, CommentsComponent as a4, CommentsStore as a5, CompactNumberPipe as a6, ComponentInputComponent as a7, ComponentStepperComponent as a8, ConfigComponent as a9, Domain as aA, DotGridComponent as aB, DragListDirective as aC, DragListItemDirective as aD, DraggableDirective as aE, DropdownInputComponent as aF, FILTER_GROUP_CONTEXT as aG, FILTER_RANGE_MODES as aH, FILTER_VARIANTS as aI, FLEX_VARIANTS as aJ, FOLDER_PICK_LISTENER as aK, FORM_ASSET_FOLDER as aL, FileService as aM, FileUploadDirective as aN, FileUploadInputComponent as aO, FlexComponent as aP, FlexItemComponent as aQ, FormGroupComponent as aR, FrameComponent as aS, FreezeService as aT, GRID_BREAKPOINTS as aU, GetNavService as aV, HighlightDirective as aW, HttpService as aX, ICON_SOURCE as aY, IS_SIDE_PANEL as aZ, IconComponent as a_, ConfirmComponent as aa, ContextMenuComponent as ab, CustomIconClass as ac, CustomIconEditComponent as ad, DEFAULT_DENIAL as ae, DEFAULT_DOC_DENSITY as af, DEFAULT_DOC_VARIANT as ag, DEFAULT_FILTER_RANGE_MODE as ah, DEFAULT_FILTER_VARIANT as ai, DEFAULT_NAV_PARAM as aj, DEFAULT_NAV_SEGMENT as ak, DEFAULT_SIZE as al, DOCK_MIN_PAGE_REMAINDER as am, DOCS_FOLDER as an, DOC_ACTION_ICONS as ao, DOC_DENSITIES as ap, DOC_EDIT_ACTIONS as aq, DOC_TOOLBAR_GROUPS as ar, DOC_VARIANTS as as, DashboardCardComponent as at, DateInputComponent as au, DatePickerComponent as av, DeviceService as aw, DocComponent as ax, DocEditorComponent as ay, DomService as az, MAX_ZOOM as b, PercentagePipe as b$, InstrumentScoreComponent as b0, InterceptorObservables as b1, JumbotronComponent as b2, KeyValueComponent as b3, LAYOUT_ASSET_FOLDER as b4, LOGIN_COMPONENT as b5, LOGIN_STORE as b6, LanguageComponent as b7, ListComponent as b8, LogoComponent as b9, NavDetailsComponent as bA, NavHeaderComponent as bB, NavMenuComponent as bC, NavStore as bD, NavTrailComponent as bE, NothingComponent as bF, NotificationElementComponent as bG, NotificationGroupComponent as bH, NotificationPopupComponent as bI, NotificationService as bJ, NotificationStore as bK, NotificationType as bL, NotificationWidgetComponent as bM, ONE_ASSET_BASE_URL as bN, OPTIONS_SOURCE as bO, OVERLAY_WIDGETS as bP, OneApp as bQ, OptionsSourceDirective as bR, OverlayBodyComponent as bS, OverlayRef as bT, OverlayService as bU, PLATFORM_BUTTON_NAV_IDS as bV, PLATFORM_EXTENSIBLE_NAV_IDS as bW, PLATFORM_NAV_MAP as bX, PLATFORM_ROOT_CHILDREN as bY, PaginationComponent as bZ, PanelComponent as b_, MAG_SOCKET_EVENT as ba, MHeroColorDirective as bb, MHeroComponent as bc, MODAL_STORE_REF as bd, MOUNT_STATIC_SEGMENT as be, MOUNT_TOKEN as bf, MRefDirective as bg, MStepComponent as bh, MURL_PARAM as bi, MURL_SEP as bj, ManifestEnrichmentService as bk, MenuComponent as bl, ModalDirective as bm, ModalRef as bn, ModalStore as bo, MoneyPipe as bp, MultiRangeInputComponent as bq, MurlUrlSerializer as br, NAV_DEFAULT_MURL as bs, NAV_ID_SEP as bt, NAV_MAIN_BUTTONS as bu, NAV_SEGMENT_RE as bv, NAV_STORE_REF as bw, NAV_WC_COMPONENTS as bx, NAV_WIDGET_MAP as by, NavComponent as bz, cropRect as c, SocketStore as c$, PlaygroundComponent as c0, PositionDirective as c1, PwaInstallComponent as c2, REMOTE_PAGE_RELOAD as c3, ROOT_NAV$1 as c4, RadioGroupComponent as c5, RadioInputComponent as c6, RatingInputComponent as c7, ReactiveElementComponent as c8, RemoteComponent as c9, SectionFilterGroupComponent as cA, SectionFilterMenuComponent as cB, SectionFilterPanelComponent as cC, SectionFilterRangePanelComponent as cD, SectionFooterComponent as cE, SectionFormComponent as cF, SectionFormItemComponent as cG, SectionHeaderComponent as cH, SectionHeroComponent as cI, SectionPaginationComponent as cJ, SectionSearchComponent as cK, SectionStepperComponent as cL, SectionTableCellComponent as cM, SectionTableComponent as cN, SectionTableRowComponent as cO, SectionTableSortPipe as cP, SectionTabsComponent as cQ, SectionToggleComponent as cR, SectionToggleItemDirective as cS, SelectableCardInputComponent as cT, SelectorDirective as cU, SettingsSearchBarComponent as cV, SettingsSearchService as cW, ShapeComponent as cX, SharedStoreRegistry as cY, SidePanelDirective as cZ, Size as c_, RemoteLoaderService as ca, ResizeElementComponent as cb, RouteContainer as cc, RowComponent as cd, SEARCH_QUERY as ce, SEARCH_RESULTS_EVENT as cf, SECTION_ACCORDION_GROUP as cg, SECTION_FORM_CONTEXT as ch, SECTION_TABLE_VARIANTS as ci, SHARED_ICONS as cj, SIZE_CONTEXT as ck, ScoreComponent as cl, ScrollComponent as cm, ScrollService as cn, SearchPanelComponent as co, SearchStore as cp, SearchUserPanelComponent as cq, SectionAccordionDirective as cr, SectionAccordionGroupDirective as cs, SectionBackComponent as ct, SectionBadgesComponent as cu, SectionButtonGroupComponent as cv, SectionCardComponent as cw, SectionCarouselComponent as cx, SectionComponent as cy, SectionFilterComponent as cz, drawFramed as d, childNavId as d$, SortComponent as d0, StatComponent as d1, StepComponent as d2, StepperComponent as d3, StepsComponent as d4, StorageService as d5, StrokeLinecap as d6, StrokeLinejoin as d7, SummaryComponent as d8, SvgGeneratorComponent as d9, UserApiService as dA, UserAvatarComponent as dB, UserComponent as dC, UserNavComponent as dD, UserSettingsComponent as dE, UserStore as dF, WC_BUNDLE as dG, WC_NAVIGATE_EVENT as dH, WC_ROUTE_CHANGED_EVENT as dI, WC_SEARCH_GROUPS as dJ, WIN_USER_TAB_HOOK as dK, WIN_USER_TAB_KEY as dL, WatermarkComponent as dM, WcRouterStore as dN, WrapperInputComponent as dO, anchorNavId as dP, applyColorsToElement as dQ, applyDocEdit as dR, assetOptions as dS, bootstrapMagApp as dT, bootstrapPwaInstall as dU, buildWcBaseUrl as dV, calculateLuminance as dW, calculateRanks as dX, cellText as dY, checkDoc as dZ, checkFilterCondition as d_, SvgGeneratorService as da, SvgService as db, TOTAL_COLUMNS as dc, TRANSLATION_SOURCE as dd, TableComponent as de, TechnicalMeterComponent as df, TextInputComponent as dg, TextareaInputComponent as dh, ThemeComponent as di, ThemeDataService as dj, ThemeService as dk, ThemeStore as dl, TimeAgoPipe as dm, TimelineComponent as dn, ToggleButtonComponent as dp, ToggleInputComponent as dq, ToggleRadioInputComponent as dr, ToolTipDirective as ds, TooltipComponent as dt, TranslateService as du, TreeGridComponent as dv, URL_SEP as dw, USER_STORE_REF as dx, USER_TAB_MAP as dy, UniverseComponent as dz, encodeFramed as e, isJson as e$, claimDenial as e0, classListSignal as e1, coerceSize as e2, cornerEdge as e3, cornerSide as e4, createMap as e5, createPlatformNavMap as e6, definedWcTierOf as e7, deriveAvatarGradient as e8, deriveContrastColor as e9, flattenTreeGridRows as eA, formatBadgeCount as eB, fullName as eC, generateClipPath as eD, generateTransform as eE, getClassList as eF, getProperty as eG, getScrollParent as eH, getTierFromPreviewPath as eI, getTreeGridRow as eJ, getUniqueId as eK, getValue as eL, hasErrorComputed as eM, hexToRgb as eN, hslToRgb$1 as eO, initMagmoniumApp as eP, initialNotificationState as eQ, initialState$2 as eR, initials as eS, injectAuthenticate as eT, injectDefaultDenial as eU, injectInstallApp as eV, injectParentSize as eW, injectScrollSticky as eX, isButtonName as eY, isCancelledComputed as eZ, isExtensiblePlatformNavId as e_, deriveOppositeColor as ea, derivePropertyName as eb, docActionKey as ec, docAssetCandidates as ed, docProblemKey as ee, docRemotePath as ef, emailValidation as eg, evaluate as eh, evaluateBool as ei, fieldBoolean as ej, fieldNumber as ek, fieldString as el, filterHoldsList as em, filterHoldsOneBound as en, filterHoldsOptions as eo, filterHoldsRange as ep, filterList as eq, filterNumber as er, filterNumberList as es, filterOne as et, filterPanelOf as eu, filterPanelWidth as ev, filterRange as ew, filterTreeGridRows as ex, filterValueList as ey, filterValues as ez, clampFraming as f, provideMurlUrlSerializer as f$, isLoadingComputed as f0, isLocalhost as f1, isNavInstanceConfig as f2, isNavMenuConfig as f3, isNavRowsConfig as f4, isNavVisibilityConfig as f5, isPlatformNavId as f6, isSize as f7, isTierPreview as f8, isUrlLocalhost as f9, navIdFor as fA, navIdSegment as fB, navIdToRoutePath as fC, navIdToSegments as fD, navParamOf as fE, navTailMatcher as fF, navToId as fG, navVisibilityGuard as fH, nextSectionTableSort as fI, normalizeAssetOptions as fJ, offsetOfLine as fK, parentNavId as fL, parseAddress as fM, parseColor as fN, parseMountPattern as fO, parsePatternNames as fP, patternValidation as fQ, patternsValidation as fR, permissionRefusal as fS, platformNavWidgets as fT, privateGuard as fU, processImageToSvg as fV, provideAppContext as fW, provideMagAppConfig as fX, provideMagWcConfig as fY, provideMagWcRoutes as fZ, provideModalComponents as f_, isValidNavId as fa, isValidNavSegment as fb, isWebComponent as fc, linkToId as fd, linkToNav as fe, loadingActions as ff, mInterceptor as fg, manualValidation as fh, matchFieldValidation as fi, matchMount as fj, maxLengthValidation as fk, maxValidation as fl, mergePlatformNav as fm, mergeUnique as fn, mergeUniqueBy as fo, mergeUniqueWith as fp, minAgeValidation as fq, minLengthValidation as fr, minValidation as fs, miniMarkToHtml as ft, mountFor as fu, mountPatternProblem as fv, mountPatternText as fw, mountSegmentOf as fx, mountTokenOf as fy, navIdChain as fz, BaseInputComponent as g, provideNavWidgets as g0, provideOverlayWidgets as g1, providePlatformNavWidgets as g2, provideSearch as g3, provideSizeContext as g4, provideUserTabs as g5, publicGuard as g6, readFieldPatterns as g7, recordDefinedWcTier as g8, remoteOutletRoute as g9, toAttrNumber as gA, toCssLength as gB, toHostNavId as gC, toLength$1 as gD, toLocalNavId as gE, toggleTreeGridRow as gF, unfetchedPlatformNav as gG, ungroupedDocActions as gH, urlValidation as gI, wcTierOf as gJ, renderAddress as ga, renderDocument as gb, requiredValidation as gc, resolveConfigAsset as gd, resolveIconSize as ge, resolvePallet as gf, resolvePatternRules as gg, resolveSize as gh, rgbToHex as gi, rgbToHsl as gj, rowHasChildren as gk, samePatterns as gl, sectionTableColumnId as gm, sectionTableColumnWidths as gn, sectionTableFrozenCount as go, segmentsToNavId as gp, setProperty as gq, setTreeGridChildren as gr, setWcTier as gs, settingsWidgets as gt, shouldShowBadge as gu, sortSectionTableRows as gv, splitNavId as gw, splitOnMatch as gx, stringToColor as gy, toAttrBool as gz, BaseTextInputComponent as h, initialFraming as i, TextOutputComponent as j, IS_DESIGN_MODE as k, APP_CONTEXT_REF as l, ASSET_BASE_URL as m, AccordionBodyDirective as n, outputSize as o, parseRatio as p, AccordionComponent as q, renameForType as r, AccordionGroupComponent as s, ActionComponent as t, AnimatedGraphsComponent as u, AppCardComponent as v, AppRelationType as w, AppTileComponent as x, AssetStore as y, AssetUrlPipe as z };
39327
+ //# sourceMappingURL=magmonium-one-magmonium-one-CCFg3oSr.mjs.map