@atlasauth/angular 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/LICENSE +21 -0
- package/dist/README.md +195 -0
- package/dist/esm2022/atlasauth-angular.mjs +5 -0
- package/dist/esm2022/lib/appearance.mjs +92 -0
- package/dist/esm2022/lib/atlas.config.mjs +8 -0
- package/dist/esm2022/lib/atlas.service.mjs +329 -0
- package/dist/esm2022/lib/atlas.ui.mjs +36 -0
- package/dist/esm2022/lib/components/attempt-flow.component.mjs +180 -0
- package/dist/esm2022/lib/components/flow-controller.mjs +88 -0
- package/dist/esm2022/lib/components/organization-profile.component.mjs +56 -0
- package/dist/esm2022/lib/components/organization-switcher.component.mjs +63 -0
- package/dist/esm2022/lib/components/sign-in.component.mjs +59 -0
- package/dist/esm2022/lib/components/sign-up.component.mjs +54 -0
- package/dist/esm2022/lib/components/user-button.component.mjs +94 -0
- package/dist/esm2022/lib/components/user-profile.component.mjs +81 -0
- package/dist/esm2022/lib/directives/atlas-root.directive.mjs +42 -0
- package/dist/esm2022/lib/directives/loaded.directive.mjs +23 -0
- package/dist/esm2022/lib/directives/loading.directive.mjs +23 -0
- package/dist/esm2022/lib/directives/protect.directive.mjs +82 -0
- package/dist/esm2022/lib/directives/signed-in.directive.mjs +29 -0
- package/dist/esm2022/lib/directives/signed-out.directive.mjs +29 -0
- package/dist/esm2022/lib/directives/toggle.directive.mjs +42 -0
- package/dist/esm2022/lib/fapi.mjs +9 -0
- package/dist/esm2022/lib/guards/atlas.guard.mjs +54 -0
- package/dist/esm2022/lib/i18n.mjs +78 -0
- package/dist/esm2022/lib/protect.mjs +9 -0
- package/dist/esm2022/lib/provide-atlas.mjs +85 -0
- package/dist/esm2022/lib/types.mjs +6 -0
- package/dist/esm2022/public-api.mjs +35 -0
- package/dist/fesm2022/atlasauth-angular.mjs +1557 -0
- package/dist/fesm2022/atlasauth-angular.mjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/lib/appearance.d.ts +85 -0
- package/dist/lib/atlas.config.d.ts +31 -0
- package/dist/lib/atlas.service.d.ts +127 -0
- package/dist/lib/atlas.ui.d.ts +22 -0
- package/dist/lib/components/attempt-flow.component.d.ts +48 -0
- package/dist/lib/components/flow-controller.d.ts +34 -0
- package/dist/lib/components/organization-profile.component.d.ts +16 -0
- package/dist/lib/components/organization-switcher.component.d.ts +15 -0
- package/dist/lib/components/sign-in.component.d.ts +21 -0
- package/dist/lib/components/sign-up.component.d.ts +21 -0
- package/dist/lib/components/user-button.component.d.ts +19 -0
- package/dist/lib/components/user-profile.component.d.ts +15 -0
- package/dist/lib/directives/atlas-root.directive.d.ts +26 -0
- package/dist/lib/directives/loaded.directive.d.ts +13 -0
- package/dist/lib/directives/loading.directive.d.ts +12 -0
- package/dist/lib/directives/protect.directive.d.ts +41 -0
- package/dist/lib/directives/signed-in.directive.d.ts +17 -0
- package/dist/lib/directives/signed-out.directive.d.ts +17 -0
- package/dist/lib/directives/toggle.directive.d.ts +25 -0
- package/dist/lib/fapi.d.ts +8 -0
- package/dist/lib/guards/atlas.guard.d.ts +43 -0
- package/dist/lib/i18n.d.ts +23 -0
- package/dist/lib/protect.d.ts +8 -0
- package/dist/lib/provide-atlas.d.ts +46 -0
- package/dist/lib/types.d.ts +48 -0
- package/dist/public-api.d.ts +28 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atlas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# @atlas/angular
|
|
2
|
+
|
|
3
|
+
The official **Angular** SDK for Atlas — the Angular peer of [`@atlas/react`](../react). It wraps the framework-agnostic [`@atlas/js`](../js) client and exposes Atlas's reactive auth state, sign-in/up flows, prebuilt UI, authorization directives, and route guards as idiomatic modern Angular (standalone APIs, RxJS **and** Signals, DI).
|
|
4
|
+
|
|
5
|
+
Requires **Angular 17+**.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @atlas/angular
|
|
13
|
+
# peers: @angular/core, @angular/common (>=17), rxjs (>=7); @angular/router if you use the guards
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
### Standalone (recommended, Angular 17+)
|
|
19
|
+
|
|
20
|
+
`provideAtlas` is the analogue of wrapping your tree in React's `<AtlasProvider>`. Register it once at the application root:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
24
|
+
import { provideRouter } from '@angular/router';
|
|
25
|
+
import { provideAtlas } from '@atlas/angular';
|
|
26
|
+
import { AppComponent } from './app/app.component';
|
|
27
|
+
import { routes } from './app/routes';
|
|
28
|
+
|
|
29
|
+
bootstrapApplication(AppComponent, {
|
|
30
|
+
providers: [
|
|
31
|
+
provideAtlas({
|
|
32
|
+
publishableKey: 'pk_test_…',
|
|
33
|
+
// frontendApi: 'https://fapi.your-instance.atlasauth.net', // omit for same-origin
|
|
34
|
+
// appearance: { baseTheme: 'dark', elements: { formButtonPrimary: 'rounded-xl' } },
|
|
35
|
+
// localization: myCatalog,
|
|
36
|
+
}),
|
|
37
|
+
provideRouter(routes),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The service begins booting immediately — completing any OAuth/hosted-page redirect, scrubbing the URL, and reading the session — exactly as the React provider does on first render.
|
|
43
|
+
|
|
44
|
+
### NgModule
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { AtlasModule } from '@atlas/angular';
|
|
48
|
+
|
|
49
|
+
@NgModule({
|
|
50
|
+
imports: [AtlasModule.forRoot({ publishableKey: 'pk_test_…' })],
|
|
51
|
+
})
|
|
52
|
+
export class AppModule {}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Feature modules `import { AtlasModule }` (no `forRoot`) to use the directives and components. Standalone components import them individually, or spread `ATLAS_UI`:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { ATLAS_UI } from '@atlas/angular';
|
|
59
|
+
|
|
60
|
+
@Component({ standalone: true, imports: [...ATLAS_UI], template: '…' })
|
|
61
|
+
export class DashboardComponent {}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## `AtlasService`
|
|
67
|
+
|
|
68
|
+
Injectable, `providedIn: 'root'`. The Angular peer of `<AtlasProvider>` + the React hooks. Exposes state twice so you can pick your idiom.
|
|
69
|
+
|
|
70
|
+
**RxJS observables** (the primary API):
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
export class HeaderComponent {
|
|
74
|
+
private atlas = inject(AtlasService);
|
|
75
|
+
|
|
76
|
+
user$ = this.atlas.user$; // Observable<AtlasUser | null>
|
|
77
|
+
session$ = this.atlas.session$; // Observable<AtlasSession | null>
|
|
78
|
+
isSignedIn$ = this.atlas.isSignedIn$; // Observable<boolean>
|
|
79
|
+
isLoaded$ = this.atlas.isLoaded$;
|
|
80
|
+
claims$ = this.atlas.claims$;
|
|
81
|
+
memberships$ = this.atlas.memberships$;
|
|
82
|
+
organization$= this.atlas.organization$;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Signals** (same values, for templates / `computed()`):
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
this.atlas.user(); // Signal<AtlasUser | null>
|
|
90
|
+
this.atlas.isSignedIn(); // Signal<boolean>
|
|
91
|
+
this.atlas.status(); // 'loading' | 'signed_in' | 'signed_out'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Methods** (mirror the React hooks' actions):
|
|
95
|
+
|
|
96
|
+
| Method | Mirrors |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `getToken(): Promise<string \| null>` | `useAuth().getToken` — always fresh; refreshes if within 5s of expiry |
|
|
99
|
+
| `getProviderToken(provider): Promise<ProviderToken \| null>` | `useUser().getProviderToken` — the user's own live token at a connected provider |
|
|
100
|
+
| `has(condition): boolean` | `useAuth().has` — imperative permission check |
|
|
101
|
+
| `signOut(): Promise<void>` | `useAuth().signOut` |
|
|
102
|
+
| `setActiveOrganization(orgId \| null)` | `useOrganization().setActive` |
|
|
103
|
+
| `reload(): Promise<void>` | re-read `/v1/client` |
|
|
104
|
+
|
|
105
|
+
The session JWT lives only in memory (a `TokenCache`); the refresh token is HttpOnly and never touched by script. Refresh is proactive and jittered off the token's own expiry.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Directives
|
|
110
|
+
|
|
111
|
+
All standalone. Structural directives mirror React's `components.tsx`:
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<!-- render only when signed in (after boot) — React <SignedIn> -->
|
|
115
|
+
<div *atlasSignedIn>Welcome back</div>
|
|
116
|
+
|
|
117
|
+
<!-- render only when signed out — React <SignedOut> -->
|
|
118
|
+
<atlas-sign-in *atlasSignedOut></atlas-sign-in>
|
|
119
|
+
|
|
120
|
+
<!-- boot-state helpers — React <AtlasLoading> / <AtlasLoaded> -->
|
|
121
|
+
<app-spinner *atlasLoading></app-spinner>
|
|
122
|
+
<main *atlasLoaded>…</main>
|
|
123
|
+
|
|
124
|
+
<!-- permission-gated rendering — React <Protect permission="…"> -->
|
|
125
|
+
<button *atlasProtect="{ permission: 'org:sys_memberships:manage' }">Invite</button>
|
|
126
|
+
|
|
127
|
+
<!-- with a fallback (identical microsyntax to *ngIf … else) -->
|
|
128
|
+
<div *atlasProtect="{ role: 'admin' }; else denied">Admin tools</div>
|
|
129
|
+
<ng-template #denied>You lack access.</ng-template>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`*atlasProtect` is a **rendering** helper, never an authorization boundary — the server checking the same permission is what actually stops a request. An empty condition (`*atlasProtect`) means simply "signed in".
|
|
133
|
+
|
|
134
|
+
**Theming host** — apply the appearance tokens as `--atlas-*` CSS variables (React does this via the provider's wrapping `<div class="atlas-root">`):
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<div atlasRoot>
|
|
138
|
+
<router-outlet></router-outlet>
|
|
139
|
+
</div>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Components
|
|
143
|
+
|
|
144
|
+
Server-driven, rendered in the host DOM (no iframe), themed via the `appearance` option:
|
|
145
|
+
|
|
146
|
+
| Selector | Mirrors |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `<atlas-sign-in>` | `<SignIn/>` |
|
|
149
|
+
| `<atlas-sign-up>` | `<SignUp/>` |
|
|
150
|
+
| `<atlas-user-button>` | `<UserButton/>` |
|
|
151
|
+
| `<atlas-organization-switcher>` | `<OrganizationSwitcher/>` |
|
|
152
|
+
| `<atlas-user-profile>` | `<UserProfile/>` |
|
|
153
|
+
| `<atlas-organization-profile>` | `<OrganizationProfile/>` |
|
|
154
|
+
|
|
155
|
+
`<atlas-sign-in>` / `<atlas-sign-up>` run the full multi-step flow (identifier → first factor → 2FA / enrollment → email code → completion, plus magic-link polling), driven entirely by the server-side attempt status through the shared `@atlas/js` engine.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Route guards
|
|
160
|
+
|
|
161
|
+
`atlasGuard` gates a route on session, and optionally on a permission/role — the router equivalent of fencing a page with `<SignedIn>` / `<Protect>`. It waits for boot to settle first, so a hard refresh onto a protected URL doesn't bounce to sign-in prematurely.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { Routes } from '@angular/router';
|
|
165
|
+
import { atlasGuard } from '@atlas/angular';
|
|
166
|
+
|
|
167
|
+
export const routes: Routes = [
|
|
168
|
+
{
|
|
169
|
+
path: 'dashboard',
|
|
170
|
+
canActivate: [atlasGuard({ redirectTo: '/sign-in' })],
|
|
171
|
+
loadComponent: () => import('./dashboard.component').then((m) => m.DashboardComponent),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
path: 'admin',
|
|
175
|
+
canActivate: [atlasGuard({ permission: 'org:admin', redirectTo: '/' })],
|
|
176
|
+
loadComponent: () => import('./admin.component').then((m) => m.AdminComponent),
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
With `redirectTo` set the guard returns a `UrlTree` (redirect, with the attempted URL preserved as `redirect_url`); without it, it returns `false`. `atlasChildGuard` is the `CanActivateChild` form.
|
|
182
|
+
|
|
183
|
+
Like the components, guards are UX, not security — the server enforces the same checks on every request.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Theming & i18n
|
|
188
|
+
|
|
189
|
+
`appearance` accepts design tokens + per-element class overrides and a `baseTheme` (`default`, `dark`, `neobrutalist`); overrides are **appended** to the base classes so your Tailwind/CSS just works. `localization` merges a catalog over `en-US`. Both are identical in shape to `@atlas/react` — see `appearance.ts` and `i18n.ts`, which are shared verbatim.
|
|
190
|
+
|
|
191
|
+
## Build & test
|
|
192
|
+
|
|
193
|
+
- **Build**: `pnpm --filter @atlas/angular build` (ng-packagr → `dist/`, an Angular Package Format bundle with FESM2022 + type definitions).
|
|
194
|
+
- **Typecheck**: `pnpm --filter @atlas/angular typecheck`.
|
|
195
|
+
- **Test**: unit specs run under the Angular CLI test runner (Karma/Jasmine or your configured runner). A starter spec lives in `src/lib/atlas.service.spec.ts`.
|
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atlas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# @atlas/angular
|
|
2
|
+
|
|
3
|
+
The official **Angular** SDK for Atlas — the Angular peer of [`@atlas/react`](../react). It wraps the framework-agnostic [`@atlas/js`](../js) client and exposes Atlas's reactive auth state, sign-in/up flows, prebuilt UI, authorization directives, and route guards as idiomatic modern Angular (standalone APIs, RxJS **and** Signals, DI).
|
|
4
|
+
|
|
5
|
+
Requires **Angular 17+**.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @atlas/angular
|
|
13
|
+
# peers: @angular/core, @angular/common (>=17), rxjs (>=7); @angular/router if you use the guards
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
### Standalone (recommended, Angular 17+)
|
|
19
|
+
|
|
20
|
+
`provideAtlas` is the analogue of wrapping your tree in React's `<AtlasProvider>`. Register it once at the application root:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
24
|
+
import { provideRouter } from '@angular/router';
|
|
25
|
+
import { provideAtlas } from '@atlas/angular';
|
|
26
|
+
import { AppComponent } from './app/app.component';
|
|
27
|
+
import { routes } from './app/routes';
|
|
28
|
+
|
|
29
|
+
bootstrapApplication(AppComponent, {
|
|
30
|
+
providers: [
|
|
31
|
+
provideAtlas({
|
|
32
|
+
publishableKey: 'pk_test_…',
|
|
33
|
+
// frontendApi: 'https://fapi.your-instance.atlasauth.net', // omit for same-origin
|
|
34
|
+
// appearance: { baseTheme: 'dark', elements: { formButtonPrimary: 'rounded-xl' } },
|
|
35
|
+
// localization: myCatalog,
|
|
36
|
+
}),
|
|
37
|
+
provideRouter(routes),
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The service begins booting immediately — completing any OAuth/hosted-page redirect, scrubbing the URL, and reading the session — exactly as the React provider does on first render.
|
|
43
|
+
|
|
44
|
+
### NgModule
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { AtlasModule } from '@atlas/angular';
|
|
48
|
+
|
|
49
|
+
@NgModule({
|
|
50
|
+
imports: [AtlasModule.forRoot({ publishableKey: 'pk_test_…' })],
|
|
51
|
+
})
|
|
52
|
+
export class AppModule {}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Feature modules `import { AtlasModule }` (no `forRoot`) to use the directives and components. Standalone components import them individually, or spread `ATLAS_UI`:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { ATLAS_UI } from '@atlas/angular';
|
|
59
|
+
|
|
60
|
+
@Component({ standalone: true, imports: [...ATLAS_UI], template: '…' })
|
|
61
|
+
export class DashboardComponent {}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## `AtlasService`
|
|
67
|
+
|
|
68
|
+
Injectable, `providedIn: 'root'`. The Angular peer of `<AtlasProvider>` + the React hooks. Exposes state twice so you can pick your idiom.
|
|
69
|
+
|
|
70
|
+
**RxJS observables** (the primary API):
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
export class HeaderComponent {
|
|
74
|
+
private atlas = inject(AtlasService);
|
|
75
|
+
|
|
76
|
+
user$ = this.atlas.user$; // Observable<AtlasUser | null>
|
|
77
|
+
session$ = this.atlas.session$; // Observable<AtlasSession | null>
|
|
78
|
+
isSignedIn$ = this.atlas.isSignedIn$; // Observable<boolean>
|
|
79
|
+
isLoaded$ = this.atlas.isLoaded$;
|
|
80
|
+
claims$ = this.atlas.claims$;
|
|
81
|
+
memberships$ = this.atlas.memberships$;
|
|
82
|
+
organization$= this.atlas.organization$;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Signals** (same values, for templates / `computed()`):
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
this.atlas.user(); // Signal<AtlasUser | null>
|
|
90
|
+
this.atlas.isSignedIn(); // Signal<boolean>
|
|
91
|
+
this.atlas.status(); // 'loading' | 'signed_in' | 'signed_out'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Methods** (mirror the React hooks' actions):
|
|
95
|
+
|
|
96
|
+
| Method | Mirrors |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `getToken(): Promise<string \| null>` | `useAuth().getToken` — always fresh; refreshes if within 5s of expiry |
|
|
99
|
+
| `getProviderToken(provider): Promise<ProviderToken \| null>` | `useUser().getProviderToken` — the user's own live token at a connected provider |
|
|
100
|
+
| `has(condition): boolean` | `useAuth().has` — imperative permission check |
|
|
101
|
+
| `signOut(): Promise<void>` | `useAuth().signOut` |
|
|
102
|
+
| `setActiveOrganization(orgId \| null)` | `useOrganization().setActive` |
|
|
103
|
+
| `reload(): Promise<void>` | re-read `/v1/client` |
|
|
104
|
+
|
|
105
|
+
The session JWT lives only in memory (a `TokenCache`); the refresh token is HttpOnly and never touched by script. Refresh is proactive and jittered off the token's own expiry.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Directives
|
|
110
|
+
|
|
111
|
+
All standalone. Structural directives mirror React's `components.tsx`:
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<!-- render only when signed in (after boot) — React <SignedIn> -->
|
|
115
|
+
<div *atlasSignedIn>Welcome back</div>
|
|
116
|
+
|
|
117
|
+
<!-- render only when signed out — React <SignedOut> -->
|
|
118
|
+
<atlas-sign-in *atlasSignedOut></atlas-sign-in>
|
|
119
|
+
|
|
120
|
+
<!-- boot-state helpers — React <AtlasLoading> / <AtlasLoaded> -->
|
|
121
|
+
<app-spinner *atlasLoading></app-spinner>
|
|
122
|
+
<main *atlasLoaded>…</main>
|
|
123
|
+
|
|
124
|
+
<!-- permission-gated rendering — React <Protect permission="…"> -->
|
|
125
|
+
<button *atlasProtect="{ permission: 'org:sys_memberships:manage' }">Invite</button>
|
|
126
|
+
|
|
127
|
+
<!-- with a fallback (identical microsyntax to *ngIf … else) -->
|
|
128
|
+
<div *atlasProtect="{ role: 'admin' }; else denied">Admin tools</div>
|
|
129
|
+
<ng-template #denied>You lack access.</ng-template>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`*atlasProtect` is a **rendering** helper, never an authorization boundary — the server checking the same permission is what actually stops a request. An empty condition (`*atlasProtect`) means simply "signed in".
|
|
133
|
+
|
|
134
|
+
**Theming host** — apply the appearance tokens as `--atlas-*` CSS variables (React does this via the provider's wrapping `<div class="atlas-root">`):
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<div atlasRoot>
|
|
138
|
+
<router-outlet></router-outlet>
|
|
139
|
+
</div>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Components
|
|
143
|
+
|
|
144
|
+
Server-driven, rendered in the host DOM (no iframe), themed via the `appearance` option:
|
|
145
|
+
|
|
146
|
+
| Selector | Mirrors |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `<atlas-sign-in>` | `<SignIn/>` |
|
|
149
|
+
| `<atlas-sign-up>` | `<SignUp/>` |
|
|
150
|
+
| `<atlas-user-button>` | `<UserButton/>` |
|
|
151
|
+
| `<atlas-organization-switcher>` | `<OrganizationSwitcher/>` |
|
|
152
|
+
| `<atlas-user-profile>` | `<UserProfile/>` |
|
|
153
|
+
| `<atlas-organization-profile>` | `<OrganizationProfile/>` |
|
|
154
|
+
|
|
155
|
+
`<atlas-sign-in>` / `<atlas-sign-up>` run the full multi-step flow (identifier → first factor → 2FA / enrollment → email code → completion, plus magic-link polling), driven entirely by the server-side attempt status through the shared `@atlas/js` engine.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Route guards
|
|
160
|
+
|
|
161
|
+
`atlasGuard` gates a route on session, and optionally on a permission/role — the router equivalent of fencing a page with `<SignedIn>` / `<Protect>`. It waits for boot to settle first, so a hard refresh onto a protected URL doesn't bounce to sign-in prematurely.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { Routes } from '@angular/router';
|
|
165
|
+
import { atlasGuard } from '@atlas/angular';
|
|
166
|
+
|
|
167
|
+
export const routes: Routes = [
|
|
168
|
+
{
|
|
169
|
+
path: 'dashboard',
|
|
170
|
+
canActivate: [atlasGuard({ redirectTo: '/sign-in' })],
|
|
171
|
+
loadComponent: () => import('./dashboard.component').then((m) => m.DashboardComponent),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
path: 'admin',
|
|
175
|
+
canActivate: [atlasGuard({ permission: 'org:admin', redirectTo: '/' })],
|
|
176
|
+
loadComponent: () => import('./admin.component').then((m) => m.AdminComponent),
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
With `redirectTo` set the guard returns a `UrlTree` (redirect, with the attempted URL preserved as `redirect_url`); without it, it returns `false`. `atlasChildGuard` is the `CanActivateChild` form.
|
|
182
|
+
|
|
183
|
+
Like the components, guards are UX, not security — the server enforces the same checks on every request.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Theming & i18n
|
|
188
|
+
|
|
189
|
+
`appearance` accepts design tokens + per-element class overrides and a `baseTheme` (`default`, `dark`, `neobrutalist`); overrides are **appended** to the base classes so your Tailwind/CSS just works. `localization` merges a catalog over `en-US`. Both are identical in shape to `@atlas/react` — see `appearance.ts` and `i18n.ts`, which are shared verbatim.
|
|
190
|
+
|
|
191
|
+
## Build & test
|
|
192
|
+
|
|
193
|
+
- **Build**: `pnpm --filter @atlas/angular build` (ng-packagr → `dist/`, an Angular Package Format bundle with FESM2022 + type definitions).
|
|
194
|
+
- **Typecheck**: `pnpm --filter @atlas/angular typecheck`.
|
|
195
|
+
- **Test**: unit specs run under the Angular CLI test runner (Karma/Jasmine or your configured runner). A starter spec lives in `src/lib/atlas.service.spec.ts`.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated bundle index. Do not edit.
|
|
3
|
+
*/
|
|
4
|
+
export * from './public-api';
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXRsYXNhdXRoLWFuZ3VsYXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXRsYXNhdXRoLWFuZ3VsYXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLGNBQWMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9wdWJsaWMtYXBpJztcbiJdfQ==
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §10.3 theming.
|
|
3
|
+
*
|
|
4
|
+
* "appearance prop: design tokens (colors, radius, font, spacing) + per-element
|
|
5
|
+
* class overrides (elements: { formButtonPrimary: "…" }). No iframe — components
|
|
6
|
+
* render in the host DOM so Tailwind/CSS just works."
|
|
7
|
+
*
|
|
8
|
+
* Rendering in the host DOM is the decision everything else follows from. An
|
|
9
|
+
* iframe would isolate styles perfectly and be unusable: it cannot be sized to
|
|
10
|
+
* its content reliably, it breaks password managers, it breaks autofill, and a
|
|
11
|
+
* Tailwind class the customer writes has no effect inside it. Sharing the DOM
|
|
12
|
+
* means the customer's CSS reaches our markup, which is exactly what they want
|
|
13
|
+
* and what makes an override system necessary rather than decorative.
|
|
14
|
+
*
|
|
15
|
+
* Element classes are APPENDED to ours, never replacing them. A customer adding
|
|
16
|
+
* `className="rounded-xl"` wants a rounder button, not an unstyled one — and
|
|
17
|
+
* replacement is a support burden where every override starts by rebuilding the
|
|
18
|
+
* component's base styles from scratch.
|
|
19
|
+
*/
|
|
20
|
+
export const DEFAULT_TOKENS = {
|
|
21
|
+
colorPrimary: '#5b5bd6',
|
|
22
|
+
colorText: '#1c1c1f',
|
|
23
|
+
colorBackground: '#ffffff',
|
|
24
|
+
colorDanger: '#d64545',
|
|
25
|
+
colorBorder: '#e4e4e9',
|
|
26
|
+
borderRadius: '8px',
|
|
27
|
+
fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
28
|
+
spacingUnit: '4px',
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* §10.3: "prebuilt themes shipped (default, dark, neobrutalist as a demo of the
|
|
32
|
+
* override system)". Neobrutalist exists to prove the token set is expressive
|
|
33
|
+
* enough for a theme that looks nothing like the default — a theming system
|
|
34
|
+
* that only produces tasteful variations of itself is not a theming system.
|
|
35
|
+
*/
|
|
36
|
+
export const THEMES = {
|
|
37
|
+
default: DEFAULT_TOKENS,
|
|
38
|
+
dark: {
|
|
39
|
+
...DEFAULT_TOKENS,
|
|
40
|
+
colorText: '#e6e9ef',
|
|
41
|
+
colorBackground: '#14171c',
|
|
42
|
+
colorBorder: '#242932',
|
|
43
|
+
colorPrimary: '#6ea8fe',
|
|
44
|
+
},
|
|
45
|
+
neobrutalist: {
|
|
46
|
+
...DEFAULT_TOKENS,
|
|
47
|
+
colorPrimary: '#ffe600',
|
|
48
|
+
colorText: '#000000',
|
|
49
|
+
colorBackground: '#ffffff',
|
|
50
|
+
colorBorder: '#000000',
|
|
51
|
+
borderRadius: '0px',
|
|
52
|
+
fontFamily: '"Courier New", ui-monospace, monospace',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Resolve tokens: defaults, then the base theme, then explicit variables.
|
|
57
|
+
*
|
|
58
|
+
* Later wins, and `undefined` never overwrites. A customer setting one variable
|
|
59
|
+
* on the dark theme expects the other eleven to stay dark, not silently revert
|
|
60
|
+
* to the light defaults.
|
|
61
|
+
*/
|
|
62
|
+
export function resolveTokens(appearance) {
|
|
63
|
+
const base = appearance?.baseTheme ? THEMES[appearance.baseTheme] : DEFAULT_TOKENS;
|
|
64
|
+
const overrides = appearance?.variables ?? {};
|
|
65
|
+
const resolved = { ...DEFAULT_TOKENS, ...base };
|
|
66
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
67
|
+
if (value !== undefined)
|
|
68
|
+
resolved[key] = value;
|
|
69
|
+
}
|
|
70
|
+
return resolved;
|
|
71
|
+
}
|
|
72
|
+
/** Tokens as CSS custom properties, for the host DOM to inherit. */
|
|
73
|
+
export function cssVariables(tokens) {
|
|
74
|
+
const vars = {};
|
|
75
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
76
|
+
// camelCase → --atlas-kebab-case, which is what a customer writes in CSS.
|
|
77
|
+
vars[`--atlas-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`] = value;
|
|
78
|
+
}
|
|
79
|
+
return vars;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Combine our base class with the customer's override.
|
|
83
|
+
*
|
|
84
|
+
* Appended, never replaced — a customer adding `rounded-xl` wants a rounder
|
|
85
|
+
* button, not an unstyled one. Theirs comes last so it wins on equal
|
|
86
|
+
* specificity, which is the behaviour anyone writing Tailwind expects.
|
|
87
|
+
*/
|
|
88
|
+
export function classFor(element, appearance, base) {
|
|
89
|
+
const override = appearance?.elements?.[element];
|
|
90
|
+
return override ? `${base} ${override}` : base;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwZWFyYW5jZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvYXBwZWFyYW5jZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBd0NILE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBMkI7SUFDcEQsWUFBWSxFQUFFLFNBQVM7SUFDdkIsU0FBUyxFQUFFLFNBQVM7SUFDcEIsZUFBZSxFQUFFLFNBQVM7SUFDMUIsV0FBVyxFQUFFLFNBQVM7SUFDdEIsV0FBVyxFQUFFLFNBQVM7SUFDdEIsWUFBWSxFQUFFLEtBQUs7SUFDbkIsVUFBVSxFQUFFLGlFQUFpRTtJQUM3RSxXQUFXLEVBQUUsS0FBSztDQUNuQixDQUFDO0FBRUY7Ozs7O0dBS0c7QUFDSCxNQUFNLENBQUMsTUFBTSxNQUFNLEdBQUc7SUFDcEIsT0FBTyxFQUFFLGNBQWM7SUFDdkIsSUFBSSxFQUFFO1FBQ0osR0FBRyxjQUFjO1FBQ2pCLFNBQVMsRUFBRSxTQUFTO1FBQ3BCLGVBQWUsRUFBRSxTQUFTO1FBQzFCLFdBQVcsRUFBRSxTQUFTO1FBQ3RCLFlBQVksRUFBRSxTQUFTO0tBQ3hCO0lBQ0QsWUFBWSxFQUFFO1FBQ1osR0FBRyxjQUFjO1FBQ2pCLFlBQVksRUFBRSxTQUFTO1FBQ3ZCLFNBQVMsRUFBRSxTQUFTO1FBQ3BCLGVBQWUsRUFBRSxTQUFTO1FBQzFCLFdBQVcsRUFBRSxTQUFTO1FBQ3RCLFlBQVksRUFBRSxLQUFLO1FBQ25CLFVBQVUsRUFBRSx3Q0FBd0M7S0FDckQ7Q0FDd0QsQ0FBQztBQUU1RDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLFVBQXVCO0lBQ25ELE1BQU0sSUFBSSxHQUFHLFVBQVUsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQztJQUNuRixNQUFNLFNBQVMsR0FBRyxVQUFVLEVBQUUsU0FBUyxJQUFJLEVBQUUsQ0FBQztJQUU5QyxNQUFNLFFBQVEsR0FBRyxFQUFFLEdBQUcsY0FBYyxFQUFFLEdBQUcsSUFBSSxFQUFFLENBQUM7SUFDaEQsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztRQUNyRCxJQUFJLEtBQUssS0FBSyxTQUFTO1lBQUUsUUFBUSxDQUFDLEdBQXlCLENBQUMsR0FBRyxLQUFLLENBQUM7SUFDdkUsQ0FBQztJQUNELE9BQU8sUUFBUSxDQUFDO0FBQ2xCLENBQUM7QUFFRCxvRUFBb0U7QUFDcEUsTUFBTSxVQUFVLFlBQVksQ0FBQyxNQUE4QjtJQUN6RCxNQUFNLElBQUksR0FBMkIsRUFBRSxDQUFDO0lBQ3hDLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFDbEQsMEVBQTBFO1FBQzFFLElBQUksQ0FBQyxXQUFXLEdBQUcsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQztJQUNqRixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLFFBQVEsQ0FDdEIsT0FBbUIsRUFDbkIsVUFBa0MsRUFDbEMsSUFBWTtJQUVaLE1BQU0sUUFBUSxHQUFHLFVBQVUsRUFBRSxRQUFRLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNqRCxPQUFPLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxJQUFJLElBQUksUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztBQUNqRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiDCpzEwLjMgdGhlbWluZy5cbiAqXG4gKiBcImFwcGVhcmFuY2UgcHJvcDogZGVzaWduIHRva2VucyAoY29sb3JzLCByYWRpdXMsIGZvbnQsIHNwYWNpbmcpICsgcGVyLWVsZW1lbnRcbiAqIGNsYXNzIG92ZXJyaWRlcyAoZWxlbWVudHM6IHsgZm9ybUJ1dHRvblByaW1hcnk6IFwi4oCmXCIgfSkuIE5vIGlmcmFtZSDigJQgY29tcG9uZW50c1xuICogcmVuZGVyIGluIHRoZSBob3N0IERPTSBzbyBUYWlsd2luZC9DU1MganVzdCB3b3Jrcy5cIlxuICpcbiAqIFJlbmRlcmluZyBpbiB0aGUgaG9zdCBET00gaXMgdGhlIGRlY2lzaW9uIGV2ZXJ5dGhpbmcgZWxzZSBmb2xsb3dzIGZyb20uIEFuXG4gKiBpZnJhbWUgd291bGQgaXNvbGF0ZSBzdHlsZXMgcGVyZmVjdGx5IGFuZCBiZSB1bnVzYWJsZTogaXQgY2Fubm90IGJlIHNpemVkIHRvXG4gKiBpdHMgY29udGVudCByZWxpYWJseSwgaXQgYnJlYWtzIHBhc3N3b3JkIG1hbmFnZXJzLCBpdCBicmVha3MgYXV0b2ZpbGwsIGFuZCBhXG4gKiBUYWlsd2luZCBjbGFzcyB0aGUgY3VzdG9tZXIgd3JpdGVzIGhhcyBubyBlZmZlY3QgaW5zaWRlIGl0LiBTaGFyaW5nIHRoZSBET01cbiAqIG1lYW5zIHRoZSBjdXN0b21lcidzIENTUyByZWFjaGVzIG91ciBtYXJrdXAsIHdoaWNoIGlzIGV4YWN0bHkgd2hhdCB0aGV5IHdhbnRcbiAqIGFuZCB3aGF0IG1ha2VzIGFuIG92ZXJyaWRlIHN5c3RlbSBuZWNlc3NhcnkgcmF0aGVyIHRoYW4gZGVjb3JhdGl2ZS5cbiAqXG4gKiBFbGVtZW50IGNsYXNzZXMgYXJlIEFQUEVOREVEIHRvIG91cnMsIG5ldmVyIHJlcGxhY2luZyB0aGVtLiBBIGN1c3RvbWVyIGFkZGluZ1xuICogYGNsYXNzTmFtZT1cInJvdW5kZWQteGxcImAgd2FudHMgYSByb3VuZGVyIGJ1dHRvbiwgbm90IGFuIHVuc3R5bGVkIG9uZSDigJQgYW5kXG4gKiByZXBsYWNlbWVudCBpcyBhIHN1cHBvcnQgYnVyZGVuIHdoZXJlIGV2ZXJ5IG92ZXJyaWRlIHN0YXJ0cyBieSByZWJ1aWxkaW5nIHRoZVxuICogY29tcG9uZW50J3MgYmFzZSBzdHlsZXMgZnJvbSBzY3JhdGNoLlxuICovXG5cbmV4cG9ydCBpbnRlcmZhY2UgRGVzaWduVG9rZW5zIHtcbiAgY29sb3JQcmltYXJ5Pzogc3RyaW5nO1xuICBjb2xvclRleHQ/OiBzdHJpbmc7XG4gIGNvbG9yQmFja2dyb3VuZD86IHN0cmluZztcbiAgY29sb3JEYW5nZXI/OiBzdHJpbmc7XG4gIGNvbG9yQm9yZGVyPzogc3RyaW5nO1xuICBib3JkZXJSYWRpdXM/OiBzdHJpbmc7XG4gIGZvbnRGYW1pbHk/OiBzdHJpbmc7XG4gIHNwYWNpbmdVbml0Pzogc3RyaW5nO1xufVxuXG4vKiogRXZlcnkgZWxlbWVudCBhIGN1c3RvbWVyIG1heSB0YXJnZXQuIE5hbWVkIGZvciB3aGF0IGl0IElTLCBub3Qgd2hlcmUgaXQgc2l0cy4gKi9cbmV4cG9ydCB0eXBlIEVsZW1lbnRLZXkgPVxuICB8ICdyb290J1xuICB8ICdjYXJkJ1xuICB8ICdoZWFkZXJUaXRsZSdcbiAgfCAnaGVhZGVyU3VidGl0bGUnXG4gIHwgJ2Zvcm1GaWVsZCdcbiAgfCAnZm9ybUZpZWxkTGFiZWwnXG4gIHwgJ2Zvcm1GaWVsZElucHV0J1xuICB8ICdmb3JtRmllbGRFcnJvcidcbiAgfCAnZm9ybUJ1dHRvblByaW1hcnknXG4gIHwgJ2Zvcm1CdXR0b25TZWNvbmRhcnknXG4gIHwgJ3NvY2lhbEJ1dHRvbidcbiAgfCAnZGl2aWRlclRleHQnXG4gIHwgJ2Zvb3RlckFjdGlvbidcbiAgfCAnYXZhdGFyJ1xuICB8ICdtZW51J1xuICB8ICdtZW51SXRlbSdcbiAgfCAnYmFkZ2UnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEFwcGVhcmFuY2Uge1xuICAvKiogQSBwcmVidWlsdCB0aGVtZSB0byBzdGFydCBmcm9tLiAqL1xuICBiYXNlVGhlbWU/OiBrZXlvZiB0eXBlb2YgVEhFTUVTO1xuICB2YXJpYWJsZXM/OiBEZXNpZ25Ub2tlbnM7XG4gIGVsZW1lbnRzPzogUGFydGlhbDxSZWNvcmQ8RWxlbWVudEtleSwgc3RyaW5nPj47XG59XG5cbmV4cG9ydCBjb25zdCBERUZBVUxUX1RPS0VOUzogUmVxdWlyZWQ8RGVzaWduVG9rZW5zPiA9IHtcbiAgY29sb3JQcmltYXJ5OiAnIzViNWJkNicsXG4gIGNvbG9yVGV4dDogJyMxYzFjMWYnLFxuICBjb2xvckJhY2tncm91bmQ6ICcjZmZmZmZmJyxcbiAgY29sb3JEYW5nZXI6ICcjZDY0NTQ1JyxcbiAgY29sb3JCb3JkZXI6ICcjZTRlNGU5JyxcbiAgYm9yZGVyUmFkaXVzOiAnOHB4JyxcbiAgZm9udEZhbWlseTogJ3VpLXNhbnMtc2VyaWYsIHN5c3RlbS11aSwgLWFwcGxlLXN5c3RlbSwgXCJTZWdvZSBVSVwiLCBzYW5zLXNlcmlmJyxcbiAgc3BhY2luZ1VuaXQ6ICc0cHgnLFxufTtcblxuLyoqXG4gKiDCpzEwLjM6IFwicHJlYnVpbHQgdGhlbWVzIHNoaXBwZWQgKGRlZmF1bHQsIGRhcmssIG5lb2JydXRhbGlzdCBhcyBhIGRlbW8gb2YgdGhlXG4gKiBvdmVycmlkZSBzeXN0ZW0pXCIuIE5lb2JydXRhbGlzdCBleGlzdHMgdG8gcHJvdmUgdGhlIHRva2VuIHNldCBpcyBleHByZXNzaXZlXG4gKiBlbm91Z2ggZm9yIGEgdGhlbWUgdGhhdCBsb29rcyBub3RoaW5nIGxpa2UgdGhlIGRlZmF1bHQg4oCUIGEgdGhlbWluZyBzeXN0ZW1cbiAqIHRoYXQgb25seSBwcm9kdWNlcyB0YXN0ZWZ1bCB2YXJpYXRpb25zIG9mIGl0c2VsZiBpcyBub3QgYSB0aGVtaW5nIHN5c3RlbS5cbiAqL1xuZXhwb3J0IGNvbnN0IFRIRU1FUyA9IHtcbiAgZGVmYXVsdDogREVGQVVMVF9UT0tFTlMsXG4gIGRhcms6IHtcbiAgICAuLi5ERUZBVUxUX1RPS0VOUyxcbiAgICBjb2xvclRleHQ6ICcjZTZlOWVmJyxcbiAgICBjb2xvckJhY2tncm91bmQ6ICcjMTQxNzFjJyxcbiAgICBjb2xvckJvcmRlcjogJyMyNDI5MzInLFxuICAgIGNvbG9yUHJpbWFyeTogJyM2ZWE4ZmUnLFxuICB9LFxuICBuZW9icnV0YWxpc3Q6IHtcbiAgICAuLi5ERUZBVUxUX1RPS0VOUyxcbiAgICBjb2xvclByaW1hcnk6ICcjZmZlNjAwJyxcbiAgICBjb2xvclRleHQ6ICcjMDAwMDAwJyxcbiAgICBjb2xvckJhY2tncm91bmQ6ICcjZmZmZmZmJyxcbiAgICBjb2xvckJvcmRlcjogJyMwMDAwMDAnLFxuICAgIGJvcmRlclJhZGl1czogJzBweCcsXG4gICAgZm9udEZhbWlseTogJ1wiQ291cmllciBOZXdcIiwgdWktbW9ub3NwYWNlLCBtb25vc3BhY2UnLFxuICB9LFxufSBhcyBjb25zdCBzYXRpc2ZpZXMgUmVjb3JkPHN0cmluZywgUmVxdWlyZWQ8RGVzaWduVG9rZW5zPj47XG5cbi8qKlxuICogUmVzb2x2ZSB0b2tlbnM6IGRlZmF1bHRzLCB0aGVuIHRoZSBiYXNlIHRoZW1lLCB0aGVuIGV4cGxpY2l0IHZhcmlhYmxlcy5cbiAqXG4gKiBMYXRlciB3aW5zLCBhbmQgYHVuZGVmaW5lZGAgbmV2ZXIgb3ZlcndyaXRlcy4gQSBjdXN0b21lciBzZXR0aW5nIG9uZSB2YXJpYWJsZVxuICogb24gdGhlIGRhcmsgdGhlbWUgZXhwZWN0cyB0aGUgb3RoZXIgZWxldmVuIHRvIHN0YXkgZGFyaywgbm90IHNpbGVudGx5IHJldmVydFxuICogdG8gdGhlIGxpZ2h0IGRlZmF1bHRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZVRva2VucyhhcHBlYXJhbmNlPzogQXBwZWFyYW5jZSk6IFJlcXVpcmVkPERlc2lnblRva2Vucz4ge1xuICBjb25zdCBiYXNlID0gYXBwZWFyYW5jZT8uYmFzZVRoZW1lID8gVEhFTUVTW2FwcGVhcmFuY2UuYmFzZVRoZW1lXSA6IERFRkFVTFRfVE9LRU5TO1xuICBjb25zdCBvdmVycmlkZXMgPSBhcHBlYXJhbmNlPy52YXJpYWJsZXMgPz8ge307XG5cbiAgY29uc3QgcmVzb2x2ZWQgPSB7IC4uLkRFRkFVTFRfVE9LRU5TLCAuLi5iYXNlIH07XG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG92ZXJyaWRlcykpIHtcbiAgICBpZiAodmFsdWUgIT09IHVuZGVmaW5lZCkgcmVzb2x2ZWRba2V5IGFzIGtleW9mIERlc2lnblRva2Vuc10gPSB2YWx1ZTtcbiAgfVxuICByZXR1cm4gcmVzb2x2ZWQ7XG59XG5cbi8qKiBUb2tlbnMgYXMgQ1NTIGN1c3RvbSBwcm9wZXJ0aWVzLCBmb3IgdGhlIGhvc3QgRE9NIHRvIGluaGVyaXQuICovXG5leHBvcnQgZnVuY3Rpb24gY3NzVmFyaWFibGVzKHRva2VuczogUmVxdWlyZWQ8RGVzaWduVG9rZW5zPik6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4ge1xuICBjb25zdCB2YXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHRva2VucykpIHtcbiAgICAvLyBjYW1lbENhc2Ug4oaSIC0tYXRsYXMta2ViYWItY2FzZSwgd2hpY2ggaXMgd2hhdCBhIGN1c3RvbWVyIHdyaXRlcyBpbiBDU1MuXG4gICAgdmFyc1tgLS1hdGxhcy0ke2tleS5yZXBsYWNlKC9bQS1aXS9nLCAoYykgPT4gYC0ke2MudG9Mb3dlckNhc2UoKX1gKX1gXSA9IHZhbHVlO1xuICB9XG4gIHJldHVybiB2YXJzO1xufVxuXG4vKipcbiAqIENvbWJpbmUgb3VyIGJhc2UgY2xhc3Mgd2l0aCB0aGUgY3VzdG9tZXIncyBvdmVycmlkZS5cbiAqXG4gKiBBcHBlbmRlZCwgbmV2ZXIgcmVwbGFjZWQg4oCUIGEgY3VzdG9tZXIgYWRkaW5nIGByb3VuZGVkLXhsYCB3YW50cyBhIHJvdW5kZXJcbiAqIGJ1dHRvbiwgbm90IGFuIHVuc3R5bGVkIG9uZS4gVGhlaXJzIGNvbWVzIGxhc3Qgc28gaXQgd2lucyBvbiBlcXVhbFxuICogc3BlY2lmaWNpdHksIHdoaWNoIGlzIHRoZSBiZWhhdmlvdXIgYW55b25lIHdyaXRpbmcgVGFpbHdpbmQgZXhwZWN0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNsYXNzRm9yKFxuICBlbGVtZW50OiBFbGVtZW50S2V5LFxuICBhcHBlYXJhbmNlOiBBcHBlYXJhbmNlIHwgdW5kZWZpbmVkLFxuICBiYXNlOiBzdHJpbmcsXG4pOiBzdHJpbmcge1xuICBjb25zdCBvdmVycmlkZSA9IGFwcGVhcmFuY2U/LmVsZW1lbnRzPy5bZWxlbWVudF07XG4gIHJldHVybiBvdmVycmlkZSA/IGAke2Jhc2V9ICR7b3ZlcnJpZGV9YCA6IGJhc2U7XG59XG4iXX0=
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* DI token carrying the resolved {@link AtlasOptions}. Populated by
|
|
4
|
+
* {@link provideAtlas} (standalone) or {@link AtlasModule.forRoot} (NgModule)
|
|
5
|
+
* and read by {@link AtlasService}. Applications rarely inject this directly.
|
|
6
|
+
*/
|
|
7
|
+
export const ATLAS_OPTIONS = new InjectionToken('ATLAS_OPTIONS');
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXRsYXMuY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9hdGxhcy5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQTJCL0M7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxJQUFJLGNBQWMsQ0FBZSxlQUFlLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGlvblRva2VuIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgdHlwZSB7IEFwcGVhcmFuY2UgfSBmcm9tICcuL2FwcGVhcmFuY2UnO1xuaW1wb3J0IHR5cGUgeyBDYXRhbG9nIH0gZnJvbSAnLi9pMThuJztcblxuLyoqXG4gKiBDb25maWd1cmF0aW9uIGZvciB0aGUgQXRsYXMgQW5ndWxhciBTREsg4oCUIHRoZSBBbmd1bGFyIGFuYWxvZ3VlIG9mIHRoZSBwcm9wc1xuICogcGFzc2VkIHRvIFJlYWN0J3MgYDxBdGxhc1Byb3ZpZGVyPmAuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXRsYXNPcHRpb25zIHtcbiAgLyoqIFRoZSBpbnN0YW5jZSdzIHB1Ymxpc2hhYmxlIGtleSAoYHBrX+KApmApLiBSZXF1aXJlZC4gKi9cbiAgcHVibGlzaGFibGVLZXk6IHN0cmluZztcbiAgLyoqXG4gICAqIFRoZSBpbnN0YW5jZSdzIEZBUEkgb3JpZ2luLiBFbXB0eSBzdHJpbmcgKHRoZSBkZWZhdWx0KSBtZWFucyBzYW1lLW9yaWdpbiDigJRcbiAgICogcmVxdWVzdHMgZ28gdG8gYC92MS8uLi5gIG9uIHRoZSBhcHAncyBvd24gaG9zdC5cbiAgICovXG4gIGZyb250ZW5kQXBpPzogc3RyaW5nO1xuICAvKiogRGVzaWduIHRva2VucyArIHBlci1lbGVtZW50IGNsYXNzIG92ZXJyaWRlcy4gU2VlIHtAbGluayBBcHBlYXJhbmNlfS4gKi9cbiAgYXBwZWFyYW5jZT86IEFwcGVhcmFuY2U7XG4gIC8qKiBBIGxvY2FsaXphdGlvbiBjYXRhbG9nIG1lcmdlZCBvdmVyIGBlbi1VU2AuICovXG4gIGxvY2FsaXphdGlvbj86IENhdGFsb2c7XG4gIC8qKlxuICAgKiBBIGBmZXRjaGAgaW1wbGVtZW50YXRpb24gdG8gdXNlIGluc3RlYWQgb2YgdGhlIGdsb2JhbCBvbmUg4oCUIGhhbmR5IGZvciBTU1JcbiAgICogb3IgdGVzdHMuIERlZmF1bHRzIHRvIGBnbG9iYWxUaGlzLmZldGNoYC5cbiAgICovXG4gIGZldGNoSW1wbD86IHR5cGVvZiBmZXRjaDtcbn1cblxuLyoqXG4gKiBESSB0b2tlbiBjYXJyeWluZyB0aGUgcmVzb2x2ZWQge0BsaW5rIEF0bGFzT3B0aW9uc30uIFBvcHVsYXRlZCBieVxuICoge0BsaW5rIHByb3ZpZGVBdGxhc30gKHN0YW5kYWxvbmUpIG9yIHtAbGluayBBdGxhc01vZHVsZS5mb3JSb290fSAoTmdNb2R1bGUpXG4gKiBhbmQgcmVhZCBieSB7QGxpbmsgQXRsYXNTZXJ2aWNlfS4gQXBwbGljYXRpb25zIHJhcmVseSBpbmplY3QgdGhpcyBkaXJlY3RseS5cbiAqL1xuZXhwb3J0IGNvbnN0IEFUTEFTX09QVElPTlMgPSBuZXcgSW5qZWN0aW9uVG9rZW48QXRsYXNPcHRpb25zPignQVRMQVNfT1BUSU9OUycpO1xuIl19
|