@23blocks/angular 6.5.15 → 6.5.17

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.
Files changed (88) hide show
  1. package/dist/README.md +792 -0
  2. package/dist/{index.esm.js → fesm2022/23blocks-angular.mjs} +1620 -1286
  3. package/dist/fesm2022/23blocks-angular.mjs.map +1 -0
  4. package/dist/index.d.ts +3874 -1
  5. package/dist/index.d.ts.map +1 -0
  6. package/package.json +18 -19
  7. package/dist/src/index.d.ts +0 -2
  8. package/dist/src/index.d.ts.map +0 -1
  9. package/dist/src/lib/assets/assets.service.d.ts +0 -123
  10. package/dist/src/lib/assets/assets.service.d.ts.map +0 -1
  11. package/dist/src/lib/assets/index.d.ts +0 -2
  12. package/dist/src/lib/assets/index.d.ts.map +0 -1
  13. package/dist/src/lib/authentication/authentication.service.d.ts +0 -684
  14. package/dist/src/lib/authentication/authentication.service.d.ts.map +0 -1
  15. package/dist/src/lib/authentication/index.d.ts +0 -2
  16. package/dist/src/lib/authentication/index.d.ts.map +0 -1
  17. package/dist/src/lib/campaigns/campaigns.service.d.ts +0 -111
  18. package/dist/src/lib/campaigns/campaigns.service.d.ts.map +0 -1
  19. package/dist/src/lib/campaigns/index.d.ts +0 -2
  20. package/dist/src/lib/campaigns/index.d.ts.map +0 -1
  21. package/dist/src/lib/company/company.service.d.ts +0 -117
  22. package/dist/src/lib/company/company.service.d.ts.map +0 -1
  23. package/dist/src/lib/company/index.d.ts +0 -2
  24. package/dist/src/lib/company/index.d.ts.map +0 -1
  25. package/dist/src/lib/content/content.service.d.ts +0 -177
  26. package/dist/src/lib/content/content.service.d.ts.map +0 -1
  27. package/dist/src/lib/content/index.d.ts +0 -2
  28. package/dist/src/lib/content/index.d.ts.map +0 -1
  29. package/dist/src/lib/conversations/conversations.service.d.ts +0 -130
  30. package/dist/src/lib/conversations/conversations.service.d.ts.map +0 -1
  31. package/dist/src/lib/conversations/index.d.ts +0 -2
  32. package/dist/src/lib/conversations/index.d.ts.map +0 -1
  33. package/dist/src/lib/crm/crm.service.d.ts +0 -182
  34. package/dist/src/lib/crm/crm.service.d.ts.map +0 -1
  35. package/dist/src/lib/crm/index.d.ts +0 -2
  36. package/dist/src/lib/crm/index.d.ts.map +0 -1
  37. package/dist/src/lib/files/files.service.d.ts +0 -326
  38. package/dist/src/lib/files/files.service.d.ts.map +0 -1
  39. package/dist/src/lib/files/index.d.ts +0 -2
  40. package/dist/src/lib/files/index.d.ts.map +0 -1
  41. package/dist/src/lib/forms/forms.service.d.ts +0 -132
  42. package/dist/src/lib/forms/forms.service.d.ts.map +0 -1
  43. package/dist/src/lib/forms/index.d.ts +0 -2
  44. package/dist/src/lib/forms/index.d.ts.map +0 -1
  45. package/dist/src/lib/geolocation/geolocation.service.d.ts +0 -317
  46. package/dist/src/lib/geolocation/geolocation.service.d.ts.map +0 -1
  47. package/dist/src/lib/geolocation/index.d.ts +0 -2
  48. package/dist/src/lib/geolocation/index.d.ts.map +0 -1
  49. package/dist/src/lib/index.d.ts +0 -22
  50. package/dist/src/lib/index.d.ts.map +0 -1
  51. package/dist/src/lib/jarvis/index.d.ts +0 -2
  52. package/dist/src/lib/jarvis/index.d.ts.map +0 -1
  53. package/dist/src/lib/jarvis/jarvis.service.d.ts +0 -181
  54. package/dist/src/lib/jarvis/jarvis.service.d.ts.map +0 -1
  55. package/dist/src/lib/onboarding/index.d.ts +0 -2
  56. package/dist/src/lib/onboarding/index.d.ts.map +0 -1
  57. package/dist/src/lib/onboarding/onboarding.service.d.ts +0 -93
  58. package/dist/src/lib/onboarding/onboarding.service.d.ts.map +0 -1
  59. package/dist/src/lib/products/index.d.ts +0 -2
  60. package/dist/src/lib/products/index.d.ts.map +0 -1
  61. package/dist/src/lib/products/products.service.d.ts +0 -154
  62. package/dist/src/lib/products/products.service.d.ts.map +0 -1
  63. package/dist/src/lib/providers.d.ts +0 -97
  64. package/dist/src/lib/providers.d.ts.map +0 -1
  65. package/dist/src/lib/rewards/index.d.ts +0 -2
  66. package/dist/src/lib/rewards/index.d.ts.map +0 -1
  67. package/dist/src/lib/rewards/rewards.service.d.ts +0 -172
  68. package/dist/src/lib/rewards/rewards.service.d.ts.map +0 -1
  69. package/dist/src/lib/sales/index.d.ts +0 -2
  70. package/dist/src/lib/sales/index.d.ts.map +0 -1
  71. package/dist/src/lib/sales/sales.service.d.ts +0 -147
  72. package/dist/src/lib/sales/sales.service.d.ts.map +0 -1
  73. package/dist/src/lib/search/index.d.ts +0 -2
  74. package/dist/src/lib/search/index.d.ts.map +0 -1
  75. package/dist/src/lib/search/search.service.d.ts +0 -106
  76. package/dist/src/lib/search/search.service.d.ts.map +0 -1
  77. package/dist/src/lib/simple-providers.d.ts +0 -203
  78. package/dist/src/lib/simple-providers.d.ts.map +0 -1
  79. package/dist/src/lib/tokens.d.ts +0 -134
  80. package/dist/src/lib/tokens.d.ts.map +0 -1
  81. package/dist/src/lib/university/index.d.ts +0 -2
  82. package/dist/src/lib/university/index.d.ts.map +0 -1
  83. package/dist/src/lib/university/university.service.d.ts +0 -252
  84. package/dist/src/lib/university/university.service.d.ts.map +0 -1
  85. package/dist/src/lib/wallet/index.d.ts +0 -2
  86. package/dist/src/lib/wallet/index.d.ts.map +0 -1
  87. package/dist/src/lib/wallet/wallet.service.d.ts +0 -71
  88. package/dist/src/lib/wallet/wallet.service.d.ts.map +0 -1
