@fluidframework/tool-utils 2.90.0 → 2.91.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/CHANGELOG.md +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/odspTokenManager.d.ts +9 -10
- package/dist/odspTokenManager.d.ts.map +1 -1
- package/dist/odspTokenManager.js +76 -72
- package/dist/odspTokenManager.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/odspTokenManager.d.ts +9 -10
- package/lib/odspTokenManager.d.ts.map +1 -1
- package/lib/odspTokenManager.js +77 -73
- package/lib/odspTokenManager.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +21 -9
- package/src/index.ts +1 -1
- package/src/odspTokenManager.ts +81 -104
- package/src/packageVersion.ts +1 -1
- package/dist/httpHelpers.d.ts +0 -19
- package/dist/httpHelpers.d.ts.map +0 -1
- package/dist/httpHelpers.js +0 -68
- package/dist/httpHelpers.js.map +0 -1
- package/lib/httpHelpers.d.ts +0 -19
- package/lib/httpHelpers.d.ts.map +0 -1
- package/lib/httpHelpers.js +0 -59
- package/lib/httpHelpers.js.map +0 -1
- package/src/httpHelpers.ts +0 -92
package/src/odspTokenManager.ts
CHANGED
|
@@ -11,7 +11,6 @@ import type {
|
|
|
11
11
|
} from "@fluidframework/odsp-doclib-utils/internal";
|
|
12
12
|
import {
|
|
13
13
|
fetchTokens,
|
|
14
|
-
getLoginPageUrl,
|
|
15
14
|
getOdspScope,
|
|
16
15
|
pushScope,
|
|
17
16
|
refreshTokens,
|
|
@@ -21,11 +20,6 @@ import { Mutex } from "async-mutex";
|
|
|
21
20
|
import { debug } from "./debug.js";
|
|
22
21
|
import type { IAsyncCache, IResources } from "./fluidToolRc.js";
|
|
23
22
|
import { loadRC, lockRC, saveRC } from "./fluidToolRc.js";
|
|
24
|
-
import { endResponse, serverListenAndHandle } from "./httpHelpers.js";
|
|
25
|
-
|
|
26
|
-
const odspAuthRedirectPort = 7000;
|
|
27
|
-
const odspAuthRedirectOrigin = `http://localhost:${odspAuthRedirectPort}`;
|
|
28
|
-
const odspAuthRedirectUri = new URL("/auth/callback", odspAuthRedirectOrigin).href;
|
|
29
23
|
|
|
30
24
|
// TODO: Add documentation
|
|
31
25
|
// eslint-disable-next-line jsdoc/require-description
|
|
@@ -45,16 +39,16 @@ export const getMicrosoftConfiguration = (): IPublicClientConfig => ({
|
|
|
45
39
|
/**
|
|
46
40
|
* @internal
|
|
47
41
|
*/
|
|
48
|
-
export type
|
|
42
|
+
export type LoginCredentials =
|
|
49
43
|
| {
|
|
50
44
|
type: "password";
|
|
51
45
|
username: string;
|
|
52
46
|
password: string;
|
|
53
47
|
}
|
|
54
48
|
| {
|
|
55
|
-
type: "
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
type: "fic";
|
|
50
|
+
username: string;
|
|
51
|
+
fetchToken(scopeEndpoint: "push" | "storage"): Promise<string>;
|
|
58
52
|
};
|
|
59
53
|
|
|
60
54
|
/**
|
|
@@ -62,7 +56,7 @@ export type OdspTokenConfig =
|
|
|
62
56
|
*/
|
|
63
57
|
export interface IOdspTokenManagerCacheKey {
|
|
64
58
|
readonly isPush: boolean;
|
|
65
|
-
readonly
|
|
59
|
+
readonly user: string;
|
|
66
60
|
}
|
|
67
61
|
|
|
68
62
|
const isValidAndNotExpiredToken = (tokens: IOdspTokens): boolean => {
|
|
@@ -82,7 +76,7 @@ const isValidAndNotExpiredToken = (tokens: IOdspTokens): boolean => {
|
|
|
82
76
|
};
|
|
83
77
|
|
|
84
78
|
const cacheKeyToString = (key: IOdspTokenManagerCacheKey): string => {
|
|
85
|
-
return `${key.
|
|
79
|
+
return `${key.user}${key.isPush ? "[Push]" : ""}`;
|
|
86
80
|
};
|
|
87
81
|
|
|
88
82
|
/**
|
|
@@ -111,37 +105,37 @@ export class OdspTokenManager {
|
|
|
111
105
|
): Promise<void> {
|
|
112
106
|
debug(`${cacheKeyToString(key)}: Saving tokens`);
|
|
113
107
|
const memoryCache = key.isPush ? this.pushCache : this.storageCache;
|
|
114
|
-
memoryCache.set(key.
|
|
108
|
+
memoryCache.set(key.user, value);
|
|
115
109
|
await this.tokenCache?.save(key, value);
|
|
116
110
|
}
|
|
117
111
|
|
|
118
112
|
public async getOdspTokens(
|
|
119
113
|
server: string,
|
|
120
114
|
clientConfig: IPublicClientConfig,
|
|
121
|
-
|
|
115
|
+
credentials: LoginCredentials,
|
|
122
116
|
forceRefresh = false,
|
|
123
117
|
forceReauth = false,
|
|
124
118
|
): Promise<IOdspTokens> {
|
|
125
119
|
debug("Getting odsp tokens");
|
|
126
|
-
return this.getTokens(false, server, clientConfig,
|
|
120
|
+
return this.getTokens(false, server, clientConfig, credentials, forceRefresh, forceReauth);
|
|
127
121
|
}
|
|
128
122
|
|
|
129
123
|
public async getPushTokens(
|
|
130
124
|
server: string,
|
|
131
125
|
clientConfig: IPublicClientConfig,
|
|
132
|
-
|
|
126
|
+
credentials: LoginCredentials,
|
|
133
127
|
forceRefresh = false,
|
|
134
128
|
forceReauth = false,
|
|
135
129
|
): Promise<IOdspTokens> {
|
|
136
130
|
debug("Getting push tokens");
|
|
137
|
-
return this.getTokens(true, server, clientConfig,
|
|
131
|
+
return this.getTokens(true, server, clientConfig, credentials, forceRefresh, forceReauth);
|
|
138
132
|
}
|
|
139
133
|
|
|
140
134
|
private async getTokenFromCache(
|
|
141
135
|
cacheKey: IOdspTokenManagerCacheKey,
|
|
142
136
|
): Promise<IOdspTokens | undefined> {
|
|
143
137
|
const memoryCache = cacheKey.isPush ? this.pushCache : this.storageCache;
|
|
144
|
-
const memoryToken = memoryCache.get(cacheKey.
|
|
138
|
+
const memoryToken = memoryCache.get(cacheKey.user);
|
|
145
139
|
if (memoryToken) {
|
|
146
140
|
debug(`${cacheKeyToString(cacheKey)}: Token found in memory `);
|
|
147
141
|
return memoryToken;
|
|
@@ -149,20 +143,18 @@ export class OdspTokenManager {
|
|
|
149
143
|
const fileToken = await this.tokenCache?.get(cacheKey);
|
|
150
144
|
if (fileToken) {
|
|
151
145
|
debug(`${cacheKeyToString(cacheKey)}: Token found in file`);
|
|
152
|
-
memoryCache.set(cacheKey.
|
|
146
|
+
memoryCache.set(cacheKey.user, fileToken);
|
|
153
147
|
return fileToken;
|
|
154
148
|
}
|
|
155
149
|
}
|
|
156
150
|
|
|
157
151
|
private static getCacheKey(
|
|
158
152
|
isPush: boolean,
|
|
159
|
-
|
|
160
|
-
server: string,
|
|
153
|
+
credentials: LoginCredentials,
|
|
161
154
|
): IOdspTokenManagerCacheKey {
|
|
162
|
-
// If we are using password, we should cache the token per user instead of per server
|
|
163
155
|
return {
|
|
164
156
|
isPush,
|
|
165
|
-
|
|
157
|
+
user: credentials.username,
|
|
166
158
|
};
|
|
167
159
|
}
|
|
168
160
|
|
|
@@ -170,7 +162,7 @@ export class OdspTokenManager {
|
|
|
170
162
|
isPush: boolean,
|
|
171
163
|
server: string,
|
|
172
164
|
clientConfig: IPublicClientConfig,
|
|
173
|
-
|
|
165
|
+
credentials: LoginCredentials,
|
|
174
166
|
forceRefresh: boolean,
|
|
175
167
|
forceReauth: boolean,
|
|
176
168
|
): Promise<IOdspTokens> {
|
|
@@ -182,7 +174,7 @@ export class OdspTokenManager {
|
|
|
182
174
|
isPush,
|
|
183
175
|
server,
|
|
184
176
|
clientConfig,
|
|
185
|
-
|
|
177
|
+
credentials,
|
|
186
178
|
forceRefresh,
|
|
187
179
|
forceReauth,
|
|
188
180
|
);
|
|
@@ -190,12 +182,11 @@ export class OdspTokenManager {
|
|
|
190
182
|
};
|
|
191
183
|
if (!forceReauth && !forceRefresh) {
|
|
192
184
|
// check and return if it exists without lock
|
|
193
|
-
const cacheKey = OdspTokenManager.getCacheKey(isPush,
|
|
185
|
+
const cacheKey = OdspTokenManager.getCacheKey(isPush, credentials);
|
|
194
186
|
const tokensFromCache = await this.getTokenFromCache(cacheKey);
|
|
195
187
|
if (tokensFromCache) {
|
|
196
188
|
if (isValidAndNotExpiredToken(tokensFromCache)) {
|
|
197
189
|
debug(`${cacheKeyToString(cacheKey)}: Token reused from cache `);
|
|
198
|
-
await this.onTokenRetrievalFromCache(tokenConfig, tokensFromCache);
|
|
199
190
|
return tokensFromCache;
|
|
200
191
|
}
|
|
201
192
|
debug(`${cacheKeyToString(cacheKey)}: Token expired from cache `);
|
|
@@ -212,12 +203,12 @@ export class OdspTokenManager {
|
|
|
212
203
|
isPush: boolean,
|
|
213
204
|
server: string,
|
|
214
205
|
clientConfig: IPublicClientConfig,
|
|
215
|
-
|
|
206
|
+
credentials: LoginCredentials,
|
|
216
207
|
forceRefresh: boolean,
|
|
217
208
|
forceReauth: boolean,
|
|
218
209
|
): Promise<IOdspTokens> {
|
|
219
210
|
const scope = isPush ? pushScope : getOdspScope(server);
|
|
220
|
-
const cacheKey = OdspTokenManager.getCacheKey(isPush,
|
|
211
|
+
const cacheKey = OdspTokenManager.getCacheKey(isPush, credentials);
|
|
221
212
|
let tokens: IOdspTokens | undefined;
|
|
222
213
|
if (!forceReauth) {
|
|
223
214
|
// check the cache again under the lock (if it is there)
|
|
@@ -225,8 +216,16 @@ export class OdspTokenManager {
|
|
|
225
216
|
if (tokensFromCache) {
|
|
226
217
|
if (forceRefresh || !isValidAndNotExpiredToken(tokensFromCache)) {
|
|
227
218
|
try {
|
|
228
|
-
|
|
229
|
-
|
|
219
|
+
if (credentials.type === "fic") {
|
|
220
|
+
const scopeEndpoint = isPush ? "push" : "storage";
|
|
221
|
+
const newTokenData = await credentials.fetchToken(scopeEndpoint);
|
|
222
|
+
tokens = this.ficTokenToIOdspTokens(newTokenData, isPush);
|
|
223
|
+
} else if (credentials.type === "password") {
|
|
224
|
+
// For OAuth flows, use refresh token
|
|
225
|
+
tokens = await refreshTokens(server, scope, clientConfig, tokensFromCache);
|
|
226
|
+
} else {
|
|
227
|
+
unreachableCase(credentials);
|
|
228
|
+
}
|
|
230
229
|
await this.updateTokensCacheWithoutLock(cacheKey, tokens);
|
|
231
230
|
} catch (error) {
|
|
232
231
|
debug(`${cacheKeyToString(cacheKey)}: Error in refreshing token. ${error}`);
|
|
@@ -236,37 +235,29 @@ export class OdspTokenManager {
|
|
|
236
235
|
debug(`${cacheKeyToString(cacheKey)}: Token reused from locked cache `);
|
|
237
236
|
}
|
|
238
237
|
}
|
|
238
|
+
if (tokens) {
|
|
239
|
+
return tokens;
|
|
240
|
+
}
|
|
239
241
|
}
|
|
240
242
|
|
|
241
|
-
|
|
242
|
-
await this.onTokenRetrievalFromCache(tokenConfig, tokens);
|
|
243
|
-
return tokens;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
switch (tokenConfig.type) {
|
|
243
|
+
switch (credentials.type) {
|
|
247
244
|
case "password": {
|
|
248
245
|
tokens = await this.acquireTokensWithPassword(
|
|
249
246
|
server,
|
|
250
247
|
scope,
|
|
251
248
|
clientConfig,
|
|
252
|
-
|
|
253
|
-
|
|
249
|
+
credentials.username,
|
|
250
|
+
credentials.password,
|
|
254
251
|
);
|
|
255
252
|
break;
|
|
256
253
|
}
|
|
257
|
-
case "
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
server,
|
|
261
|
-
clientConfig,
|
|
262
|
-
scope,
|
|
263
|
-
tokenConfig.navigator,
|
|
264
|
-
tokenConfig.redirectUriCallback,
|
|
265
|
-
);
|
|
254
|
+
case "fic": {
|
|
255
|
+
const tokenData = await credentials.fetchToken(isPush ? "push" : "storage");
|
|
256
|
+
tokens = this.ficTokenToIOdspTokens(tokenData, isPush);
|
|
266
257
|
break;
|
|
267
258
|
}
|
|
268
259
|
default: {
|
|
269
|
-
unreachableCase(
|
|
260
|
+
unreachableCase(credentials);
|
|
270
261
|
}
|
|
271
262
|
}
|
|
272
263
|
|
|
@@ -296,64 +287,50 @@ export class OdspTokenManager {
|
|
|
296
287
|
return fetchTokens(server, scope, clientConfig, credentials);
|
|
297
288
|
}
|
|
298
289
|
|
|
299
|
-
private
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const credentials: TokenRequestCredentials = {
|
|
311
|
-
grant_type: "authorization_code",
|
|
312
|
-
code: this.extractAuthorizationCode(req.url),
|
|
313
|
-
redirect_uri: odspAuthRedirectUri,
|
|
290
|
+
private ficTokenToIOdspTokens(token: string, isPush: boolean): IOdspTokens {
|
|
291
|
+
// eslint-disable-next-line unicorn/prefer-ternary -- using if statement for clarity
|
|
292
|
+
if (isPush) {
|
|
293
|
+
// Push tokens are not standard JWTs. With direct token exchange, the second leg includes information about expiry.
|
|
294
|
+
// This is not available in the FIC flow, but in direct token exchange we request tokens with 1 hour expiry so default to that.
|
|
295
|
+
// At worst this should result in some higher latency when a token is returned from the cache when it should really be
|
|
296
|
+
// refreshed immediately (as attempting to use such a token will trigger a token refresh flow indirectly).
|
|
297
|
+
return {
|
|
298
|
+
accessToken: token,
|
|
299
|
+
receivedAt: Math.floor(Date.now() / 1000),
|
|
300
|
+
expiresIn: 3600,
|
|
314
301
|
};
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
// redirect now that the browser is done with auth
|
|
318
|
-
if (redirectUriCallback) {
|
|
319
|
-
res.writeHead(301, { Location: await redirectUriCallback(tokens) });
|
|
320
|
-
await endResponse(res);
|
|
321
|
-
} else {
|
|
322
|
-
res.write("Please close the window");
|
|
323
|
-
await endResponse(res);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return tokens;
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// Now that our local redirect handler is up, navigate the browser to the login page
|
|
330
|
-
navigator(loginPageUrl);
|
|
331
|
-
|
|
332
|
-
// Receive and extract the tokens
|
|
333
|
-
const odspTokens = await tokenGetter();
|
|
334
|
-
|
|
335
|
-
return odspTokens;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
private async onTokenRetrievalFromCache(
|
|
339
|
-
config: OdspTokenConfig,
|
|
340
|
-
tokens: IOdspTokens,
|
|
341
|
-
): Promise<void> {
|
|
342
|
-
if (config.type === "browserLogin" && config.redirectUriCallback) {
|
|
343
|
-
config.navigator(await config.redirectUriCallback(tokens));
|
|
302
|
+
} else {
|
|
303
|
+
return this.jwtToIOdspTokens(token);
|
|
344
304
|
}
|
|
345
305
|
}
|
|
346
306
|
|
|
347
|
-
private
|
|
348
|
-
|
|
349
|
-
|
|
307
|
+
private jwtToIOdspTokens(token: string): IOdspTokens {
|
|
308
|
+
let receivedAt: number;
|
|
309
|
+
let expiresIn: number;
|
|
310
|
+
const payloadSegment = token.split(".")[1];
|
|
311
|
+
if (payloadSegment === undefined) {
|
|
312
|
+
throw new Error("Invalid JWT format");
|
|
313
|
+
}
|
|
314
|
+
const payload = JSON.parse(Buffer.from(payloadSegment, "base64url").toString("utf8")) as {
|
|
315
|
+
iat?: number;
|
|
316
|
+
exp?: number;
|
|
317
|
+
};
|
|
318
|
+
if (typeof payload.iat === "number") {
|
|
319
|
+
receivedAt = payload.iat;
|
|
320
|
+
} else {
|
|
321
|
+
throw new TypeError("JWT payload lacks valid iat claim.");
|
|
350
322
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
throw new
|
|
323
|
+
if (typeof payload.exp === "number" && typeof payload.iat === "number") {
|
|
324
|
+
expiresIn = payload.exp - payload.iat;
|
|
325
|
+
} else {
|
|
326
|
+
throw new TypeError("JWT payload lacks valid exp claim.");
|
|
355
327
|
}
|
|
356
|
-
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
accessToken: token,
|
|
331
|
+
receivedAt,
|
|
332
|
+
expiresIn,
|
|
333
|
+
};
|
|
357
334
|
}
|
|
358
335
|
}
|
|
359
336
|
|
|
@@ -374,7 +351,7 @@ async function loadAndPatchRC(): Promise<IResources> {
|
|
|
374
351
|
export const odspTokensCache: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens> = {
|
|
375
352
|
async get(key: IOdspTokenManagerCacheKey): Promise<IOdspTokens | undefined> {
|
|
376
353
|
const rc = await loadAndPatchRC();
|
|
377
|
-
return rc.tokens?.data[key.
|
|
354
|
+
return rc.tokens?.data[key.user]?.[key.isPush ? "push" : "storage"];
|
|
378
355
|
},
|
|
379
356
|
async save(key: IOdspTokenManagerCacheKey, tokens: IOdspTokens): Promise<void> {
|
|
380
357
|
const rc = await loadAndPatchRC();
|
|
@@ -385,10 +362,10 @@ export const odspTokensCache: IAsyncCache<IOdspTokenManagerCacheKey, IOdspTokens
|
|
|
385
362
|
data: {},
|
|
386
363
|
};
|
|
387
364
|
}
|
|
388
|
-
let prevTokens = rc.tokens.data[key.
|
|
365
|
+
let prevTokens = rc.tokens.data[key.user];
|
|
389
366
|
if (!prevTokens) {
|
|
390
367
|
prevTokens = {};
|
|
391
|
-
rc.tokens.data[key.
|
|
368
|
+
rc.tokens.data[key.user] = prevTokens;
|
|
392
369
|
}
|
|
393
370
|
prevTokens[key.isPush ? "push" : "storage"] = tokens;
|
|
394
371
|
return saveRC(rc);
|
package/src/packageVersion.ts
CHANGED
package/dist/httpHelpers.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
/// <reference types="node" />
|
|
6
|
-
/// <reference types="node" />
|
|
7
|
-
import http from "node:http";
|
|
8
|
-
import type { Socket } from "node:net";
|
|
9
|
-
export interface ITrackedHttpServer {
|
|
10
|
-
readonly server: http.Server;
|
|
11
|
-
readonly sockets: Set<Socket>;
|
|
12
|
-
fullyClose(): void;
|
|
13
|
-
}
|
|
14
|
-
export declare function createTrackedServer(port: number, requestListener: http.RequestListener): ITrackedHttpServer;
|
|
15
|
-
export type OnceListenerHandler<T> = (req: http.IncomingMessage, res: http.ServerResponse) => Promise<T>;
|
|
16
|
-
export type OnceListenerResult<T> = Promise<() => Promise<T>>;
|
|
17
|
-
export declare const serverListenAndHandle: <T>(port: number, handler: OnceListenerHandler<T>) => OnceListenerResult<T>;
|
|
18
|
-
export declare const endResponse: (response: http.ServerResponse) => Promise<void>;
|
|
19
|
-
//# sourceMappingURL=httpHelpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"httpHelpers.d.ts","sourceRoot":"","sources":["../src/httpHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;;;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIvC,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,UAAU,IAAI,IAAI,CAAC;CACnB;AAID,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,IAAI,CAAC,eAAe,GACnC,kBAAkB,CAmBpB;AAID,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CACpC,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,KACpB,OAAO,CAAC,CAAC,CAAC,CAAC;AAIhB,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAI9D,eAAO,MAAM,qBAAqB,YAC3B,MAAM,WACH,oBAAoB,CAAC,CAAC,KAC7B,mBAAmB,CAAC,CAqBpB,CAAC;AAIJ,eAAO,MAAM,WAAW,aAAoB,KAAK,cAAc,KAAG,QAAQ,IAAI,CAQ3E,CAAC"}
|
package/dist/httpHelpers.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*!
|
|
3
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
4
|
-
* Licensed under the MIT License.
|
|
5
|
-
*/
|
|
6
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
-
};
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.endResponse = exports.serverListenAndHandle = exports.createTrackedServer = void 0;
|
|
11
|
-
const node_http_1 = __importDefault(require("node:http"));
|
|
12
|
-
// TODO: Add documentation
|
|
13
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
14
|
-
function createTrackedServer(port, requestListener) {
|
|
15
|
-
const server = node_http_1.default.createServer(requestListener).listen(port);
|
|
16
|
-
const sockets = new Set();
|
|
17
|
-
server.on("connection", (socket) => {
|
|
18
|
-
sockets.add(socket);
|
|
19
|
-
socket.on("close", () => sockets.delete(socket));
|
|
20
|
-
});
|
|
21
|
-
return {
|
|
22
|
-
server,
|
|
23
|
-
sockets,
|
|
24
|
-
fullyClose() {
|
|
25
|
-
server.close();
|
|
26
|
-
for (const socket of sockets) {
|
|
27
|
-
socket.destroy();
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
exports.createTrackedServer = createTrackedServer;
|
|
33
|
-
// TODO: Add documentation
|
|
34
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
35
|
-
const serverListenAndHandle = async (port, handler) =>
|
|
36
|
-
// eslint-disable-next-line promise/param-names
|
|
37
|
-
new Promise((outerResolve, outerReject) => {
|
|
38
|
-
const innerP = new Promise((innerResolve, innerReject) => {
|
|
39
|
-
const httpServer = createTrackedServer(port, (req, res) => {
|
|
40
|
-
// ignore favicon
|
|
41
|
-
if (req.url === "/favicon.ico") {
|
|
42
|
-
res.writeHead(200, { "Content-Type": "image/x-icon" });
|
|
43
|
-
res.end();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
handler(req, res)
|
|
47
|
-
.finally(() => httpServer.fullyClose())
|
|
48
|
-
.then((result) => innerResolve(result),
|
|
49
|
-
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
50
|
-
(error) => innerReject(error));
|
|
51
|
-
});
|
|
52
|
-
outerResolve(async () => innerP);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
exports.serverListenAndHandle = serverListenAndHandle;
|
|
56
|
-
// TODO: Add documentation
|
|
57
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
58
|
-
const endResponse = async (response) => new Promise((resolve, reject) => {
|
|
59
|
-
try {
|
|
60
|
-
response.end(resolve);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
64
|
-
reject(error);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
exports.endResponse = endResponse;
|
|
68
|
-
//# sourceMappingURL=httpHelpers.js.map
|
package/dist/httpHelpers.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"httpHelpers.js","sourceRoot":"","sources":["../src/httpHelpers.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;AAEH,0DAA6B;AAW7B,0BAA0B;AAC1B,+CAA+C;AAC/C,SAAgB,mBAAmB,CAClC,IAAY,EACZ,eAAqC;IAErC,MAAM,MAAM,GAAG,mBAAI,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE;QAClC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,OAAO;QACN,MAAM;QACN,OAAO;QACP,UAAU;YACT,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC9B,MAAM,CAAC,OAAO,EAAE,CAAC;YAClB,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC;AAtBD,kDAsBC;AAaD,0BAA0B;AAC1B,+CAA+C;AACxC,MAAM,qBAAqB,GAAG,KAAK,EACzC,IAAY,EACZ,OAA+B,EACP,EAAE;AAC1B,+CAA+C;AAC/C,IAAI,OAAO,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE;IACzC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAI,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE;QAC3D,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACzD,iBAAiB;YACjB,IAAI,GAAG,CAAC,GAAG,KAAK,cAAc,EAAE,CAAC;gBAChC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC,CAAC;gBACvD,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACR,CAAC;YACD,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;iBACf,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;iBACtC,IAAI,CACJ,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;YAChC,2EAA2E;YAC3E,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAC7B,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAxBS,QAAA,qBAAqB,yBAwB9B;AAEJ,0BAA0B;AAC1B,+CAA+C;AACxC,MAAM,WAAW,GAAG,KAAK,EAAE,QAA6B,EAAiB,EAAE,CACjF,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC/B,IAAI,CAAC;QACJ,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,2EAA2E;QAC3E,MAAM,CAAC,KAAK,CAAC,CAAC;IACf,CAAC;AACF,CAAC,CAAC,CAAC;AARS,QAAA,WAAW,eAQpB","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport http from \"node:http\";\nimport type { Socket } from \"node:net\";\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport interface ITrackedHttpServer {\n\treadonly server: http.Server;\n\treadonly sockets: Set<Socket>;\n\tfullyClose(): void;\n}\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport function createTrackedServer(\n\tport: number,\n\trequestListener: http.RequestListener,\n): ITrackedHttpServer {\n\tconst server = http.createServer(requestListener).listen(port);\n\tconst sockets = new Set<Socket>();\n\n\tserver.on(\"connection\", (socket) => {\n\t\tsockets.add(socket);\n\t\tsocket.on(\"close\", () => sockets.delete(socket));\n\t});\n\n\treturn {\n\t\tserver,\n\t\tsockets,\n\t\tfullyClose(): void {\n\t\t\tserver.close();\n\t\t\tfor (const socket of sockets) {\n\t\t\t\tsocket.destroy();\n\t\t\t}\n\t\t},\n\t};\n}\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport type OnceListenerHandler<T> = (\n\treq: http.IncomingMessage,\n\tres: http.ServerResponse,\n) => Promise<T>;\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport type OnceListenerResult<T> = Promise<() => Promise<T>>;\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport const serverListenAndHandle = async <T>(\n\tport: number,\n\thandler: OnceListenerHandler<T>,\n): OnceListenerResult<T> =>\n\t// eslint-disable-next-line promise/param-names\n\tnew Promise((outerResolve, outerReject) => {\n\t\tconst innerP = new Promise<T>((innerResolve, innerReject) => {\n\t\t\tconst httpServer = createTrackedServer(port, (req, res) => {\n\t\t\t\t// ignore favicon\n\t\t\t\tif (req.url === \"/favicon.ico\") {\n\t\t\t\t\tres.writeHead(200, { \"Content-Type\": \"image/x-icon\" });\n\t\t\t\t\tres.end();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\thandler(req, res)\n\t\t\t\t\t.finally(() => httpServer.fullyClose())\n\t\t\t\t\t.then(\n\t\t\t\t\t\t(result) => innerResolve(result),\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n\t\t\t\t\t\t(error) => innerReject(error),\n\t\t\t\t\t);\n\t\t\t});\n\t\t\touterResolve(async () => innerP);\n\t\t});\n\t});\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport const endResponse = async (response: http.ServerResponse): Promise<void> =>\n\tnew Promise((resolve, reject) => {\n\t\ttry {\n\t\t\tresponse.end(resolve);\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n\t\t\treject(error);\n\t\t}\n\t});\n"]}
|
package/lib/httpHelpers.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
6
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
7
|
-
import http from "node:http";
|
|
8
|
-
import type { Socket } from "node:net";
|
|
9
|
-
export interface ITrackedHttpServer {
|
|
10
|
-
readonly server: http.Server;
|
|
11
|
-
readonly sockets: Set<Socket>;
|
|
12
|
-
fullyClose(): void;
|
|
13
|
-
}
|
|
14
|
-
export declare function createTrackedServer(port: number, requestListener: http.RequestListener): ITrackedHttpServer;
|
|
15
|
-
export type OnceListenerHandler<T> = (req: http.IncomingMessage, res: http.ServerResponse) => Promise<T>;
|
|
16
|
-
export type OnceListenerResult<T> = Promise<() => Promise<T>>;
|
|
17
|
-
export declare const serverListenAndHandle: <T>(port: number, handler: OnceListenerHandler<T>) => OnceListenerResult<T>;
|
|
18
|
-
export declare const endResponse: (response: http.ServerResponse) => Promise<void>;
|
|
19
|
-
//# sourceMappingURL=httpHelpers.d.ts.map
|
package/lib/httpHelpers.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"httpHelpers.d.ts","sourceRoot":"","sources":["../src/httpHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;;;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIvC,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,UAAU,IAAI,IAAI,CAAC;CACnB;AAID,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,IAAI,CAAC,eAAe,GACnC,kBAAkB,CAmBpB;AAID,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CACpC,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,KACpB,OAAO,CAAC,CAAC,CAAC,CAAC;AAIhB,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAI9D,eAAO,MAAM,qBAAqB,YAC3B,MAAM,WACH,oBAAoB,CAAC,CAAC,KAC7B,mBAAmB,CAAC,CAqBpB,CAAC;AAIJ,eAAO,MAAM,WAAW,aAAoB,KAAK,cAAc,KAAG,QAAQ,IAAI,CAQ3E,CAAC"}
|
package/lib/httpHelpers.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
import http from "node:http";
|
|
6
|
-
// TODO: Add documentation
|
|
7
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
8
|
-
export function createTrackedServer(port, requestListener) {
|
|
9
|
-
const server = http.createServer(requestListener).listen(port);
|
|
10
|
-
const sockets = new Set();
|
|
11
|
-
server.on("connection", (socket) => {
|
|
12
|
-
sockets.add(socket);
|
|
13
|
-
socket.on("close", () => sockets.delete(socket));
|
|
14
|
-
});
|
|
15
|
-
return {
|
|
16
|
-
server,
|
|
17
|
-
sockets,
|
|
18
|
-
fullyClose() {
|
|
19
|
-
server.close();
|
|
20
|
-
for (const socket of sockets) {
|
|
21
|
-
socket.destroy();
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
// TODO: Add documentation
|
|
27
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
28
|
-
export const serverListenAndHandle = async (port, handler) =>
|
|
29
|
-
// eslint-disable-next-line promise/param-names
|
|
30
|
-
new Promise((outerResolve, outerReject) => {
|
|
31
|
-
const innerP = new Promise((innerResolve, innerReject) => {
|
|
32
|
-
const httpServer = createTrackedServer(port, (req, res) => {
|
|
33
|
-
// ignore favicon
|
|
34
|
-
if (req.url === "/favicon.ico") {
|
|
35
|
-
res.writeHead(200, { "Content-Type": "image/x-icon" });
|
|
36
|
-
res.end();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
handler(req, res)
|
|
40
|
-
.finally(() => httpServer.fullyClose())
|
|
41
|
-
.then((result) => innerResolve(result),
|
|
42
|
-
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
43
|
-
(error) => innerReject(error));
|
|
44
|
-
});
|
|
45
|
-
outerResolve(async () => innerP);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
// TODO: Add documentation
|
|
49
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
50
|
-
export const endResponse = async (response) => new Promise((resolve, reject) => {
|
|
51
|
-
try {
|
|
52
|
-
response.end(resolve);
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
56
|
-
reject(error);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
//# sourceMappingURL=httpHelpers.js.map
|
package/lib/httpHelpers.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"httpHelpers.js","sourceRoot":"","sources":["../src/httpHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAW7B,0BAA0B;AAC1B,+CAA+C;AAC/C,MAAM,UAAU,mBAAmB,CAClC,IAAY,EACZ,eAAqC;IAErC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE;QAClC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,OAAO;QACN,MAAM;QACN,OAAO;QACP,UAAU;YACT,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC9B,MAAM,CAAC,OAAO,EAAE,CAAC;YAClB,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC;AAaD,0BAA0B;AAC1B,+CAA+C;AAC/C,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EACzC,IAAY,EACZ,OAA+B,EACP,EAAE;AAC1B,+CAA+C;AAC/C,IAAI,OAAO,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE;IACzC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAI,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE;QAC3D,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YACzD,iBAAiB;YACjB,IAAI,GAAG,CAAC,GAAG,KAAK,cAAc,EAAE,CAAC;gBAChC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC,CAAC;gBACvD,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACR,CAAC;YACD,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC;iBACf,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;iBACtC,IAAI,CACJ,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;YAChC,2EAA2E;YAC3E,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAC7B,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEJ,0BAA0B;AAC1B,+CAA+C;AAC/C,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,QAA6B,EAAiB,EAAE,CACjF,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC/B,IAAI,CAAC;QACJ,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,2EAA2E;QAC3E,MAAM,CAAC,KAAK,CAAC,CAAC;IACf,CAAC;AACF,CAAC,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport http from \"node:http\";\nimport type { Socket } from \"node:net\";\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport interface ITrackedHttpServer {\n\treadonly server: http.Server;\n\treadonly sockets: Set<Socket>;\n\tfullyClose(): void;\n}\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport function createTrackedServer(\n\tport: number,\n\trequestListener: http.RequestListener,\n): ITrackedHttpServer {\n\tconst server = http.createServer(requestListener).listen(port);\n\tconst sockets = new Set<Socket>();\n\n\tserver.on(\"connection\", (socket) => {\n\t\tsockets.add(socket);\n\t\tsocket.on(\"close\", () => sockets.delete(socket));\n\t});\n\n\treturn {\n\t\tserver,\n\t\tsockets,\n\t\tfullyClose(): void {\n\t\t\tserver.close();\n\t\t\tfor (const socket of sockets) {\n\t\t\t\tsocket.destroy();\n\t\t\t}\n\t\t},\n\t};\n}\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport type OnceListenerHandler<T> = (\n\treq: http.IncomingMessage,\n\tres: http.ServerResponse,\n) => Promise<T>;\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport type OnceListenerResult<T> = Promise<() => Promise<T>>;\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport const serverListenAndHandle = async <T>(\n\tport: number,\n\thandler: OnceListenerHandler<T>,\n): OnceListenerResult<T> =>\n\t// eslint-disable-next-line promise/param-names\n\tnew Promise((outerResolve, outerReject) => {\n\t\tconst innerP = new Promise<T>((innerResolve, innerReject) => {\n\t\t\tconst httpServer = createTrackedServer(port, (req, res) => {\n\t\t\t\t// ignore favicon\n\t\t\t\tif (req.url === \"/favicon.ico\") {\n\t\t\t\t\tres.writeHead(200, { \"Content-Type\": \"image/x-icon\" });\n\t\t\t\t\tres.end();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\thandler(req, res)\n\t\t\t\t\t.finally(() => httpServer.fullyClose())\n\t\t\t\t\t.then(\n\t\t\t\t\t\t(result) => innerResolve(result),\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n\t\t\t\t\t\t(error) => innerReject(error),\n\t\t\t\t\t);\n\t\t\t});\n\t\t\touterResolve(async () => innerP);\n\t\t});\n\t});\n\n// TODO: Add documentation\n// eslint-disable-next-line jsdoc/require-jsdoc\nexport const endResponse = async (response: http.ServerResponse): Promise<void> =>\n\tnew Promise((resolve, reject) => {\n\t\ttry {\n\t\t\tresponse.end(resolve);\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors\n\t\t\treject(error);\n\t\t}\n\t});\n"]}
|
package/src/httpHelpers.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import http from "node:http";
|
|
7
|
-
import type { Socket } from "node:net";
|
|
8
|
-
|
|
9
|
-
// TODO: Add documentation
|
|
10
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
11
|
-
export interface ITrackedHttpServer {
|
|
12
|
-
readonly server: http.Server;
|
|
13
|
-
readonly sockets: Set<Socket>;
|
|
14
|
-
fullyClose(): void;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// TODO: Add documentation
|
|
18
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
19
|
-
export function createTrackedServer(
|
|
20
|
-
port: number,
|
|
21
|
-
requestListener: http.RequestListener,
|
|
22
|
-
): ITrackedHttpServer {
|
|
23
|
-
const server = http.createServer(requestListener).listen(port);
|
|
24
|
-
const sockets = new Set<Socket>();
|
|
25
|
-
|
|
26
|
-
server.on("connection", (socket) => {
|
|
27
|
-
sockets.add(socket);
|
|
28
|
-
socket.on("close", () => sockets.delete(socket));
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
server,
|
|
33
|
-
sockets,
|
|
34
|
-
fullyClose(): void {
|
|
35
|
-
server.close();
|
|
36
|
-
for (const socket of sockets) {
|
|
37
|
-
socket.destroy();
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// TODO: Add documentation
|
|
44
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
45
|
-
export type OnceListenerHandler<T> = (
|
|
46
|
-
req: http.IncomingMessage,
|
|
47
|
-
res: http.ServerResponse,
|
|
48
|
-
) => Promise<T>;
|
|
49
|
-
|
|
50
|
-
// TODO: Add documentation
|
|
51
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
52
|
-
export type OnceListenerResult<T> = Promise<() => Promise<T>>;
|
|
53
|
-
|
|
54
|
-
// TODO: Add documentation
|
|
55
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
56
|
-
export const serverListenAndHandle = async <T>(
|
|
57
|
-
port: number,
|
|
58
|
-
handler: OnceListenerHandler<T>,
|
|
59
|
-
): OnceListenerResult<T> =>
|
|
60
|
-
// eslint-disable-next-line promise/param-names
|
|
61
|
-
new Promise((outerResolve, outerReject) => {
|
|
62
|
-
const innerP = new Promise<T>((innerResolve, innerReject) => {
|
|
63
|
-
const httpServer = createTrackedServer(port, (req, res) => {
|
|
64
|
-
// ignore favicon
|
|
65
|
-
if (req.url === "/favicon.ico") {
|
|
66
|
-
res.writeHead(200, { "Content-Type": "image/x-icon" });
|
|
67
|
-
res.end();
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
handler(req, res)
|
|
71
|
-
.finally(() => httpServer.fullyClose())
|
|
72
|
-
.then(
|
|
73
|
-
(result) => innerResolve(result),
|
|
74
|
-
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
75
|
-
(error) => innerReject(error),
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
outerResolve(async () => innerP);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// TODO: Add documentation
|
|
83
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
84
|
-
export const endResponse = async (response: http.ServerResponse): Promise<void> =>
|
|
85
|
-
new Promise((resolve, reject) => {
|
|
86
|
-
try {
|
|
87
|
-
response.end(resolve);
|
|
88
|
-
} catch (error) {
|
|
89
|
-
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
90
|
-
reject(error);
|
|
91
|
-
}
|
|
92
|
-
});
|