@capitalos/angular 0.1.0-rc.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.
- package/README.md +119 -0
- package/esm2020/capitalos-angular.mjs +5 -0
- package/esm2020/lib/capitalos-auth.service.mjs +134 -0
- package/esm2020/lib/capitalos.module.mjs +48 -0
- package/esm2020/lib/cards-app.component.mjs +93 -0
- package/esm2020/lib/provide-capitalos.mjs +39 -0
- package/esm2020/lib/token-exchange.service.mjs +54 -0
- package/esm2020/public-api.mjs +13 -0
- package/fesm2015/capitalos-angular.mjs +369 -0
- package/fesm2015/capitalos-angular.mjs.map +1 -0
- package/fesm2020/capitalos-angular.mjs +360 -0
- package/fesm2020/capitalos-angular.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/capitalos-auth.service.d.ts +45 -0
- package/lib/capitalos.module.d.ts +33 -0
- package/lib/cards-app.component.d.ts +35 -0
- package/lib/provide-capitalos.d.ts +50 -0
- package/lib/token-exchange.service.d.ts +25 -0
- package/package.json +40 -0
- package/public-api.d.ts +8 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, Injectable, EventEmitter, Component, Input, Output, ViewChild, NgModule } from '@angular/core';
|
|
3
|
+
import { exchangeOneTimeToken, TokenType, TokenParamKey, TokenParamLocation, createLogger, IframeManager, CapitalOSError } from '@capitalos/core';
|
|
4
|
+
export { CapitalOSError, ErrorCode } from '@capitalos/core';
|
|
5
|
+
import { BehaviorSubject, combineLatest, map } from 'rxjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Injection token for CapitalOS configuration
|
|
9
|
+
*/
|
|
10
|
+
const CAPITALOS_CONFIG = new InjectionToken('CAPITALOS_CONFIG');
|
|
11
|
+
/**
|
|
12
|
+
* Provides the CapitalOS SDK with the specified configuration.
|
|
13
|
+
*
|
|
14
|
+
* Usage in app.config.ts (standalone):
|
|
15
|
+
* ```typescript
|
|
16
|
+
* bootstrapApplication(AppComponent, {
|
|
17
|
+
* providers: [
|
|
18
|
+
* provideCapitalOs({
|
|
19
|
+
* getToken: async () => {
|
|
20
|
+
* const response = await fetch('/api/capitalos/token');
|
|
21
|
+
* const data = await response.json();
|
|
22
|
+
* return data.token;
|
|
23
|
+
* },
|
|
24
|
+
* enableLogging: true,
|
|
25
|
+
* }),
|
|
26
|
+
* ],
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* For NgModule-based apps, use CapitalOsModule.forRoot() instead.
|
|
31
|
+
*/
|
|
32
|
+
function provideCapitalOs(config) {
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
provide: CAPITALOS_CONFIG,
|
|
36
|
+
useValue: config,
|
|
37
|
+
},
|
|
38
|
+
TokenExchangeService,
|
|
39
|
+
CapitalOsAuthService,
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const TOKEN_EXCHANGE_TIMEOUT_MS = 10000;
|
|
44
|
+
/**
|
|
45
|
+
* Service responsible for exchanging one-time tokens for long-lived JWTs.
|
|
46
|
+
* Creates a hidden iframe to perform the token exchange securely.
|
|
47
|
+
*/
|
|
48
|
+
class TokenExchangeService {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.config = inject(CAPITALOS_CONFIG);
|
|
51
|
+
this.isDestroyed = false;
|
|
52
|
+
this.activeAbortController = null;
|
|
53
|
+
}
|
|
54
|
+
ngOnDestroy() {
|
|
55
|
+
this.isDestroyed = true;
|
|
56
|
+
this.activeAbortController?.abort();
|
|
57
|
+
this.activeAbortController = null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Exchanges a one-time token for a long-lived JWT.
|
|
61
|
+
* Delegates to @capitalos/core (browser-only) which handles iframe + Penpal lifecycle.
|
|
62
|
+
*/
|
|
63
|
+
async exchange(oneTimeToken, options = {}) {
|
|
64
|
+
if (this.isDestroyed) {
|
|
65
|
+
throw new Error('TokenExchangeService was destroyed');
|
|
66
|
+
}
|
|
67
|
+
// Abort any in-flight exchange when a new one begins.
|
|
68
|
+
this.activeAbortController?.abort();
|
|
69
|
+
const abortController = new AbortController();
|
|
70
|
+
this.activeAbortController = abortController;
|
|
71
|
+
try {
|
|
72
|
+
return await exchangeOneTimeToken({
|
|
73
|
+
oneTimeToken,
|
|
74
|
+
enableLogging: options.enableLogging,
|
|
75
|
+
logger: this.config.logger,
|
|
76
|
+
timeoutMs: TOKEN_EXCHANGE_TIMEOUT_MS,
|
|
77
|
+
signal: abortController.signal,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
if (this.activeAbortController === abortController) {
|
|
82
|
+
this.activeAbortController = null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
TokenExchangeService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: TokenExchangeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
88
|
+
TokenExchangeService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: TokenExchangeService });
|
|
89
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: TokenExchangeService, decorators: [{
|
|
90
|
+
type: Injectable
|
|
91
|
+
}] });
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Converts an unknown error to an Error object
|
|
95
|
+
*/
|
|
96
|
+
function toError(error) {
|
|
97
|
+
if (error instanceof Error) {
|
|
98
|
+
return error;
|
|
99
|
+
}
|
|
100
|
+
return new Error(String(error));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Service that manages CapitalOS authentication state.
|
|
104
|
+
* This is the Angular equivalent of React's CapitalOsAuthenticationProvider.
|
|
105
|
+
*
|
|
106
|
+
* It handles:
|
|
107
|
+
* - Fetching one-time tokens via the provided getToken callback
|
|
108
|
+
* - Exchanging one-time tokens for long-lived JWTs
|
|
109
|
+
* - Managing token state via observables
|
|
110
|
+
* - Auto-refreshing tokens when they expire
|
|
111
|
+
*/
|
|
112
|
+
class CapitalOsAuthService {
|
|
113
|
+
constructor() {
|
|
114
|
+
this.config = inject(CAPITALOS_CONFIG);
|
|
115
|
+
this.tokenExchangeService = inject(TokenExchangeService);
|
|
116
|
+
// Private BehaviorSubjects for internal state
|
|
117
|
+
this._longLivedToken$ = new BehaviorSubject(undefined);
|
|
118
|
+
this._baseUrl$ = new BehaviorSubject(undefined);
|
|
119
|
+
this._isLoading$ = new BehaviorSubject(false);
|
|
120
|
+
this._error$ = new BehaviorSubject(undefined);
|
|
121
|
+
// Public observable for token data
|
|
122
|
+
this.tokenData$ = combineLatest([this._longLivedToken$, this._baseUrl$]).pipe(map(([token, baseUrl]) => {
|
|
123
|
+
if (!token || !baseUrl) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
token,
|
|
128
|
+
tokenType: TokenType.longLived,
|
|
129
|
+
baseUrl,
|
|
130
|
+
paramKey: TokenParamKey.accessToken,
|
|
131
|
+
paramLocation: TokenParamLocation.hash,
|
|
132
|
+
};
|
|
133
|
+
}));
|
|
134
|
+
// Public observables for UI state
|
|
135
|
+
this.isLoading$ = this._isLoading$.asObservable();
|
|
136
|
+
this.error$ = this._error$.asObservable();
|
|
137
|
+
this.authState$ = combineLatest([this._isLoading$, this._error$, this.tokenData$]).pipe(map(([isLoading, error, tokenData]) => {
|
|
138
|
+
if (isLoading)
|
|
139
|
+
return 'loading';
|
|
140
|
+
if (error)
|
|
141
|
+
return 'error';
|
|
142
|
+
if (tokenData)
|
|
143
|
+
return 'authenticated';
|
|
144
|
+
return 'idle';
|
|
145
|
+
}));
|
|
146
|
+
this.isDestroyed = false;
|
|
147
|
+
this.logger = createLogger(this.config.enableLogging, this.config.logger);
|
|
148
|
+
// Start authentication flow eagerly on construction.
|
|
149
|
+
// This is intentional: auth begins as soon as the app boots so the token
|
|
150
|
+
// is ready when CardsAppComponent renders, reducing perceived load time.
|
|
151
|
+
this.refreshToken();
|
|
152
|
+
}
|
|
153
|
+
ngOnDestroy() {
|
|
154
|
+
this.isDestroyed = true;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Invalidates the current token, triggering a refresh.
|
|
158
|
+
* Called when the iframe signals that the token has expired.
|
|
159
|
+
*/
|
|
160
|
+
invalidateToken() {
|
|
161
|
+
this.logger.log('CapitalOsAuthService: Invalidating token');
|
|
162
|
+
this._longLivedToken$.next(undefined);
|
|
163
|
+
this._baseUrl$.next(undefined);
|
|
164
|
+
this._error$.next(undefined);
|
|
165
|
+
this.refreshToken();
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Refreshes the token by fetching a new one-time token and exchanging it.
|
|
169
|
+
*/
|
|
170
|
+
async refreshToken() {
|
|
171
|
+
if (this.isDestroyed) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Prevent concurrent refresh calls - if already loading, skip
|
|
175
|
+
if (this._isLoading$.value) {
|
|
176
|
+
this.logger.log('CapitalOsAuthService: Token refresh already in progress, skipping');
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
this.logger.log('CapitalOsAuthService: Starting token refresh');
|
|
180
|
+
this._isLoading$.next(true);
|
|
181
|
+
this._error$.next(undefined);
|
|
182
|
+
try {
|
|
183
|
+
// Get one-time token from platform
|
|
184
|
+
const oneTimeToken = await this.config.getToken();
|
|
185
|
+
this.logger.log('CapitalOsAuthService: Received one-time token');
|
|
186
|
+
if (this.isDestroyed) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// Exchange for long-lived token
|
|
190
|
+
const result = await this.tokenExchangeService.exchange(oneTimeToken, {
|
|
191
|
+
enableLogging: this.config.enableLogging,
|
|
192
|
+
});
|
|
193
|
+
if (this.isDestroyed) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
this._longLivedToken$.next(result.longLivedToken);
|
|
197
|
+
this._baseUrl$.next(result.baseUrl);
|
|
198
|
+
this._error$.next(undefined);
|
|
199
|
+
this.logger.log('CapitalOsAuthService: Token exchange complete');
|
|
200
|
+
}
|
|
201
|
+
catch (rawError) {
|
|
202
|
+
if (!this.isDestroyed) {
|
|
203
|
+
const error = toError(rawError);
|
|
204
|
+
this.logger.error(`CapitalOsAuthService: Token refresh failed: ${error.message}`);
|
|
205
|
+
this._error$.next(error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
if (!this.isDestroyed) {
|
|
210
|
+
this._isLoading$.next(false);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
CapitalOsAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CapitalOsAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
216
|
+
CapitalOsAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CapitalOsAuthService });
|
|
217
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CapitalOsAuthService, decorators: [{
|
|
218
|
+
type: Injectable
|
|
219
|
+
}], ctorParameters: function () { return []; } });
|
|
220
|
+
|
|
221
|
+
// TODO: Automate version injection at build time
|
|
222
|
+
const SDK_VERSION = '0.1.0-rc.1';
|
|
223
|
+
/**
|
|
224
|
+
* CardsApp component that renders the CapitalOS cards interface in an iframe.
|
|
225
|
+
*
|
|
226
|
+
* The component emits events for loading state - consumers should handle their own loading UI:
|
|
227
|
+
*
|
|
228
|
+
* ```html
|
|
229
|
+
* <div *ngIf="!cardsLoaded">
|
|
230
|
+
* <my-loading-spinner></my-loading-spinner>
|
|
231
|
+
* </div>
|
|
232
|
+
* <capitalos-cards-app
|
|
233
|
+
* (loaded)="cardsLoaded = true"
|
|
234
|
+
* (error)="onCardsError($event)"
|
|
235
|
+
* ></capitalos-cards-app>
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
class CardsAppComponent {
|
|
239
|
+
constructor() {
|
|
240
|
+
// Injected services
|
|
241
|
+
this.authService = inject(CapitalOsAuthService);
|
|
242
|
+
this.config = inject(CAPITALOS_CONFIG);
|
|
243
|
+
this.heightOffsetPx = 12;
|
|
244
|
+
// Outputs
|
|
245
|
+
this.loaded = new EventEmitter();
|
|
246
|
+
this.error = new EventEmitter();
|
|
247
|
+
}
|
|
248
|
+
ngAfterViewInit() {
|
|
249
|
+
// Subscribe to tokenData$ and initialize iframe when ready
|
|
250
|
+
this.tokenDataSubscription = this.authService.tokenData$.subscribe((tokenData) => {
|
|
251
|
+
if (tokenData && this.iframeContainer) {
|
|
252
|
+
this.initializeIframe(tokenData, this.iframeContainer.nativeElement);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
ngOnDestroy() {
|
|
257
|
+
this.tokenDataSubscription?.unsubscribe();
|
|
258
|
+
this.iframeManager?.destroy();
|
|
259
|
+
}
|
|
260
|
+
initializeIframe(tokenData, container) {
|
|
261
|
+
// Destroy previous iframe if exists
|
|
262
|
+
this.iframeManager?.destroy();
|
|
263
|
+
const resolvedEnableLogging = this.enableLogging ?? this.config.enableLogging ?? false;
|
|
264
|
+
const resolvedTheme = this.theme ?? this.config.theme;
|
|
265
|
+
this.iframeManager = new IframeManager({
|
|
266
|
+
container,
|
|
267
|
+
tokenData,
|
|
268
|
+
renderingContext: { entryPoint: 'cardsApp' },
|
|
269
|
+
theme: resolvedTheme,
|
|
270
|
+
enableLogging: resolvedEnableLogging,
|
|
271
|
+
logger: this.config.logger,
|
|
272
|
+
sdkVersion: SDK_VERSION,
|
|
273
|
+
heightOffsetPx: this.heightOffsetPx,
|
|
274
|
+
callbacks: {
|
|
275
|
+
onLoad: () => {
|
|
276
|
+
this.loaded.emit();
|
|
277
|
+
},
|
|
278
|
+
onError: (rawError) => {
|
|
279
|
+
const capitalOsError = new CapitalOSError(rawError);
|
|
280
|
+
this.error.emit(capitalOsError);
|
|
281
|
+
},
|
|
282
|
+
onTokenExpired: () => {
|
|
283
|
+
this.authService.invalidateToken();
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
CardsAppComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CardsAppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
290
|
+
CardsAppComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: CardsAppComponent, isStandalone: true, selector: "capitalos-cards-app", inputs: { theme: "theme", heightOffsetPx: "heightOffsetPx", enableLogging: "enableLogging" }, outputs: { loaded: "loaded", error: "error" }, viewQueries: [{ propertyName: "iframeContainer", first: true, predicate: ["iframeContainer"], descendants: true }], ngImport: i0, template: `<div #iframeContainer class="capitalos-iframe-container"></div>`, isInline: true, styles: [":host{display:block;width:100%}.capitalos-iframe-container{width:100%}\n"] });
|
|
291
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CardsAppComponent, decorators: [{
|
|
292
|
+
type: Component,
|
|
293
|
+
args: [{ selector: 'capitalos-cards-app', standalone: true, template: `<div #iframeContainer class="capitalos-iframe-container"></div>`, styles: [":host{display:block;width:100%}.capitalos-iframe-container{width:100%}\n"] }]
|
|
294
|
+
}], propDecorators: { theme: [{
|
|
295
|
+
type: Input
|
|
296
|
+
}], heightOffsetPx: [{
|
|
297
|
+
type: Input
|
|
298
|
+
}], enableLogging: [{
|
|
299
|
+
type: Input
|
|
300
|
+
}], loaded: [{
|
|
301
|
+
type: Output
|
|
302
|
+
}], error: [{
|
|
303
|
+
type: Output
|
|
304
|
+
}], iframeContainer: [{
|
|
305
|
+
type: ViewChild,
|
|
306
|
+
args: ['iframeContainer']
|
|
307
|
+
}] } });
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* NgModule for CapitalOS SDK.
|
|
311
|
+
*
|
|
312
|
+
* Use this module for NgModule-based Angular applications.
|
|
313
|
+
* For standalone applications, use provideCapitalOs() instead.
|
|
314
|
+
*
|
|
315
|
+
* Usage:
|
|
316
|
+
* ```typescript
|
|
317
|
+
* @NgModule({
|
|
318
|
+
* imports: [
|
|
319
|
+
* CapitalOsModule.forRoot({
|
|
320
|
+
* getToken: async () => {
|
|
321
|
+
* const response = await fetch('/api/capitalos/token');
|
|
322
|
+
* const data = await response.json();
|
|
323
|
+
* return data.token;
|
|
324
|
+
* },
|
|
325
|
+
* enableLogging: true,
|
|
326
|
+
* }),
|
|
327
|
+
* ],
|
|
328
|
+
* })
|
|
329
|
+
* export class AppModule {}
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
class CapitalOsModule {
|
|
333
|
+
static forRoot(config) {
|
|
334
|
+
return {
|
|
335
|
+
ngModule: CapitalOsModule,
|
|
336
|
+
providers: [{ provide: CAPITALOS_CONFIG, useValue: config }, TokenExchangeService, CapitalOsAuthService],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
CapitalOsModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CapitalOsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
341
|
+
CapitalOsModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: CapitalOsModule, imports: [CardsAppComponent], exports: [CardsAppComponent] });
|
|
342
|
+
CapitalOsModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CapitalOsModule, imports: [CardsAppComponent] });
|
|
343
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: CapitalOsModule, decorators: [{
|
|
344
|
+
type: NgModule,
|
|
345
|
+
args: [{
|
|
346
|
+
imports: [CardsAppComponent],
|
|
347
|
+
exports: [CardsAppComponent],
|
|
348
|
+
}]
|
|
349
|
+
}] });
|
|
350
|
+
|
|
351
|
+
/*
|
|
352
|
+
* Public API Surface of @capitalos/angular
|
|
353
|
+
*/
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Generated bundle index. Do not edit.
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
export { CAPITALOS_CONFIG, CapitalOsAuthService, CapitalOsModule, CardsAppComponent, provideCapitalOs };
|
|
360
|
+
//# sourceMappingURL=capitalos-angular.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capitalos-angular.mjs","sources":["../../../projects/capitalos-angular/src/lib/provide-capitalos.ts","../../../projects/capitalos-angular/src/lib/token-exchange.service.ts","../../../projects/capitalos-angular/src/lib/capitalos-auth.service.ts","../../../projects/capitalos-angular/src/lib/cards-app.component.ts","../../../projects/capitalos-angular/src/lib/capitalos.module.ts","../../../projects/capitalos-angular/src/public-api.ts","../../../projects/capitalos-angular/src/capitalos-angular.ts"],"sourcesContent":["import { InjectionToken, Provider } from '@angular/core'\nimport { Logger, ThemeColorScheme } from '@capitalos/core'\nimport { CapitalOsAuthService } from './capitalos-auth.service'\nimport { TokenExchangeService } from './token-exchange.service'\n\n/**\n * Configuration for CapitalOS SDK\n */\nexport interface CapitalOsConfig {\n /**\n * Function that returns a promise resolving to a one-time token.\n * This is typically a call to your backend that initiates a CapitalOS login.\n */\n getToken: () => Promise<string>\n\n /**\n * Whether to enable logging for debugging purposes.\n */\n enableLogging?: boolean\n\n /**\n * Optional custom logger for capturing SDK logs.\n */\n logger?: Logger\n\n /**\n * Theme color scheme for the SDK components.\n */\n theme?: ThemeColorScheme\n}\n\n/**\n * Injection token for CapitalOS configuration\n */\nexport const CAPITALOS_CONFIG = new InjectionToken<CapitalOsConfig>('CAPITALOS_CONFIG')\n\n/**\n * Provides the CapitalOS SDK with the specified configuration.\n *\n * Usage in app.config.ts (standalone):\n * ```typescript\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideCapitalOs({\n * getToken: async () => {\n * const response = await fetch('/api/capitalos/token');\n * const data = await response.json();\n * return data.token;\n * },\n * enableLogging: true,\n * }),\n * ],\n * });\n * ```\n *\n * For NgModule-based apps, use CapitalOsModule.forRoot() instead.\n */\nexport function provideCapitalOs(config: CapitalOsConfig): Provider[] {\n return [\n {\n provide: CAPITALOS_CONFIG,\n useValue: config,\n },\n TokenExchangeService,\n CapitalOsAuthService,\n ]\n}\n","import { Injectable, inject, OnDestroy } from '@angular/core'\nimport { exchangeOneTimeToken } from '@capitalos/core'\nimport { CAPITALOS_CONFIG } from './provide-capitalos'\n\nconst TOKEN_EXCHANGE_TIMEOUT_MS = 10_000\n\nexport interface TokenExchangeResult {\n longLivedToken: string\n baseUrl: string\n}\n\n/**\n * Service responsible for exchanging one-time tokens for long-lived JWTs.\n * Creates a hidden iframe to perform the token exchange securely.\n */\n@Injectable()\nexport class TokenExchangeService implements OnDestroy {\n private readonly config = inject(CAPITALOS_CONFIG)\n private isDestroyed = false\n private activeAbortController: AbortController | null = null\n\n ngOnDestroy(): void {\n this.isDestroyed = true\n this.activeAbortController?.abort()\n this.activeAbortController = null\n }\n\n /**\n * Exchanges a one-time token for a long-lived JWT.\n * Delegates to @capitalos/core (browser-only) which handles iframe + Penpal lifecycle.\n */\n async exchange(oneTimeToken: string, options: { enableLogging?: boolean } = {}): Promise<TokenExchangeResult> {\n if (this.isDestroyed) {\n throw new Error('TokenExchangeService was destroyed')\n }\n\n // Abort any in-flight exchange when a new one begins.\n this.activeAbortController?.abort()\n\n const abortController = new AbortController()\n this.activeAbortController = abortController\n\n try {\n return await exchangeOneTimeToken({\n oneTimeToken,\n enableLogging: options.enableLogging,\n logger: this.config.logger,\n timeoutMs: TOKEN_EXCHANGE_TIMEOUT_MS,\n signal: abortController.signal,\n })\n } finally {\n if (this.activeAbortController === abortController) {\n this.activeAbortController = null\n }\n }\n }\n}\n","import { Injectable, inject, OnDestroy } from '@angular/core'\nimport { BehaviorSubject, Observable, combineLatest, map } from 'rxjs'\nimport { TokenData, TokenType, TokenParamKey, TokenParamLocation, createLogger } from '@capitalos/core'\nimport { TokenExchangeService } from './token-exchange.service'\nimport { CAPITALOS_CONFIG } from './provide-capitalos'\n\n/**\n * Converts an unknown error to an Error object\n */\nfunction toError(error: unknown): Error {\n if (error instanceof Error) {\n return error\n }\n return new Error(String(error))\n}\n\n/**\n * Authentication state enum\n */\nexport type AuthState = 'idle' | 'loading' | 'authenticated' | 'error'\n\n/**\n * Service that manages CapitalOS authentication state.\n * This is the Angular equivalent of React's CapitalOsAuthenticationProvider.\n *\n * It handles:\n * - Fetching one-time tokens via the provided getToken callback\n * - Exchanging one-time tokens for long-lived JWTs\n * - Managing token state via observables\n * - Auto-refreshing tokens when they expire\n */\n@Injectable()\nexport class CapitalOsAuthService implements OnDestroy {\n private readonly config = inject(CAPITALOS_CONFIG)\n private readonly tokenExchangeService = inject(TokenExchangeService)\n\n // Private BehaviorSubjects for internal state\n private readonly _longLivedToken$ = new BehaviorSubject<string | undefined>(undefined)\n private readonly _baseUrl$ = new BehaviorSubject<string | undefined>(undefined)\n private readonly _isLoading$ = new BehaviorSubject(false)\n private readonly _error$ = new BehaviorSubject<Error | undefined>(undefined)\n\n // Public observable for token data\n readonly tokenData$: Observable<TokenData | undefined> = combineLatest([this._longLivedToken$, this._baseUrl$]).pipe(\n map(([token, baseUrl]) => {\n if (!token || !baseUrl) {\n return undefined\n }\n\n return {\n token,\n tokenType: TokenType.longLived,\n baseUrl,\n paramKey: TokenParamKey.accessToken,\n paramLocation: TokenParamLocation.hash,\n }\n })\n )\n\n // Public observables for UI state\n readonly isLoading$: Observable<boolean> = this._isLoading$.asObservable()\n readonly error$: Observable<Error | undefined> = this._error$.asObservable()\n readonly authState$: Observable<AuthState> = combineLatest([this._isLoading$, this._error$, this.tokenData$]).pipe(\n map(([isLoading, error, tokenData]) => {\n if (isLoading) return 'loading'\n if (error) return 'error'\n if (tokenData) return 'authenticated'\n return 'idle'\n })\n )\n\n private isDestroyed = false\n private logger: ReturnType<typeof createLogger>\n\n constructor() {\n this.logger = createLogger(this.config.enableLogging, this.config.logger)\n\n // Start authentication flow eagerly on construction.\n // This is intentional: auth begins as soon as the app boots so the token\n // is ready when CardsAppComponent renders, reducing perceived load time.\n this.refreshToken()\n }\n\n ngOnDestroy(): void {\n this.isDestroyed = true\n }\n\n /**\n * Invalidates the current token, triggering a refresh.\n * Called when the iframe signals that the token has expired.\n */\n invalidateToken(): void {\n this.logger.log('CapitalOsAuthService: Invalidating token')\n this._longLivedToken$.next(undefined)\n this._baseUrl$.next(undefined)\n this._error$.next(undefined)\n this.refreshToken()\n }\n\n /**\n * Refreshes the token by fetching a new one-time token and exchanging it.\n */\n async refreshToken(): Promise<void> {\n if (this.isDestroyed) {\n return\n }\n\n // Prevent concurrent refresh calls - if already loading, skip\n if (this._isLoading$.value) {\n this.logger.log('CapitalOsAuthService: Token refresh already in progress, skipping')\n return\n }\n\n this.logger.log('CapitalOsAuthService: Starting token refresh')\n this._isLoading$.next(true)\n this._error$.next(undefined)\n\n try {\n // Get one-time token from platform\n const oneTimeToken = await this.config.getToken()\n this.logger.log('CapitalOsAuthService: Received one-time token')\n\n if (this.isDestroyed) {\n return\n }\n\n // Exchange for long-lived token\n const result = await this.tokenExchangeService.exchange(oneTimeToken, {\n enableLogging: this.config.enableLogging,\n })\n\n if (this.isDestroyed) {\n return\n }\n\n this._longLivedToken$.next(result.longLivedToken)\n this._baseUrl$.next(result.baseUrl)\n this._error$.next(undefined)\n this.logger.log('CapitalOsAuthService: Token exchange complete')\n } catch (rawError) {\n if (!this.isDestroyed) {\n const error = toError(rawError)\n this.logger.error(`CapitalOsAuthService: Token refresh failed: ${error.message}`)\n this._error$.next(error)\n }\n } finally {\n if (!this.isDestroyed) {\n this._isLoading$.next(false)\n }\n }\n }\n}\n","import {\n Component,\n inject,\n Input,\n Output,\n EventEmitter,\n ViewChild,\n ElementRef,\n AfterViewInit,\n OnDestroy,\n} from '@angular/core'\nimport { Subscription } from 'rxjs'\nimport { IframeManager, CapitalOSError, TokenData, RawErrorDetails, ThemeColorScheme } from '@capitalos/core'\nimport { CapitalOsAuthService } from './capitalos-auth.service'\nimport { CAPITALOS_CONFIG } from './provide-capitalos'\n\n// TODO: Automate version injection at build time\nconst SDK_VERSION = '0.1.0-rc.1'\n\n/**\n * CardsApp component that renders the CapitalOS cards interface in an iframe.\n *\n * The component emits events for loading state - consumers should handle their own loading UI:\n *\n * ```html\n * <div *ngIf=\"!cardsLoaded\">\n * <my-loading-spinner></my-loading-spinner>\n * </div>\n * <capitalos-cards-app\n * (loaded)=\"cardsLoaded = true\"\n * (error)=\"onCardsError($event)\"\n * ></capitalos-cards-app>\n * ```\n */\n@Component({\n selector: 'capitalos-cards-app',\n standalone: true,\n template: `<div #iframeContainer class=\"capitalos-iframe-container\"></div>`,\n styles: [\n `\n :host {\n display: block;\n width: 100%;\n }\n\n .capitalos-iframe-container {\n width: 100%;\n }\n `,\n ],\n})\nexport class CardsAppComponent implements AfterViewInit, OnDestroy {\n // Injected services\n private readonly authService = inject(CapitalOsAuthService)\n private readonly config = inject(CAPITALOS_CONFIG)\n\n // Inputs\n @Input() theme?: ThemeColorScheme\n @Input() heightOffsetPx = 12\n @Input() enableLogging?: boolean\n\n // Outputs\n @Output() loaded = new EventEmitter<void>()\n @Output() error = new EventEmitter<CapitalOSError>()\n\n // View children\n @ViewChild('iframeContainer') iframeContainer?: ElementRef<HTMLDivElement>\n\n // Internal state\n private iframeManager?: IframeManager\n private tokenDataSubscription?: Subscription\n\n ngAfterViewInit(): void {\n // Subscribe to tokenData$ and initialize iframe when ready\n this.tokenDataSubscription = this.authService.tokenData$.subscribe((tokenData) => {\n if (tokenData && this.iframeContainer) {\n this.initializeIframe(tokenData, this.iframeContainer.nativeElement)\n }\n })\n }\n\n ngOnDestroy(): void {\n this.tokenDataSubscription?.unsubscribe()\n this.iframeManager?.destroy()\n }\n\n private initializeIframe(tokenData: TokenData, container: HTMLElement): void {\n // Destroy previous iframe if exists\n this.iframeManager?.destroy()\n\n const resolvedEnableLogging = this.enableLogging ?? this.config.enableLogging ?? false\n const resolvedTheme = this.theme ?? this.config.theme\n\n this.iframeManager = new IframeManager({\n container,\n tokenData,\n renderingContext: { entryPoint: 'cardsApp' },\n theme: resolvedTheme,\n enableLogging: resolvedEnableLogging,\n logger: this.config.logger,\n sdkVersion: SDK_VERSION,\n heightOffsetPx: this.heightOffsetPx,\n callbacks: {\n onLoad: () => {\n this.loaded.emit()\n },\n onError: (rawError: RawErrorDetails) => {\n const capitalOsError = new CapitalOSError(rawError)\n this.error.emit(capitalOsError)\n },\n onTokenExpired: () => {\n this.authService.invalidateToken()\n },\n },\n })\n }\n}\n","import { ModuleWithProviders, NgModule } from '@angular/core'\nimport { CardsAppComponent } from './cards-app.component'\nimport { CapitalOsConfig, CAPITALOS_CONFIG } from './provide-capitalos'\nimport { CapitalOsAuthService } from './capitalos-auth.service'\nimport { TokenExchangeService } from './token-exchange.service'\n\n/**\n * NgModule for CapitalOS SDK.\n *\n * Use this module for NgModule-based Angular applications.\n * For standalone applications, use provideCapitalOs() instead.\n *\n * Usage:\n * ```typescript\n * @NgModule({\n * imports: [\n * CapitalOsModule.forRoot({\n * getToken: async () => {\n * const response = await fetch('/api/capitalos/token');\n * const data = await response.json();\n * return data.token;\n * },\n * enableLogging: true,\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n@NgModule({\n imports: [CardsAppComponent],\n exports: [CardsAppComponent],\n})\nexport class CapitalOsModule {\n static forRoot(config: CapitalOsConfig): ModuleWithProviders<CapitalOsModule> {\n return {\n ngModule: CapitalOsModule,\n providers: [{ provide: CAPITALOS_CONFIG, useValue: config }, TokenExchangeService, CapitalOsAuthService],\n }\n }\n}\n","/*\n * Public API Surface of @capitalos/angular\n */\n\n// Components\nexport { CardsAppComponent } from './lib/cards-app.component'\n\n// Services\nexport { CapitalOsAuthService } from './lib/capitalos-auth.service'\nexport type { AuthState } from './lib/capitalos-auth.service'\n\n// Provider (standalone)\nexport { provideCapitalOs, CAPITALOS_CONFIG } from './lib/provide-capitalos'\nexport type { CapitalOsConfig } from './lib/provide-capitalos'\n\n// Module (NgModule-based apps)\nexport { CapitalOsModule } from './lib/capitalos.module'\n\n// Re-export commonly used types from core\nexport type { ThemeColorScheme, Logger, TokenData, RawErrorDetails } from '@capitalos/core'\n\nexport { CapitalOSError, ErrorCode } from '@capitalos/core'\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { OnDestroy } from '@angular/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { TokenData } from '@capitalos/core';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Authentication state enum
|
|
7
|
+
*/
|
|
8
|
+
export declare type AuthState = 'idle' | 'loading' | 'authenticated' | 'error';
|
|
9
|
+
/**
|
|
10
|
+
* Service that manages CapitalOS authentication state.
|
|
11
|
+
* This is the Angular equivalent of React's CapitalOsAuthenticationProvider.
|
|
12
|
+
*
|
|
13
|
+
* It handles:
|
|
14
|
+
* - Fetching one-time tokens via the provided getToken callback
|
|
15
|
+
* - Exchanging one-time tokens for long-lived JWTs
|
|
16
|
+
* - Managing token state via observables
|
|
17
|
+
* - Auto-refreshing tokens when they expire
|
|
18
|
+
*/
|
|
19
|
+
export declare class CapitalOsAuthService implements OnDestroy {
|
|
20
|
+
private readonly config;
|
|
21
|
+
private readonly tokenExchangeService;
|
|
22
|
+
private readonly _longLivedToken$;
|
|
23
|
+
private readonly _baseUrl$;
|
|
24
|
+
private readonly _isLoading$;
|
|
25
|
+
private readonly _error$;
|
|
26
|
+
readonly tokenData$: Observable<TokenData | undefined>;
|
|
27
|
+
readonly isLoading$: Observable<boolean>;
|
|
28
|
+
readonly error$: Observable<Error | undefined>;
|
|
29
|
+
readonly authState$: Observable<AuthState>;
|
|
30
|
+
private isDestroyed;
|
|
31
|
+
private logger;
|
|
32
|
+
constructor();
|
|
33
|
+
ngOnDestroy(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Invalidates the current token, triggering a refresh.
|
|
36
|
+
* Called when the iframe signals that the token has expired.
|
|
37
|
+
*/
|
|
38
|
+
invalidateToken(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Refreshes the token by fetching a new one-time token and exchanging it.
|
|
41
|
+
*/
|
|
42
|
+
refreshToken(): Promise<void>;
|
|
43
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CapitalOsAuthService, never>;
|
|
44
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<CapitalOsAuthService>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ModuleWithProviders } from '@angular/core';
|
|
2
|
+
import { CapitalOsConfig } from './provide-capitalos';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import * as i1 from "./cards-app.component";
|
|
5
|
+
/**
|
|
6
|
+
* NgModule for CapitalOS SDK.
|
|
7
|
+
*
|
|
8
|
+
* Use this module for NgModule-based Angular applications.
|
|
9
|
+
* For standalone applications, use provideCapitalOs() instead.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```typescript
|
|
13
|
+
* @NgModule({
|
|
14
|
+
* imports: [
|
|
15
|
+
* CapitalOsModule.forRoot({
|
|
16
|
+
* getToken: async () => {
|
|
17
|
+
* const response = await fetch('/api/capitalos/token');
|
|
18
|
+
* const data = await response.json();
|
|
19
|
+
* return data.token;
|
|
20
|
+
* },
|
|
21
|
+
* enableLogging: true,
|
|
22
|
+
* }),
|
|
23
|
+
* ],
|
|
24
|
+
* })
|
|
25
|
+
* export class AppModule {}
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare class CapitalOsModule {
|
|
29
|
+
static forRoot(config: CapitalOsConfig): ModuleWithProviders<CapitalOsModule>;
|
|
30
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CapitalOsModule, never>;
|
|
31
|
+
static ɵmod: i0.ɵɵNgModuleDeclaration<CapitalOsModule, never, [typeof i1.CardsAppComponent], [typeof i1.CardsAppComponent]>;
|
|
32
|
+
static ɵinj: i0.ɵɵInjectorDeclaration<CapitalOsModule>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EventEmitter, ElementRef, AfterViewInit, OnDestroy } from '@angular/core';
|
|
2
|
+
import { CapitalOSError, ThemeColorScheme } from '@capitalos/core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* CardsApp component that renders the CapitalOS cards interface in an iframe.
|
|
6
|
+
*
|
|
7
|
+
* The component emits events for loading state - consumers should handle their own loading UI:
|
|
8
|
+
*
|
|
9
|
+
* ```html
|
|
10
|
+
* <div *ngIf="!cardsLoaded">
|
|
11
|
+
* <my-loading-spinner></my-loading-spinner>
|
|
12
|
+
* </div>
|
|
13
|
+
* <capitalos-cards-app
|
|
14
|
+
* (loaded)="cardsLoaded = true"
|
|
15
|
+
* (error)="onCardsError($event)"
|
|
16
|
+
* ></capitalos-cards-app>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class CardsAppComponent implements AfterViewInit, OnDestroy {
|
|
20
|
+
private readonly authService;
|
|
21
|
+
private readonly config;
|
|
22
|
+
theme?: ThemeColorScheme;
|
|
23
|
+
heightOffsetPx: number;
|
|
24
|
+
enableLogging?: boolean;
|
|
25
|
+
loaded: EventEmitter<void>;
|
|
26
|
+
error: EventEmitter<CapitalOSError>;
|
|
27
|
+
iframeContainer?: ElementRef<HTMLDivElement>;
|
|
28
|
+
private iframeManager?;
|
|
29
|
+
private tokenDataSubscription?;
|
|
30
|
+
ngAfterViewInit(): void;
|
|
31
|
+
ngOnDestroy(): void;
|
|
32
|
+
private initializeIframe;
|
|
33
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CardsAppComponent, never>;
|
|
34
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CardsAppComponent, "capitalos-cards-app", never, { "theme": "theme"; "heightOffsetPx": "heightOffsetPx"; "enableLogging": "enableLogging"; }, { "loaded": "loaded"; "error": "error"; }, never, never, true>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
2
|
+
import { Logger, ThemeColorScheme } from '@capitalos/core';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for CapitalOS SDK
|
|
5
|
+
*/
|
|
6
|
+
export interface CapitalOsConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Function that returns a promise resolving to a one-time token.
|
|
9
|
+
* This is typically a call to your backend that initiates a CapitalOS login.
|
|
10
|
+
*/
|
|
11
|
+
getToken: () => Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to enable logging for debugging purposes.
|
|
14
|
+
*/
|
|
15
|
+
enableLogging?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Optional custom logger for capturing SDK logs.
|
|
18
|
+
*/
|
|
19
|
+
logger?: Logger;
|
|
20
|
+
/**
|
|
21
|
+
* Theme color scheme for the SDK components.
|
|
22
|
+
*/
|
|
23
|
+
theme?: ThemeColorScheme;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Injection token for CapitalOS configuration
|
|
27
|
+
*/
|
|
28
|
+
export declare const CAPITALOS_CONFIG: InjectionToken<CapitalOsConfig>;
|
|
29
|
+
/**
|
|
30
|
+
* Provides the CapitalOS SDK with the specified configuration.
|
|
31
|
+
*
|
|
32
|
+
* Usage in app.config.ts (standalone):
|
|
33
|
+
* ```typescript
|
|
34
|
+
* bootstrapApplication(AppComponent, {
|
|
35
|
+
* providers: [
|
|
36
|
+
* provideCapitalOs({
|
|
37
|
+
* getToken: async () => {
|
|
38
|
+
* const response = await fetch('/api/capitalos/token');
|
|
39
|
+
* const data = await response.json();
|
|
40
|
+
* return data.token;
|
|
41
|
+
* },
|
|
42
|
+
* enableLogging: true,
|
|
43
|
+
* }),
|
|
44
|
+
* ],
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* For NgModule-based apps, use CapitalOsModule.forRoot() instead.
|
|
49
|
+
*/
|
|
50
|
+
export declare function provideCapitalOs(config: CapitalOsConfig): Provider[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OnDestroy } from '@angular/core';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export interface TokenExchangeResult {
|
|
4
|
+
longLivedToken: string;
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Service responsible for exchanging one-time tokens for long-lived JWTs.
|
|
9
|
+
* Creates a hidden iframe to perform the token exchange securely.
|
|
10
|
+
*/
|
|
11
|
+
export declare class TokenExchangeService implements OnDestroy {
|
|
12
|
+
private readonly config;
|
|
13
|
+
private isDestroyed;
|
|
14
|
+
private activeAbortController;
|
|
15
|
+
ngOnDestroy(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Exchanges a one-time token for a long-lived JWT.
|
|
18
|
+
* Delegates to @capitalos/core (browser-only) which handles iframe + Penpal lifecycle.
|
|
19
|
+
*/
|
|
20
|
+
exchange(oneTimeToken: string, options?: {
|
|
21
|
+
enableLogging?: boolean;
|
|
22
|
+
}): Promise<TokenExchangeResult>;
|
|
23
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TokenExchangeService, never>;
|
|
24
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<TokenExchangeService>;
|
|
25
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@capitalos/angular",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"description": "Angular SDK for CapitalOS",
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"@angular/common": ">=14.0.0",
|
|
7
|
+
"@angular/core": ">=14.0.0"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@capitalos/core": "^0.1.0-rc.1",
|
|
11
|
+
"tslib": "^2.3.0"
|
|
12
|
+
},
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"keywords": [
|
|
15
|
+
"capitalos",
|
|
16
|
+
"angular",
|
|
17
|
+
"sdk"
|
|
18
|
+
],
|
|
19
|
+
"author": "CapitalOS",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"module": "fesm2015/capitalos-angular.mjs",
|
|
22
|
+
"es2020": "fesm2020/capitalos-angular.mjs",
|
|
23
|
+
"esm2020": "esm2020/capitalos-angular.mjs",
|
|
24
|
+
"fesm2020": "fesm2020/capitalos-angular.mjs",
|
|
25
|
+
"fesm2015": "fesm2015/capitalos-angular.mjs",
|
|
26
|
+
"typings": "index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
"./package.json": {
|
|
29
|
+
"default": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./index.d.ts",
|
|
33
|
+
"esm2020": "./esm2020/capitalos-angular.mjs",
|
|
34
|
+
"es2020": "./fesm2020/capitalos-angular.mjs",
|
|
35
|
+
"es2015": "./fesm2015/capitalos-angular.mjs",
|
|
36
|
+
"node": "./fesm2015/capitalos-angular.mjs",
|
|
37
|
+
"default": "./fesm2020/capitalos-angular.mjs"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
package/public-api.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { CardsAppComponent } from './lib/cards-app.component';
|
|
2
|
+
export { CapitalOsAuthService } from './lib/capitalos-auth.service';
|
|
3
|
+
export type { AuthState } from './lib/capitalos-auth.service';
|
|
4
|
+
export { provideCapitalOs, CAPITALOS_CONFIG } from './lib/provide-capitalos';
|
|
5
|
+
export type { CapitalOsConfig } from './lib/provide-capitalos';
|
|
6
|
+
export { CapitalOsModule } from './lib/capitalos.module';
|
|
7
|
+
export type { ThemeColorScheme, Logger, TokenData, RawErrorDetails } from '@capitalos/core';
|
|
8
|
+
export { CapitalOSError, ErrorCode } from '@capitalos/core';
|