package/dist/README.md ADDED
@@ -0,0 +1,792 @@
1
+ # @23blocks/angular
2
+
3
+ Angular bindings for the 23blocks SDK - Injectable services with RxJS Observables.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@23blocks/angular.svg)](https://www.npmjs.com/package/@23blocks/angular)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
+ // Optional: Enable debug logging
128
+ debug: !environment.production,
129
+ })
130
+ ```
131
+
132
+ ### Token Mode (Default)
133
+
134
+ ```typescript
135
+ provideBlocks23({
136
+ apiKey: 'your-api-key',
137
+ urls: { authentication: 'https://auth.yourapp.com' },
138
+ authMode: 'token', // default
139
+ storage: 'localStorage', // default
140
+ })
141
+ ```
142
+
143
+ ### Cookie Mode (Recommended for Security)
144
+
145
+ ```typescript
146
+ provideBlocks23({
147
+ apiKey: 'your-api-key',
148
+ urls: { authentication: 'https://auth.yourapp.com' },
149
+ authMode: 'cookie',
150
+ })
151
+ ```
152
+
153
+ ### Multi-Tenant Setup
154
+
155
+ ```typescript
156
+ provideBlocks23({
157
+ apiKey: 'your-api-key',
158
+ urls: { authentication: 'https://auth.yourapp.com' },
159
+ tenantId: 'tenant-123',
160
+ })
161
+ ```
162
+
163
+ ### NgModule-based Applications
164
+
165
+ ```typescript
166
+ // app.module.ts
167
+ import { NgModule } from '@angular/core';
168
+ import { getBlocks23Providers } from '@23blocks/angular';
169
+
170
+ @NgModule({
171
+ providers: [
172
+ ...getBlocks23Providers({
173
+ apiKey: 'your-api-key',
174
+ urls: { authentication: 'https://auth.yourapp.com' },
175
+ }),
176
+ ],
177
+ })
178
+ export class AppModule {}
179
+ ```
180
+
181
+ ## Available Services
182
+
183
+ | Service | Description |
184
+ |---------|-------------|
185
+ | `AuthenticationService` | Sign in, sign up, password reset, MFA |
186
+ | `SearchService` | Full-text search, favorites |
187
+ | `ProductsService` | Products, categories, variants, cart |
188
+ | `CrmService` | Contacts, accounts, leads, opportunities |
189
+ | `ContentService` | Posts, comments, categories, tags |
190
+ | `GeolocationService` | Addresses, locations, areas |
191
+ | `ConversationsService` | Messages, groups, notifications |
192
+ | `FilesService` | File uploads, storage |
193
+ | `FormsService` | Form builder, submissions |
194
+ | `AssetsService` | Asset management, tracking |
195
+ | `CampaignsService` | Marketing campaigns, audiences |
196
+ | `CompanyService` | Company settings, departments, teams |
197
+ | `RewardsService` | Rewards, coupons, loyalty, badges |
198
+ | `SalesService` | Orders, payments, subscriptions |
199
+ | `WalletService` | Digital wallet, transactions |
200
+ | `JarvisService` | AI assistant, prompts, workflows |
201
+ | `OnboardingService` | User onboarding flows |
202
+ | `UniversityService` | Courses, lessons, enrollments |
203
+
204
+ ## Authentication Examples
205
+
206
+ ### Sign In
207
+
208
+ ```typescript
209
+ import { Component, inject } from '@angular/core';
210
+ import { AuthenticationService } from '@23blocks/angular';
211
+
212
+ @Component({ ... })
213
+ export class LoginComponent {
214
+ private auth = inject(AuthenticationService);
215
+
216
+ email = '';
217
+ password = '';
218
+
219
+ signIn() {
220
+ // Required: email, password
221
+ this.auth.signIn({ email: this.email, password: this.password }).subscribe({
222
+ next: ({ user, accessToken, refreshToken, expiresIn }) => {
223
+ console.log('Welcome', user.email);
224
+ // In token mode, tokens are automatically stored
225
+ },
226
+ error: (err) => {
227
+ console.error('Login failed:', err.message);
228
+ },
229
+ });
230
+ }
231
+ }
232
+ ```
233
+
234
+ ### Sign Up (Registration)
235
+
236
+ ```typescript
237
+ @Component({ ... })
238
+ export class RegisterComponent {
239
+ private auth = inject(AuthenticationService);
240
+
241
+ // Sign up with required fields only
242
+ signUp() {
243
+ this.auth.signUp({
244
+ email: 'new@example.com', // Required
245
+ password: 'password', // Required
246
+ passwordConfirmation: 'password', // Required - must match password
247
+ }).subscribe({
248
+ next: ({ user, accessToken, message }) => {
249
+ // accessToken may be undefined if email confirmation is enabled
250
+ if (accessToken) {
251
+ console.log('Logged in as', user.email);
252
+ } else {
253
+ console.log(message); // "Confirmation email sent"
254
+ }
255
+ },
256
+ });
257
+ }
258
+
259
+ // Sign up with all optional fields
260
+ signUpFull() {
261
+ this.auth.signUp({
262
+ // Required
263
+ email: 'new@example.com',
264
+ password: 'securePassword123',
265
+ passwordConfirmation: 'securePassword123',
266
+
267
+ // Optional
268
+ name: 'John Doe',
269
+ username: 'johndoe',
270
+ roleId: 'role-uuid',
271
+ confirmSuccessUrl: 'https://yourapp.com/confirmed', // Redirect after email confirmation
272
+ timeZone: 'America/New_York',
273
+ preferredLanguage: 'en',
274
+ payload: { referralCode: 'ABC123' },
275
+ subscription: 'premium-plan',
276
+ }).subscribe();
277
+ }
278
+ }
279
+ ```
280
+
281
+ ### Sign Out
282
+
283
+ ```typescript
284
+ signOut() {
285
+ this.auth.signOut().subscribe({
286
+ next: () => {
287
+ console.log('Signed out');
288
+ // Tokens are automatically cleared
289
+ },
290
+ });
291
+ }
292
+ ```
293
+
294
+ ### Email Confirmation
295
+
296
+ ```typescript
297
+ // Confirm email with token from URL
298
+ confirmEmail(token: string) {
299
+ this.auth.confirmEmail(token).subscribe({
300
+ next: (user) => {
301
+ console.log('Email confirmed for', user.email);
302
+ },
303
+ });
304
+ }
305
+
306
+ // Resend confirmation email
307
+ resendConfirmation(email: string) {
308
+ this.auth.resendConfirmation({
309
+ email,
310
+ confirmSuccessUrl: 'https://yourapp.com/confirmed', // Optional
311
+ }).subscribe({
312
+ next: () => {
313
+ console.log('Confirmation email sent');
314
+ },
315
+ });
316
+ }
317
+ ```
318
+
319
+ ### Get Current User
320
+
321
+ ```typescript
322
+ // Returns user with role, avatar, and profile included
323
+ getCurrentUser() {
324
+ return this.auth.getCurrentUser();
325
+ }
326
+ ```
327
+
328
+ ### Check Authentication
329
+
330
+ ```typescript
331
+ // Token mode: returns true/false
332
+ // Cookie mode: returns null (use validateToken instead)
333
+ isAuthenticated(): boolean | null {
334
+ return this.auth.isAuthenticated();
335
+ }
336
+ ```
337
+
338
+ ### Full AuthenticationService Example
339
+
340
+ ```typescript
341
+ import { Component, inject } from '@angular/core';
342
+ import { AuthenticationService } from '@23blocks/angular';
343
+
344
+ @Component({ ... })
345
+ export class AuthComponent {
346
+ private auth = inject(AuthenticationService);
347
+
348
+ // Sign in
349
+ signIn() {
350
+ this.auth.signIn({ email, password }).subscribe({
351
+ next: ({ user, accessToken }) => console.log('Welcome', user.email),
352
+ });
353
+ }
354
+
355
+ // Sign up
356
+ signUp() {
357
+ this.auth.signUp({
358
+ email: 'new@example.com',
359
+ password: 'password',
360
+ passwordConfirmation: 'password',
361
+ }).subscribe();
362
+ }
363
+
364
+ // Sign out
365
+ signOut() {
366
+ this.auth.signOut().subscribe();
367
+ }
368
+
369
+ // Check if authenticated
370
+ isAuthenticated(): boolean | null {
371
+ return this.auth.isAuthenticated();
372
+ }
373
+
374
+ // Get current user
375
+ getCurrentUser() {
376
+ return this.auth.getCurrentUser();
377
+ }
378
+ }
379
+ ```
380
+
381
+ ## SearchService Example
382
+
383
+ ```typescript
384
+ import { Component, inject } from '@angular/core';
385
+ import { SearchService } from '@23blocks/angular';
386
+ import { Subject, debounceTime, switchMap } from 'rxjs';
387
+
388
+ @Component({
389
+ selector: 'app-search',
390
+ template: `
391
+ <input (input)="onSearch($event)" placeholder="Search..." />
392
+ <ul>
393
+ <li *ngFor="let result of results">{{ result.title }}</li>
394
+ </ul>
395
+ `,
396
+ })
397
+ export class SearchComponent {
398
+ private search = inject(SearchService);
399
+ private searchSubject = new Subject<string>();
400
+ results: any[] = [];
401
+
402
+ constructor() {
403
+ this.searchSubject.pipe(
404
+ debounceTime(300),
405
+ switchMap((query) => this.search.search({ query, limit: 10 }))
406
+ ).subscribe({
407
+ next: (response) => this.results = response.results,
408
+ });
409
+ }
410
+
411
+ onSearch(event: Event) {
412
+ const query = (event.target as HTMLInputElement).value;
413
+ this.searchSubject.next(query);
414
+ }
415
+ }
416
+ ```
417
+
418
+ ## ProductsService Example
419
+
420
+ ```typescript
421
+ import { Component, inject, OnInit } from '@angular/core';
422
+ import { ProductsService } from '@23blocks/angular';
423
+
424
+ @Component({ ... })
425
+ export class ProductsComponent implements OnInit {
426
+ private products = inject(ProductsService);
427
+ productList$ = this.products.list({ limit: 20 });
428
+
429
+ addToCart(productId: string) {
430
+ this.products.addToCart({ productId, quantity: 1 }).subscribe();
431
+ }
432
+ }
433
+ ```
434
+
435
+ ## ContentService Example
436
+
437
+ The ContentService provides access to posts, comments, categories, tags, and **series** (collections of posts).
438
+
439
+ ### Basic Content Operations
440
+
441
+ ```typescript
442
+ import { Component, inject } from '@angular/core';
443
+ import { ContentService } from '@23blocks/angular';
444
+
445
+ @Component({ ... })
446
+ export class BlogComponent {
447
+ private content = inject(ContentService);
448
+
449
+ // List posts with pagination
450
+ posts$ = this.content.listPosts({ page: 1, perPage: 10 });
451
+
452
+ // Get a single post
453
+ loadPost(uniqueId: string) {
454
+ this.content.getPost(uniqueId).subscribe({
455
+ next: (post) => console.log('Post:', post.title),
456
+ });
457
+ }
458
+
459
+ // Create a new post
460
+ createPost() {
461
+ this.content.createPost({
462
+ title: 'My New Post',
463
+ body: 'Post content here...',
464
+ status: 'published',
465
+ }).subscribe();
466
+ }
467
+ }
468
+ ```
469
+
470
+ ### Series Operations
471
+
472
+ Series allow you to group posts into ordered collections (e.g., tutorials, courses, article series).
473
+
474
+ ```typescript
475
+ import { Component, inject } from '@angular/core';
476
+ import { ContentService } from '@23blocks/angular';
477
+
478
+ @Component({ ... })
479
+ export class SeriesComponent {
480
+ private content = inject(ContentService);
481
+
482
+ // List all series
483
+ series$ = this.content.listSeries({ page: 1, perPage: 10 });
484
+
485
+ // Query series with filters
486
+ loadPublicSeries() {
487
+ this.content.querySeries({
488
+ visibility: 'public',
489
+ completionStatus: 'ongoing',
490
+ page: 1,
491
+ perPage: 20,
492
+ }).subscribe({
493
+ next: (result) => console.log('Series:', result.data),
494
+ });
495
+ }
496
+
497
+ // Get a single series
498
+ loadSeries(uniqueId: string) {
499
+ this.content.getSeries(uniqueId).subscribe({
500
+ next: (series) => console.log('Series:', series.title),
501
+ });
502
+ }
503
+
504
+ // Create a new series
505
+ createSeries() {
506
+ this.content.createSeries({
507
+ title: 'TypeScript Fundamentals',
508
+ description: 'A complete guide to TypeScript',
509
+ visibility: 'public',
510
+ completionStatus: 'ongoing',
511
+ }).subscribe({
512
+ next: (series) => console.log('Created:', series.uniqueId),
513
+ });
514
+ }
515
+
516
+ // Update a series
517
+ updateSeries(uniqueId: string) {
518
+ this.content.updateSeries(uniqueId, {
519
+ completionStatus: 'completed',
520
+ }).subscribe();
521
+ }
522
+
523
+ // Delete a series
524
+ deleteSeries(uniqueId: string) {
525
+ this.content.deleteSeries(uniqueId).subscribe();
526
+ }
527
+ }
528
+ ```
529
+
530
+ ### Series Social Actions
531
+
532
+ ```typescript
533
+ // Like/dislike a series
534
+ likeSeries(uniqueId: string) {
535
+ this.content.likeSeries(uniqueId).subscribe({
536
+ next: (series) => console.log('Likes:', series.likes),
537
+ });
538
+ }
539
+
540
+ dislikeSeries(uniqueId: string) {
541
+ this.content.dislikeSeries(uniqueId).subscribe();
542
+ }
543
+
544
+ // Follow/unfollow a series
545
+ followSeries(uniqueId: string) {
546
+ this.content.followSeries(uniqueId).subscribe({
547
+ next: (series) => console.log('Followers:', series.followers),
548
+ });
549
+ }
550
+
551
+ unfollowSeries(uniqueId: string) {
552
+ this.content.unfollowSeries(uniqueId).subscribe();
553
+ }
554
+
555
+ // Save/unsave a series (bookmarking)
556
+ saveSeries(uniqueId: string) {
557
+ this.content.saveSeries(uniqueId).subscribe();
558
+ }
559
+
560
+ unsaveSeries(uniqueId: string) {
561
+ this.content.unsaveSeries(uniqueId).subscribe();
562
+ }
563
+ ```
564
+
565
+ ### Series Post Management
566
+
567
+ ```typescript
568
+ // Get posts in a series (ordered)
569
+ loadSeriesPosts(seriesUniqueId: string) {
570
+ this.content.getSeriesPosts(seriesUniqueId).subscribe({
571
+ next: (posts) => console.log('Posts in series:', posts.length),
572
+ });
573
+ }
574
+
575
+ // Add a post to a series with optional sequence
576
+ addPostToSeries(seriesUniqueId: string, postUniqueId: string) {
577
+ this.content.addSeriesPost(seriesUniqueId, postUniqueId, 1).subscribe();
578
+ }
579
+
580
+ // Remove a post from a series
581
+ removePostFromSeries(seriesUniqueId: string, postUniqueId: string) {
582
+ this.content.removeSeriesPost(seriesUniqueId, postUniqueId).subscribe();
583
+ }
584
+
585
+ // Reorder posts in a series
586
+ reorderPosts(seriesUniqueId: string) {
587
+ this.content.reorderSeriesPosts(seriesUniqueId, {
588
+ posts: [
589
+ { postUniqueId: 'post-1', sequence: 1 },
590
+ { postUniqueId: 'post-2', sequence: 2 },
591
+ { postUniqueId: 'post-3', sequence: 3 },
592
+ ],
593
+ }).subscribe();
594
+ }
595
+ ```
596
+
597
+ ### Series Types
598
+
599
+ ```typescript
600
+ import type {
601
+ Series,
602
+ CreateSeriesRequest,
603
+ UpdateSeriesRequest,
604
+ ListSeriesParams,
605
+ QuerySeriesParams,
606
+ ReorderPostsRequest,
607
+ SeriesVisibility, // 'public' | 'private' | 'unlisted'
608
+ SeriesCompletionStatus // 'ongoing' | 'completed' | 'hiatus' | 'cancelled'
609
+ } from '@23blocks/block-content';
610
+ ```
611
+
612
+ ## RxJS Patterns
613
+
614
+ ### Combining Multiple Services
615
+
616
+ ```typescript
617
+ import { forkJoin } from 'rxjs';
618
+
619
+ // Fetch user and favorites in parallel
620
+ forkJoin({
621
+ user: this.auth.getCurrentUser(),
622
+ favorites: this.search.listFavorites({ limit: 10 }),
623
+ }).subscribe({
624
+ next: ({ user, favorites }) => {
625
+ console.log(user, favorites);
626
+ },
627
+ });
628
+ ```
629
+
630
+ ### Caching with shareReplay
631
+
632
+ ```typescript
633
+ import { shareReplay } from 'rxjs';
634
+
635
+ // Cache the current user
636
+ currentUser$ = this.auth.getCurrentUser().pipe(
637
+ shareReplay(1)
638
+ );
639
+ ```
640
+
641
+ ### Error Handling
642
+
643
+ Every error includes a unique request ID for easy debugging and support:
644
+
645
+ ```typescript
646
+ import { isBlockErrorException, ErrorCodes } from '@23blocks/contracts';
647
+
648
+ this.auth.signIn({ email, password }).subscribe({
649
+ error: (err) => {
650
+ if (isBlockErrorException(err)) {
651
+ // Request tracing for debugging
652
+ console.log('Request ID:', err.requestId); // "req_m5abc_xyz123"
653
+ console.log('Duration:', err.duration); // 145 (ms)
654
+
655
+ switch (err.code) {
656
+ case ErrorCodes.INVALID_CREDENTIALS:
657
+ this.error = 'Invalid email or password';
658
+ break;
659
+ case ErrorCodes.UNAUTHORIZED:
660
+ this.error = 'Session expired';
661
+ break;
662
+ case ErrorCodes.VALIDATION_ERROR:
663
+ this.error = err.message;
664
+ break;
665
+ default:
666
+ this.error = err.message;
667
+ }
668
+
669
+ // Send request ID to support for debugging
670
+ // "Please check request req_m5abc_xyz123"
671
+ }
672
+ },
673
+ });
674
+ ```
675
+
676
+ ## Advanced Setup (Custom Transport)
677
+
678
+ For advanced use cases requiring custom transport configuration:
679
+
680
+ ```typescript
681
+ import { ApplicationConfig } from '@angular/core';
682
+ import { provide23Blocks } from '@23blocks/angular';
683
+ import { createHttpTransport } from '@23blocks/transport-http';
684
+
685
+ const transport = createHttpTransport({
686
+ baseUrl: 'https://auth.yourapp.com',
687
+ headers: () => {
688
+ const token = localStorage.getItem('access_token');
689
+ return {
690
+ 'x-api-key': 'your-api-key',
691
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
692
+ };
693
+ },
694
+ });
695
+
696
+ export const appConfig: ApplicationConfig = {
697
+ providers: [
698
+ provide23Blocks({
699
+ transport,
700
+ authentication: { apiKey: 'your-api-key' },
701
+ search: { apiKey: 'your-api-key' },
702
+ products: { apiKey: 'your-api-key' },
703
+ }),
704
+ ],
705
+ };
706
+ ```
707
+
708
+ ## Testing
709
+
710
+ Mock services in your tests:
711
+
712
+ ```typescript
713
+ import { TestBed } from '@angular/core/testing';
714
+ import { AuthenticationService } from '@23blocks/angular';
715
+ import { of } from 'rxjs';
716
+
717
+ describe('LoginComponent', () => {
718
+ const mockAuth = {
719
+ signIn: jest.fn().mockReturnValue(of({
720
+ user: { email: 'test@test.com' },
721
+ accessToken: 'token',
722
+ })),
723
+ };
724
+
725
+ beforeEach(() => {
726
+ TestBed.configureTestingModule({
727
+ providers: [
728
+ { provide: AuthenticationService, useValue: mockAuth },
729
+ ],
730
+ });
731
+ });
732
+ });
733
+ ```
734
+
735
+ ## Environment Variables
736
+
737
+ ```typescript
738
+ // environment.ts
739
+ export const environment = {
740
+ production: false,
741
+ apiKey: 'your-api-key',
742
+ urls: {
743
+ authentication: 'https://auth.yourapp.com',
744
+ products: 'https://products.yourapp.com',
745
+ },
746
+ };
747
+
748
+ // app.config.ts
749
+ import { environment } from './environments/environment';
750
+
751
+ export const appConfig: ApplicationConfig = {
752
+ providers: [
753
+ provideBlocks23({
754
+ apiKey: environment.apiKey,
755
+ urls: environment.urls,
756
+ }),
757
+ ],
758
+ };
759
+ ```
760
+
761
+ ## TypeScript
762
+
763
+ All services are fully typed:
764
+
765
+ ```typescript
766
+ import type { User, SignInResponse, SignUpResponse } from '@23blocks/block-authentication';
767
+
768
+ // SignInResponse
769
+ interface SignInResponse {
770
+ user: User;
771
+ accessToken: string;
772
+ refreshToken?: string;
773
+ tokenType: string;
774
+ expiresIn?: number;
775
+ }
776
+
777
+ // SignUpResponse - accessToken is optional (email confirmation)
778
+ interface SignUpResponse {
779
+ user: User;
780
+ accessToken?: string; // undefined if email confirmation enabled
781
+ message?: string;
782
+ }
783
+ ```
784
+
785
+ ## Related Packages
786
+
787
+ - [`@23blocks/sdk`](https://www.npmjs.com/package/@23blocks/sdk) - Full SDK package
788
+ - [`@23blocks/react`](https://www.npmjs.com/package/@23blocks/react) - React integration
789
+
790
+ ## License
791
+
792
+ MIT - Copyright (c) 2024 23blocks