@telperion/ng-pack 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/fesm2022/telperion-ng-pack-sse-client.mjs +152 -0
- package/fesm2022/telperion-ng-pack-sse-client.mjs.map +1 -0
- package/fesm2022/telperion-ng-pack-utils.mjs.map +1 -1
- package/package.json +5 -1
- package/sse-client/README.md +575 -0
- package/types/telperion-ng-pack-sse-client.d.ts +141 -0
- package/types/telperion-ng-pack-utils.d.ts +2 -2
package/README.md
CHANGED
|
@@ -60,6 +60,77 @@ export class SettingsComponent {
|
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
63
|
+
### SSE Client
|
|
64
|
+
|
|
65
|
+
**Import:** `@telperion/ng-pack/sse-client`
|
|
66
|
+
|
|
67
|
+
Angular service for Server-Sent Events (SSE) with RxJS Observables and HttpClient-inspired interceptors.
|
|
68
|
+
|
|
69
|
+
#### Key Features
|
|
70
|
+
|
|
71
|
+
- 🚀 Observable-based API integrated with RxJS ecosystem
|
|
72
|
+
- 🔗 HttpClient-inspired interceptor chain for request manipulation
|
|
73
|
+
- 🎯 Type-safe with full generic support
|
|
74
|
+
- ⚡ EventSource wrapper with automatic cleanup
|
|
75
|
+
- 🔄 Real-time streaming data updates
|
|
76
|
+
- 🎨 Feature-based configuration
|
|
77
|
+
|
|
78
|
+
#### Quick Start
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { ApplicationConfig } from '@angular/core';
|
|
82
|
+
import { provideSseClient, withSseInterceptors } from '@telperion/ng-pack/sse-client';
|
|
83
|
+
|
|
84
|
+
export const appConfig: ApplicationConfig = {
|
|
85
|
+
providers: [
|
|
86
|
+
provideSseClient(
|
|
87
|
+
withSseInterceptors(loggingInterceptor, authInterceptor)
|
|
88
|
+
),
|
|
89
|
+
]
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { Component, inject } from '@angular/core';
|
|
95
|
+
import { SseClient } from '@telperion/ng-pack/sse-client';
|
|
96
|
+
import { AsyncPipe } from '@angular/common';
|
|
97
|
+
|
|
98
|
+
interface StockUpdate {
|
|
99
|
+
symbol: string;
|
|
100
|
+
price: number;
|
|
101
|
+
change: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Component({
|
|
105
|
+
selector: 'app-stock-ticker',
|
|
106
|
+
template: `
|
|
107
|
+
<div>
|
|
108
|
+
<h2>Live Stock Prices</h2>
|
|
109
|
+
@if (stockUpdates$ | async; as update) {
|
|
110
|
+
<div class="stock-card">
|
|
111
|
+
<span>{{ update.symbol }}: \${{ update.price }}</span>
|
|
112
|
+
<span [class.positive]="update.change > 0">
|
|
113
|
+
{{ update.change > 0 ? '+' : '' }}{{ update.change }}%
|
|
114
|
+
</span>
|
|
115
|
+
</div>
|
|
116
|
+
}
|
|
117
|
+
</div>
|
|
118
|
+
`,
|
|
119
|
+
standalone: true,
|
|
120
|
+
imports: [AsyncPipe]
|
|
121
|
+
})
|
|
122
|
+
export class StockTickerComponent {
|
|
123
|
+
private sseClient = inject(SseClient);
|
|
124
|
+
|
|
125
|
+
// Start SSE connection and get Observable stream
|
|
126
|
+
stockUpdates$ = this.sseClient.start<StockUpdate>('/api/stocks/stream');
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
[Full documentation →](./sse-client/README.md)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
63
134
|
### Template Signal Forms
|
|
64
135
|
|
|
65
136
|
**Import:** `@telperion/ng-pack/template-signal-forms`
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, Injectable } from '@angular/core';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enum identifying different types of SSE features.
|
|
7
|
+
*/
|
|
8
|
+
var SseFeatureKind;
|
|
9
|
+
(function (SseFeatureKind) {
|
|
10
|
+
SseFeatureKind[SseFeatureKind["InterceptorFunction"] = 0] = "InterceptorFunction";
|
|
11
|
+
})(SseFeatureKind || (SseFeatureKind = {}));
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Configures functional interceptors for the SSE client.
|
|
15
|
+
* Interceptors are executed in the order they are provided.
|
|
16
|
+
*
|
|
17
|
+
* @param interceptors - One or more functional interceptor functions
|
|
18
|
+
* @returns Array of SSE features to be used with provideSseClient()
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const loggingInterceptor: SseInterceptorFn<any> = (req, next) => {
|
|
23
|
+
* console.log('Connecting to:', req.url);
|
|
24
|
+
* return next(req);
|
|
25
|
+
* };
|
|
26
|
+
*
|
|
27
|
+
* provideSseClient(
|
|
28
|
+
* withSseInterceptors(loggingInterceptor)
|
|
29
|
+
* )
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function withSseInterceptors(...interceptors) {
|
|
33
|
+
return interceptors.map(interceptor => ({
|
|
34
|
+
kind: SseFeatureKind.InterceptorFunction,
|
|
35
|
+
interceptor,
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Injection token for providing SSE interceptors.
|
|
41
|
+
* Use with multi: true to provide multiple interceptors.
|
|
42
|
+
*/
|
|
43
|
+
const SSE_INTERCEPTORS = new InjectionToken('Telperion/SSE_INTERCEPTORS');
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Angular service for managing Server-Sent Events (SSE) connections.
|
|
47
|
+
* Wraps the EventSource API with RxJS Observables and an HttpClient-inspired interceptor chain.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* class MyComponent {
|
|
52
|
+
* private sseClient = inject(SseClient);
|
|
53
|
+
*
|
|
54
|
+
* messages$ = this.sseClient.start<string>('/api/events');
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
class SseClient {
|
|
59
|
+
#interceptors = inject(SSE_INTERCEPTORS, { optional: true }) ?? [];
|
|
60
|
+
constructor() {
|
|
61
|
+
if (!(this.#interceptors instanceof Array)) {
|
|
62
|
+
throw new Error('SSE_INTERCEPTORS must be provided as an array. Please ensure you are using multi: true when providing interceptors.');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Starts a new Server-Sent Events connection and returns an Observable stream.
|
|
67
|
+
*
|
|
68
|
+
* @param url - The URL endpoint for the SSE connection
|
|
69
|
+
* @param init - Optional EventSource initialization configuration
|
|
70
|
+
* @returns Observable stream of server-sent events
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* this.sseClient.start<MessageData>('/api/notifications', { withCredentials: true })
|
|
75
|
+
* .subscribe(data => console.log('Received:', data));
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
start(url, init = {}) {
|
|
79
|
+
return this.#interceptors.reduceRight((next, interceptor) => (req) => interceptor.sseIntercept(req, next), (req) => this.#createEventSource(req))({ url, init });
|
|
80
|
+
}
|
|
81
|
+
#createEventSource(request) {
|
|
82
|
+
return new Observable((subscriber) => {
|
|
83
|
+
const eventSource = new EventSource(request.url, request.init);
|
|
84
|
+
function handleMessage(event) {
|
|
85
|
+
subscriber.next(event.data);
|
|
86
|
+
}
|
|
87
|
+
function handleError(err) {
|
|
88
|
+
subscriber.error(err);
|
|
89
|
+
eventSource.close();
|
|
90
|
+
}
|
|
91
|
+
eventSource.addEventListener('message', handleMessage);
|
|
92
|
+
eventSource.addEventListener('error', handleError);
|
|
93
|
+
return () => {
|
|
94
|
+
eventSource.removeEventListener('message', handleMessage);
|
|
95
|
+
eventSource.removeEventListener('error', handleError);
|
|
96
|
+
eventSource.close();
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SseClient, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
101
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SseClient });
|
|
102
|
+
}
|
|
103
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SseClient, decorators: [{
|
|
104
|
+
type: Injectable
|
|
105
|
+
}], ctorParameters: () => [] });
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Provides the SseClient service with optional features.
|
|
109
|
+
* Configure SSE client with interceptors and other features using a functional API.
|
|
110
|
+
*
|
|
111
|
+
* @param features - Optional feature configurations (e.g., withSseInterceptors())
|
|
112
|
+
* @returns Array of Angular providers
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* import { provideSseClient, withSseInterceptors } from '@telperion/ng-pack/sse-client';
|
|
117
|
+
*
|
|
118
|
+
* export const appConfig: ApplicationConfig = {
|
|
119
|
+
* providers: [
|
|
120
|
+
* provideSseClient(
|
|
121
|
+
* withSseInterceptors(loggingInterceptor, authInterceptor)
|
|
122
|
+
* )
|
|
123
|
+
* ]
|
|
124
|
+
* };
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
function provideSseClient(...features) {
|
|
128
|
+
const interceptorFns = features
|
|
129
|
+
.flat()
|
|
130
|
+
.filter(feature => feature.kind === SseFeatureKind.InterceptorFunction)
|
|
131
|
+
.map(feature => ({
|
|
132
|
+
provide: SSE_INTERCEPTORS,
|
|
133
|
+
useValue: {
|
|
134
|
+
sseIntercept: feature.interceptor,
|
|
135
|
+
},
|
|
136
|
+
multi: true,
|
|
137
|
+
}));
|
|
138
|
+
return [
|
|
139
|
+
{
|
|
140
|
+
provide: SseClient,
|
|
141
|
+
useClass: SseClient
|
|
142
|
+
},
|
|
143
|
+
...interceptorFns
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Generated bundle index. Do not edit.
|
|
149
|
+
*/
|
|
150
|
+
|
|
151
|
+
export { SSE_INTERCEPTORS, SseClient, SseFeatureKind, provideSseClient, withSseInterceptors };
|
|
152
|
+
//# sourceMappingURL=telperion-ng-pack-sse-client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telperion-ng-pack-sse-client.mjs","sources":["../../sse-client/src/features/feature.ts","../../sse-client/src/features/with-interceptors.ts","../../sse-client/src/interceptor.ts","../../sse-client/src/client.ts","../../sse-client/src/provider.ts","../../sse-client/src/telperion-ng-pack-sse-client.ts"],"sourcesContent":["/**\r\n * Enum identifying different types of SSE features.\r\n */\r\nexport enum SseFeatureKind {\r\n InterceptorFunction\r\n}\r\n\r\n/**\r\n * Base interface for SSE feature configurations.\r\n */\r\nexport interface SseFeature {\r\n kind: SseFeatureKind;\r\n}\r\n","import { SseInterceptorFn } from \"../interceptor\";\r\nimport { SseFeature, SseFeatureKind } from \"./feature\";\r\n\r\n/**\r\n * Feature interface for functional interceptors.\r\n * @internal\r\n */\r\nexport interface SseInterceptorFunctionFeature extends SseFeature {\r\n kind: SseFeatureKind.InterceptorFunction;\r\n interceptor: SseInterceptorFn<unknown>;\r\n}\r\n\r\n/**\r\n * Configures functional interceptors for the SSE client.\r\n * Interceptors are executed in the order they are provided.\r\n *\r\n * @param interceptors - One or more functional interceptor functions\r\n * @returns Array of SSE features to be used with provideSseClient()\r\n *\r\n * @example\r\n * ```typescript\r\n * const loggingInterceptor: SseInterceptorFn<any> = (req, next) => {\r\n * console.log('Connecting to:', req.url);\r\n * return next(req);\r\n * };\r\n *\r\n * provideSseClient(\r\n * withSseInterceptors(loggingInterceptor)\r\n * )\r\n * ```\r\n */\r\nexport function withSseInterceptors<T = unknown>(...interceptors: SseInterceptorFn<T>[]): SseFeature[] {\r\n return interceptors.map(interceptor => ({\r\n kind: SseFeatureKind.InterceptorFunction,\r\n interceptor,\r\n }));\r\n}\r\n","import { InjectionToken } from \"@angular/core\";\r\nimport type { Observable } from \"rxjs\";\r\n\r\n/**\r\n * Represents an SSE request with URL and initialization options.\r\n */\r\nexport interface SseRequest {\r\n url: string;\r\n init: EventSourceInit;\r\n}\r\n\r\n/**\r\n * Function type for passing the request to the next handler in the interceptor chain.\r\n */\r\nexport type SseNextFn<T> = (request: SseRequest) => Observable<T>;\r\n\r\n/**\r\n * Functional interceptor type for intercepting SSE requests.\r\n * Interceptors can modify requests, handle errors, add logging, etc.\r\n *\r\n * @example\r\n * ```typescript\r\n * const loggingInterceptor: SseInterceptorFn<any> = (req, next) => {\r\n * console.log('SSE Request:', req.url);\r\n * return next(req);\r\n * };\r\n * ```\r\n */\r\nexport type SseInterceptorFn<T> = (\r\n request: SseRequest,\r\n next: SseNextFn<T>\r\n) => Observable<T>;\r\n\r\n/**\r\n * Class-based interceptor interface for SSE requests.\r\n * Implements the same pattern as Angular's HttpInterceptor.\r\n */\r\nexport interface SseInterceptor<T = unknown> {\r\n sseIntercept<U = T>(...args: Parameters<SseInterceptorFn<U>>): ReturnType<SseInterceptorFn<U>>;\r\n}\r\n\r\n/**\r\n * Injection token for providing SSE interceptors.\r\n * Use with multi: true to provide multiple interceptors.\r\n */\r\nexport const SSE_INTERCEPTORS = new InjectionToken<SseInterceptor<unknown>[]>('Telperion/SSE_INTERCEPTORS');\r\n","import { inject, Injectable } from \"@angular/core\";\r\nimport { Observable } from \"rxjs\";\r\n\r\nimport { SSE_INTERCEPTORS, SseRequest } from \"./interceptor\";\r\n\r\n/**\r\n * Angular service for managing Server-Sent Events (SSE) connections.\r\n * Wraps the EventSource API with RxJS Observables and an HttpClient-inspired interceptor chain.\r\n *\r\n * @example\r\n * ```typescript\r\n * class MyComponent {\r\n * private sseClient = inject(SseClient);\r\n *\r\n * messages$ = this.sseClient.start<string>('/api/events');\r\n * }\r\n * ```\r\n */\r\n@Injectable()\r\nexport class SseClient {\r\n #interceptors = inject(SSE_INTERCEPTORS, { optional: true }) ?? [];\r\n\r\n constructor() {\r\n if (!(this.#interceptors instanceof Array)) {\r\n throw new Error('SSE_INTERCEPTORS must be provided as an array. Please ensure you are using multi: true when providing interceptors.');\r\n }\r\n }\r\n\r\n /**\r\n * Starts a new Server-Sent Events connection and returns an Observable stream.\r\n *\r\n * @param url - The URL endpoint for the SSE connection\r\n * @param init - Optional EventSource initialization configuration\r\n * @returns Observable stream of server-sent events\r\n *\r\n * @example\r\n * ```typescript\r\n * this.sseClient.start<MessageData>('/api/notifications', { withCredentials: true })\r\n * .subscribe(data => console.log('Received:', data));\r\n * ```\r\n */\r\n start<T>(url: string, init: EventSourceInit = {}): Observable<T> {\r\n return this.#interceptors.reduceRight(\r\n (next, interceptor) => (req) => interceptor.sseIntercept(req, next),\r\n (req: SseRequest) => this.#createEventSource(req) as Observable<T>\r\n )({ url, init });\r\n }\r\n\r\n #createEventSource<T>(request: SseRequest): Observable<T> {\r\n return new Observable<T>((subscriber) => {\r\n const eventSource = new EventSource(request.url, request.init);\r\n\r\n function handleMessage(event: MessageEvent) {\r\n subscriber.next(event.data);\r\n }\r\n\r\n function handleError(err: Event) {\r\n subscriber.error(err);\r\n eventSource.close();\r\n }\r\n\r\n eventSource.addEventListener('message', handleMessage);\r\n eventSource.addEventListener('error', handleError);\r\n\r\n return () => {\r\n eventSource.removeEventListener('message', handleMessage);\r\n eventSource.removeEventListener('error', handleError);\r\n eventSource.close();\r\n };\r\n });\r\n }\r\n}\r\n","import { Provider } from \"@angular/core\";\r\n\r\nimport { SseClient } from \"./client\";\r\nimport { SseFeature, SseFeatureKind, SseInterceptorFunctionFeature } from \"./features\";\r\nimport { SSE_INTERCEPTORS } from \"./interceptor\";\r\n\r\n/**\r\n * Provides the SseClient service with optional features.\r\n * Configure SSE client with interceptors and other features using a functional API.\r\n *\r\n * @param features - Optional feature configurations (e.g., withSseInterceptors())\r\n * @returns Array of Angular providers\r\n *\r\n * @example\r\n * ```typescript\r\n * import { provideSseClient, withSseInterceptors } from '@telperion/ng-pack/sse-client';\r\n *\r\n * export const appConfig: ApplicationConfig = {\r\n * providers: [\r\n * provideSseClient(\r\n * withSseInterceptors(loggingInterceptor, authInterceptor)\r\n * )\r\n * ]\r\n * };\r\n * ```\r\n */\r\nexport function provideSseClient(...features: SseFeature[][]): Provider[] {\r\n const interceptorFns = features\r\n .flat()\r\n .filter(feature => feature.kind === SseFeatureKind.InterceptorFunction)\r\n .map(feature => ({\r\n provide: SSE_INTERCEPTORS,\r\n useValue: {\r\n sseIntercept: (feature as SseInterceptorFunctionFeature).interceptor,\r\n },\r\n multi: true,\r\n }));\r\n\r\n return [\r\n {\r\n provide: SseClient,\r\n useClass: SseClient\r\n },\r\n ...interceptorFns\r\n ];\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telperion-ng-pack-utils.mjs","sources":["../../utils/src/provide-service-directive.ts","../../utils/src/event-modifiers.plugin.ts","../../utils/src/construct-fetcher.ts","../../utils/src/telperion-ng-pack-utils.ts"],"sourcesContent":["import { forwardRef, InjectionToken, Provider, Type } from \"@angular/core\";\r\n\r\n/**\r\n * Utility function to create a provider for a directive/component that can be injected as a service.\r\n * This is useful for cases where you want to inject a directive/component instance into another directive/component.\r\n *\r\n * Example usage:\r\n *\r\n * files:\r\n * - parent.directive.ts\r\n * - child.directive.ts\r\n * - parent.service.ts\r\n *\r\n * parent.service.ts:\r\n * ```\r\n * import { InjectionToken } from \"@angular/core\";\r\n * import type { ParentDirective } from \"./parent.directive\";\r\n *\r\n * export const ParentService = new InjectionToken<ParentDirective>(\"ParentService\");\r\n * ```\r\n *\r\n * parent.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'parent',\r\n * providers: [provideServiceDirective(ParentService, ParentDirective)],\r\n * })\r\n * export class ParentDirective {...}\r\n * ```\r\n * child.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'child',\r\n * })\r\n * export class ChildDirective {\r\n * #parent = inject(ParentService);\r\n * }\r\n * ```\r\n *\r\n * @param token - The injection token to provide.\r\n * @param directive - The directive/component class that will be provided.\r\n * @returns A provider object that can be used in the providers array of an Angular module or component.\r\n */\r\nexport function provideServiceDirective<T
|
|
1
|
+
{"version":3,"file":"telperion-ng-pack-utils.mjs","sources":["../../utils/src/provide-service-directive.ts","../../utils/src/event-modifiers.plugin.ts","../../utils/src/construct-fetcher.ts","../../utils/src/telperion-ng-pack-utils.ts"],"sourcesContent":["import { forwardRef, InjectionToken, Provider, Type } from \"@angular/core\";\r\n\r\n/**\r\n * Utility function to create a provider for a directive/component that can be injected as a service.\r\n * This is useful for cases where you want to inject a directive/component instance into another directive/component.\r\n *\r\n * Example usage:\r\n *\r\n * files:\r\n * - parent.directive.ts\r\n * - child.directive.ts\r\n * - parent.service.ts\r\n *\r\n * parent.service.ts:\r\n * ```\r\n * import { InjectionToken } from \"@angular/core\";\r\n * import type { ParentDirective } from \"./parent.directive\";\r\n *\r\n * export const ParentService = new InjectionToken<ParentDirective>(\"ParentService\");\r\n * ```\r\n *\r\n * parent.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'parent',\r\n * providers: [provideServiceDirective(ParentService, ParentDirective)],\r\n * })\r\n * export class ParentDirective {...}\r\n * ```\r\n * child.directive.ts:\r\n * ```\r\n * @Directive({\r\n * selector: 'child',\r\n * })\r\n * export class ChildDirective {\r\n * #parent = inject(ParentService);\r\n * }\r\n * ```\r\n *\r\n * @param token - The injection token to provide.\r\n * @param directive - The directive/component class that will be provided.\r\n * @returns A provider object that can be used in the providers array of an Angular module or component.\r\n */\r\nexport function provideServiceDirective<T>(\r\n token: InjectionToken<T>,\r\n directive: Type<NoInfer<T>>\r\n): Provider {\r\n return {\r\n provide: token,\r\n useExisting: forwardRef(() => directive)\r\n }\r\n}\r\n","/**\r\n * Angular Event Manager Plugin that enables event modifier syntax in templates.\r\n *\r\n * @example\r\n * ```html\r\n * <form (submit.pd)=\"onSubmit()\">...</form>\r\n * <div (click.sp)=\"handleClick()\">...</div>\r\n * <button (click.pd.sp)=\"handleButtonClick()\">Click me</button>\r\n * ```\r\n */\r\n\r\nimport { DOCUMENT } from '@angular/common';\r\nimport { Inject, Injectable, Provider } from '@angular/core';\r\nimport { EVENT_MANAGER_PLUGINS, EventManagerPlugin } from '@angular/platform-browser';\r\n\r\n/**\r\n * Event Manager Plugin that adds support for event modifiers.\r\n * @internal\r\n */\r\n@Injectable()\r\nexport class EventModifiersPlugin extends EventManagerPlugin {\r\n /**\r\n * Supported modifiers: `pd` (preventDefault), `sp` (stopPropagation)\r\n */\r\n private static modifierMap: { [key: string]: (e: Event) => void } = {\r\n 'pd': (e: Event) => e.preventDefault(),\r\n 'sp': (e: Event) => e.stopPropagation(),\r\n };\r\n\r\n constructor(@Inject(DOCUMENT) doc: Document) {\r\n super(doc);\r\n }\r\n\r\n supports(eventName: string): boolean {\r\n return eventName.includes('.') &&\r\n eventName.split('.').some(part => part in EventModifiersPlugin.modifierMap);\r\n }\r\n\r\n addEventListener(element: HTMLElement, eventName: string, handler: (event: Event) => void): () => void {\r\n const parts = eventName.split('.');\r\n const domEventName = parts[0];\r\n const modifiers = parts.slice(1).filter(m => m in EventModifiersPlugin.modifierMap);\r\n\r\n const wrappedHandler = (event: Event) => {\r\n modifiers.forEach(mod => EventModifiersPlugin.modifierMap[mod](event));\r\n\r\n return handler(event);\r\n };\r\n\r\n element.addEventListener(domEventName, wrappedHandler);\r\n\r\n return () => element.removeEventListener(domEventName, wrappedHandler);\r\n }\r\n}\r\n\r\n/**\r\n * Provides the Event Modifiers Plugin.\r\n *\r\n * @example\r\n * ```typescript\r\n * bootstrapApplication(AppComponent, {\r\n * providers: [provideEventModifiersPlugin()]\r\n * });\r\n * ```\r\n */\r\nexport function provideEventModifiersPlugin(): Provider {\r\n return {\r\n provide: EVENT_MANAGER_PLUGINS,\r\n useClass: EventModifiersPlugin,\r\n multi: true\r\n };\r\n}\r\n","import { HttpClient } from \"@angular/common/http\";\nimport { inject } from \"@angular/core\";\nimport { AnyFunction } from '@thalesrc/extra-ts-types';\nimport { tryCatch } from \"@thalesrc/js-utils/promise/try-catch\";\nimport { firstValueFrom, Observable } from 'rxjs';\n\nexport type Fetcher = {\n [P in keyof HttpClient]: HttpClient[P] extends (...args: infer A) => Observable<unknown> ? <T>(...args: A) => Promise<[unknown, T]> : never;\n}\n\n/**\n * Constructs a Fetcher that proxies HttpClient methods and returns a tuple of [error, response].\n * If the HttpClient method throws an error, it will be caught and returned as the first element of the tuple.\n * If the HttpClient method succeeds, the response will be returned as the second element of the tuple.\n * Example usage:\n * ```\n * @Component({\n * selector: 'app-example',\n * template: `<button (click)=\"fetchData()\">Fetch Data</button>`,\n * })\n * export class ExampleComponent {\n * private fetcher = constructFetcher();\n *\n * async fetchData() {\n * const [error, response] = await this.fetcher.get('/api/data');\n *\n* if (error) {\n* console.error('Error fetching data:', error);\n* } else {\n* console.log('Fetched data:', response);\n* }\n * }\n * }\n * ```\n */\nexport function constructFetcher() {\n const client = inject(HttpClient);\n\n return new Proxy({} as Fetcher, {\n get(target, prop) {\n if (prop in client) {\n return (...args: any[]) => {\n const method = (client as any)[prop] as AnyFunction;\n\n return tryCatch(firstValueFrom(method.apply(client, args)));\n }\n }\n\n throw new Error(`HttpClient does not have method ${String(prop)}`);\n }\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCG;AACG,SAAU,uBAAuB,CACrC,KAAwB,EACxB,SAA2B,EAAA;IAE3B,OAAO;AACL,QAAA,OAAO,EAAE,KAAK;AACd,QAAA,WAAW,EAAE,UAAU,CAAC,MAAM,SAAS;KACxC;AACH;;ACnDA;;;;;;;;;AASG;AAMH;;;AAGG;AAEG,MAAO,oBAAqB,SAAQ,kBAAkB,CAAA;AAC1D;;AAEG;IACK,OAAO,WAAW,GAA0C;QAClE,IAAI,EAAE,CAAC,CAAQ,KAAK,CAAC,CAAC,cAAc,EAAE;QACtC,IAAI,EAAE,CAAC,CAAQ,KAAK,CAAC,CAAC,eAAe,EAAE;KACxC;AAED,IAAA,WAAA,CAA8B,GAAa,EAAA;QACzC,KAAK,CAAC,GAAG,CAAC;IACZ;AAEA,IAAA,QAAQ,CAAC,SAAiB,EAAA;AACxB,QAAA,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,oBAAoB,CAAC,WAAW,CAAC;IAC/E;AAEA,IAAA,gBAAgB,CAAC,OAAoB,EAAE,SAAiB,EAAE,OAA+B,EAAA;QACvF,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;AAClC,QAAA,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC;QAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,WAAW,CAAC;AAEnF,QAAA,MAAM,cAAc,GAAG,CAAC,KAAY,KAAI;AACtC,YAAA,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,oBAAoB,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAEtE,YAAA,OAAO,OAAO,CAAC,KAAK,CAAC;AACvB,QAAA,CAAC;AAED,QAAA,OAAO,CAAC,gBAAgB,CAAC,YAAY,EAAE,cAAc,CAAC;QAEtD,OAAO,MAAM,OAAO,CAAC,mBAAmB,CAAC,YAAY,EAAE,cAAc,CAAC;IACxE;AAhCW,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,oBAAoB,kBASX,QAAQ,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GATjB,oBAAoB,EAAA,CAAA;;2FAApB,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBADhC;;0BAUc,MAAM;2BAAC,QAAQ;;AA0B9B;;;;;;;;;AASG;SACa,2BAA2B,GAAA;IACzC,OAAO;AACL,QAAA,OAAO,EAAE,qBAAqB;AAC9B,QAAA,QAAQ,EAAE,oBAAoB;AAC9B,QAAA,KAAK,EAAE;KACR;AACH;;AC7DA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;SACa,gBAAgB,GAAA;AAC9B,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC;AAEjC,IAAA,OAAO,IAAI,KAAK,CAAC,EAAa,EAAE;QAC9B,GAAG,CAAC,MAAM,EAAE,IAAI,EAAA;AACd,YAAA,IAAI,IAAI,IAAI,MAAM,EAAE;AAClB,gBAAA,OAAO,CAAC,GAAG,IAAW,KAAI;AACxB,oBAAA,MAAM,MAAM,GAAI,MAAc,CAAC,IAAI,CAAgB;AAEnD,oBAAA,OAAO,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AAC7D,gBAAA,CAAC;YACH;YAEA,MAAM,IAAI,KAAK,CAAC,CAAA,gCAAA,EAAmC,MAAM,CAAC,IAAI,CAAC,CAAA,CAAE,CAAC;QACpE;AACD,KAAA,CAAC;AACJ;;ACnDA;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telperion/ng-pack",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": "^21.0.0",
|
|
6
6
|
"@angular/core": "^21.0.0",
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"types": "./types/telperion-ng-pack.d.ts",
|
|
41
41
|
"default": "./fesm2022/telperion-ng-pack.mjs"
|
|
42
42
|
},
|
|
43
|
+
"./sse-client": {
|
|
44
|
+
"types": "./types/telperion-ng-pack-sse-client.d.ts",
|
|
45
|
+
"default": "./fesm2022/telperion-ng-pack-sse-client.mjs"
|
|
46
|
+
},
|
|
43
47
|
"./storage-signals": {
|
|
44
48
|
"types": "./types/telperion-ng-pack-storage-signals.d.ts",
|
|
45
49
|
"default": "./fesm2022/telperion-ng-pack-storage-signals.mjs"
|
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
# @telperion/ng-pack/sse-client
|
|
2
|
+
|
|
3
|
+
Angular service for Server-Sent Events (SSE) with RxJS Observables and HttpClient-inspired interceptors.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Observable-based API** - Seamlessly integrate with RxJS ecosystem
|
|
8
|
+
- 🔗 **HttpClient-inspired interceptors** - Familiar pattern for request/response manipulation
|
|
9
|
+
- 🎯 **Type-safe** - Full TypeScript support with generic typing
|
|
10
|
+
- ⚡ **EventSource wrapper** - Clean abstraction over native EventSource API
|
|
11
|
+
- 🔄 **Reactive streaming** - Real-time data updates with automatic cleanup
|
|
12
|
+
- 🎨 **Feature-based configuration** - Extensible architecture following modern Angular patterns
|
|
13
|
+
- 🧹 **Automatic cleanup** - Proper resource management on unsubscribe
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
This is a secondary entry point of `@telperion/ng-pack`. Import from `@telperion/ng-pack/sse-client`.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @telperion/ng-pack
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
Configure the SSE client in your application config:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { ApplicationConfig } from '@angular/core';
|
|
29
|
+
import { provideSseClient } from '@telperion/ng-pack/sse-client';
|
|
30
|
+
|
|
31
|
+
export const appConfig: ApplicationConfig = {
|
|
32
|
+
providers: [
|
|
33
|
+
provideSseClient(),
|
|
34
|
+
// ... other providers
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
### Basic Usage
|
|
42
|
+
|
|
43
|
+
Create a component that connects to an SSE endpoint and displays real-time data:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Component, inject } from '@angular/core';
|
|
47
|
+
import { SseClient } from '@telperion/ng-pack/sse-client';
|
|
48
|
+
import { AsyncPipe } from '@angular/common';
|
|
49
|
+
|
|
50
|
+
interface StockUpdate {
|
|
51
|
+
symbol: string;
|
|
52
|
+
price: number;
|
|
53
|
+
change: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Component({
|
|
57
|
+
selector: 'app-stock-ticker',
|
|
58
|
+
template: `
|
|
59
|
+
<div class="stock-ticker">
|
|
60
|
+
<h2>Live Stock Prices</h2>
|
|
61
|
+
@if (stockUpdates$ | async; as update) {
|
|
62
|
+
<div class="stock-card">
|
|
63
|
+
<span class="symbol">{{ update.symbol }}</span>
|
|
64
|
+
<span class="price">\${{ update.price }}</span>
|
|
65
|
+
<span [class.positive]="update.change > 0" [class.negative]="update.change < 0">
|
|
66
|
+
{{ update.change > 0 ? '+' : '' }}{{ update.change }}%
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
}
|
|
70
|
+
</div>
|
|
71
|
+
`,
|
|
72
|
+
standalone: true,
|
|
73
|
+
imports: [AsyncPipe]
|
|
74
|
+
})
|
|
75
|
+
export class StockTickerComponent {
|
|
76
|
+
private sseClient = inject(SseClient);
|
|
77
|
+
|
|
78
|
+
// Start SSE connection and get Observable stream
|
|
79
|
+
stockUpdates$ = this.sseClient.start<StockUpdate>('/api/stocks/stream');
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Using Interceptors
|
|
84
|
+
|
|
85
|
+
Interceptors allow you to modify requests, add logging, handle authentication, or implement retry logic. They work just like Angular's HttpClient interceptors.
|
|
86
|
+
|
|
87
|
+
#### Logging Interceptor
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { SseInterceptorFn } from '@telperion/ng-pack/sse-client';
|
|
91
|
+
import { tap } from 'rxjs/operators';
|
|
92
|
+
|
|
93
|
+
export const loggingInterceptor: SseInterceptorFn<any> = (req, next) => {
|
|
94
|
+
console.log('[SSE] Connecting to:', req.url);
|
|
95
|
+
|
|
96
|
+
return next(req).pipe(
|
|
97
|
+
tap({
|
|
98
|
+
next: data => console.log('[SSE] Received:', data),
|
|
99
|
+
error: err => console.error('[SSE] Error:', err),
|
|
100
|
+
complete: () => console.log('[SSE] Connection closed')
|
|
101
|
+
})
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Authentication Interceptor
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { SseInterceptorFn } from '@telperion/ng-pack/sse-client';
|
|
110
|
+
|
|
111
|
+
export const authInterceptor: SseInterceptorFn<any> = (req, next) => {
|
|
112
|
+
// Add authentication token to URL or modify request
|
|
113
|
+
const token = localStorage.getItem('auth_token');
|
|
114
|
+
const authenticatedUrl = `${req.url}?token=${token}`;
|
|
115
|
+
|
|
116
|
+
return next({
|
|
117
|
+
...req,
|
|
118
|
+
url: authenticatedUrl
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### Retry Interceptor
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { SseInterceptorFn } from '@telperion/ng-pack/sse-client';
|
|
127
|
+
import { retry, timer } from 'rxjs';
|
|
128
|
+
|
|
129
|
+
export const retryInterceptor: SseInterceptorFn<any> = (req, next) => {
|
|
130
|
+
return next(req).pipe(
|
|
131
|
+
retry({
|
|
132
|
+
count: 3,
|
|
133
|
+
delay: (error, retryCount) => {
|
|
134
|
+
console.log(`[SSE] Retry attempt ${retryCount} after error:`, error);
|
|
135
|
+
return timer(1000 * retryCount); // Exponential backoff
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Configuring Interceptors
|
|
143
|
+
|
|
144
|
+
Provide interceptors using the `withSseInterceptors()` feature function:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { ApplicationConfig } from '@angular/core';
|
|
148
|
+
import { provideSseClient, withSseInterceptors } from '@telperion/ng-pack/sse-client';
|
|
149
|
+
import { loggingInterceptor, authInterceptor, retryInterceptor } from './interceptors';
|
|
150
|
+
|
|
151
|
+
export const appConfig: ApplicationConfig = {
|
|
152
|
+
providers: [
|
|
153
|
+
provideSseClient(
|
|
154
|
+
withSseInterceptors(
|
|
155
|
+
loggingInterceptor,
|
|
156
|
+
authInterceptor,
|
|
157
|
+
retryInterceptor
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
]
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Interceptor execution order:** Interceptors are executed in the order they are provided. In the example above, the chain is: logging → auth → retry → EventSource connection.
|
|
165
|
+
|
|
166
|
+
### Advanced Usage
|
|
167
|
+
|
|
168
|
+
#### Real-time Notifications
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { Component, inject } from '@angular/core';
|
|
172
|
+
import { SseClient } from '@telperion/ng-pack/sse-client';
|
|
173
|
+
import { AsyncPipe } from '@angular/common';
|
|
174
|
+
|
|
175
|
+
interface Notification {
|
|
176
|
+
id: string;
|
|
177
|
+
message: string;
|
|
178
|
+
type: 'info' | 'warning' | 'error';
|
|
179
|
+
timestamp: number;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@Component({
|
|
183
|
+
selector: 'app-notifications',
|
|
184
|
+
template: `
|
|
185
|
+
<div class="notifications">
|
|
186
|
+
@for (notification of notifications$ | async; track notification.id) {
|
|
187
|
+
<div [class]="'notification ' + notification.type">
|
|
188
|
+
<span class="message">{{ notification.message }}</span>
|
|
189
|
+
<span class="time">{{ notification.timestamp | date:'short' }}</span>
|
|
190
|
+
</div>
|
|
191
|
+
}
|
|
192
|
+
</div>
|
|
193
|
+
`,
|
|
194
|
+
standalone: true,
|
|
195
|
+
imports: [AsyncPipe]
|
|
196
|
+
})
|
|
197
|
+
export class NotificationsComponent {
|
|
198
|
+
private sseClient = inject(SseClient);
|
|
199
|
+
|
|
200
|
+
notifications$ = this.sseClient.start<Notification>('/api/notifications/stream');
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Managing Subscriptions
|
|
205
|
+
|
|
206
|
+
Always unsubscribe from SSE connections when they're no longer needed:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { Component, inject, OnDestroy } from '@angular/core';
|
|
210
|
+
import { SseClient } from '@telperion/ng-pack/sse-client';
|
|
211
|
+
import { Subscription } from 'rxjs';
|
|
212
|
+
|
|
213
|
+
@Component({
|
|
214
|
+
selector: 'app-dashboard',
|
|
215
|
+
template: `
|
|
216
|
+
<div>
|
|
217
|
+
<h2>Dashboard</h2>
|
|
218
|
+
<p>Active users: {{ activeUsers }}</p>
|
|
219
|
+
</div>
|
|
220
|
+
`
|
|
221
|
+
})
|
|
222
|
+
export class DashboardComponent implements OnDestroy {
|
|
223
|
+
private sseClient = inject(SseClient);
|
|
224
|
+
private subscription?: Subscription;
|
|
225
|
+
|
|
226
|
+
activeUsers = 0;
|
|
227
|
+
|
|
228
|
+
ngOnInit() {
|
|
229
|
+
// Subscribe manually for more control
|
|
230
|
+
this.subscription = this.sseClient
|
|
231
|
+
.start<{ count: number }>('/api/dashboard/active-users')
|
|
232
|
+
.subscribe({
|
|
233
|
+
next: data => this.activeUsers = data.count,
|
|
234
|
+
error: err => console.error('SSE error:', err)
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
ngOnDestroy() {
|
|
239
|
+
// Clean up subscription and close EventSource
|
|
240
|
+
this.subscription?.unsubscribe();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Best practice:** Use the `AsyncPipe` when possible to automatically handle subscription management.
|
|
246
|
+
|
|
247
|
+
#### Error Handling
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { Component, inject } from '@angular/core';
|
|
251
|
+
import { SseClient } from '@telperion/ng-pack/sse-client';
|
|
252
|
+
import { catchError, of } from 'rxjs';
|
|
253
|
+
|
|
254
|
+
@Component({
|
|
255
|
+
selector: 'app-feed',
|
|
256
|
+
template: `
|
|
257
|
+
<div>
|
|
258
|
+
@if (error) {
|
|
259
|
+
<div class="error">{{ error }}</div>
|
|
260
|
+
} @else {
|
|
261
|
+
<div>{{ data$ | async }}</div>
|
|
262
|
+
}
|
|
263
|
+
</div>
|
|
264
|
+
`
|
|
265
|
+
})
|
|
266
|
+
export class FeedComponent {
|
|
267
|
+
private sseClient = inject(SseClient);
|
|
268
|
+
error?: string;
|
|
269
|
+
|
|
270
|
+
data$ = this.sseClient.start('/api/feed').pipe(
|
|
271
|
+
catchError(err => {
|
|
272
|
+
this.error = 'Failed to connect to live feed';
|
|
273
|
+
console.error('SSE connection error:', err);
|
|
274
|
+
return of(null);
|
|
275
|
+
})
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### Custom EventSource Configuration
|
|
281
|
+
|
|
282
|
+
Pass EventSource initialization options to configure credentials, CORS, etc.:
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
@Component({
|
|
286
|
+
selector: 'app-secure-feed',
|
|
287
|
+
template: `<div>{{ data$ | async }}</div>`
|
|
288
|
+
})
|
|
289
|
+
export class SecureFeedComponent {
|
|
290
|
+
private sseClient = inject(SseClient);
|
|
291
|
+
|
|
292
|
+
data$ = this.sseClient.start('/api/secure/feed', {
|
|
293
|
+
withCredentials: true // Send cookies with the request
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## API Reference
|
|
299
|
+
|
|
300
|
+
### `provideSseClient(...features: SseFeature[][]): Provider[]`
|
|
301
|
+
|
|
302
|
+
Provides the SSE client service with optional feature configurations.
|
|
303
|
+
|
|
304
|
+
**Parameters:**
|
|
305
|
+
- `features` (optional) - Feature configurations such as `withSseInterceptors()`
|
|
306
|
+
|
|
307
|
+
**Returns:** Array of Angular providers
|
|
308
|
+
|
|
309
|
+
**Example:**
|
|
310
|
+
```typescript
|
|
311
|
+
provideSseClient(
|
|
312
|
+
withSseInterceptors(loggingInterceptor, authInterceptor)
|
|
313
|
+
)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### `SseClient`
|
|
319
|
+
|
|
320
|
+
Injectable service for managing SSE connections.
|
|
321
|
+
|
|
322
|
+
#### `start<T>(url: string, init?: EventSourceInit): Observable<T>`
|
|
323
|
+
|
|
324
|
+
Starts a new Server-Sent Events connection and returns an Observable stream.
|
|
325
|
+
|
|
326
|
+
**Parameters:**
|
|
327
|
+
- `url` - The endpoint URL for the SSE connection
|
|
328
|
+
- `init` (optional) - EventSource initialization options (e.g., `{ withCredentials: true }`)
|
|
329
|
+
|
|
330
|
+
**Returns:** Observable stream of typed server-sent events
|
|
331
|
+
|
|
332
|
+
**Example:**
|
|
333
|
+
```typescript
|
|
334
|
+
sseClient.start<MessageData>('/api/events', { withCredentials: true })
|
|
335
|
+
.subscribe(data => console.log('Received:', data));
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
### `withSseInterceptors(...interceptors: SseInterceptorFn<T>[]): SseFeature[]`
|
|
341
|
+
|
|
342
|
+
Configures functional interceptors for the SSE client.
|
|
343
|
+
|
|
344
|
+
**Parameters:**
|
|
345
|
+
- `interceptors` - One or more functional interceptor functions
|
|
346
|
+
|
|
347
|
+
**Returns:** Array of SSE features to be used with `provideSseClient()`
|
|
348
|
+
|
|
349
|
+
**Example:**
|
|
350
|
+
```typescript
|
|
351
|
+
withSseInterceptors(
|
|
352
|
+
loggingInterceptor,
|
|
353
|
+
authInterceptor,
|
|
354
|
+
retryInterceptor
|
|
355
|
+
)
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### `SseInterceptorFn<T>`
|
|
361
|
+
|
|
362
|
+
Type definition for functional interceptors.
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
type SseInterceptorFn<T> = (
|
|
366
|
+
request: SseRequest,
|
|
367
|
+
next: SseNextFn<T>
|
|
368
|
+
) => Observable<T>;
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Parameters:**
|
|
372
|
+
- `request` - The SSE request containing `url` and `init`
|
|
373
|
+
- `next` - Function to call the next interceptor in the chain
|
|
374
|
+
|
|
375
|
+
**Returns:** Observable of the intercepted request
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
### `SseRequest`
|
|
380
|
+
|
|
381
|
+
Interface representing an SSE request.
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
interface SseRequest {
|
|
385
|
+
url: string;
|
|
386
|
+
init: EventSourceInit;
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### `SseInterceptor<T>`
|
|
393
|
+
|
|
394
|
+
Class-based interceptor interface (for advanced use cases).
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
interface SseInterceptor<T = unknown> {
|
|
398
|
+
sseIntercept<U = T>(
|
|
399
|
+
request: SseRequest,
|
|
400
|
+
next: SseNextFn<U>
|
|
401
|
+
): Observable<U>;
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Best Practices
|
|
406
|
+
|
|
407
|
+
### 1. Always Unsubscribe
|
|
408
|
+
|
|
409
|
+
Use the `AsyncPipe` or manually unsubscribe to prevent memory leaks and close connections:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// ✅ Good - using AsyncPipe (automatic cleanup)
|
|
413
|
+
data$ = this.sseClient.start('/api/feed');
|
|
414
|
+
|
|
415
|
+
// ✅ Good - manual subscription with cleanup
|
|
416
|
+
ngOnInit() {
|
|
417
|
+
this.subscription = this.sseClient.start('/api/feed').subscribe(...);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
ngOnDestroy() {
|
|
421
|
+
this.subscription?.unsubscribe();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ❌ Bad - no cleanup
|
|
425
|
+
ngOnInit() {
|
|
426
|
+
this.sseClient.start('/api/feed').subscribe(...);
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### 2. Handle Errors Gracefully
|
|
431
|
+
|
|
432
|
+
Always implement error handling for SSE connections:
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
data$ = this.sseClient.start('/api/feed').pipe(
|
|
436
|
+
catchError(err => {
|
|
437
|
+
console.error('SSE error:', err);
|
|
438
|
+
// Return fallback value or retry
|
|
439
|
+
return of(null);
|
|
440
|
+
})
|
|
441
|
+
);
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### 3. Use Type Safety
|
|
445
|
+
|
|
446
|
+
Leverage TypeScript generics for type-safe event data:
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
// ✅ Good - typed
|
|
450
|
+
interface StockUpdate {
|
|
451
|
+
symbol: string;
|
|
452
|
+
price: number;
|
|
453
|
+
}
|
|
454
|
+
data$ = this.sseClient.start<StockUpdate>('/api/stocks');
|
|
455
|
+
|
|
456
|
+
// ❌ Bad - untyped
|
|
457
|
+
data$ = this.sseClient.start('/api/stocks');
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### 4. Interceptor Order Matters
|
|
461
|
+
|
|
462
|
+
Place interceptors in logical order. Generally: logging → authentication → error handling → retry:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
provideSseClient(
|
|
466
|
+
withSseInterceptors(
|
|
467
|
+
loggingInterceptor, // First: log everything
|
|
468
|
+
authInterceptor, // Second: add auth
|
|
469
|
+
errorHandlerInterceptor, // Third: handle errors
|
|
470
|
+
retryInterceptor // Last: retry failed connections
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### 5. Consider Reconnection Strategies
|
|
476
|
+
|
|
477
|
+
For production applications, implement retry logic with exponential backoff:
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
const retryInterceptor: SseInterceptorFn<any> = (req, next) => {
|
|
481
|
+
return next(req).pipe(
|
|
482
|
+
retry({
|
|
483
|
+
count: 5,
|
|
484
|
+
delay: (error, retryCount) => timer(Math.min(1000 * Math.pow(2, retryCount), 30000))
|
|
485
|
+
})
|
|
486
|
+
);
|
|
487
|
+
};
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### 6. Use Environment-Specific Configuration
|
|
491
|
+
|
|
492
|
+
Configure different interceptors for development and production:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
// app.config.ts
|
|
496
|
+
const interceptors = environment.production
|
|
497
|
+
? [authInterceptor, retryInterceptor]
|
|
498
|
+
: [loggingInterceptor, authInterceptor, retryInterceptor];
|
|
499
|
+
|
|
500
|
+
export const appConfig: ApplicationConfig = {
|
|
501
|
+
providers: [
|
|
502
|
+
provideSseClient(withSseInterceptors(...interceptors))
|
|
503
|
+
]
|
|
504
|
+
};
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
## Common Use Cases
|
|
508
|
+
|
|
509
|
+
### Real-time Chat
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
interface ChatMessage {
|
|
513
|
+
id: string;
|
|
514
|
+
user: string;
|
|
515
|
+
message: string;
|
|
516
|
+
timestamp: number;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
messages$ = this.sseClient.start<ChatMessage>('/api/chat/stream');
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Live Dashboard Metrics
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
interface Metrics {
|
|
526
|
+
cpu: number;
|
|
527
|
+
memory: number;
|
|
528
|
+
activeUsers: number;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
metrics$ = this.sseClient.start<Metrics>('/api/dashboard/metrics');
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Stock Price Updates
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
interface StockPrice {
|
|
538
|
+
symbol: string;
|
|
539
|
+
price: number;
|
|
540
|
+
volume: number;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
stocks$ = this.sseClient.start<StockPrice>('/api/stocks/live');
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Server Logs Streaming
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
interface LogEntry {
|
|
550
|
+
level: 'info' | 'warn' | 'error';
|
|
551
|
+
message: string;
|
|
552
|
+
timestamp: number;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
logs$ = this.sseClient.start<LogEntry>('/api/logs/stream');
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## Comparison with HttpClient
|
|
559
|
+
|
|
560
|
+
The SSE client deliberately mirrors Angular's HttpClient pattern:
|
|
561
|
+
|
|
562
|
+
| Feature | HttpClient | SseClient |
|
|
563
|
+
|---------|------------|-----------|
|
|
564
|
+
| **Return Type** | `Observable` | `Observable` |
|
|
565
|
+
| **Interceptors** | `HttpInterceptor` | `SseInterceptor` |
|
|
566
|
+
| **Provider** | `provideHttpClient()` | `provideSseClient()` |
|
|
567
|
+
| **Features** | `withInterceptors()` | `withSseInterceptors()` |
|
|
568
|
+
| **Connection** | Request/Response | Persistent stream |
|
|
569
|
+
| **Use Case** | REST APIs | Real-time updates |
|
|
570
|
+
|
|
571
|
+
This familiar pattern makes it easy for Angular developers to adopt SSE for real-time features.
|
|
572
|
+
|
|
573
|
+
## License
|
|
574
|
+
|
|
575
|
+
This library is part of the Telperion monorepo.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enum identifying different types of SSE features.
|
|
7
|
+
*/
|
|
8
|
+
declare enum SseFeatureKind {
|
|
9
|
+
InterceptorFunction = 0
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Base interface for SSE feature configurations.
|
|
13
|
+
*/
|
|
14
|
+
interface SseFeature {
|
|
15
|
+
kind: SseFeatureKind;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Represents an SSE request with URL and initialization options.
|
|
20
|
+
*/
|
|
21
|
+
interface SseRequest {
|
|
22
|
+
url: string;
|
|
23
|
+
init: EventSourceInit;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Function type for passing the request to the next handler in the interceptor chain.
|
|
27
|
+
*/
|
|
28
|
+
type SseNextFn<T> = (request: SseRequest) => Observable<T>;
|
|
29
|
+
/**
|
|
30
|
+
* Functional interceptor type for intercepting SSE requests.
|
|
31
|
+
* Interceptors can modify requests, handle errors, add logging, etc.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const loggingInterceptor: SseInterceptorFn<any> = (req, next) => {
|
|
36
|
+
* console.log('SSE Request:', req.url);
|
|
37
|
+
* return next(req);
|
|
38
|
+
* };
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
type SseInterceptorFn<T> = (request: SseRequest, next: SseNextFn<T>) => Observable<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Class-based interceptor interface for SSE requests.
|
|
44
|
+
* Implements the same pattern as Angular's HttpInterceptor.
|
|
45
|
+
*/
|
|
46
|
+
interface SseInterceptor<T = unknown> {
|
|
47
|
+
sseIntercept<U = T>(...args: Parameters<SseInterceptorFn<U>>): ReturnType<SseInterceptorFn<U>>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Injection token for providing SSE interceptors.
|
|
51
|
+
* Use with multi: true to provide multiple interceptors.
|
|
52
|
+
*/
|
|
53
|
+
declare const SSE_INTERCEPTORS: InjectionToken<SseInterceptor<unknown>[]>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Feature interface for functional interceptors.
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
interface SseInterceptorFunctionFeature extends SseFeature {
|
|
60
|
+
kind: SseFeatureKind.InterceptorFunction;
|
|
61
|
+
interceptor: SseInterceptorFn<unknown>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Configures functional interceptors for the SSE client.
|
|
65
|
+
* Interceptors are executed in the order they are provided.
|
|
66
|
+
*
|
|
67
|
+
* @param interceptors - One or more functional interceptor functions
|
|
68
|
+
* @returns Array of SSE features to be used with provideSseClient()
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const loggingInterceptor: SseInterceptorFn<any> = (req, next) => {
|
|
73
|
+
* console.log('Connecting to:', req.url);
|
|
74
|
+
* return next(req);
|
|
75
|
+
* };
|
|
76
|
+
*
|
|
77
|
+
* provideSseClient(
|
|
78
|
+
* withSseInterceptors(loggingInterceptor)
|
|
79
|
+
* )
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare function withSseInterceptors<T = unknown>(...interceptors: SseInterceptorFn<T>[]): SseFeature[];
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Angular service for managing Server-Sent Events (SSE) connections.
|
|
86
|
+
* Wraps the EventSource API with RxJS Observables and an HttpClient-inspired interceptor chain.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* class MyComponent {
|
|
91
|
+
* private sseClient = inject(SseClient);
|
|
92
|
+
*
|
|
93
|
+
* messages$ = this.sseClient.start<string>('/api/events');
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
declare class SseClient {
|
|
98
|
+
#private;
|
|
99
|
+
constructor();
|
|
100
|
+
/**
|
|
101
|
+
* Starts a new Server-Sent Events connection and returns an Observable stream.
|
|
102
|
+
*
|
|
103
|
+
* @param url - The URL endpoint for the SSE connection
|
|
104
|
+
* @param init - Optional EventSource initialization configuration
|
|
105
|
+
* @returns Observable stream of server-sent events
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* this.sseClient.start<MessageData>('/api/notifications', { withCredentials: true })
|
|
110
|
+
* .subscribe(data => console.log('Received:', data));
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
start<T>(url: string, init?: EventSourceInit): Observable<T>;
|
|
114
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SseClient, never>;
|
|
115
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SseClient>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Provides the SseClient service with optional features.
|
|
120
|
+
* Configure SSE client with interceptors and other features using a functional API.
|
|
121
|
+
*
|
|
122
|
+
* @param features - Optional feature configurations (e.g., withSseInterceptors())
|
|
123
|
+
* @returns Array of Angular providers
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* import { provideSseClient, withSseInterceptors } from '@telperion/ng-pack/sse-client';
|
|
128
|
+
*
|
|
129
|
+
* export const appConfig: ApplicationConfig = {
|
|
130
|
+
* providers: [
|
|
131
|
+
* provideSseClient(
|
|
132
|
+
* withSseInterceptors(loggingInterceptor, authInterceptor)
|
|
133
|
+
* )
|
|
134
|
+
* ]
|
|
135
|
+
* };
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
declare function provideSseClient(...features: SseFeature[][]): Provider[];
|
|
139
|
+
|
|
140
|
+
export { SSE_INTERCEPTORS, SseClient, SseFeatureKind, provideSseClient, withSseInterceptors };
|
|
141
|
+
export type { SseFeature, SseInterceptor, SseInterceptorFn, SseInterceptorFunctionFeature, SseNextFn, SseRequest };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InjectionToken, Type, Provider } from '@angular/core';
|
|
2
2
|
import { HttpClient } from '@angular/common/http';
|
|
3
3
|
import { Observable } from 'rxjs';
|
|
4
4
|
|
|
@@ -43,7 +43,7 @@ import { Observable } from 'rxjs';
|
|
|
43
43
|
* @param directive - The directive/component class that will be provided.
|
|
44
44
|
* @returns A provider object that can be used in the providers array of an Angular module or component.
|
|
45
45
|
*/
|
|
46
|
-
declare function provideServiceDirective<T
|
|
46
|
+
declare function provideServiceDirective<T>(token: InjectionToken<T>, directive: Type<NoInfer<T>>): Provider;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Provides the Event Modifiers Plugin.
|