@23blocks/angular 4.4.0 → 5.0.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
ADDED
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
# @23blocks/angular
|
|
2
|
+
|
|
3
|
+
Angular bindings for the 23blocks SDK - Injectable services with RxJS Observables.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@23blocks/angular)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @23blocks/angular
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
This package provides Angular-specific bindings for the 23blocks SDK:
|
|
17
|
+
|
|
18
|
+
- **Injectable Services** - All blocks exposed as Angular services
|
|
19
|
+
- **RxJS Observables** - All methods return Observables
|
|
20
|
+
- **Token Management** - Automatic token storage and refresh
|
|
21
|
+
- **Dependency Injection** - Full DI support with providers
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Configure providers
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// app.config.ts
|
|
29
|
+
import { ApplicationConfig } from '@angular/core';
|
|
30
|
+
import { provideBlocks23 } from '@23blocks/angular';
|
|
31
|
+
|
|
32
|
+
export const appConfig: ApplicationConfig = {
|
|
33
|
+
providers: [
|
|
34
|
+
provideBlocks23({
|
|
35
|
+
apiKey: 'your-api-key',
|
|
36
|
+
urls: {
|
|
37
|
+
authentication: 'https://auth.yourapp.com',
|
|
38
|
+
// Add other service URLs as needed
|
|
39
|
+
// products: 'https://products.yourapp.com',
|
|
40
|
+
// crm: 'https://crm.yourapp.com',
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Bootstrap with the config
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// main.ts
|
|
51
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
52
|
+
import { appConfig } from './app/app.config';
|
|
53
|
+
import { AppComponent } from './app/app.component';
|
|
54
|
+
|
|
55
|
+
bootstrapApplication(AppComponent, appConfig);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Use the services
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Component, inject } from '@angular/core';
|
|
62
|
+
import { AuthenticationService } from '@23blocks/angular';
|
|
63
|
+
|
|
64
|
+
@Component({
|
|
65
|
+
selector: 'app-login',
|
|
66
|
+
template: `
|
|
67
|
+
<form (ngSubmit)="login()">
|
|
68
|
+
<input [(ngModel)]="email" placeholder="Email" />
|
|
69
|
+
<input [(ngModel)]="password" type="password" placeholder="Password" />
|
|
70
|
+
<button type="submit" [disabled]="loading">
|
|
71
|
+
{{ loading ? 'Signing in...' : 'Sign In' }}
|
|
72
|
+
</button>
|
|
73
|
+
</form>
|
|
74
|
+
`,
|
|
75
|
+
})
|
|
76
|
+
export class LoginComponent {
|
|
77
|
+
private auth = inject(AuthenticationService);
|
|
78
|
+
|
|
79
|
+
email = '';
|
|
80
|
+
password = '';
|
|
81
|
+
loading = false;
|
|
82
|
+
|
|
83
|
+
login() {
|
|
84
|
+
this.loading = true;
|
|
85
|
+
this.auth.signIn({ email: this.email, password: this.password })
|
|
86
|
+
.subscribe({
|
|
87
|
+
next: (response) => {
|
|
88
|
+
console.log('Welcome', response.user.email);
|
|
89
|
+
},
|
|
90
|
+
error: (err) => {
|
|
91
|
+
console.error('Login failed:', err.message);
|
|
92
|
+
},
|
|
93
|
+
complete: () => {
|
|
94
|
+
this.loading = false;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Configuration Options
|
|
102
|
+
|
|
103
|
+
### provideBlocks23 Options
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
provideBlocks23({
|
|
107
|
+
// Required: Your API key
|
|
108
|
+
apiKey: 'your-api-key',
|
|
109
|
+
|
|
110
|
+
// Required: Service URLs (only configure what you need)
|
|
111
|
+
urls: {
|
|
112
|
+
authentication: 'https://auth.yourapp.com',
|
|
113
|
+
products: 'https://products.yourapp.com',
|
|
114
|
+
crm: 'https://crm.yourapp.com',
|
|
115
|
+
// ... other services
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// Optional: Tenant ID for multi-tenant setups
|
|
119
|
+
tenantId: 'tenant-123',
|
|
120
|
+
|
|
121
|
+
// Optional: Authentication mode (default: 'token')
|
|
122
|
+
authMode: 'token', // 'token' | 'cookie'
|
|
123
|
+
|
|
124
|
+
// Optional: Token storage (default: 'localStorage')
|
|
125
|
+
storage: 'localStorage', // 'localStorage' | 'sessionStorage' | 'memory'
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Token Mode (Default)
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
provideBlocks23({
|
|
133
|
+
apiKey: 'your-api-key',
|
|
134
|
+
urls: { authentication: 'https://auth.yourapp.com' },
|
|
135
|
+
authMode: 'token', // default
|
|
136
|
+
storage: 'localStorage', // default
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Cookie Mode (Recommended for Security)
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
provideBlocks23({
|
|
144
|
+
apiKey: 'your-api-key',
|
|
145
|
+
urls: { authentication: 'https://auth.yourapp.com' },
|
|
146
|
+
authMode: 'cookie',
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Multi-Tenant Setup
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
provideBlocks23({
|
|
154
|
+
apiKey: 'your-api-key',
|
|
155
|
+
urls: { authentication: 'https://auth.yourapp.com' },
|
|
156
|
+
tenantId: 'tenant-123',
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### NgModule-based Applications
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// app.module.ts
|
|
164
|
+
import { NgModule } from '@angular/core';
|
|
165
|
+
import { getBlocks23Providers } from '@23blocks/angular';
|
|
166
|
+
|
|
167
|
+
@NgModule({
|
|
168
|
+
providers: [
|
|
169
|
+
...getBlocks23Providers({
|
|
170
|
+
apiKey: 'your-api-key',
|
|
171
|
+
urls: { authentication: 'https://auth.yourapp.com' },
|
|
172
|
+
}),
|
|
173
|
+
],
|
|
174
|
+
})
|
|
175
|
+
export class AppModule {}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Available Services
|
|
179
|
+
|
|
180
|
+
| Service | Description |
|
|
181
|
+
|---------|-------------|
|
|
182
|
+
| `AuthenticationService` | Sign in, sign up, password reset, MFA |
|
|
183
|
+
| `SearchService` | Full-text search, favorites |
|
|
184
|
+
| `ProductsService` | Products, categories, variants, cart |
|
|
185
|
+
| `CrmService` | Contacts, accounts, leads, opportunities |
|
|
186
|
+
| `ContentService` | Posts, comments, categories, tags |
|
|
187
|
+
| `GeolocationService` | Addresses, locations, areas |
|
|
188
|
+
| `ConversationsService` | Messages, groups, notifications |
|
|
189
|
+
| `FilesService` | File uploads, storage |
|
|
190
|
+
| `FormsService` | Form builder, submissions |
|
|
191
|
+
| `AssetsService` | Asset management, tracking |
|
|
192
|
+
| `CampaignsService` | Marketing campaigns, audiences |
|
|
193
|
+
| `CompanyService` | Company settings, departments, teams |
|
|
194
|
+
| `RewardsService` | Rewards, coupons, loyalty, badges |
|
|
195
|
+
| `SalesService` | Orders, payments, subscriptions |
|
|
196
|
+
| `WalletService` | Digital wallet, transactions |
|
|
197
|
+
| `JarvisService` | AI assistant, prompts, workflows |
|
|
198
|
+
| `OnboardingService` | User onboarding flows |
|
|
199
|
+
| `UniversityService` | Courses, lessons, enrollments |
|
|
200
|
+
|
|
201
|
+
## Authentication Examples
|
|
202
|
+
|
|
203
|
+
### Sign In
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { Component, inject } from '@angular/core';
|
|
207
|
+
import { AuthenticationService } from '@23blocks/angular';
|
|
208
|
+
|
|
209
|
+
@Component({ ... })
|
|
210
|
+
export class LoginComponent {
|
|
211
|
+
private auth = inject(AuthenticationService);
|
|
212
|
+
|
|
213
|
+
email = '';
|
|
214
|
+
password = '';
|
|
215
|
+
|
|
216
|
+
signIn() {
|
|
217
|
+
// Required: email, password
|
|
218
|
+
this.auth.signIn({ email: this.email, password: this.password }).subscribe({
|
|
219
|
+
next: ({ user, accessToken, refreshToken, expiresIn }) => {
|
|
220
|
+
console.log('Welcome', user.email);
|
|
221
|
+
// In token mode, tokens are automatically stored
|
|
222
|
+
},
|
|
223
|
+
error: (err) => {
|
|
224
|
+
console.error('Login failed:', err.message);
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Sign Up (Registration)
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
@Component({ ... })
|
|
235
|
+
export class RegisterComponent {
|
|
236
|
+
private auth = inject(AuthenticationService);
|
|
237
|
+
|
|
238
|
+
// Sign up with required fields only
|
|
239
|
+
signUp() {
|
|
240
|
+
this.auth.signUp({
|
|
241
|
+
email: 'new@example.com', // Required
|
|
242
|
+
password: 'password', // Required
|
|
243
|
+
passwordConfirmation: 'password', // Required - must match password
|
|
244
|
+
}).subscribe({
|
|
245
|
+
next: ({ user, accessToken, message }) => {
|
|
246
|
+
// accessToken may be undefined if email confirmation is enabled
|
|
247
|
+
if (accessToken) {
|
|
248
|
+
console.log('Logged in as', user.email);
|
|
249
|
+
} else {
|
|
250
|
+
console.log(message); // "Confirmation email sent"
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Sign up with all optional fields
|
|
257
|
+
signUpFull() {
|
|
258
|
+
this.auth.signUp({
|
|
259
|
+
// Required
|
|
260
|
+
email: 'new@example.com',
|
|
261
|
+
password: 'securePassword123',
|
|
262
|
+
passwordConfirmation: 'securePassword123',
|
|
263
|
+
|
|
264
|
+
// Optional
|
|
265
|
+
name: 'John Doe',
|
|
266
|
+
username: 'johndoe',
|
|
267
|
+
roleId: 'role-uuid',
|
|
268
|
+
confirmSuccessUrl: 'https://yourapp.com/confirmed', // Redirect after email confirmation
|
|
269
|
+
timeZone: 'America/New_York',
|
|
270
|
+
preferredLanguage: 'en',
|
|
271
|
+
payload: { referralCode: 'ABC123' },
|
|
272
|
+
subscription: 'premium-plan',
|
|
273
|
+
}).subscribe();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Sign Out
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
signOut() {
|
|
282
|
+
this.auth.signOut().subscribe({
|
|
283
|
+
next: () => {
|
|
284
|
+
console.log('Signed out');
|
|
285
|
+
// Tokens are automatically cleared
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Email Confirmation
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Confirm email with token from URL
|
|
295
|
+
confirmEmail(token: string) {
|
|
296
|
+
this.auth.confirmEmail(token).subscribe({
|
|
297
|
+
next: (user) => {
|
|
298
|
+
console.log('Email confirmed for', user.email);
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Resend confirmation email
|
|
304
|
+
resendConfirmation(email: string) {
|
|
305
|
+
this.auth.resendConfirmation({
|
|
306
|
+
email,
|
|
307
|
+
confirmSuccessUrl: 'https://yourapp.com/confirmed', // Optional
|
|
308
|
+
}).subscribe({
|
|
309
|
+
next: () => {
|
|
310
|
+
console.log('Confirmation email sent');
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Get Current User
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// Returns user with role, avatar, and profile included
|
|
320
|
+
getCurrentUser() {
|
|
321
|
+
return this.auth.getCurrentUser();
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Check Authentication
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// Token mode: returns true/false
|
|
329
|
+
// Cookie mode: returns null (use validateToken instead)
|
|
330
|
+
isAuthenticated(): boolean | null {
|
|
331
|
+
return this.auth.isAuthenticated();
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Full AuthenticationService Example
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { Component, inject } from '@angular/core';
|
|
339
|
+
import { AuthenticationService } from '@23blocks/angular';
|
|
340
|
+
|
|
341
|
+
@Component({ ... })
|
|
342
|
+
export class AuthComponent {
|
|
343
|
+
private auth = inject(AuthenticationService);
|
|
344
|
+
|
|
345
|
+
// Sign in
|
|
346
|
+
signIn() {
|
|
347
|
+
this.auth.signIn({ email, password }).subscribe({
|
|
348
|
+
next: ({ user, accessToken }) => console.log('Welcome', user.email),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Sign up
|
|
353
|
+
signUp() {
|
|
354
|
+
this.auth.signUp({
|
|
355
|
+
email: 'new@example.com',
|
|
356
|
+
password: 'password',
|
|
357
|
+
passwordConfirmation: 'password',
|
|
358
|
+
}).subscribe();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Sign out
|
|
362
|
+
signOut() {
|
|
363
|
+
this.auth.signOut().subscribe();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Check if authenticated
|
|
367
|
+
isAuthenticated(): boolean | null {
|
|
368
|
+
return this.auth.isAuthenticated();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Get current user
|
|
372
|
+
getCurrentUser() {
|
|
373
|
+
return this.auth.getCurrentUser();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## SearchService Example
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { Component, inject } from '@angular/core';
|
|
382
|
+
import { SearchService } from '@23blocks/angular';
|
|
383
|
+
import { Subject, debounceTime, switchMap } from 'rxjs';
|
|
384
|
+
|
|
385
|
+
@Component({
|
|
386
|
+
selector: 'app-search',
|
|
387
|
+
template: `
|
|
388
|
+
<input (input)="onSearch($event)" placeholder="Search..." />
|
|
389
|
+
<ul>
|
|
390
|
+
<li *ngFor="let result of results">{{ result.title }}</li>
|
|
391
|
+
</ul>
|
|
392
|
+
`,
|
|
393
|
+
})
|
|
394
|
+
export class SearchComponent {
|
|
395
|
+
private search = inject(SearchService);
|
|
396
|
+
private searchSubject = new Subject<string>();
|
|
397
|
+
results: any[] = [];
|
|
398
|
+
|
|
399
|
+
constructor() {
|
|
400
|
+
this.searchSubject.pipe(
|
|
401
|
+
debounceTime(300),
|
|
402
|
+
switchMap((query) => this.search.search({ query, limit: 10 }))
|
|
403
|
+
).subscribe({
|
|
404
|
+
next: (response) => this.results = response.results,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
onSearch(event: Event) {
|
|
409
|
+
const query = (event.target as HTMLInputElement).value;
|
|
410
|
+
this.searchSubject.next(query);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## ProductsService Example
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
import { Component, inject, OnInit } from '@angular/core';
|
|
419
|
+
import { ProductsService } from '@23blocks/angular';
|
|
420
|
+
|
|
421
|
+
@Component({ ... })
|
|
422
|
+
export class ProductsComponent implements OnInit {
|
|
423
|
+
private products = inject(ProductsService);
|
|
424
|
+
productList$ = this.products.list({ limit: 20 });
|
|
425
|
+
|
|
426
|
+
addToCart(productId: string) {
|
|
427
|
+
this.products.addToCart({ productId, quantity: 1 }).subscribe();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## RxJS Patterns
|
|
433
|
+
|
|
434
|
+
### Combining Multiple Services
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { forkJoin } from 'rxjs';
|
|
438
|
+
|
|
439
|
+
// Fetch user and favorites in parallel
|
|
440
|
+
forkJoin({
|
|
441
|
+
user: this.auth.getCurrentUser(),
|
|
442
|
+
favorites: this.search.listFavorites({ limit: 10 }),
|
|
443
|
+
}).subscribe({
|
|
444
|
+
next: ({ user, favorites }) => {
|
|
445
|
+
console.log(user, favorites);
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### Caching with shareReplay
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
import { shareReplay } from 'rxjs';
|
|
454
|
+
|
|
455
|
+
// Cache the current user
|
|
456
|
+
currentUser$ = this.auth.getCurrentUser().pipe(
|
|
457
|
+
shareReplay(1)
|
|
458
|
+
);
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Error Handling
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
import { isBlockErrorException, ErrorCodes } from '@23blocks/contracts';
|
|
465
|
+
|
|
466
|
+
this.auth.signIn({ email, password }).subscribe({
|
|
467
|
+
error: (err) => {
|
|
468
|
+
if (isBlockErrorException(err)) {
|
|
469
|
+
switch (err.code) {
|
|
470
|
+
case ErrorCodes.INVALID_CREDENTIALS:
|
|
471
|
+
this.error = 'Invalid email or password';
|
|
472
|
+
break;
|
|
473
|
+
case ErrorCodes.UNAUTHORIZED:
|
|
474
|
+
this.error = 'Session expired';
|
|
475
|
+
break;
|
|
476
|
+
case ErrorCodes.VALIDATION_ERROR:
|
|
477
|
+
this.error = err.message;
|
|
478
|
+
break;
|
|
479
|
+
default:
|
|
480
|
+
this.error = err.message;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Advanced Setup (Custom Transport)
|
|
488
|
+
|
|
489
|
+
For advanced use cases requiring custom transport configuration:
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
import { ApplicationConfig } from '@angular/core';
|
|
493
|
+
import { provide23Blocks } from '@23blocks/angular';
|
|
494
|
+
import { createHttpTransport } from '@23blocks/transport-http';
|
|
495
|
+
|
|
496
|
+
const transport = createHttpTransport({
|
|
497
|
+
baseUrl: 'https://auth.yourapp.com',
|
|
498
|
+
headers: () => {
|
|
499
|
+
const token = localStorage.getItem('access_token');
|
|
500
|
+
return {
|
|
501
|
+
'api-key': 'your-api-key',
|
|
502
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
503
|
+
};
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
export const appConfig: ApplicationConfig = {
|
|
508
|
+
providers: [
|
|
509
|
+
provide23Blocks({
|
|
510
|
+
transport,
|
|
511
|
+
authentication: { apiKey: 'your-api-key' },
|
|
512
|
+
search: { apiKey: 'your-api-key' },
|
|
513
|
+
products: { apiKey: 'your-api-key' },
|
|
514
|
+
}),
|
|
515
|
+
],
|
|
516
|
+
};
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## Testing
|
|
520
|
+
|
|
521
|
+
Mock services in your tests:
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
import { TestBed } from '@angular/core/testing';
|
|
525
|
+
import { AuthenticationService } from '@23blocks/angular';
|
|
526
|
+
import { of } from 'rxjs';
|
|
527
|
+
|
|
528
|
+
describe('LoginComponent', () => {
|
|
529
|
+
const mockAuth = {
|
|
530
|
+
signIn: jest.fn().mockReturnValue(of({
|
|
531
|
+
user: { email: 'test@test.com' },
|
|
532
|
+
accessToken: 'token',
|
|
533
|
+
})),
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
beforeEach(() => {
|
|
537
|
+
TestBed.configureTestingModule({
|
|
538
|
+
providers: [
|
|
539
|
+
{ provide: AuthenticationService, useValue: mockAuth },
|
|
540
|
+
],
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## Environment Variables
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
// environment.ts
|
|
550
|
+
export const environment = {
|
|
551
|
+
production: false,
|
|
552
|
+
apiKey: 'your-api-key',
|
|
553
|
+
urls: {
|
|
554
|
+
authentication: 'https://auth.yourapp.com',
|
|
555
|
+
products: 'https://products.yourapp.com',
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// app.config.ts
|
|
560
|
+
import { environment } from './environments/environment';
|
|
561
|
+
|
|
562
|
+
export const appConfig: ApplicationConfig = {
|
|
563
|
+
providers: [
|
|
564
|
+
provideBlocks23({
|
|
565
|
+
apiKey: environment.apiKey,
|
|
566
|
+
urls: environment.urls,
|
|
567
|
+
}),
|
|
568
|
+
],
|
|
569
|
+
};
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
## TypeScript
|
|
573
|
+
|
|
574
|
+
All services are fully typed:
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
import type { User, SignInResponse, SignUpResponse } from '@23blocks/block-authentication';
|
|
578
|
+
|
|
579
|
+
// SignInResponse
|
|
580
|
+
interface SignInResponse {
|
|
581
|
+
user: User;
|
|
582
|
+
accessToken: string;
|
|
583
|
+
refreshToken?: string;
|
|
584
|
+
tokenType: string;
|
|
585
|
+
expiresIn?: number;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// SignUpResponse - accessToken is optional (email confirmation)
|
|
589
|
+
interface SignUpResponse {
|
|
590
|
+
user: User;
|
|
591
|
+
accessToken?: string; // undefined if email confirmation enabled
|
|
592
|
+
message?: string;
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
## Related Packages
|
|
597
|
+
|
|
598
|
+
- [`@23blocks/sdk`](https://www.npmjs.com/package/@23blocks/sdk) - Full SDK package
|
|
599
|
+
- [`@23blocks/react`](https://www.npmjs.com/package/@23blocks/react) - React integration
|
|
600
|
+
|
|
601
|
+
## License
|
|
602
|
+
|
|
603
|
+
MIT - Copyright (c) 2024 23blocks
|
package/dist/index.esm.js
CHANGED
|
@@ -112,9 +112,9 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
112
112
|
*/ const PROVIDER_CONFIG = new InjectionToken('23blocks.provider-config');
|
|
113
113
|
/** @deprecated Use PROVIDER_CONFIG instead */ const SIMPLE_CONFIG = PROVIDER_CONFIG;
|
|
114
114
|
/**
|
|
115
|
-
* Generate storage key scoped to
|
|
116
|
-
*/ function getStorageKey(type,
|
|
117
|
-
const scope = tenantId ? `${
|
|
115
|
+
* Generate storage key scoped to API key and tenant
|
|
116
|
+
*/ function getStorageKey(type, apiKey, tenantId) {
|
|
117
|
+
const scope = tenantId ? `${apiKey}_${tenantId}` : apiKey;
|
|
118
118
|
return `23blocks_${scope}_${type}_token`;
|
|
119
119
|
}
|
|
120
120
|
/**
|
|
@@ -136,10 +136,10 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
136
136
|
};
|
|
137
137
|
/**
|
|
138
138
|
* Create a token manager with scoped storage keys and cross-tab sync
|
|
139
|
-
*/ function createTokenManager(
|
|
139
|
+
*/ function createTokenManager(apiKey, storageType, tenantId) {
|
|
140
140
|
const isBrowser = typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';
|
|
141
|
-
const accessTokenKey = getStorageKey('access',
|
|
142
|
-
const refreshTokenKey = getStorageKey('refresh',
|
|
141
|
+
const accessTokenKey = getStorageKey('access', apiKey, tenantId);
|
|
142
|
+
const refreshTokenKey = getStorageKey('refresh', apiKey, tenantId);
|
|
143
143
|
let storage;
|
|
144
144
|
if (!isBrowser) {
|
|
145
145
|
storage = new MemoryStorage();
|
|
@@ -217,7 +217,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
217
217
|
credentials: config.authMode === 'cookie' ? 'include' : undefined,
|
|
218
218
|
headers: ()=>{
|
|
219
219
|
const headers = _({}, config.headers, {
|
|
220
|
-
'
|
|
220
|
+
'api-key': config.apiKey
|
|
221
221
|
});
|
|
222
222
|
if (config.tenantId) {
|
|
223
223
|
headers['tenant-id'] = config.tenantId;
|
|
@@ -251,7 +251,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
251
251
|
* export const appConfig: ApplicationConfig = {
|
|
252
252
|
* providers: [
|
|
253
253
|
* provideBlocks23({
|
|
254
|
-
*
|
|
254
|
+
* apiKey: 'your-api-key',
|
|
255
255
|
* urls: {
|
|
256
256
|
* authentication: 'https://gateway.23blocks.com',
|
|
257
257
|
* crm: 'https://crm.23blocks.com',
|
|
@@ -267,7 +267,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
267
267
|
* export const appConfig: ApplicationConfig = {
|
|
268
268
|
* providers: [
|
|
269
269
|
* provideBlocks23({
|
|
270
|
-
*
|
|
270
|
+
* apiKey: 'your-api-key',
|
|
271
271
|
* authMode: 'cookie',
|
|
272
272
|
* urls: {
|
|
273
273
|
* authentication: 'https://gateway.23blocks.com',
|
|
@@ -280,7 +280,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
280
280
|
*/ function provideBlocks23(config) {
|
|
281
281
|
// Block config for all services
|
|
282
282
|
const blockConfig = {
|
|
283
|
-
|
|
283
|
+
apiKey: config.apiKey,
|
|
284
284
|
tenantId: config.tenantId
|
|
285
285
|
};
|
|
286
286
|
// Helper to create transport provider for a service URL
|
|
@@ -306,7 +306,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
306
306
|
useFactory: ()=>{
|
|
307
307
|
var _config_storage;
|
|
308
308
|
const storage = (_config_storage = config.storage) != null ? _config_storage : 'localStorage';
|
|
309
|
-
return createTokenManager(config.
|
|
309
|
+
return createTokenManager(config.apiKey, storage, config.tenantId);
|
|
310
310
|
}
|
|
311
311
|
},
|
|
312
312
|
// Per-service transport factories (null if URL not configured)
|
|
@@ -440,7 +440,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
440
440
|
* @NgModule({
|
|
441
441
|
* providers: [
|
|
442
442
|
* ...getBlocks23Providers({
|
|
443
|
-
*
|
|
443
|
+
* apiKey: 'your-api-key',
|
|
444
444
|
* urls: {
|
|
445
445
|
* authentication: 'https://gateway.23blocks.com',
|
|
446
446
|
* crm: 'https://crm.23blocks.com',
|
|
@@ -453,7 +453,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
453
453
|
*/ function getBlocks23Providers(config) {
|
|
454
454
|
// Block config for all services
|
|
455
455
|
const blockConfig = {
|
|
456
|
-
|
|
456
|
+
apiKey: config.apiKey,
|
|
457
457
|
tenantId: config.tenantId
|
|
458
458
|
};
|
|
459
459
|
// Helper to create transport provider for a service URL
|
|
@@ -479,7 +479,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
479
479
|
useFactory: ()=>{
|
|
480
480
|
var _config_storage;
|
|
481
481
|
const storage = (_config_storage = config.storage) != null ? _config_storage : 'localStorage';
|
|
482
|
-
return createTokenManager(config.
|
|
482
|
+
return createTokenManager(config.apiKey, storage, config.tenantId);
|
|
483
483
|
}
|
|
484
484
|
},
|
|
485
485
|
// Per-service transport factories (null if URL not configured)
|
|
@@ -616,10 +616,10 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
616
616
|
* providers: [
|
|
617
617
|
* provide23Blocks({
|
|
618
618
|
* transport,
|
|
619
|
-
* authentication: {
|
|
620
|
-
* search: {
|
|
621
|
-
* products: {
|
|
622
|
-
* crm: {
|
|
619
|
+
* authentication: { apiKey: 'my-api-key' },
|
|
620
|
+
* search: { apiKey: 'my-api-key' },
|
|
621
|
+
* products: { apiKey: 'my-api-key' },
|
|
622
|
+
* crm: { apiKey: 'my-api-key' },
|
|
623
623
|
* }),
|
|
624
624
|
* ],
|
|
625
625
|
* };
|
|
@@ -754,7 +754,7 @@ import { createUniversityBlock } from '@23blocks/block-university';
|
|
|
754
754
|
* providers: [
|
|
755
755
|
* ...get23BlocksProviders({
|
|
756
756
|
* transport,
|
|
757
|
-
* authentication: {
|
|
757
|
+
* authentication: { apiKey: 'my-api-key' },
|
|
758
758
|
* }),
|
|
759
759
|
* ],
|
|
760
760
|
* })
|
|
@@ -63,10 +63,10 @@ export interface Provide23BlocksConfig {
|
|
|
63
63
|
* providers: [
|
|
64
64
|
* provide23Blocks({
|
|
65
65
|
* transport,
|
|
66
|
-
* authentication: {
|
|
67
|
-
* search: {
|
|
68
|
-
* products: {
|
|
69
|
-
* crm: {
|
|
66
|
+
* authentication: { apiKey: 'my-api-key' },
|
|
67
|
+
* search: { apiKey: 'my-api-key' },
|
|
68
|
+
* products: { apiKey: 'my-api-key' },
|
|
69
|
+
* crm: { apiKey: 'my-api-key' },
|
|
70
70
|
* }),
|
|
71
71
|
* ],
|
|
72
72
|
* };
|
|
@@ -86,7 +86,7 @@ export declare function provide23Blocks(config: Provide23BlocksConfig): Environm
|
|
|
86
86
|
* providers: [
|
|
87
87
|
* ...get23BlocksProviders({
|
|
88
88
|
* transport,
|
|
89
|
-
* authentication: {
|
|
89
|
+
* authentication: { apiKey: 'my-api-key' },
|
|
90
90
|
* }),
|
|
91
91
|
* ],
|
|
92
92
|
* })
|
|
@@ -61,7 +61,7 @@ export interface ProviderConfig {
|
|
|
61
61
|
* @example
|
|
62
62
|
* ```typescript
|
|
63
63
|
* provideBlocks23({
|
|
64
|
-
*
|
|
64
|
+
* apiKey: 'your-api-key',
|
|
65
65
|
* urls: {
|
|
66
66
|
* authentication: 'https://gateway.23blocks.com',
|
|
67
67
|
* crm: 'https://crm.23blocks.com',
|
|
@@ -72,9 +72,9 @@ export interface ProviderConfig {
|
|
|
72
72
|
*/
|
|
73
73
|
urls: ServiceUrls;
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
75
|
+
* API Key for authenticating with 23blocks services
|
|
76
76
|
*/
|
|
77
|
-
|
|
77
|
+
apiKey: string;
|
|
78
78
|
/**
|
|
79
79
|
* Tenant ID (optional, for multi-tenant setups)
|
|
80
80
|
*/
|
|
@@ -142,7 +142,7 @@ export interface TokenManagerService {
|
|
|
142
142
|
* export const appConfig: ApplicationConfig = {
|
|
143
143
|
* providers: [
|
|
144
144
|
* provideBlocks23({
|
|
145
|
-
*
|
|
145
|
+
* apiKey: 'your-api-key',
|
|
146
146
|
* urls: {
|
|
147
147
|
* authentication: 'https://gateway.23blocks.com',
|
|
148
148
|
* crm: 'https://crm.23blocks.com',
|
|
@@ -158,7 +158,7 @@ export interface TokenManagerService {
|
|
|
158
158
|
* export const appConfig: ApplicationConfig = {
|
|
159
159
|
* providers: [
|
|
160
160
|
* provideBlocks23({
|
|
161
|
-
*
|
|
161
|
+
* apiKey: 'your-api-key',
|
|
162
162
|
* authMode: 'cookie',
|
|
163
163
|
* urls: {
|
|
164
164
|
* authentication: 'https://gateway.23blocks.com',
|
|
@@ -185,7 +185,7 @@ export declare function provideBlocks23(config: ProviderConfig): EnvironmentProv
|
|
|
185
185
|
* @NgModule({
|
|
186
186
|
* providers: [
|
|
187
187
|
* ...getBlocks23Providers({
|
|
188
|
-
*
|
|
188
|
+
* apiKey: 'your-api-key',
|
|
189
189
|
* urls: {
|
|
190
190
|
* authentication: 'https://gateway.23blocks.com',
|
|
191
191
|
* crm: 'https://crm.23blocks.com',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simple-providers.d.ts","sourceRoot":"","sources":["../../../src/lib/simple-providers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AA8CpE;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,gBAAgB,GAAG,QAAQ,CAAC;AAEvE;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,iCAAiC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,EAAE,WAAW,CAAC;IAElB;;OAEG;IACH,
|
|
1
|
+
{"version":3,"file":"simple-providers.d.ts","sourceRoot":"","sources":["../../../src/lib/simple-providers.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED