@atlasauth/angular 0.1.3 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, PLATFORM_ID, computed, Injectable, TemplateRef, ViewContainerRef, Directive, Input, ElementRef, Renderer2, RendererStyleFlags2, EventEmitter, Output, ChangeDetectionStrategy, Component, signal, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, NgModule } from '@angular/core';
3
- import { isPlatformBrowser } from '@angular/common';
2
+ import { InjectionToken, inject, PLATFORM_ID, computed, Injectable, TemplateRef, ViewContainerRef, Directive, Input, ElementRef, Renderer2, EventEmitter, Output, ChangeDetectionStrategy, Component, signal, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, NgModule } from '@angular/core';
3
+ import { isPlatformBrowser, DOCUMENT } from '@angular/common';
4
4
  import { toSignal } from '@angular/core/rxjs-interop';
5
5
  import { BehaviorSubject, combineLatest, firstValueFrom } from 'rxjs';
6
6
  import { map, distinctUntilChanged, filter, take } from 'rxjs/operators';
7
- import { TokenCache, FapiClient, getProviderToken, readRedirectResult, stripRedirectParams, readExpiry, msUntilRefresh, nextStep, flowFromPending, initialFlow, advance, shouldKeepPolling, pollAttempt } from '@atlasauth/js';
7
+ import { TokenCache, FapiClient, getProviderToken, runFrontChannelLogoutFromResponse, readRedirectResult, stripRedirectParams, readExpiry, msUntilRefresh, nextStep, flowFromPending, initialFlow, advance, shouldKeepPolling, pollAttempt } from '@atlasauth/js';
8
8
  export { FapiClient, advance, flowFromPending, getProviderToken, initialFlow, pollAttempt, prepareFactor, requestForStep, shouldKeepPolling } from '@atlasauth/js';
9
9
  import { evaluate } from '@atlasauth/authz';
10
10
  export { evaluate, hasFromClaims } from '@atlasauth/authz';
11
+ import { renderDesignAppearance, instanceDesignFrom, applyAppearanceToDom } from '@atlasauth/design/runtime';
12
+ export { instanceDesignFrom } from '@atlasauth/design/runtime';
11
13
  import { Router } from '@angular/router';
12
14
 
13
15
  /**
@@ -108,6 +110,39 @@ function classFor(element, appearance, base) {
108
110
  const override = appearance?.elements?.[element];
109
111
  return override ? `${base} ${override}` : base;
110
112
  }
113
+ // ---- the design system ---------------------------------------------------------
114
+ /**
115
+ * The legacy tokens the developer chose EXPLICITLY: a non-default `baseTheme`
116
+ * plus `variables`. Only these map onto a design — the token defaults must not
117
+ * overwrite the dashboard design.
118
+ */
119
+ function explicitTokens(appearance) {
120
+ const out = {};
121
+ if (appearance?.baseTheme && appearance.baseTheme !== 'default') {
122
+ Object.assign(out, THEMES[appearance.baseTheme]);
123
+ }
124
+ for (const [key, value] of Object.entries(appearance?.variables ?? {})) {
125
+ if (value !== undefined)
126
+ out[key] = value;
127
+ }
128
+ return out;
129
+ }
130
+ /**
131
+ * Turn an `appearance` (+ the instance's dashboard design from the Frontend
132
+ * API) into root attributes and a stylesheet — the same semantics as
133
+ * `@atlasauth/react`. Precedence, lowest first: dashboard design < `preset` <
134
+ * `variables` (and a non-default `baseTheme`) < `design`. Without a dashboard
135
+ * design and without `preset`/`design`, the output is exactly the
136
+ * pre-design-system one: the token variables, no stylesheet, no attributes.
137
+ */
138
+ function renderAppearance(appearance, instanceDesign, options = {}) {
139
+ return renderDesignAppearance(appearance, instanceDesign, {
140
+ tokens: explicitTokens(appearance),
141
+ legacyStyle: () => cssVariables(resolveTokens(appearance)),
142
+ frontendApi: options.frontendApi,
143
+ warn: options.warn,
144
+ });
145
+ }
111
146
 
112
147
  /**
113
148
  * §10.3: "Every string passes through an i18n catalog; en-US ships, catalog
@@ -304,6 +339,33 @@ class AtlasService {
304
339
  appearanceVariables() {
305
340
  return cssVariables(resolveTokens(this.options.appearance));
306
341
  }
342
+ /**
343
+ * The full appearance render for a root element: inline variables, the
344
+ * `data-atlas-*` attributes and the design stylesheet. Pass the dashboard
345
+ * design (from {@link loadInstanceDesign}) to layer code overrides on it —
346
+ * the same semantics as `@atlasauth/react`.
347
+ */
348
+ appearanceRender(instanceDesign) {
349
+ return renderAppearance(this.options.appearance, instanceDesign ?? null, {
350
+ frontendApi: this.baseUrl,
351
+ });
352
+ }
353
+ /**
354
+ * The design configured in the Atlas dashboard (`GET /v1/appearance`), or null
355
+ * when there is none, it cannot be read, or the app opted out with
356
+ * `appearance.instanceDesign: false`.
357
+ */
358
+ async loadInstanceDesign() {
359
+ if (this.options.appearance?.instanceDesign === false)
360
+ return null;
361
+ try {
362
+ const response = await this.call('/v1/appearance');
363
+ return response.ok ? instanceDesignFrom(await response.json()) : null;
364
+ }
365
+ catch {
366
+ return null;
367
+ }
368
+ }
307
369
  /** A fresh {@link FapiClient} for driving a sign-in/sign-up flow. */
