@dev-crew-berlin/enter-js-utils 0.95.2 → 0.97.2
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/dist/api-client/api-base.d.ts +3 -0
- package/dist/api-client/api-base.js +84 -42
- package/dist/api-client/index.d.ts +15 -0
- package/dist/api-client/index.js +18 -0
- package/dist/generated/api-schema.d.ts +605 -13
- package/dist/models/event.d.ts +8 -8
- package/dist/ui/button.js +3 -3
- package/dist/ui/checkin-progress-bar.js +36 -19
- package/dist/ui/form-elements/input.js +4 -4
- package/dist/ui/form-elements/search-input.js +7 -7
- package/dist/ui/form-elements/segmented-control.js +7 -7
- package/dist/ui/form-elements/select.js +6 -6
- package/dist/ui/guest-card.js +13 -13
- package/package.json +3 -2
|
@@ -36,6 +36,8 @@ export default class APIBase {
|
|
|
36
36
|
onLogout: (reason?: string) => void;
|
|
37
37
|
});
|
|
38
38
|
private static fetchResult;
|
|
39
|
+
private buildHeaders;
|
|
40
|
+
private fetchResponse;
|
|
39
41
|
/**
|
|
40
42
|
* @category Auth
|
|
41
43
|
*/
|
|
@@ -54,6 +56,7 @@ export default class APIBase {
|
|
|
54
56
|
expires: Date;
|
|
55
57
|
}>>;
|
|
56
58
|
private fetchFromEnter;
|
|
59
|
+
protected stream<T>(endpoint: string, options?: FetchOptions): AsyncGenerator<T>;
|
|
57
60
|
protected get<Path extends keyof paths>(endpoint: Path, options: FetchOptions): Promise<APIResult<ResponseType<Path, 'get'>>>;
|
|
58
61
|
protected post<Path extends keyof paths>(endpoint: Path, data: RequestBody<Path, 'post'>, options: FetchOptions): Promise<APIResult<ResponseType<Path, 'post'>>>;
|
|
59
62
|
protected put<Path extends keyof paths>(endpoint: Path, data: RequestBody<Path, 'put'>, options: FetchOptions): Promise<APIResult<ResponseType<Path, 'put'>>>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import jws from 'jws';
|
|
2
|
+
import { EventSourceParserStream } from 'eventsource-parser/stream';
|
|
2
3
|
import { failure, success } from '../lib';
|
|
3
4
|
export default class APIBase {
|
|
4
5
|
constructor(args) {
|
|
@@ -16,6 +17,53 @@ export default class APIBase {
|
|
|
16
17
|
return failure(e);
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
buildHeaders(extraHeaders = {}) {
|
|
21
|
+
return {
|
|
22
|
+
Authorization: `bearer ${this.credentials.accessToken}`,
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
'X-Enter-Device-Name': this.deviceName,
|
|
25
|
+
'X-Enter-Requester-Id': this.requesterId,
|
|
26
|
+
...extraHeaders
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async fetchResponse(endpoint, init) {
|
|
30
|
+
const res = await APIBase.fetchResult(`${this.credentials.url}${endpoint}`, init);
|
|
31
|
+
if (!res.success) {
|
|
32
|
+
return failure([{
|
|
33
|
+
type: 'network-error',
|
|
34
|
+
message: res.error.message,
|
|
35
|
+
endpoint
|
|
36
|
+
}]);
|
|
37
|
+
}
|
|
38
|
+
if (res.data.status === 401) {
|
|
39
|
+
if (this.isLoggedIn) this.logout();
|
|
40
|
+
return failure([{
|
|
41
|
+
type: 'http-error',
|
|
42
|
+
statusCode: 401,
|
|
43
|
+
endpoint,
|
|
44
|
+
message: res.data.statusText
|
|
45
|
+
}]);
|
|
46
|
+
}
|
|
47
|
+
if (res.data.status >= 300) {
|
|
48
|
+
try {
|
|
49
|
+
const error = await res.data.json();
|
|
50
|
+
return failure([{
|
|
51
|
+
type: 'http-error',
|
|
52
|
+
statusCode: res.data.status,
|
|
53
|
+
message: error.error ?? res.data.statusText,
|
|
54
|
+
endpoint
|
|
55
|
+
}]);
|
|
56
|
+
} catch {
|
|
57
|
+
return failure([{
|
|
58
|
+
type: 'http-error',
|
|
59
|
+
statusCode: res.data.status,
|
|
60
|
+
message: `invalid response from backend: ${res.data.status} (${res.data.statusText}) for: ${endpoint}`,
|
|
61
|
+
endpoint
|
|
62
|
+
}]);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return success(res.data);
|
|
66
|
+
}
|
|
19
67
|
|
|
20
68
|
/**
|
|
21
69
|
* @category Auth
|
|
@@ -74,58 +122,52 @@ export default class APIBase {
|
|
|
74
122
|
}
|
|
75
123
|
async fetchFromEnter(endpoint, method, data, options) {
|
|
76
124
|
const body = data !== null ? JSON.stringify(data) : null;
|
|
77
|
-
const
|
|
78
|
-
Authorization: `bearer ${this.credentials.accessToken}`,
|
|
79
|
-
'Content-Type': 'application/json',
|
|
80
|
-
'X-Enter-Device-Name': this.deviceName,
|
|
81
|
-
'X-Enter-Requester-Id': this.requesterId
|
|
82
|
-
};
|
|
83
|
-
const res = await APIBase.fetchResult(`${this.credentials.url}${endpoint}`, {
|
|
125
|
+
const res = await this.fetchResponse(endpoint, {
|
|
84
126
|
method: method.toUpperCase(),
|
|
85
|
-
headers,
|
|
127
|
+
headers: this.buildHeaders(),
|
|
86
128
|
body,
|
|
87
129
|
...options
|
|
88
130
|
});
|
|
89
|
-
if (!res.success)
|
|
90
|
-
return failure([{
|
|
91
|
-
type: 'network-error',
|
|
92
|
-
message: res.error.message,
|
|
93
|
-
endpoint
|
|
94
|
-
}]);
|
|
95
|
-
}
|
|
96
|
-
if (res.data.status === 401) {
|
|
97
|
-
if (this.isLoggedIn) this.logout();
|
|
98
|
-
return failure([{
|
|
99
|
-
type: 'http-error',
|
|
100
|
-
statusCode: 401,
|
|
101
|
-
endpoint,
|
|
102
|
-
message: res.data.statusText
|
|
103
|
-
}]);
|
|
104
|
-
}
|
|
105
|
-
if (res.data.status >= 300) {
|
|
106
|
-
try {
|
|
107
|
-
const error = await res.data.json();
|
|
108
|
-
return failure([{
|
|
109
|
-
type: 'http-error',
|
|
110
|
-
statusCode: res.data.status,
|
|
111
|
-
message: error.error ?? res.data.statusText,
|
|
112
|
-
endpoint
|
|
113
|
-
}]);
|
|
114
|
-
} catch (e) {
|
|
115
|
-
return failure([{
|
|
116
|
-
type: 'http-error',
|
|
117
|
-
statusCode: res.data.status,
|
|
118
|
-
message: `invalid response from backend: ${res.data.status} (${res.data.statusText}) for: ${endpoint}`,
|
|
119
|
-
endpoint
|
|
120
|
-
}]);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
131
|
+
if (!res.success) return res;
|
|
123
132
|
|
|
124
133
|
// HTTP status 204 means no content so we can't parse it as json
|
|
125
134
|
if (res.data.status === 204) return success(null);
|
|
126
135
|
const json = await res.data.json();
|
|
127
136
|
return success(json);
|
|
128
137
|
}
|
|
138
|
+
async *stream(endpoint, options = {}) {
|
|
139
|
+
const res = await this.fetchResponse(endpoint, {
|
|
140
|
+
method: 'GET',
|
|
141
|
+
headers: this.buildHeaders({
|
|
142
|
+
Accept: 'text/event-stream'
|
|
143
|
+
}),
|
|
144
|
+
...options
|
|
145
|
+
});
|
|
146
|
+
if (!res.success) {
|
|
147
|
+
throw new Error(res.error[0]?.message ?? 'Unknown error');
|
|
148
|
+
}
|
|
149
|
+
if (!res.data.body) {
|
|
150
|
+
throw new Error('Response body is null');
|
|
151
|
+
}
|
|
152
|
+
const reader = res.data.body.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream()).getReader();
|
|
153
|
+
try {
|
|
154
|
+
while (true) {
|
|
155
|
+
const {
|
|
156
|
+
done,
|
|
157
|
+
value: event
|
|
158
|
+
} = await reader.read();
|
|
159
|
+
if (done) break;
|
|
160
|
+
if (event.data === '[DONE]') return;
|
|
161
|
+
try {
|
|
162
|
+
yield JSON.parse(event.data);
|
|
163
|
+
} catch {
|
|
164
|
+
console.warn('[SSE] Failed to parse event data:', event.data);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} finally {
|
|
168
|
+
reader.releaseLock();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
129
171
|
async get(endpoint, options) {
|
|
130
172
|
const rawResponse = await this.fetchFromEnter(endpoint, 'get', undefined, options);
|
|
131
173
|
if (!rawResponse.success) return rawResponse;
|
|
@@ -161,5 +161,20 @@ declare class API extends APIBase {
|
|
|
161
161
|
* @category Events
|
|
162
162
|
*/
|
|
163
163
|
createEvents(args: Event[], options?: FetchOptions): Promise<APIResult<null>>;
|
|
164
|
+
/**
|
|
165
|
+
* Subscribe to the SSE event stream. Yields events as they arrive.
|
|
166
|
+
* Use `for await` to consume events, and `break` or `return` to unsubscribe.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* for await (const event of api.subscribeToEvents({ instanceName: 'my-instance' })) {
|
|
170
|
+
* console.log(event)
|
|
171
|
+
* }
|
|
172
|
+
*
|
|
173
|
+
* @category Events
|
|
174
|
+
*/
|
|
175
|
+
subscribeToEvents(args: {
|
|
176
|
+
instanceName: string;
|
|
177
|
+
eventType?: string[];
|
|
178
|
+
}, options?: FetchOptions): AsyncGenerator<Event>;
|
|
164
179
|
}
|
|
165
180
|
export default API;
|
package/dist/api-client/index.js
CHANGED
|
@@ -221,5 +221,23 @@ class API extends APIBase {
|
|
|
221
221
|
async createEvents(args, options = {}) {
|
|
222
222
|
return this.post(`/events`, args, options);
|
|
223
223
|
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Subscribe to the SSE event stream. Yields events as they arrive.
|
|
227
|
+
* Use `for await` to consume events, and `break` or `return` to unsubscribe.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* for await (const event of api.subscribeToEvents({ instanceName: 'my-instance' })) {
|
|
231
|
+
* console.log(event)
|
|
232
|
+
* }
|
|
233
|
+
*
|
|
234
|
+
* @category Events
|
|
235
|
+
*/
|
|
236
|
+
async *subscribeToEvents(args, options = {}) {
|
|
237
|
+
const query = new URLSearchParams();
|
|
238
|
+
query.append('instance', args.instanceName);
|
|
239
|
+
args.eventType?.forEach(t => query.append('event_type', t));
|
|
240
|
+
yield* this.stream(`/events/stream?${query.toString()}`, options);
|
|
241
|
+
}
|
|
224
242
|
}
|
|
225
243
|
export default API;
|