@bloonio/chat-angular 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # @bloonio/chat-angular
2
+
3
+ Angular library for the bloonio chat PaaS. Standalone components,
4
+ signals-based service, SSR-safe. Built on `@bloonio/chat-client-core`.
5
+
6
+ Targets Angular 17+ (signals stable + `@if`/`@for` control flow + `afterNextRender`).
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @bloonio/chat-angular @bloonio/chat-client-core
12
+ ```
13
+
14
+ Then import the stylesheet once in your global stylesheet (`styles.css`):
15
+
16
+ ```css
17
+ @import "@bloonio/chat-angular/styles.css";
18
+ ```
19
+
20
+ ## Two-minute integration
21
+
22
+ ```ts
23
+ // app.config.ts
24
+ import { ApplicationConfig } from '@angular/core';
25
+ import { provideBloonioChat } from '@bloonio/chat-angular';
26
+ import { inject } from '@angular/core';
27
+ import { HttpClient } from '@angular/common/http';
28
+ import { map } from 'rxjs';
29
+
30
+ export const appConfig: ApplicationConfig = {
31
+ providers: [
32
+ provideBloonioChat({
33
+ tenantId: '019e4ae7-…',
34
+ publicKey: 'pk_…',
35
+ baseUrl: 'https://chat-relay.example.com',
36
+ locale: 'fr',
37
+ // Optional — bind anonymous sessions to your authenticated users.
38
+ // Your backend computes HMAC-SHA256(tenant_secret, user_id) once
39
+ // per logged-in visitor.
40
+ visitorTokenProvider: () => inject(HttpClient)
41
+ .get<{token: string}>('/api/chat/visitor-token')
42
+ .pipe(map(r => r.token)), // "userId:signature"
43
+ }),
44
+ ],
45
+ };
46
+ ```
47
+
48
+ ```ts
49
+ // any standalone component — drop the launcher anywhere
50
+ import { BloonioChatLauncherComponent } from '@bloonio/chat-angular';
51
+
52
+ @Component({
53
+ standalone: true,
54
+ imports: [BloonioChatLauncherComponent],
55
+ template: `<router-outlet /> <bloonio-chat-launcher />`,
56
+ })
57
+ export class AppComponent {}
58
+ ```
59
+
60
+ ## Three rendering modes
61
+
62
+ | Mode | Component | When |
63
+ |---|---|---|
64
+ | **Floating** | `<bloonio-chat-launcher />` (+ the launcher renders the panel itself on toggle) | Tidio-style — a bubble in the corner that expands on click. |
65
+ | **Docked panel** | `<bloonio-chat-panel mode="docked" />` | Always-on chat in a sidebar / dedicated column. No floating chrome. |
66
+ | **Headless thread** | `<bloonio-chat-thread />` | Just messages + input, no header. Embed inside your own support pages. |
67
+
68
+ All three share the same `BloonioChatService`, so a launcher and a thread on the same page stay in sync.
69
+
70
+ ## Programmatic access
71
+
72
+ ```ts
73
+ import { Component, inject } from '@angular/core';
74
+ import { BloonioChatService } from '@bloonio/chat-angular';
75
+
76
+ @Component({...})
77
+ export class SupportButton {
78
+ private readonly chat = inject(BloonioChatService);
79
+
80
+ readonly unread = this.chat.unreadCount; // Signal<number>
81
+ readonly typing = this.chat.botTyping; // Signal<boolean>
82
+
83
+ open() { this.chat.openPanel(); }
84
+ send(content: string) { this.chat.sendMessage(content); }
85
+ }
86
+ ```
87
+
88
+ ## SSR
89
+
90
+ Bootstrap (`fetchWidgetConfig`) is deferred via `afterNextRender` so the
91
+ server-side render produces zero network calls. The components render
92
+ their static shell during pre-render and connect when the browser hydrates.
93
+
94
+ For tenants on `@angular/ssr`, no extra configuration is needed.
95
+
96
+ ## Build
97
+
98
+ ```bash
99
+ npm install
100
+ npm run build # ng-packagr → dist/ (fesm2022 + .d.ts + Ivy partial)
101
+ npm run typecheck # tsc --noEmit -p tsconfig.lib.json
102
+ ```
103
+
104
+ Every component uses inline `template:` strings, so there are no separate
105
+ template files to ship — ng-packagr emits the Angular Package Format (APF)
106
+ that the consumer's Ivy compiler links at build time.
107
+
108
+ ## Reference
109
+
110
+ - `@bloonio/chat-client-core` — transport + protocol layer this library wraps
111
+ - `@bloonio/chat-widget` — the Preact embeddable using the same core lib
112
+ - `bloonio_chat_api/docs/12_PAAS_CONVERSION_PLAN.md` — full PaaS plan, §7.3 + §13.9
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ export * from './index';
5
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvb25pby1jaGF0LWFuZ3VsYXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYmxvb25pby1jaGF0LWFuZ3VsYXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLFNBQVMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9pbmRleCc7XG4iXX0=
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @bloonio/chat-angular — Angular library for the bloonio chat PaaS.
3
+ *
4
+ * Public surface:
5
+ * - {@link provideBloonioChat} — environment-provider factory for app.config.ts
6
+ * - {@link BloonioChatService} — signals-based reactive state
7
+ * - Three standalone components:
8
+ * <bloonio-chat-launcher> floating bubble (collapsed)
9
+ * <bloonio-chat-panel> expandable card (the full chat UX)
10
+ * <bloonio-chat-thread> headless variant for inline embedding
11
+ *
12
+ * Styles ship separately at `@bloonio/chat-angular/styles.css` — import once
13
+ * in your global stylesheet (see README).
14
+ */
15
+ export { provideBloonioChat, BLOONIO_CHAT_CONFIG, injectBloonioChatConfig } from './lib/bloonio-chat.token';
16
+ export { BloonioChatService } from './lib/bloonio-chat.service';
17
+ export { BloonioChatLauncherComponent } from './lib/components/launcher.component';
18
+ export { BloonioChatPanelComponent, } from './lib/components/panel.component';
19
+ export { BloonioChatThreadComponent } from './lib/components/thread.component';
20
+ export { MessageListComponent } from './lib/components/message-list.component';
21
+ export { InputBarComponent } from './lib/components/input-bar.component';
22
+ export { QuickRepliesComponent } from './lib/components/quick-replies.component';
23
+ export { BloonioChatError, BloonioChatNetworkError, BloonioChatServerError, BloonioChatAuthError, } from '@bloonio/chat-client-core';
24
+ export const VERSION = '0.1.0';
25
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUVILE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxtQkFBbUIsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBTTVHLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBR2hFLE9BQU8sRUFBRSw0QkFBNEIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDO0FBQ25GLE9BQU8sRUFDTCx5QkFBeUIsR0FFMUIsTUFBTSxrQ0FBa0MsQ0FBQztBQUMxQyxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMvRSxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUMvRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUN6RSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSwwQ0FBMEMsQ0FBQztBQVlqRixPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLHVCQUF1QixFQUN2QixzQkFBc0IsRUFDdEIsb0JBQW9CLEdBQ3JCLE1BQU0sMkJBQTJCLENBQUM7QUFFbkMsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGJsb29uaW8vY2hhdC1hbmd1bGFyIOKAlCBBbmd1bGFyIGxpYnJhcnkgZm9yIHRoZSBibG9vbmlvIGNoYXQgUGFhUy5cbiAqXG4gKiBQdWJsaWMgc3VyZmFjZTpcbiAqICAgLSB7QGxpbmsgcHJvdmlkZUJsb29uaW9DaGF0fSDigJQgZW52aXJvbm1lbnQtcHJvdmlkZXIgZmFjdG9yeSBmb3IgYXBwLmNvbmZpZy50c1xuICogICAtIHtAbGluayBCbG9vbmlvQ2hhdFNlcnZpY2V9ICDigJQgc2lnbmFscy1iYXNlZCByZWFjdGl2ZSBzdGF0ZVxuICogICAtIFRocmVlIHN0YW5kYWxvbmUgY29tcG9uZW50czpcbiAqICAgICAgIDxibG9vbmlvLWNoYXQtbGF1bmNoZXI+ICAgZmxvYXRpbmcgYnViYmxlIChjb2xsYXBzZWQpXG4gKiAgICAgICA8Ymxvb25pby1jaGF0LXBhbmVsPiAgICAgIGV4cGFuZGFibGUgY2FyZCAodGhlIGZ1bGwgY2hhdCBVWClcbiAqICAgICAgIDxibG9vbmlvLWNoYXQtdGhyZWFkPiAgICAgaGVhZGxlc3MgdmFyaWFudCBmb3IgaW5saW5lIGVtYmVkZGluZ1xuICpcbiAqIFN0eWxlcyBzaGlwIHNlcGFyYXRlbHkgYXQgYEBibG9vbmlvL2NoYXQtYW5ndWxhci9zdHlsZXMuY3NzYCDigJQgaW1wb3J0IG9uY2VcbiAqIGluIHlvdXIgZ2xvYmFsIHN0eWxlc2hlZXQgKHNlZSBSRUFETUUpLlxuICovXG5cbmV4cG9ydCB7IHByb3ZpZGVCbG9vbmlvQ2hhdCwgQkxPT05JT19DSEFUX0NPTkZJRywgaW5qZWN0Qmxvb25pb0NoYXRDb25maWcgfSBmcm9tICcuL2xpYi9ibG9vbmlvLWNoYXQudG9rZW4nO1xuZXhwb3J0IHR5cGUge1xuICBCbG9vbmlvQ2hhdENvbmZpZyxcbiAgVmlzaXRvclRva2VuUHJvdmlkZXIsXG59IGZyb20gJy4vbGliL2Jsb29uaW8tY2hhdC50b2tlbic7XG5cbmV4cG9ydCB7IEJsb29uaW9DaGF0U2VydmljZSB9IGZyb20gJy4vbGliL2Jsb29uaW8tY2hhdC5zZXJ2aWNlJztcbmV4cG9ydCB0eXBlIHsgQ2hhdE1lc3NhZ2UgfSBmcm9tICcuL2xpYi9ibG9vbmlvLWNoYXQuc2VydmljZSc7XG5cbmV4cG9ydCB7IEJsb29uaW9DaGF0TGF1bmNoZXJDb21wb25lbnQgfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL2xhdW5jaGVyLmNvbXBvbmVudCc7XG5leHBvcnQge1xuICBCbG9vbmlvQ2hhdFBhbmVsQ29tcG9uZW50LFxuICB0eXBlIENoYXRQYW5lbE1vZGUsXG59IGZyb20gJy4vbGliL2NvbXBvbmVudHMvcGFuZWwuY29tcG9uZW50JztcbmV4cG9ydCB7IEJsb29uaW9DaGF0VGhyZWFkQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy90aHJlYWQuY29tcG9uZW50JztcbmV4cG9ydCB7IE1lc3NhZ2VMaXN0Q29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9tZXNzYWdlLWxpc3QuY29tcG9uZW50JztcbmV4cG9ydCB7IElucHV0QmFyQ29tcG9uZW50IH0gZnJvbSAnLi9saWIvY29tcG9uZW50cy9pbnB1dC1iYXIuY29tcG9uZW50JztcbmV4cG9ydCB7IFF1aWNrUmVwbGllc0NvbXBvbmVudCB9IGZyb20gJy4vbGliL2NvbXBvbmVudHMvcXVpY2stcmVwbGllcy5jb21wb25lbnQnO1xuXG4vLyBSZS1leHBvcnQgdGhlIGJpdHMgb2YgQGJsb29uaW8vY2hhdC1jbGllbnQtY29yZSBhIGNvbnN1bWVyIGlzIGxpa2VseSB0b1xuLy8gdG91Y2ggKHR5cGVzLCBlcnJvciBjbGFzc2VzKS4gS2VlcHMgYGltcG9ydCB74oCmfSBmcm9tICdAYmxvb25pby9jaGF0LWFuZ3VsYXInYFxuLy8gdGhlIG9uZSBlbnRyeSBwb2ludCBmb3IgbW9zdCBjYXNlcy5cbmV4cG9ydCB0eXBlIHtcbiAgV2lkZ2V0Qm9vdHN0cmFwQ29uZmlnLFxuICBXaWRnZXRQb3NpdGlvbixcbiAgV2lkZ2V0VmlzaWJpbGl0eSxcbiAgU2VydmVyTXNnRnJhbWUsXG4gIE1lc3NhZ2VTZW5kZXIsXG59IGZyb20gJ0BibG9vbmlvL2NoYXQtY2xpZW50LWNvcmUnO1xuZXhwb3J0IHtcbiAgQmxvb25pb0NoYXRFcnJvcixcbiAgQmxvb25pb0NoYXROZXR3b3JrRXJyb3IsXG4gIEJsb29uaW9DaGF0U2VydmVyRXJyb3IsXG4gIEJsb29uaW9DaGF0QXV0aEVycm9yLFxufSBmcm9tICdAYmxvb25pby9jaGF0LWNsaWVudC1jb3JlJztcblxuZXhwb3J0IGNvbnN0IFZFUlNJT04gPSAnMC4xLjAnO1xuIl19
@@ -0,0 +1,311 @@
1
+ /**
2
+ * BloonioChatService — signals-based reactive state container.
3
+ *
4
+ * Mirrors the Preact widget's `Widget.tsx` state machine but uses Angular
5
+ * signals so templates auto-track the slots they read. Public surface:
6
+ *
7
+ * readonly bootstrap = signal<WidgetBootstrapConfig | null>(null);
8
+ * readonly session = signal<CreateVisitorSessionResponse | null>(null);
9
+ * readonly messages = signal<ChatMessage[]>([]);
10
+ * readonly botTyping = signal(false);
11
+ * readonly open = signal(false);
12
+ * readonly connecting= signal(false);
13
+ * readonly unreadCount = computed(...);
14
+ *
15
+ * async open(): Promise<void>
16
+ * close(): void
17
+ * async toggle(): Promise<void>
18
+ * sendMessage(text: string): void
19
+ *
20
+ * SSR-safe: every transport call (fetch, WebSocket) is gated behind a
21
+ * platform check via `afterNextRender()` so server pre-rendering doesn't
22
+ * try to open sockets.
23
+ */
24
+ import { afterNextRender, computed, DestroyRef, inject, Injectable, signal, } from '@angular/core';
25
+ import { isPlatformBrowser } from '@angular/common';
26
+ import { PLATFORM_ID } from '@angular/core';
27
+ import { firstValueFrom, isObservable } from 'rxjs';
28
+ import { ChatClient, WSClient, } from '@bloonio/chat-client-core';
29
+ import { injectBloonioChatConfig } from './bloonio-chat.token';
30
+ import * as i0 from "@angular/core";
31
+ export class BloonioChatService {
32
+ config = injectBloonioChatConfig();
33
+ platformId = inject(PLATFORM_ID);
34
+ destroyRef = inject(DestroyRef);
35
+ /** True when running in the browser (vs. server pre-render). */
36
+ isBrowser = isPlatformBrowser(this.platformId);
37
+ /** Bootstrap config — populated by the first `loadBootstrap()` call. */
38
+ bootstrap = signal(null);
39
+ /** Visitor session (id + JWT + expires_at) — set lazily on first open. */
40
+ session = signal(null);
41
+ /** Message log, oldest → newest. */
42
+ messages = signal([]);
43
+ /** True between bot's typing(start) and typing(stop) frames. */
44
+ botTyping = signal(false);
45
+ /** Panel visibility — drives Launcher / Panel rendering. */
46
+ open = signal(false);
47
+ /** True while `ensureSession()` is in flight (initial WS handshake). */
48
+ connecting = signal(false);
49
+ /** Set when the WS handshake fails permanently (e.g. tenant suspended). */
50
+ fatalError = signal(null);
51
+ /** Number of bot/agent messages received while the panel was closed.
52
+ * Resets to 0 the moment the panel opens. */
53
+ _seenCount = signal(0);
54
+ unreadCount = computed(() => {
55
+ if (this.open())
56
+ return 0;
57
+ const total = this.messages().filter((m) => m.from === 'bot' || m.from === 'agent').length;
58
+ return Math.max(0, total - this._seenCount());
59
+ });
60
+ client = null;
61
+ ws = null;
62
+ bootstrapPromise = null;
63
+ sessionPromise = null;
64
+ constructor() {
65
+ // SSR safety: schedule the initial bootstrap fetch on first browser render.
66
+ afterNextRender(() => {
67
+ this.loadBootstrap();
68
+ });
69
+ this.destroyRef.onDestroy(() => {
70
+ this.ws?.close();
71
+ this.ws = null;
72
+ });
73
+ }
74
+ // ── Public methods ─────────────────────────────────────────────────────
75
+ /** Toggle panel visibility. First open also opens the WS session. */
76
+ async toggle() {
77
+ const next = !this.open();
78
+ this.open.set(next);
79
+ if (next) {
80
+ this._seenCount.set(this.messages().filter((m) => m.from === 'bot' || m.from === 'agent').length);
81
+ await this.ensureSession();
82
+ }
83
+ }
84
+ /** Force the panel open. */
85
+ async openPanel() {
86
+ if (this.open())
87
+ return;
88
+ await this.toggle();
89
+ }
90
+ /** Force the panel closed. Keeps the WS open so reopening is instant. */
91
+ closePanel() {
92
+ this.open.set(false);
93
+ }
94
+ /** Send a visitor message. No-op if the WS isn't connected. */
95
+ sendMessage(content) {
96
+ const trimmed = content.trim();
97
+ if (!trimmed || !this.ws)
98
+ return;
99
+ const clientMsgId = `c_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
100
+ this.messages.update((prev) => [
101
+ ...prev,
102
+ {
103
+ id: clientMsgId,
104
+ client_msg_id: clientMsgId,
105
+ from: 'visitor',
106
+ content: trimmed,
107
+ pending: true,
108
+ },
109
+ ]);
110
+ this.ws.send({ type: 'msg', client_msg_id: clientMsgId, content: trimmed });
111
+ }
112
+ // ── Internals ──────────────────────────────────────────────────────────
113
+ async loadBootstrap() {
114
+ if (!this.isBrowser)
115
+ return null;
116
+ if (this.bootstrap())
117
+ return this.bootstrap();
118
+ if (this.bootstrapPromise)
119
+ return this.bootstrapPromise;
120
+ this.bootstrapPromise = (async () => {
121
+ try {
122
+ const client = this.getClient();
123
+ const cfg = await client.fetchWidgetConfig({
124
+ tenantId: this.config.tenantId,
125
+ widgetPublicKey: this.config.publicKey,
126
+ origin: this.resolvedOrigin(),
127
+ });
128
+ this.bootstrap.set(cfg);
129
+ return cfg;
130
+ }
131
+ catch (err) {
132
+ const msg = err instanceof Error ? err.message : 'unknown error';
133
+ this.fatalError.set(`Bootstrap failed: ${msg}`);
134
+ return null;
135
+ }
136
+ })();
137
+ return this.bootstrapPromise;
138
+ }
139
+ async ensureSession() {
140
+ if (!this.isBrowser)
141
+ return null;
142
+ if (this.session())
143
+ return this.session();
144
+ if (this.sessionPromise)
145
+ return this.sessionPromise;
146
+ this.connecting.set(true);
147
+ this.sessionPromise = (async () => {
148
+ try {
149
+ // Make sure bootstrap is resolved first — the visibility/online
150
+ // flags it carries influence whether we connect at all.
151
+ await this.loadBootstrap();
152
+ const client = this.getClient();
153
+ const session = await client.createVisitorSession({
154
+ tenantId: this.config.tenantId,
155
+ widgetPublicKey: this.config.publicKey,
156
+ origin: this.resolvedOrigin(),
157
+ locale: this.config.locale ?? 'fr',
158
+ metadata: this.config.metadata ?? {},
159
+ });
160
+ this.session.set(session);
161
+ this.openWebSocket(session);
162
+ // Optional: identify the visitor server-side if the consumer wired
163
+ // a token provider.
164
+ this.tryIdentify(session).catch(() => undefined);
165
+ return session;
166
+ }
167
+ catch (err) {
168
+ const msg = err instanceof Error ? err.message : 'unknown error';
169
+ this.fatalError.set(`Session failed: ${msg}`);
170
+ return null;
171
+ }
172
+ finally {
173
+ this.connecting.set(false);
174
+ }
175
+ })();
176
+ return this.sessionPromise;
177
+ }
178
+ openWebSocket(session) {
179
+ if (this.ws)
180
+ return;
181
+ const ws = new WSClient({
182
+ baseUrl: this.config.baseUrl,
183
+ visitorToken: session.visitor_token,
184
+ heartbeatIntervalMs: 30_000,
185
+ }, {
186
+ onWelcome: () => undefined,
187
+ onMessage: (frame) => {
188
+ this.messages.update((prev) => [
189
+ ...prev,
190
+ { id: frame.id, from: frame.from, content: frame.content },
191
+ ]);
192
+ },
193
+ onAck: (frame) => {
194
+ this.messages.update((prev) => prev.map((m) => m.client_msg_id === frame.client_msg_id
195
+ ? { ...m, id: frame.server_msg_id, pending: false }
196
+ : m));
197
+ },
198
+ onTyping: (frame) => {
199
+ if (frame.from === 'bot' || frame.from === 'agent') {
200
+ this.botTyping.set(frame.state === 'start');
201
+ }
202
+ },
203
+ onError: (frame) => {
204
+ this.messages.update((prev) => [
205
+ ...prev,
206
+ { id: `err_${Date.now()}`, from: 'system', content: `Error: ${frame.message}` },
207
+ ]);
208
+ },
209
+ // Phase 10c — agent takeover visibility. Renders a system message
210
+ // and clears any stale bot-typing indicator (the bot's mid-stream
211
+ // turn may have been pre-empted by the operator claim).
212
+ onAgentJoined: (frame) => {
213
+ this.messages.update((prev) => [
214
+ ...prev,
215
+ {
216
+ id: `sys_join_${Date.now()}`,
217
+ from: 'system',
218
+ content: `${frame.agent_display_name} has joined the chat.`,
219
+ },
220
+ ]);
221
+ this.botTyping.set(false);
222
+ },
223
+ onAgentLeft: () => {
224
+ this.messages.update((prev) => [
225
+ ...prev,
226
+ {
227
+ id: `sys_left_${Date.now()}`,
228
+ from: 'system',
229
+ content: 'The agent has left the chat.',
230
+ },
231
+ ]);
232
+ this.botTyping.set(false);
233
+ },
234
+ // Phase 10f — chat_api opened a support ticket.
235
+ onTicketCreated: (frame) => {
236
+ const sla = frame.sla_hours === 1
237
+ ? 'within 1h'
238
+ : frame.sla_hours <= 24
239
+ ? `within ${frame.sla_hours}h`
240
+ : `within ${Math.round(frame.sla_hours / 24)}d`;
241
+ this.messages.update((prev) => [
242
+ ...prev,
243
+ {
244
+ id: `sys_ticket_${frame.ticket_id}`,
245
+ from: 'system',
246
+ content: `🎫 Support ticket #${frame.ticket_number} created — ` +
247
+ `${frame.category} · ${frame.priority}. ` +
248
+ `We'll follow up ${sla}.`,
249
+ },
250
+ ]);
251
+ },
252
+ });
253
+ this.ws = ws;
254
+ ws.connect();
255
+ }
256
+ async tryIdentify(session) {
257
+ const provider = this.config.visitorTokenProvider;
258
+ if (!provider)
259
+ return;
260
+ let raw;
261
+ const result = provider();
262
+ if (typeof result === 'string') {
263
+ raw = result;
264
+ }
265
+ else if (isObservable(result)) {
266
+ raw = await firstValueFrom(result);
267
+ }
268
+ else {
269
+ raw = await result;
270
+ }
271
+ if (!raw)
272
+ return;
273
+ // Caller's token is `userId:signature` (colon-delimited). Falling back
274
+ // to plain user_id when no colon is present is too easy to misuse —
275
+ // require explicit signature to avoid identity forgery footguns.
276
+ const sep = raw.indexOf(':');
277
+ if (sep < 0) {
278
+ this.fatalError.set('visitorTokenProvider returned a value without "userId:signature" — refusing to identify.');
279
+ return;
280
+ }
281
+ const userId = raw.slice(0, sep);
282
+ const userSignature = raw.slice(sep + 1);
283
+ await this.getClient().identifyVisitor({
284
+ visitorToken: session.visitor_token,
285
+ userId,
286
+ userSignature,
287
+ });
288
+ }
289
+ getClient() {
290
+ if (!this.client) {
291
+ this.client = new ChatClient({ baseUrl: this.config.baseUrl });
292
+ }
293
+ return this.client;
294
+ }
295
+ resolvedOrigin() {
296
+ if (this.config.origin)
297
+ return this.config.origin;
298
+ if (this.isBrowser && typeof window !== 'undefined') {
299
+ return window.location.origin;
300
+ }
301
+ // SSR-safe fallback; the relay validates origins server-side anyway.
302
+ return '';
303
+ }
304
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
305
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatService, providedIn: 'root' });
306
+ }
307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: BloonioChatService, decorators: [{
308
+ type: Injectable,
309
+ args: [{ providedIn: 'root' }]
310
+ }], ctorParameters: () => [] });
311
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvb25pby1jaGF0LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL2Jsb29uaW8tY2hhdC5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JHO0FBRUgsT0FBTyxFQUNMLGVBQWUsRUFDZixRQUFRLEVBQ1IsVUFBVSxFQUNWLE1BQU0sRUFDTixVQUFVLEVBQ1YsTUFBTSxHQUVQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDNUMsT0FBTyxFQUFFLGNBQWMsRUFBRSxZQUFZLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFcEQsT0FBTyxFQUNMLFVBQVUsRUFDVixRQUFRLEdBSVQsTUFBTSwyQkFBMkIsQ0FBQztBQUVuQyxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQzs7QUFXL0QsTUFBTSxPQUFPLGtCQUFrQjtJQUNaLE1BQU0sR0FBRyx1QkFBdUIsRUFBRSxDQUFDO0lBQ25DLFVBQVUsR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDakMsVUFBVSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUVqRCxnRUFBZ0U7SUFDL0MsU0FBUyxHQUFHLGlCQUFpQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUVoRSx3RUFBd0U7SUFDL0QsU0FBUyxHQUFHLE1BQU0sQ0FBK0IsSUFBSSxDQUFDLENBQUM7SUFDaEUsMEVBQTBFO0lBQ2pFLE9BQU8sR0FBRyxNQUFNLENBQXNDLElBQUksQ0FBQyxDQUFDO0lBQ3JFLG9DQUFvQztJQUMzQixRQUFRLEdBQUcsTUFBTSxDQUFnQixFQUFFLENBQUMsQ0FBQztJQUM5QyxnRUFBZ0U7SUFDdkQsU0FBUyxHQUFHLE1BQU0s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QU0sS0FBSyxXQUFXLEVBQUUsQ0FBQztZQUNwRCxPQUFPLE1BQU0sQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDO1FBQ2hDLENBQUM7UUFDRCxxRUFBcUU7UUFDckUsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO3dHQXRTVSxrQkFBa0I7NEdBQWxCLGtCQUFrQixjQURMLE1BQU07OzRGQUNuQixrQkFBa0I7a0JBRDlCLFVBQVU7bUJBQUMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBCbG9vbmlvQ2hhdFNlcnZpY2Ug4oCUIHNpZ25hbHMtYmFzZWQgcmVhY3RpdmUgc3RhdGUgY29udGFpbmVyLlxuICpcbiAqIE1pcnJvcnMgdGhlIFByZWFjdCB3aWRnZXQncyBgV2lkZ2V0LnRzeGAgc3RhdGUgbWFjaGluZSBidXQgdXNlcyBBbmd1bGFyXG4gKiBzaWduYWxzIHNvIHRlbXBsYXRlcyBhdXRvLXRyYWNrIHRoZSBzbG90cyB0aGV5IHJlYWQuIFB1YmxpYyBzdXJmYWNlOlxuICpcbiAqICAgcmVhZG9ubHkgYm9vdHN0cmFwID0gc2lnbmFsPFdpZGdldEJvb3RzdHJhcENvbmZpZyB8IG51bGw+KG51bGwpO1xuICogICByZWFkb25seSBzZXNzaW9uICAgPSBzaWduYWw8Q3JlYXRlVmlzaXRvclNlc3Npb25SZXNwb25zZSB8IG51bGw+KG51bGwpO1xuICogICByZWFkb25seSBtZXNzYWdlcyAgPSBzaWduYWw8Q2hhdE1lc3NhZ2VbXT4oW10pO1xuICogICByZWFkb25seSBib3RUeXBpbmcgPSBzaWduYWwoZmFsc2UpO1xuICogICByZWFkb25seSBvcGVuICAgICAgPSBzaWduYWwoZmFsc2UpO1xuICogICByZWFkb25seSBjb25uZWN0aW5nPSBzaWduYWwoZmFsc2UpO1xuICogICByZWFkb25seSB1bnJlYWRDb3VudCA9IGNvbXB1dGVkKC4uLik7XG4gKlxuICogICBhc3luYyBvcGVuKCk6IFByb21pc2U8dm9pZD5cbiAqICAgY2xvc2UoKTogdm9pZFxuICogICBhc3luYyB0b2dnbGUoKTogUHJvbWlzZTx2b2lkPlxuICogICBzZW5kTWVzc2FnZSh0ZXh0OiBzdHJpbmcpOiB2b2lkXG4gKlxuICogU1NSLXNhZmU6IGV2ZXJ5IHRyYW5zcG9ydCBjYWxsIChmZXRjaCwgV2ViU29ja2V0KSBpcyBnYXRlZCBiZWhpbmQgYVxuICogcGxhdGZvcm0gY2hlY2sgdmlhIGBhZnRlck5leHRSZW5kZXIoKWAgc28gc2VydmVyIHByZS1yZW5kZXJpbmcgZG9lc24ndFxuICogdHJ5IHRvIG9wZW4gc29ja2V0cy5cbiAqL1xuXG5pbXBvcnQge1xuICBhZnRlck5leHRSZW5kZXIsXG4gIGNvbXB1dGVkLFxuICBEZXN0cm95UmVmLFxuICBpbmplY3QsXG4gIEluamVjdGFibGUsXG4gIHNpZ25hbCxcbiAgdHlwZSBTaWduYWwsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgaXNQbGF0Zm9ybUJyb3dzZXIgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgUExBVEZPUk1fSUQgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IGZpcnN0VmFsdWVGcm9tLCBpc09ic2VydmFibGUgfSBmcm9tICdyeGpzJztcblxuaW1wb3J0IHtcbiAgQ2hhdENsaWVudCxcbiAgV1NDbGllbnQsXG4gIHR5cGUgQ3JlYXRlVmlzaXRvclNlc3Npb25SZXNwb25zZSxcbiAgdHlwZSBTZXJ2ZXJNc2dGcmFtZSxcbiAgdHlwZSBXaWRnZXRCb290c3RyYXBDb25maWcsXG59IGZyb20gJ0BibG9vbmlvL2NoYXQtY2xpZW50LWNvcmUnO1xuXG5pbXBvcnQgeyBpbmplY3RCbG9vbmlvQ2hhdENvbmZpZyB9IGZyb20gJy4vYmxvb25pby1jaGF0LnRva2VuJztcblxuZXhwb3J0IGludGVyZmFjZSBDaGF0TWVzc2FnZSB7XG4gIGlkOiBzdHJpbmc7XG4gIGZyb206ICd2aXNpdG9yJyB8ICdib3QnIHwgJ2FnZW50JyB8ICdzeXN0ZW0nO1xuICBjb250ZW50OiBzdHJpbmc7XG4gIHBlbmRpbmc/OiBib29sZWFuO1xuICBjbGllbnRfbXNnX2lkPzogc3RyaW5nO1xufVxuXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIEJsb29uaW9DaGF0U2VydmljZSB7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnID0gaW5qZWN0Qmxvb25pb0NoYXRDb25maWcoKTtcbiAgcHJpdmF0ZSByZWFkb25seSBwbGF0Zm9ybUlkID0gaW5qZWN0KFBMQVRGT1JNX0lEKTtcbiAgcHJpdmF0ZSByZWFkb25seSBkZXN0cm95UmVmID0gaW5qZWN0KERlc3Ryb3lSZWYpO1xuXG4gIC8qKiBUcnVlIHdoZW4gcnVubmluZyBpbiB0aGUgYnJvd3NlciAodnMuIHNlcnZlciBwcmUtcmVuZGVyKS4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBpc0Jyb3dzZXIgPSBpc1BsYXRmb3JtQnJvd3Nlcih0aGlzLnBsYXRmb3JtSWQpO1xuXG4gIC8qKiBCb290c3RyYXAgY29uZmlnIOKAlCBwb3B1bGF0ZWQgYnkgdGhlIGZpcnN0IGBsb2FkQm9vdHN0cmFwKClgIGNhbGwuICovXG4gIHJlYWRvbmx5IGJvb3RzdHJhcCA9IHNpZ25hbDxXaWRnZXRCb290c3RyYXBDb25maWcgfCBudWxsPihudWxsKTtcbiAgLyoqIFZpc2l0b3Igc2Vzc2lvbiAoaWQgKyBKV1QgKyBleHBpcmVzX2F0KSDigJQgc2V0IGxhemlseSBvbiBmaXJzdCBvcGVuLiAqL1xuICByZWFkb25seSBzZXNzaW9uID0gc2lnbmFsPENyZWF0ZVZpc2l0b3JTZXNzaW9uUmVzcG9uc2UgfCBudWxsPihudWxsKTtcbiAgLyoqIE1lc3NhZ2UgbG9nLCBvbGRlc3Qg4oaSIG5ld2VzdC4gKi9cbiAgcmVhZG9ubHkgbWVzc2FnZXMgPSBzaWduYWw8Q2hhdE1lc3NhZ2VbXT4oW10pO1xuICAvKiogVHJ1ZSBiZXR3ZWVuIGJvdCdzIHR5cGluZyhzdGFydCkgYW5kIHR5cGluZyhzdG9wKSBmcmFtZXMuICovXG4gIHJlYWRvbmx5IGJvdFR5cGluZyA9IHNpZ25hbChmYWxzZSk7XG4gIC8qKiBQYW5lbCB2aXNpYmlsaXR5IOKAlCBkcml2ZXMgTGF1bmNoZXIgLyBQYW5lbCByZW5kZXJpbmcuICovXG4gIHJlYWRvbmx5IG9wZW4gPSBzaWduYWwoZmFsc2UpO1xuICAvKiogVHJ1ZSB3aGlsZSBgZW5zdXJlU2Vzc2lvbigpYCBpcyBpbiBmbGlnaHQgKGluaXRpYWwgV1MgaGFuZHNoYWtlKS4gKi9cbiAgcmVhZG9ubHkgY29ubmVjdGluZyA9IHNpZ25hbChmYWxzZSk7XG4gIC8qKiBTZXQgd2hlbiB0aGUgV1MgaGFuZHNoYWtlIGZhaWxzIHBlcm1hbmVudGx5IChlLmcuIHRlbmFudCBzdXNwZW5kZWQpLiAqL1xuICByZWFkb25seSBmYXRhbEVycm9yID0gc2lnbmFsPHN0cmluZyB8IG51bGw+KG51bGwpO1xuXG4gIC8qKiBOdW1iZXIgb2YgYm90L2FnZW50IG1lc3NhZ2VzIHJlY2VpdmVkIHdoaWxlIHRoZSBwYW5lbCB3YXMgY2xvc2VkLlxuICAgKiAgUmVzZXRzIHRvIDAgdGhlIG1vbWVudCB0aGUgcGFuZWwgb3BlbnMuICovXG4gIHByaXZhdGUgcmVhZG9ubHkgX3NlZW5Db3VudCA9IHNpZ25hbCgwKTtcbiAgcmVhZG9ubHkgdW5yZWFkQ291bnQ6IFNpZ25hbDxudW1iZXI+ID0gY29tcHV0ZWQoKCkgPT4ge1xuICAgIGlmICh0aGlzLm9wZW4oKSkgcmV0dXJuIDA7XG4gICAgY29uc3QgdG90YWwgPSB0aGlzLm1lc3NhZ2VzKCkuZmlsdGVyKFxuICAgICAgKG0pID0+IG0uZnJvbSA9PT0gJ2JvdCcgfHwgbS5mcm9tID09PSAnYWdlbnQnLFxuICAgICkubGVuZ3RoO1xuICAgIHJldHVybiBNYXRoLm1heCgwLCB0b3RhbCAtIHRoaXMuX3NlZW5Db3VudCgpKTtcbiAgfSk7XG5cbiAgcHJpdmF0ZSBjbGllbnQ6IENoYXRDbGllbnQgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSB3czogV1NDbGllbnQgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBib290c3RyYXBQcm9taXNlOiBQcm9taXNlPFdpZGdldEJvb3RzdHJhcENvbmZpZyB8IG51bGw+IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgc2Vzc2lvblByb21pc2U6IFByb21pc2U8Q3JlYXRlVmlzaXRvclNlc3Npb25SZXNwb25zZSB8IG51bGw+IHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgLy8gU1NSIHNhZmV0eTogc2NoZWR1bGUgdGhlIGluaXRpYWwgYm9vdHN0cmFwIGZldGNoIG9uIGZpcnN0IGJyb3dzZXIgcmVuZGVyLlxuICAgIGFmdGVyTmV4dFJlbmRlcigoKSA9PiB7XG4gICAgICB0aGlzLmxvYWRCb290c3RyYXAoKTtcbiAgICB9KTtcblxuICAgIHRoaXMuZGVzdHJveVJlZi5vbkRlc3Ryb3koKCkgPT4ge1xuICAgICAgdGhpcy53cz8uY2xvc2UoKTtcbiAgICAgIHRoaXMud3MgPSBudWxsO1xuICAgIH0pO1xuICB9XG5cbiAgLy8g4pSA4pSAIFB1YmxpYyBtZXRob2RzIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKiBUb2dnbGUgcGFuZWwgdmlzaWJpbGl0eS4gRmlyc3Qgb3BlbiBhbHNvIG9wZW5zIHRoZSBXUyBzZXNzaW9uLiAqL1xuICBhc3luYyB0b2dnbGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbmV4dCA9ICF0aGlzLm9wZW4oKTtcbiAgICB0aGlzLm9wZW4uc2V0KG5leHQpO1xuICAgIGlmIChuZXh0KSB7XG4gICAgICB0aGlzLl9zZWVuQ291bnQuc2V0KFxuICAgICAgICB0aGlzLm1lc3NhZ2VzKCkuZmlsdGVyKChtKSA9PiBtLmZyb20gPT09ICdib3QnIHx8IG0uZnJvbSA9PT0gJ2FnZW50JykubGVuZ3RoLFxuICAgICAgKTtcbiAgICAgIGF3YWl0IHRoaXMuZW5zdXJlU2Vzc2lvbigpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBGb3JjZSB0aGUgcGFuZWwgb3Blbi4gKi9cbiAgYXN5bmMgb3BlblBhbmVsKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLm9wZW4oKSkgcmV0dXJuO1xuICAgIGF3YWl0IHRoaXMudG9nZ2xlKCk7XG4gIH1cblxuICAvKiogRm9yY2UgdGhlIHBhbmVsIGNsb3NlZC4gS2VlcHMgdGhlIFdTIG9wZW4gc28gcmVvcGVuaW5nIGlzIGluc3RhbnQuICovXG4gIGNsb3NlUGFuZWwoKTogdm9pZCB7XG4gICAgdGhpcy5vcGVuLnNldChmYWxzZSk7XG4gIH1cblxuICAvKiogU2VuZCBhIHZpc2l0b3IgbWVzc2FnZS4gTm8tb3AgaWYgdGhlIFdTIGlzbid0IGNvbm5lY3RlZC4gKi9cbiAgc2VuZE1lc3NhZ2UoY29udGVudDogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgdHJpbW1lZCA9IGNvbnRlbnQudHJpbSgpO1xuICAgIGlmICghdHJpbW1lZCB8fCAhdGhpcy53cykgcmV0dXJuO1xuICAgIGNvbnN0IGNsaWVudE1zZ0lkID0gYGNfJHtEYXRlLm5vdygpfV8ke01hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnNsaWNlKDIsIDgpfWA7XG4gICAgdGhpcy5tZXNzYWdlcy51cGRhdGUoKHByZXYpID0+IFtcbiAgICAgIC4uLnByZXYsXG4gICAgICB7XG4gICAgICAgIGlkOiBjbGllbnRNc2dJZCxcbiAgICAgICAgY2xpZW50X21zZ19pZDogY2xpZW50TXNnSWQsXG4gICAgICAgIGZyb206ICd2aXNpdG9yJyxcbiAgICAgICAgY29udGVudDogdHJpbW1lZCxcbiAgICAgICAgcGVuZGluZzogdHJ1ZSxcbiAgICAgIH0sXG4gICAgXSk7XG4gICAgdGhpcy53cy5zZW5kKHsgdHlwZTogJ21zZycsIGNsaWVudF9tc2dfaWQ6IGNsaWVudE1zZ0lkLCBjb250ZW50OiB0cmltbWVkIH0pO1xuICB9XG5cbiAgLy8g4pSA4pSAIEludGVybmFscyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICBwcml2YXRlIGFzeW5jIGxvYWRCb290c3RyYXAoKTogUHJvbWlzZTxXaWRnZXRCb290c3RyYXBDb25maWcgfCBudWxsPiB7XG4gICAgaWYgKCF0aGlzLmlzQnJvd3NlcikgcmV0dXJuIG51bGw7XG4gICAgaWYgKHRoaXMuYm9vdHN0cmFwKCkpIHJldHVybiB0aGlzLmJvb3RzdHJhcCgpO1xuICAgIGlmICh0aGlzLmJvb3RzdHJhcFByb21pc2UpIHJldHVybiB0aGlzLmJvb3RzdHJhcFByb21pc2U7XG5cbiAgICB0aGlzLmJvb3RzdHJhcFByb21pc2UgPSAoYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY2xpZW50ID0gdGhpcy5nZXRDbGllbnQoKTtcbiAgICAgICAgY29uc3QgY2ZnID0gYXdhaXQgY2xpZW50LmZldGNoV2lkZ2V0Q29uZmlnKHtcbiAgICAgICAgICB0ZW5hbnRJZDogdGhpcy5jb25maWcudGVuYW50SWQsXG4gICAgICAgICAgd2lkZ2V0UHVibGljS2V5OiB0aGlzLmNvbmZpZy5wdWJsaWNLZXksXG4gICAgICAgICAgb3JpZ2luOiB0aGlzLnJlc29sdmVkT3JpZ2luKCksXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmJvb3RzdHJhcC5zZXQoY2ZnKTtcbiAgICAgICAgcmV0dXJuIGNmZztcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zdCBtc2cgPSBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogJ3Vua25vd24gZXJyb3InO1xuICAgICAgICB0aGlzLmZhdGFsRXJyb3Iuc2V0KGBCb290c3RyYXAgZmFpbGVkOiAke21zZ31gKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgfSkoKTtcbiAgICByZXR1cm4gdGhpcy5ib290c3RyYXBQcm9taXNlO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVTZXNzaW9uKCk6IFByb21pc2U8Q3JlYXRlVmlzaXRvclNlc3Npb25SZXNwb25zZSB8IG51bGw+IHtcbiAgICBpZiAoIXRoaXMuaXNCcm93c2VyKSByZXR1cm4gbnVsbDtcbiAgICBpZiAodGhpcy5zZXNzaW9uKCkpIHJldHVybiB0aGlzLnNlc3Npb24oKTtcbiAgICBpZiAodGhpcy5zZXNzaW9uUHJvbWlzZSkgcmV0dXJuIHRoaXMuc2Vzc2lvblByb21pc2U7XG5cbiAgICB0aGlzLmNvbm5lY3Rpbmcuc2V0KHRydWUpO1xuICAgIHRoaXMuc2Vzc2lvblByb21pc2UgPSAoYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gTWFrZSBzdXJlIGJvb3RzdHJhcCBpcyByZXNvbHZlZCBmaXJzdCDigJQgdGhlIHZpc2liaWxpdHkvb25saW5lXG4gICAgICAgIC8vIGZsYWdzIGl0IGNhcnJpZXMgaW5mbHVlbmNlIHdoZXRoZXIgd2UgY29ubmVjdCBhdCBhbGwuXG4gICAgICAgIGF3YWl0IHRoaXMubG9hZEJvb3RzdHJhcCgpO1xuXG4gICAgICAgIGNvbnN0IGNsaWVudCA9IHRoaXMuZ2V0Q2xpZW50KCk7XG4gICAgICAgIGNvbnN0IHNlc3Npb24gPSBhd2FpdCBjbGllbnQuY3JlYXRlVmlzaXRvclNlc3Npb24oe1xuICAgICAgICAgIHRlbmFudElkOiB0aGlzLmNvbmZpZy50ZW5hbnRJZCxcbiAgICAgICAgICB3aWRnZXRQdWJsaWNLZXk6IHRoaXMuY29uZmlnLnB1YmxpY0tleSxcbiAgICAgICAgICBvcmlnaW46IHRoaXMucmVzb2x2ZWRPcmlnaW4oKSxcbiAgICAgICAgICBsb2NhbGU6IHRoaXMuY29uZmlnLmxvY2FsZSA/PyAnZnInLFxuICAgICAgICAgIG1ldGFkYXRhOiB0aGlzLmNvbmZpZy5tZXRhZGF0YSA/PyB7fSxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuc2Vzc2lvbi5zZXQoc2Vzc2lvbik7XG4gICAgICAgIHRoaXMub3BlbldlYlNvY2tldChzZXNzaW9uKTtcbiAgICAgICAgLy8gT3B0aW9uYWw6IGlkZW50aWZ5IHRoZSB2aXNpdG9yIHNlcnZlci1zaWRlIGlmIHRoZSBjb25zdW1lciB3aXJlZFxuICAgICAgICAvLyBhIHRva2VuIHByb3ZpZGVyLlxuICAgICAgICB0aGlzLnRyeUlkZW50aWZ5KHNlc3Npb24pLmNhdGNoKCgpID0+IHVuZGVmaW5lZCk7XG4gICAgICAgIHJldHVybiBzZXNzaW9uO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGNvbnN0IG1zZyA9IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiAndW5rbm93biBlcnJvcic7XG4gICAgICAgIHRoaXMuZmF0YWxFcnJvci5zZXQoYFNlc3Npb24gZmFpbGVkOiAke21zZ31gKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICB0aGlzLmNvbm5lY3Rpbmcuc2V0KGZhbHNlKTtcbiAgICAgIH1cbiAgICB9KSgpO1xuICAgIHJldHVybiB0aGlzLnNlc3Npb25Qcm9taXNlO1xuICB9XG5cbiAgcHJpdmF0ZSBvcGVuV2ViU29ja2V0KHNlc3Npb246IENyZWF0ZVZpc2l0b3JTZXNzaW9uUmVzcG9uc2UpOiB2b2lkIHtcbiAgICBpZiAodGhpcy53cykgcmV0dXJuO1xuICAgIGNvbnN0IHdzID0gbmV3IFdTQ2xpZW50KFxuICAgICAge1xuICAgICAgICBiYXNlVXJsOiB0aGlzLmNvbmZpZy5iYXNlVXJsLFxuICAgICAgICB2aXNpdG9yVG9rZW46IHNlc3Npb24udmlzaXRvcl90b2tlbixcbiAgICAgICAgaGVhcnRiZWF0SW50ZXJ2YWxNczogMzBfMDAwLFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgb25XZWxjb21lOiAoKSA9PiB1bmRlZmluZWQsXG4gICAgICAgIG9uTWVzc2FnZTogKGZyYW1lOiBTZXJ2ZXJNc2dGcmFtZSkgPT4ge1xuICAgICAgICAgIHRoaXMubWVzc2FnZXMudXBkYXRlKChwcmV2KSA9PiBbXG4gICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgeyBpZDogZnJhbWUuaWQsIGZyb206IGZyYW1lLmZyb20sIGNvbnRlbnQ6IGZyYW1lLmNvbnRlbnQgfSxcbiAgICAgICAgICBdKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25BY2s6IChmcmFtZSkgPT4ge1xuICAgICAgICAgIHRoaXMubWVzc2FnZXMudXBkYXRlKChwcmV2KSA9PlxuICAgICAgICAgICAgcHJldi5tYXAoKG0pID0+XG4gICAgICAgICAgICAgIG0uY2xpZW50X21zZ19pZCA9PT0gZnJhbWUuY2xpZW50X21zZ19pZFxuICAgICAgICAgICAgICAgID8geyAuLi5tLCBpZDogZnJhbWUuc2VydmVyX21zZ19pZCwgcGVuZGluZzogZmFsc2UgfVxuICAgICAgICAgICAgICAgIDogbSxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25UeXBpbmc6IChmcmFtZSkgPT4ge1xuICAgICAgICAgIGlmIChmcmFtZS5mcm9tID09PSAnYm90JyB8fCBmcmFtZS5mcm9tID09PSAnYWdlbnQnKSB7XG4gICAgICAgICAgICB0aGlzLmJvdFR5cGluZy5zZXQoZnJhbWUuc3RhdGUgPT09ICdzdGFydCcpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgb25FcnJvcjogKGZyYW1lKSA9PiB7XG4gICAgICAgICAgdGhpcy5tZXNzYWdlcy51cGRhdGUoKHByZXYpID0+IFtcbiAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICB7IGlkOiBgZXJyXyR7RGF0ZS5ub3coKX1gLCBmcm9tOiAnc3lzdGVtJywgY29udGVudDogYEVycm9yOiAke2ZyYW1lLm1lc3NhZ2V9YCB9LFxuICAgICAgICAgIF0pO1xuICAgICAgICB9LFxuICAgICAgICAvLyBQaGFzZSAxMGMg4oCUIGFnZW50IHRha2VvdmVyIHZpc2liaWxpdHkuIFJlbmRlcnMgYSBzeXN0ZW0gbWVzc2FnZVxuICAgICAgICAvLyBhbmQgY2xlYXJzIGFueSBzdGFsZSBib3QtdHlwaW5nIGluZGljYXRvciAodGhlIGJvdCdzIG1pZC1zdHJlYW1cbiAgICAgICAgLy8gdHVybiBtYXkgaGF2ZSBiZWVuIHByZS1lbXB0ZWQgYnkgdGhlIG9wZXJhdG9yIGNsYWltKS5cbiAgICAgICAgb25BZ2VudEpvaW5lZDogKGZyYW1lKSA9PiB7XG4gICAgICAgICAgdGhpcy5tZXNzYWdlcy51cGRhdGUoKHByZXYpID0+IFtcbiAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGlkOiBgc3lzX2pvaW5fJHtEYXRlLm5vdygpfWAsXG4gICAgICAgICAgICAgIGZyb206ICdzeXN0ZW0nLFxuICAgICAgICAgICAgICBjb250ZW50OiBgJHtmcmFtZS5hZ2VudF9kaXNwbGF5X25hbWV9IGhhcyBqb2luZWQgdGhlIGNoYXQuYCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSk7XG4gICAgICAgICAgdGhpcy5ib3RUeXBpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25BZ2VudExlZnQ6ICgpID0+IHtcbiAgICAgICAgICB0aGlzLm1lc3NhZ2VzLnVwZGF0ZSgocHJldikgPT4gW1xuICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgaWQ6IGBzeXNfbGVmdF8ke0RhdGUubm93KCl9YCxcbiAgICAgICAgICAgICAgZnJvbTogJ3N5c3RlbScsXG4gICAgICAgICAgICAgIGNvbnRlbnQ6ICdUaGUgYWdlbnQgaGFzIGxlZnQgdGhlIGNoYXQuJyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSk7XG4gICAgICAgICAgdGhpcy5ib3RUeXBpbmcuc2V0KGZhbHNlKTtcbiAgICAgICAgfSxcbiAgICAgICAgLy8gUGhhc2UgMTBmIOKAlCBjaGF0X2FwaSBvcGVuZWQgYSBzdXBwb3J0IHRpY2tldC5cbiAgICAgICAgb25UaWNrZXRDcmVhdGVkOiAoZnJhbWUpID0+IHtcbiAgICAgICAgICBjb25zdCBzbGEgPVxuICAgICAgICAgICAgZnJhbWUuc2xhX2hvdXJzID09PSAxXG4gICAgICAgICAgICAgID8gJ3dpdGhpbiAxaCdcbiAgICAgICAgICAgICAgOiBmcmFtZS5zbGFfaG91cnMgPD0gMjRcbiAgICAgICAgICAgICAgICA/IGB3aXRoaW4gJHtmcmFtZS5zbGFfaG91cnN9aGBcbiAgICAgICAgICAgICAgICA6IGB3aXRoaW4gJHtNYXRoLnJvdW5kKGZyYW1lLnNsYV9ob3VycyAvIDI0KX1kYDtcbiAgICAgICAgICB0aGlzLm1lc3NhZ2VzLnVwZGF0ZSgocHJldikgPT4gW1xuICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgaWQ6IGBzeXNfdGlja2V0XyR7ZnJhbWUudGlja2V0X2lkfWAsXG4gICAgICAgICAgICAgIGZyb206ICdzeXN0ZW0nLFxuICAgICAgICAgICAgICBjb250ZW50OlxuICAgICAgICAgICAgICAgIGDwn46rIFN1cHBvcnQgdGlja2V0ICMke2ZyYW1lLnRpY2tldF9udW1iZXJ9IGNyZWF0ZWQg4oCUIGAgK1xuICAgICAgICAgICAgICAgIGAke2ZyYW1lLmNhdGVnb3J5fSDCtyAke2ZyYW1lLnByaW9yaXR5fS4gYCArXG4gICAgICAgICAgICAgICAgYFdlJ2xsIGZvbGxvdyB1cCAke3NsYX0uYCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSk7XG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICk7XG4gICAgdGhpcy53cyA9IHdzO1xuICAgIHdzLmNvbm5lY3QoKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgdHJ5SWRlbnRpZnkoXG4gICAgc2Vzc2lvbjogQ3JlYXRlVmlzaXRvclNlc3Npb25SZXNwb25zZSxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcHJvdmlkZXIgPSB0aGlzLmNvbmZpZy52aXNpdG9yVG9rZW5Qcm92aWRlcjtcbiAgICBpZiAoIXByb3ZpZGVyKSByZXR1cm47XG5cbiAgICBsZXQgcmF3OiBzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkO1xuICAgIGNvbnN0IHJlc3VsdCA9IHByb3ZpZGVyKCk7XG4gICAgaWYgKHR5cGVvZiByZXN1bHQgPT09ICdzdHJpbmcnKSB7XG4gICAgICByYXcgPSByZXN1bHQ7XG4gICAgfSBlbHNlIGlmIChpc09ic2VydmFibGUocmVzdWx0KSkge1xuICAgICAgcmF3ID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20ocmVzdWx0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmF3ID0gYXdhaXQgcmVzdWx0O1xuICAgIH1cbiAgICBpZiAoIXJhdykgcmV0dXJuO1xuXG4gICAgLy8gQ2FsbGVyJ3MgdG9rZW4gaXMgYHVzZXJJZDpzaWduYXR1cmVgIChjb2xvbi1kZWxpbWl0ZWQpLiBGYWxsaW5nIGJhY2tcbiAgICAvLyB0byBwbGFpbiB1c2VyX2lkIHdoZW4gbm8gY29sb24gaXMgcHJlc2VudCBpcyB0b28gZWFzeSB0byBtaXN1c2Ug4oCUXG4gICAgLy8gcmVxdWlyZSBleHBsaWNpdCBzaWduYXR1cmUgdG8gYXZvaWQgaWRlbnRpdHkgZm9yZ2VyeSBmb290Z3Vucy5cbiAgICBjb25zdCBzZXAgPSByYXcuaW5kZXhPZignOicpO1xuICAgIGlmIChzZXAgPCAwKSB7XG4gICAgICB0aGlzLmZhdGFsRXJyb3Iuc2V0KFxuICAgICAgICAndmlzaXRvclRva2VuUHJvdmlkZXIgcmV0dXJuZWQgYSB2YWx1ZSB3aXRob3V0IFwidXNlcklkOnNpZ25hdHVyZVwiIOKAlCByZWZ1c2luZyB0byBpZGVudGlmeS4nLFxuICAgICAgKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdXNlcklkID0gcmF3LnNsaWNlKDAsIHNlcCk7XG4gICAgY29uc3QgdXNlclNpZ25hdHVyZSA9IHJhdy5zbGljZShzZXAgKyAxKTtcbiAgICBhd2FpdCB0aGlzLmdldENsaWVudCgpLmlkZW50aWZ5VmlzaXRvcih7XG4gICAgICB2aXNpdG9yVG9rZW46IHNlc3Npb24udmlzaXRvcl90b2tlbixcbiAgICAgIHVzZXJJZCxcbiAgICAgIHVzZXJTaWduYXR1cmUsXG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGdldENsaWVudCgpOiBDaGF0Q2xpZW50IHtcbiAgICBpZiAoIXRoaXMuY2xpZW50KSB7XG4gICAgICB0aGlzLmNsaWVudCA9IG5ldyBDaGF0Q2xpZW50KHsgYmFzZVVybDogdGhpcy5jb25maWcuYmFzZVVybCB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50O1xuICB9XG5cbiAgcHJpdmF0ZSByZXNvbHZlZE9yaWdpbigpOiBzdHJpbmcge1xuICAgIGlmICh0aGlzLmNvbmZpZy5vcmlnaW4pIHJldHVybiB0aGlzLmNvbmZpZy5vcmlnaW47XG4gICAgaWYgKHRoaXMuaXNCcm93c2VyICYmIHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICByZXR1cm4gd2luZG93LmxvY2F0aW9uLm9yaWdpbjtcbiAgICB9XG4gICAgLy8gU1NSLXNhZmUgZmFsbGJhY2s7IHRoZSByZWxheSB2YWxpZGF0ZXMgb3JpZ2lucyBzZXJ2ZXItc2lkZSBhbnl3YXkuXG4gICAgcmV0dXJuICcnO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Dependency-injection plumbing for the Angular chat SDK.
3
+ *
4
+ * Consumers wire the SDK into their app via the standard Angular pattern
5
+ * documented in docs/12 §7.3:
6
+ *
7
+ * // app.config.ts
8
+ * export const appConfig: ApplicationConfig = {
9
+ * providers: [
10
+ * provideBloonioChat({
11
+ * tenantId: env.CHAT_TENANT_ID,
12
+ * publicKey: env.CHAT_PUBLIC_KEY,
13
+ * baseUrl: 'https://chat-relay.bloonio.com',
14
+ * visitorTokenProvider: () => inject(HttpClient).get<{token: string}>(
15
+ * '/api/chat/visitor-token'
16
+ * ).pipe(map(r => r.token)),
17
+ * locale: 'fr',
18
+ * }),
19
+ * ],
20
+ * };
21
+ */
22
+ import { inject, InjectionToken, makeEnvironmentProviders, } from '@angular/core';
23
+ export const BLOONIO_CHAT_CONFIG = new InjectionToken('BLOONIO_CHAT_CONFIG');
24
+ /** Standalone-provider factory.
25
+ *
26
+ * Returns ``EnvironmentProviders`` so consumers can spread it into
27
+ * ``app.config.ts``'s providers array. The service itself is
28
+ * ``providedIn: 'root'`` so we don't need an explicit provider entry — we
29
+ * only need the config token to be registered.
30
+ */
31
+ export function provideBloonioChat(config) {
32
+ return makeEnvironmentProviders([
33
+ { provide: BLOONIO_CHAT_CONFIG, useValue: config },
34
+ ]);
35
+ }
36
+ /** Internal accessor used by the components to fail loud when the
37
+ * consumer forgot ``provideBloonioChat()``. */
38
+ export function injectBloonioChatConfig() {
39
+ const cfg = inject(BLOONIO_CHAT_CONFIG, { optional: true });
40
+ if (!cfg) {
41
+ throw new Error('[bloonio-chat-angular] provideBloonioChat() was not called in your app.config.ts. ' +
42
+ 'Add it to providers: [...] alongside provideRouter/provideHttpClient.');
43
+ }
44
+ return cfg;
45
+ }
46
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxvb25pby1jaGF0LnRva2VuLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9ibG9vbmlvLWNoYXQudG9rZW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0JHO0FBRUgsT0FBTyxFQUNMLE1BQU0sRUFDTixjQUFjLEVBRWQsd0JBQXdCLEdBQ3pCLE1BQU0sZUFBZSxDQUFDO0FBd0N2QixNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLGNBQWMsQ0FDbkQscUJBQXFCLENBQ3RCLENBQUM7QUFFRjs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQ2hDLE1BQXlCO0lBRXpCLE9BQU8sd0JBQXdCLENBQUM7UUFDOUIsRUFBRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRTtLQUNuRCxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7Z0RBQ2dEO0FBQ2hELE1BQU0sVUFBVSx1QkFBdUI7SUFDckMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLG1CQUFtQixFQUFFLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDNUQsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ1QsTUFBTSxJQUFJLEtBQUssQ0FDYixvRkFBb0Y7WUFDcEYsdUVBQXVFLENBQ3hFLENBQUM7SUFDSixDQUFDO0lBQ0QsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEZXBlbmRlbmN5LWluamVjdGlvbiBwbHVtYmluZyBmb3IgdGhlIEFuZ3VsYXIgY2hhdCBTREsuXG4gKlxuICogQ29uc3VtZXJzIHdpcmUgdGhlIFNESyBpbnRvIHRoZWlyIGFwcCB2aWEgdGhlIHN0YW5kYXJkIEFuZ3VsYXIgcGF0dGVyblxuICogZG9jdW1lbnRlZCBpbiBkb2NzLzEyIMKnNy4zOlxuICpcbiAqICAgLy8gYXBwLmNvbmZpZy50c1xuICogICBleHBvcnQgY29uc3QgYXBwQ29uZmlnOiBBcHBsaWNhdGlvbkNvbmZpZyA9IHtcbiAqICAgICBwcm92aWRlcnM6IFtcbiAqICAgICAgIHByb3ZpZGVCbG9vbmlvQ2hhdCh7XG4gKiAgICAgICAgIHRlbmFudElkOiBlbnYuQ0hBVF9URU5BTlRfSUQsXG4gKiAgICAgICAgIHB1YmxpY0tleTogZW52LkNIQVRfUFVCTElDX0tFWSxcbiAqICAgICAgICAgYmFzZVVybDogJ2h0dHBzOi8vY2hhdC1yZWxheS5ibG9vbmlvLmNvbScsXG4gKiAgICAgICAgIHZpc2l0b3JUb2tlblByb3ZpZGVyOiAoKSA9PiBpbmplY3QoSHR0cENsaWVudCkuZ2V0PHt0b2tlbjogc3RyaW5nfT4oXG4gKiAgICAgICAgICAgJy9hcGkvY2hhdC92aXNpdG9yLXRva2VuJ1xuICogICAgICAgICApLnBpcGUobWFwKHIgPT4gci50b2tlbikpLFxuICogICAgICAgICBsb2NhbGU6ICdmcicsXG4gKiAgICAgICB9KSxcbiAqICAgICBdLFxuICogICB9O1xuICovXG5cbmltcG9ydCB7XG4gIGluamVjdCxcbiAgSW5qZWN0aW9uVG9rZW4sXG4gIHR5cGUgRW52aXJvbm1lbnRQcm92aWRlcnMsXG4gIG1ha2VFbnZpcm9ubWVudFByb3ZpZGVycyxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgdHlwZSB7IE9ic2VydmFibGUgfSBmcm9tICdyeGpzJztcblxuaW1wb3J0IHR5cGUgeyBBdHRhY2htZW50IH0gZnJvbSAnQGJsb29uaW8vY2hhdC1jbGllbnQtY29yZSc7XG5cbi8qKiBDYWxsZXItc3VwcGxpZWQgcmVzb2x2ZXIgZm9yIHRoZSB0ZW5hbnQtc2lnbmVkIHZpc2l0b3IgdG9rZW4uXG4gKlxuICogVHdvIGFjY2VwdGFibGUgc2hhcGVzOlxuICogICAtIFJldHVybnMgbm90aGluZyAob3IgYG51bGxgKSDihpIgU0RLIGZhbGxzIGJhY2sgdG8gYW5vbnltb3VzIG1vZGUuXG4gKiAgICAgQSBzZXNzaW9uIGlzIG1pbnRlZCB2aWEgYC9jcmVhdGUvdmlzaXRvci1zZXNzaW9uYCBhbmQgdGhlIHZpc2l0b3JcbiAqICAgICBzdGF5cyB1bmlkZW50aWZpZWQgdW50aWwgeW91IHN1cHBseSBvbmUuXG4gKiAgIC0gUmV0dXJucyBhIHN0cmluZyAvIE9ic2VydmFibGU8c3RyaW5nPiDihpIgU0RLIGNhbGxzIGAvaWRlbnRpZnkvdmlzaXRvcmBcbiAqICAgICB3aXRoIHRoaXMgc2lnbmVkIHRva2VuIHRvIGJpbmQgdGhlIGFub255bW91cyBzZXNzaW9uIHRvIGEgdGVuYW50IHVzZXIuXG4gKi9cbmV4cG9ydCB0eXBlIFZpc2l0b3JUb2tlblByb3ZpZGVyID1cbiAgfCAoKCkgPT4gc3RyaW5nIHwgbnVsbCB8IFByb21pc2U8c3RyaW5nIHwgbnVsbD4gfCBPYnNlcnZhYmxlPHN0cmluZyB8IG51bGw+KVxuICB8IHVuZGVmaW5lZDtcblxuZXhwb3J0IGludGVyZmFjZSBCbG9vbmlvQ2hhdENvbmZpZyB7XG4gIC8qKiBUZW5hbnQgVVVJRCB2NyAodGhlIGNhbm9uaWNhbCBoYW5kbGUgaXNzdWVkIGJ5IGNoYXRfcmVsYXkpLiAqL1xuICB0ZW5hbnRJZDogc3RyaW5nO1xuICAvKiogYHBrX+KApmAgd2lkZ2V0IGtleSBib3VuZCB0byB0aGUgdGVuYW50J3MgYGFsbG93ZWRfb3JpZ2luc2AuICovXG4gIHB1YmxpY0tleTogc3RyaW5nO1xuICAvKiogQmFzZSBVUkwgb2YgdGhlIGNoYXQgcmVsYXkgKGh0dHBzOi8vY2hhdC1yZWxheS5ibG9vbmlvLmNvbSBvciBsb2NhbCkuICovXG4gIGJhc2VVcmw6IHN0cmluZztcbiAgLyoqIFZpc2l0b3ItaWRlbnRpZmljYXRpb24gcmVzb2x2ZXIuIFNlZSB7QGxpbmsgVmlzaXRvclRva2VuUHJvdmlkZXJ9LiAqL1xuICB2aXNpdG9yVG9rZW5Qcm92aWRlcj86IFZpc2l0b3JUb2tlblByb3ZpZGVyO1xuICAvKiogRGVmYXVsdCBsb2NhbGUgZm9yIG5ldyBjb252ZXJzYXRpb25zLiAqL1xuICBsb2NhbGU/OiBzdHJpbmc7XG4gIC8qKiBGcmVlLWZvcm0gbWV0YWRhdGEgYXR0YWNoZWQgdG8gZXZlcnkgdmlzaXRvciBzZXNzaW9uLiAqL1xuICBtZXRhZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAvKiogT3B0aW9uYWwgb3ZlcnJpZGUgb2YgYHdpbmRvdy5sb2NhdGlvbi5vcmlnaW5gICh0ZXN0cyAvIFNTUikuICovXG4gIG9yaWdpbj86IHN0cmluZztcbiAgLyoqXG4gICAqIFByZS1mZXRjaGVkIGF0dGFjaG1lbnRzIHRvIGF0dGFjaCB0byB0aGUgZmlyc3Qgb3V0Ym91bmQgbWVzc2FnZS5cbiAgICogUmVzZXJ2ZWQgZm9yIHBoYXNlIDExOyBrZXB0IG9uIHRoZSBpbnRlcmZhY2UgZm9yIGZvcndhcmQgY29tcGF0LlxuICAgKi9cbiAgYXR0YWNobWVudHM/OiBBdHRhY2htZW50W107XG59XG5cbmV4cG9ydCBjb25zdCBCTE9PTklPX0NIQVRfQ09ORklHID0gbmV3IEluamVjdGlvblRva2VuPEJsb29uaW9DaGF0Q29uZmlnPihcbiAgJ0JMT09OSU9fQ0hBVF9DT05GSUcnLFxuKTtcblxuLyoqIFN0YW5kYWxvbmUtcHJvdmlkZXIgZmFjdG9yeS5cbiAqXG4gKiBSZXR1cm5zIGBgRW52aXJvbm1lbnRQcm92aWRlcnNgYCBzbyBjb25zdW1lcnMgY2FuIHNwcmVhZCBpdCBpbnRvXG4gKiBgYGFwcC5jb25maWcudHNgYCdzIHByb3ZpZGVycyBhcnJheS4gVGhlIHNlcnZpY2UgaXRzZWxmIGlzXG4gKiBgYHByb3ZpZGVkSW46ICdyb290J2BgIHNvIHdlIGRvbid0IG5lZWQgYW4gZXhwbGljaXQgcHJvdmlkZXIgZW50cnkg4oCUIHdlXG4gKiBvbmx5IG5lZWQgdGhlIGNvbmZpZyB0b2tlbiB0byBiZSByZWdpc3RlcmVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJvdmlkZUJsb29uaW9DaGF0KFxuICBjb25maWc6IEJsb29uaW9DaGF0Q29uZmlnLFxuKTogRW52aXJvbm1lbnRQcm92aWRlcnMge1xuICByZXR1cm4gbWFrZUVudmlyb25tZW50UHJvdmlkZXJzKFtcbiAgICB7IHByb3ZpZGU6IEJMT09OSU9fQ0hBVF9DT05GSUcsIHVzZVZhbHVlOiBjb25maWcgfSxcbiAgXSk7XG59XG5cbi8qKiBJbnRlcm5hbCBhY2Nlc3NvciB1c2VkIGJ5IHRoZSBjb21wb25lbnRzIHRvIGZhaWwgbG91ZCB3aGVuIHRoZVxuICogIGNvbnN1bWVyIGZvcmdvdCBgYHByb3ZpZGVCbG9vbmlvQ2hhdCgpYGAuICovXG5leHBvcnQgZnVuY3Rpb24gaW5qZWN0Qmxvb25pb0NoYXRDb25maWcoKTogQmxvb25pb0NoYXRDb25maWcge1xuICBjb25zdCBjZmcgPSBpbmplY3QoQkxPT05JT19DSEFUX0NPTkZJRywgeyBvcHRpb25hbDogdHJ1ZSB9KTtcbiAgaWYgKCFjZmcpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAnW2Jsb29uaW8tY2hhdC1hbmd1bGFyXSBwcm92aWRlQmxvb25pb0NoYXQoKSB3YXMgbm90IGNhbGxlZCBpbiB5b3VyIGFwcC5jb25maWcudHMuICcgK1xuICAgICAgJ0FkZCBpdCB0byBwcm92aWRlcnM6IFsuLi5dIGFsb25nc2lkZSBwcm92aWRlUm91dGVyL3Byb3ZpZGVIdHRwQ2xpZW50LicsXG4gICAgKTtcbiAgfVxuICByZXR1cm4gY2ZnO1xufVxuIl19