308
370
  createFapiClient() {
309
371
  return new FapiClient({ publishableKey: this.publishableKey, baseUrl: this.baseUrl });
@@ -335,12 +397,16 @@ class AtlasService {
335
397
  return evaluate(this._claims.value, condition).allowed;
336
398
  }
337
399
  /** Revoke the current session server-side and drop all local auth state. */
338
- async signOut() {
400
+ async signOut(options) {
339
401
  const current = this.cache.peek();
340
402
  if (current) {
341
- await this.call(`/v1/client/sessions/${current.sessionId}/revoke`, {
403
+ const revoked = await this.call(`/v1/client/sessions/${current.sessionId}/revoke`, {
342
404
  method: 'POST',
343
405
  }).catch(() => undefined);
406
+ // OIDC Front-Channel Logout: let the apps signed in through this session
407
+ // clear theirs in this browser (hidden iframes, capped at 5 s). Best-effort.
408
+ if (options?.frontchannel !== false)
409
+ await runFrontChannelLogoutFromResponse(revoked);
344
410
  }
345
411
  this.clearRefreshTimer();
346
412
  this.cache.clear();
@@ -722,18 +788,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
722
788
  * <router-outlet></router-outlet>
723
789
  * </div>
724
790
  * ```
791
+ *
792
+ * With a design (a code-level `appearance.preset` / `appearance.design`, or
793
+ * the dashboard design, fetched in the browser) the host also gets the stable
794
+ * `data-atlas-*` attributes and the design stylesheet is added to `<head>` once.
725
795
  */
726
796
  class AtlasRootDirective {
727
797
  atlas = inject(AtlasService);
728
798
  el = inject(ElementRef);
729
799
  renderer = inject(Renderer2);
800
+ document = inject(DOCUMENT);
801
+ platformId = inject(PLATFORM_ID);
730
802
  ngOnInit() {
731
- this.renderer.addClass(this.el.nativeElement, 'atlas-root');
732
- const vars = this.atlas.appearanceVariables();
733
- for (const [name, value] of Object.entries(vars)) {
734
- // DashCase so a `--atlas-*` custom property is written verbatim, not camelCased.
735
- this.renderer.setStyle(this.el.nativeElement, name, value, RendererStyleFlags2.DashCase);
736
- }
803
+ const host = this.el.nativeElement;
804
+ this.renderer.addClass(host, 'atlas-root');
805
+ // Custom properties via `style.setProperty` (written verbatim, never camelCased).
806
+ let applied = applyAppearanceToDom(this.document, host, this.atlas.appearanceRender());
807
+ if (!isPlatformBrowser(this.platformId))
808
+ return;
809
+ void this.atlas.loadInstanceDesign().then((design) => {
810
+ if (!design)
811
+ return;
812
+ applied = applyAppearanceToDom(this.document, host, this.atlas.appearanceRender(design), applied);
813
+ });
737
814
  }
738
815
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AtlasRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
739
816
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: AtlasRootDirective, isStandalone: true, selector: "[atlasRoot]", ngImport: i0 });
@@ -1553,5 +1630,5 @@ function atlasChildGuard(options = {}) {
1553
1630
  * Generated bundle index. Do not edit.
1554
1631
  */
1555
1632
 
1556
- export { ATLAS_OPTIONS, ATLAS_UI, AtlasFlowController, AtlasLoadedDirective, AtlasLoadingDirective, AtlasModule, AtlasProtectDirective, AtlasRootDirective, AtlasService, AtlasSignedInDirective, AtlasSignedOutDirective, AttemptFlowComponent, DEFAULT_TOKENS, EN_US, OrganizationProfileComponent, OrganizationSwitcherComponent, SignInComponent, SignUpComponent, THEMES, UserButtonComponent, UserProfileComponent, atlasChildGuard, atlasGuard, classFor, cssVariables, provideAtlas, resolveTokens, translate, withFallback };
1633
+ export { ATLAS_OPTIONS, ATLAS_UI, AtlasFlowController, AtlasLoadedDirective, AtlasLoadingDirective, AtlasModule, AtlasProtectDirective, AtlasRootDirective, AtlasService, AtlasSignedInDirective, AtlasSignedOutDirective, AttemptFlowComponent, DEFAULT_TOKENS, EN_US, OrganizationProfileComponent, OrganizationSwitcherComponent, SignInComponent, SignUpComponent, THEMES, UserButtonComponent, UserProfileComponent, atlasChildGuard, atlasGuard, classFor, cssVariables, explicitTokens, provideAtlas, renderAppearance, resolveTokens, translate, withFallback };
1557
1634
  //# sourceMappingURL=atlasauth-angular.mjs.map