tempo.ts 0.7.2 → 0.7.4
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 +12 -0
- package/dist/server/Handler.d.ts +7 -4
- package/dist/server/Handler.d.ts.map +1 -1
- package/dist/server/Handler.js +87 -43
- package/dist/server/Handler.js.map +1 -1
- package/package.json +1 -1
- package/src/server/Handler.test.ts +341 -0
- package/src/server/Handler.ts +129 -74
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# tempo.ts
|
|
2
2
|
|
|
3
|
+
## 0.7.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`1b5c63b`](https://github.com/tempoxyz/tempo-ts/commit/1b5c63bfd3ebb348049b3051baf093620af2b2a6) Thanks [@jxom](https://github.com/jxom)! - Added `headers` to `Handler` functions.
|
|
8
|
+
|
|
9
|
+
## 0.7.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`f0369cd`](https://github.com/tempoxyz/tempo-ts/commit/f0369cd13c6d651c2aff1abe85e7984a60588f0f) Thanks [@jxom](https://github.com/jxom)! - Added error handling to `Handler.feePayer`.
|
|
14
|
+
|
|
3
15
|
## 0.7.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/server/Handler.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type Handler = Router & {
|
|
|
11
11
|
};
|
|
12
12
|
export declare function compose(handlers: Handler[], options?: compose.Options): Handler;
|
|
13
13
|
export declare namespace compose {
|
|
14
|
-
type Options = {
|
|
14
|
+
type Options = from.Options & {
|
|
15
15
|
/** The path to use for the handler. */
|
|
16
16
|
path?: string | undefined;
|
|
17
17
|
};
|
|
@@ -24,7 +24,10 @@ export declare namespace compose {
|
|
|
24
24
|
*/
|
|
25
25
|
export declare function from(options?: from.Options): Handler;
|
|
26
26
|
export declare namespace from {
|
|
27
|
-
type Options = RouterOptions
|
|
27
|
+
type Options = RouterOptions & {
|
|
28
|
+
/** Headers to add to the response. */
|
|
29
|
+
headers?: Headers | Record<string, string> | undefined;
|
|
30
|
+
};
|
|
28
31
|
}
|
|
29
32
|
/**
|
|
30
33
|
* Defines a Key Manager request handler.
|
|
@@ -135,7 +138,7 @@ export declare namespace from {
|
|
|
135
138
|
*/
|
|
136
139
|
export declare function keyManager(options: keyManager.Options): Handler;
|
|
137
140
|
export declare namespace keyManager {
|
|
138
|
-
type Options = {
|
|
141
|
+
type Options = from.Options & {
|
|
139
142
|
/** The KV store to use for key management. */
|
|
140
143
|
kv: Kv.Kv;
|
|
141
144
|
/** The path to use for the handler. */
|
|
@@ -336,7 +339,7 @@ export declare namespace keyManager {
|
|
|
336
339
|
*/
|
|
337
340
|
export declare function feePayer(options: feePayer.Options): Handler;
|
|
338
341
|
export declare namespace feePayer {
|
|
339
|
-
type Options = {
|
|
342
|
+
type Options = from.Options & {
|
|
340
343
|
/** Account to use as the fee payer. */
|
|
341
344
|
account: LocalAccount;
|
|
342
345
|
/** Function to call before handling the request. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Handler.d.ts","sourceRoot":"","sources":["../../src/server/Handler.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Handler.d.ts","sourceRoot":"","sources":["../../src/server/Handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,UAAU,EAAe,MAAM,IAAI,CAAA;AAE5C,OAAO,KAAK,GAAG,MAAM,QAAQ,CAAA;AAC7B,OAAO,KAAK,KAAK,YAAY,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,MAAM,EAAgB,KAAK,SAAS,EAAE,MAAM,MAAM,CAAA;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAIjD,OAAO,KAAK,KAAK,EAAE,MAAM,SAAS,CAAA;AAElC,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAC7B,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;CACvC,CAAA;AAED,wBAAgB,OAAO,CACrB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,GAAE,OAAO,CAAC,OAAY,GAC5B,OAAO,CAmBT;AAED,MAAM,CAAC,OAAO,WAAW,OAAO,CAAC;IAC/B,KAAY,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG;QACnC,uCAAuC;QACvC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC1B,CAAA;CACF;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE,IAAI,CAAC,OAAY,GAAG,OAAO,CAYxD;AAED,MAAM,CAAC,OAAO,WAAW,IAAI,CAAC;IAC5B,KAAY,OAAO,GAAG,aAAa,GAAG;QACpC,sCAAsC;QACtC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;KACvD,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,WAmHrD;AAED,MAAM,CAAC,OAAO,WAAW,UAAU,CAAC;IAClC,KAAY,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG;QACnC,8CAA8C;QAC9C,EAAE,EAAE,EAAE,CAAC,EAAE,CAAA;QACT,uCAAuC;QACvC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACzB,kCAAkC;QAClC,EAAE,CAAC,EACC,MAAM,GACN;YACE,EAAE,EAAE,MAAM,CAAA;YACV,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;SAC1B,GACD,SAAS,CAAA;KACd,CAAA;IAED,KAAY,iBAAiB,GAAG;QAC9B,SAAS,EAAE,GAAG,CAAC,GAAG,CAAA;QAClB,EAAE,CAAC,EACC;YACE,EAAE,EAAE,MAAM,CAAA;YACV,IAAI,EAAE,MAAM,CAAA;SACb,GACD,SAAS,CAAA;KACd,CAAA;IAED,KAAY,sBAAsB,GAAG;QACnC,UAAU,EAAE,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;KAC/C,CAAA;IAED,KAAY,sBAAsB,GAAG;QACnC,UAAU,EAAE,YAAY,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;QAC9C,SAAS,EAAE,GAAG,CAAC,GAAG,CAAA;KACnB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4KG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,WAqGjD;AAED,MAAM,CAAC,OAAO,WAAW,QAAQ,CAAC;IAChC,KAAY,OAAO,GAAG,IAAI,CAAC,OAAO,GAAG;QACnC,uCAAuC;QACvC,OAAO,EAAE,YAAY,CAAA;QACrB,oDAAoD;QACpD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QAC7D,mCAAmC;QACnC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAC1B,GAAG,KAAK,CACH;QACE,qBAAqB;QACrB,MAAM,EAAE,MAAM,CAAA;KACf,GACD;QACE,oBAAoB;QACpB,KAAK,EAAE,KAAK,CAAA;QACZ,wBAAwB;QACxB,SAAS,EAAE,SAAS,CAAA;KACrB,CACJ,CAAA;CACJ"}
|
package/dist/server/Handler.js
CHANGED
|
@@ -10,6 +10,7 @@ import * as RequestListener from './internal/requestListener.js';
|
|
|
10
10
|
export function compose(handlers, options = {}) {
|
|
11
11
|
const path = options.path ?? '/';
|
|
12
12
|
return from({
|
|
13
|
+
...options,
|
|
13
14
|
async defaultHandler(context) {
|
|
14
15
|
const url = new URL(context.request.url);
|
|
15
16
|
if (!url.pathname.startsWith(path))
|
|
@@ -32,7 +33,10 @@ export function compose(handlers, options = {}) {
|
|
|
32
33
|
* @returns Handler instance
|
|
33
34
|
*/
|
|
34
35
|
export function from(options = {}) {
|
|
35
|
-
const router = createRouter(
|
|
36
|
+
const router = createRouter({
|
|
37
|
+
...options,
|
|
38
|
+
middleware: [headers(options.headers), preflight(options.headers)],
|
|
39
|
+
});
|
|
36
40
|
return {
|
|
37
41
|
...router,
|
|
38
42
|
listener: RequestListener.fromFetchHandler((request) => {
|
|
@@ -160,7 +164,7 @@ export function keyManager(options) {
|
|
|
160
164
|
};
|
|
161
165
|
return undefined;
|
|
162
166
|
})();
|
|
163
|
-
const router = from();
|
|
167
|
+
const router = from(options);
|
|
164
168
|
// Get challenge for WebAuthn credential creation
|
|
165
169
|
router.get(`${path}/challenge`, async () => {
|
|
166
170
|
// Generate a random challenge
|
|
@@ -410,50 +414,90 @@ export function feePayer(options) {
|
|
|
410
414
|
const router = from();
|
|
411
415
|
router.post(path, async ({ request: req }) => {
|
|
412
416
|
const request = RpcRequest.from((await req.json()));
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
417
|
+
try {
|
|
418
|
+
await onRequest?.(request);
|
|
419
|
+
if (request.method === 'eth_signTransaction') {
|
|
420
|
+
const transactionRequest = formatTransaction(request.params?.[0]);
|
|
421
|
+
const serializedTransaction = await signTransaction(client, {
|
|
422
|
+
...transactionRequest,
|
|
423
|
+
account,
|
|
424
|
+
// @ts-expect-error
|
|
425
|
+
feePayer: account,
|
|
426
|
+
});
|
|
427
|
+
return Response.json(RpcResponse.from({ result: serializedTransaction }, { request }));
|
|
428
|
+
}
|
|
429
|
+
if (request.method === 'eth_signRawTransaction') {
|
|
430
|
+
const serialized = request.params?.[0];
|
|
431
|
+
const transaction = Transaction.deserialize(serialized);
|
|
432
|
+
const serializedTransaction = await signTransaction(client, {
|
|
433
|
+
...transaction,
|
|
434
|
+
account,
|
|
435
|
+
// @ts-expect-error
|
|
436
|
+
feePayer: account,
|
|
437
|
+
});
|
|
438
|
+
return Response.json(RpcResponse.from({ result: serializedTransaction }, { request }));
|
|
439
|
+
}
|
|
440
|
+
if (request.method === 'eth_sendRawTransaction' ||
|
|
441
|
+
request.method === 'eth_sendRawTransactionSync') {
|
|
442
|
+
const serialized = request.params?.[0];
|
|
443
|
+
const transaction = Transaction.deserialize(serialized);
|
|
444
|
+
const serializedTransaction = await signTransaction(client, {
|
|
445
|
+
...transaction,
|
|
446
|
+
account,
|
|
447
|
+
// @ts-expect-error
|
|
448
|
+
feePayer: account,
|
|
449
|
+
});
|
|
450
|
+
const result = await client.request({
|
|
451
|
+
method: request.method,
|
|
452
|
+
params: [serializedTransaction],
|
|
453
|
+
});
|
|
454
|
+
return Response.json(RpcResponse.from({ result }, { request }));
|
|
455
|
+
}
|
|
456
|
+
return Response.json(RpcResponse.from({
|
|
457
|
+
error: new RpcResponse.MethodNotSupportedError({
|
|
458
|
+
message: `Method not supported: ${request.method}`,
|
|
459
|
+
}),
|
|
460
|
+
}, { request }));
|
|
434
461
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
account,
|
|
442
|
-
// @ts-expect-error
|
|
443
|
-
feePayer: account,
|
|
444
|
-
});
|
|
445
|
-
const result = await client.request({
|
|
446
|
-
method: request.method,
|
|
447
|
-
params: [serializedTransaction],
|
|
448
|
-
});
|
|
449
|
-
return Response.json(RpcResponse.from({ result }, { request }));
|
|
462
|
+
catch (error) {
|
|
463
|
+
return Response.json(RpcResponse.from({
|
|
464
|
+
error: new RpcResponse.InternalError({
|
|
465
|
+
message: error.message,
|
|
466
|
+
}),
|
|
467
|
+
}, { request }));
|
|
450
468
|
}
|
|
451
|
-
return Response.json(RpcResponse.from({
|
|
452
|
-
error: new RpcResponse.MethodNotSupportedError({
|
|
453
|
-
message: `Method not supported: ${request.method}`,
|
|
454
|
-
}),
|
|
455
|
-
}, { request }), { status: 400 });
|
|
456
469
|
});
|
|
457
470
|
return router;
|
|
458
471
|
}
|
|
472
|
+
/** @internal */
|
|
473
|
+
function normalizeHeaders(headers) {
|
|
474
|
+
if (!headers)
|
|
475
|
+
return new Headers();
|
|
476
|
+
if (headers instanceof Headers)
|
|
477
|
+
return headers;
|
|
478
|
+
return new Headers(headers);
|
|
479
|
+
}
|
|
480
|
+
/** @internal */
|
|
481
|
+
function headers(headers) {
|
|
482
|
+
const normalizedHeaders = normalizeHeaders(headers);
|
|
483
|
+
return async (_, next) => {
|
|
484
|
+
const response = await next();
|
|
485
|
+
const headers = new Headers(response.headers);
|
|
486
|
+
for (const [key, value] of normalizedHeaders.entries())
|
|
487
|
+
headers.set(key, value);
|
|
488
|
+
return new Response(response.body, {
|
|
489
|
+
headers,
|
|
490
|
+
status: response.status,
|
|
491
|
+
statusText: response.statusText,
|
|
492
|
+
});
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
/** @internal */
|
|
496
|
+
function preflight(headers) {
|
|
497
|
+
const normalizedHeaders = normalizeHeaders(headers);
|
|
498
|
+
return async (context) => {
|
|
499
|
+
if (context.request.method === 'OPTIONS')
|
|
500
|
+
return new Response(null, { headers: normalizedHeaders });
|
|
501
|
+
};
|
|
502
|
+
}
|
|
459
503
|
//# sourceMappingURL=Handler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Handler.js","sourceRoot":"","sources":["../../src/server/Handler.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,
|
|
1
|
+
{"version":3,"file":"Handler.js","sourceRoot":"","sources":["../../src/server/Handler.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,GAIb,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,IAAI,CAAA;AAC5C,OAAO,KAAK,MAAM,MAAM,WAAW,CAAA;AACnC,OAAO,KAAK,GAAG,MAAM,QAAQ,CAAA;AAE7B,OAAO,EAA2B,YAAY,EAAkB,MAAM,MAAM,CAAA;AAE5E,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,eAAe,MAAM,+BAA+B,CAAA;AAOhE,MAAM,UAAU,OAAO,CACrB,QAAmB,EACnB,UAA2B,EAAE;IAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,CAAA;IAEhC,OAAO,IAAI,CAAC;QACV,GAAG,OAAO;QACV,KAAK,CAAC,cAAc,CAAC,OAAO;YAC1B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACxC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;gBAChC,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;YAEnD,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YAC7C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA;gBACzD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;gBAC7C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;oBAAE,OAAO,QAAQ,CAAA;YAC9C,CAAC;YACD,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QACnD,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AASD;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAAC,UAAwB,EAAE;IAC7C,MAAM,MAAM,GAAG,YAAY,CAAC;QAC1B,GAAG,OAAO;QACV,UAAU,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;KACnE,CAAC,CAAA;IAEF,OAAO;QACL,GAAG,MAAM;QACT,QAAQ,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE,EAAE;YACrD,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC9B,CAAC,CAAC;KACH,CAAA;AACH,CAAC;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GG;AACH,MAAM,UAAU,UAAU,CAAC,OAA2B;IACpD,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAA;IAEtB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAA;IAE/B,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE;QACf,IAAI,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ;YAChC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAA;QAC7C,IAAI,OAAO,CAAC,EAAE;YACZ,OAAO;gBACL,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE;gBACjB,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,IAAI,OAAO,CAAC,EAAE,CAAC,EAAE;aACvC,CAAA;QACH,OAAO,SAAS,CAAA;IAClB,CAAC,CAAC,EAAE,CAAA;IAEJ,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA;IAE5B,iDAAiD;IACjD,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,YAAY,EAAE,KAAK,IAAI,EAAE;QACzC,8BAA8B;QAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAEhC,iDAAiD;QACjD,MAAM,EAAE,CAAC,GAAG,CAAC,aAAa,SAAS,EAAE,EAAE,GAAG,CAAC,CAAA;QAE3C,OAAO,QAAQ,CAAC,IAAI,CAAC;YACnB,SAAS;YACT,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACiB,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;IAEF,kCAAkC;IAClC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QAC7C,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,CAAA;QAErB,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC,GAAG,CAAU,cAAc,EAAE,EAAE,CAAC,CAAA;QAE3D,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,QAAQ,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QAE5E,OAAO,QAAQ,CAAC,IAAI,CAAC;YACnB,SAAS;SACV,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,kCAAkC;IAClC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACvD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,CAAA;QACrB,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAQ,CAAA;QAE/D,IAAI,CAAC,UAAU;YACb,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QAC1E,IAAI,CAAC,SAAS;YACZ,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QAEzE,mCAAmC;QACnC,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAC/B,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,cAAmC,CAAC,CACzE,CAAA;QAED,mBAAmB;QACnB,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;QAExD,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAS,aAAa,SAAS,EAAE,CAAC,CAAC;YACnD,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,gCAAgC,EAAE,EAC3C,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QAEH,cAAc;QACd,IAAI,cAAc,CAAC,IAAI,KAAK,iBAAiB;YAC3C,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,+BAA+B,EAAE,EAC1C,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QAEH,gBAAgB;QAChB,IACE,EAAE,EAAE,EAAE;YACN,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC5B,cAAc,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM;YAE5D,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,iCAAiC,EAAE,EAC5C,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QAEH,0BAA0B;QAC1B,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CACrC,UAAU,CAAC,QAAgB,CAAC,iBAAiB,CAC/C,CAAA;QAED,wBAAwB;QACxB,MAAM,KAAK,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAA;QACnC,IAAI,CAAC,KAAK;YACR,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,6BAA6B,EAAE,EACxC,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;QAEH,uCAAuC;QACvC,MAAM,WAAW,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QACxC,IAAI,CAAC,WAAW;YACd,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QAEtE,0DAA0D;QAC1D,MAAM,EAAE,CAAC,MAAM,CAAC,aAAa,SAAS,EAAE,CAAC,CAAA;QAEzC,uBAAuB;QACvB,MAAM,EAAE,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;QAE3C,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC;AAsCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4KG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAyB;IAChD,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,GAAG,GAAG,EAAE,GAAG,OAAO,CAAA;IAElD,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;QACnB,IAAI,QAAQ,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC,MAAO,CAAA;QAC/C,IAAI,OAAO,IAAI,OAAO,IAAI,WAAW,IAAI,OAAO;YAC9C,OAAO,YAAY,CAAC;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,SAAS,EAAE,OAAO,CAAC,SAAS;aAC7B,CAAC,CAAA;QACJ,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAChD,CAAC,CAAC,EAAE,CAAA;IAEJ,MAAM,MAAM,GAAG,IAAI,EAAE,CAAA;IAErB,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE;QAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAQ,CAAC,CAAA;QAE1D,IAAI,CAAC;YACH,MAAM,SAAS,EAAE,CAAC,OAAO,CAAC,CAAA;YAE1B,IAAI,OAAO,CAAC,MAAM,KAAK,qBAAqB,EAAE,CAAC;gBAC7C,MAAM,kBAAkB,GAAG,iBAAiB,CAC1C,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAU,CAC7B,CAAA;gBAED,MAAM,qBAAqB,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE;oBAC1D,GAAG,kBAAkB;oBACrB,OAAO;oBACP,mBAAmB;oBACnB,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAA;gBAEF,OAAO,QAAQ,CAAC,IAAI,CAClB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CACjE,CAAA;YACH,CAAC;YAED,IAAK,OAAe,CAAC,MAAM,KAAK,wBAAwB,EAAE,CAAC;gBACzD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAoB,CAAA;gBACzD,MAAM,WAAW,GAAG,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;gBAEvD,MAAM,qBAAqB,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE;oBAC1D,GAAG,WAAW;oBACd,OAAO;oBACP,mBAAmB;oBACnB,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAA;gBAEF,OAAO,QAAQ,CAAC,IAAI,CAClB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,qBAAqB,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CACjE,CAAA;YACH,CAAC;YAED,IACE,OAAO,CAAC,MAAM,KAAK,wBAAwB;gBAC3C,OAAO,CAAC,MAAM,KAAK,4BAA4B,EAC/C,CAAC;gBACD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAoB,CAAA;gBACzD,MAAM,WAAW,GAAG,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;gBAEvD,MAAM,qBAAqB,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE;oBAC1D,GAAG,WAAW;oBACd,OAAO;oBACP,mBAAmB;oBACnB,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAA;gBAEF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;oBAClC,MAAM,EAAE,OAAO,CAAC,MAAM;oBACtB,MAAM,EAAE,CAAC,qBAAqB,CAAC;iBAChC,CAAC,CAAA;gBAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;YACjE,CAAC;YAED,OAAO,QAAQ,CAAC,IAAI,CAClB,WAAW,CAAC,IAAI,CACd;gBACE,KAAK,EAAE,IAAI,WAAW,CAAC,uBAAuB,CAAC;oBAC7C,OAAO,EAAE,yBAAyB,OAAO,CAAC,MAAM,EAAE;iBACnD,CAAC;aACH,EACD,EAAE,OAAO,EAAE,CACZ,CACF,CAAA;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,QAAQ,CAAC,IAAI,CAClB,WAAW,CAAC,IAAI,CACd;gBACE,KAAK,EAAE,IAAI,WAAW,CAAC,aAAa,CAAC;oBACnC,OAAO,EAAG,KAAe,CAAC,OAAO;iBAClC,CAAC;aACH,EACD,EAAE,OAAO,EAAE,CACZ,CACF,CAAA;QACH,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC;AAwBD,gBAAgB;AAChB,SAAS,gBAAgB,CAAC,OAA0C;IAClE,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,OAAO,EAAE,CAAA;IAClC,IAAI,OAAO,YAAY,OAAO;QAAE,OAAO,OAAO,CAAA;IAC9C,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;AAC7B,CAAC;AAED,gBAAgB;AAChB,SAAS,OAAO,CAAC,OAA0C;IACzD,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACnD,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAA;QAC7B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,iBAAiB,CAAC,OAAO,EAAE;YACpD,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QACzB,OAAO,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;YACjC,OAAO;YACP,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;SAChC,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC;AAED,gBAAgB;AAChB,SAAS,SAAS,CAAC,OAA0C;IAC3D,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACnD,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS;YACtC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAA;IAC7D,CAAC,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -74,6 +74,211 @@ describe('compose', () => {
|
|
|
74
74
|
}
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
+
test('behavior: headers', async () => {
|
|
78
|
+
const handler1 = Handler.from()
|
|
79
|
+
handler1.get('/test', () => new Response('test'))
|
|
80
|
+
const handler2 = Handler.from()
|
|
81
|
+
handler2.get('/test2', () => new Response('test2'))
|
|
82
|
+
|
|
83
|
+
const headers = new Headers({
|
|
84
|
+
'Access-Control-Allow-Origin': '*',
|
|
85
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
86
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const handler = Handler.compose([handler1, handler2], {
|
|
90
|
+
headers,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
{
|
|
94
|
+
const response = await handler.fetch(new Request('http://localhost/test'))
|
|
95
|
+
expect(response.status).toBe(200)
|
|
96
|
+
expect(await response.text()).toBe('test')
|
|
97
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
98
|
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe(
|
|
99
|
+
'POST, OPTIONS',
|
|
100
|
+
)
|
|
101
|
+
expect(response.headers.get('Access-Control-Allow-Headers')).toBe(
|
|
102
|
+
'Content-Type, Authorization',
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
{
|
|
107
|
+
const response = await handler.fetch(
|
|
108
|
+
new Request('http://localhost/test2'),
|
|
109
|
+
)
|
|
110
|
+
expect(response.status).toBe(200)
|
|
111
|
+
expect(await response.text()).toBe('test2')
|
|
112
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('behavior: headers + path', async () => {
|
|
117
|
+
const handler1 = Handler.from()
|
|
118
|
+
handler1.get('/test', () => new Response('test'))
|
|
119
|
+
const handler2 = Handler.from()
|
|
120
|
+
handler2.get('/test2', () => new Response('test2'))
|
|
121
|
+
|
|
122
|
+
const headers = new Headers({
|
|
123
|
+
'Access-Control-Allow-Origin': '*',
|
|
124
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const handler = Handler.compose([handler1, handler2], {
|
|
128
|
+
headers,
|
|
129
|
+
path: '/api',
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
const response = await handler.fetch(
|
|
134
|
+
new Request('http://localhost/api/test'),
|
|
135
|
+
)
|
|
136
|
+
expect(response.status).toBe(200)
|
|
137
|
+
expect(await response.text()).toBe('test')
|
|
138
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
139
|
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe(
|
|
140
|
+
'POST, OPTIONS',
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
{
|
|
145
|
+
const response = await handler.fetch(
|
|
146
|
+
new Request('http://localhost/api/test2'),
|
|
147
|
+
)
|
|
148
|
+
expect(response.status).toBe(200)
|
|
149
|
+
expect(await response.text()).toBe('test2')
|
|
150
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('behavior: headers + OPTIONS', async () => {
|
|
155
|
+
const handler1 = Handler.from()
|
|
156
|
+
handler1.get('/test', () => new Response('test'))
|
|
157
|
+
|
|
158
|
+
const headers = new Headers({
|
|
159
|
+
'Access-Control-Allow-Origin': '*',
|
|
160
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
161
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
162
|
+
'Access-Control-Max-Age': '86400',
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const handler = Handler.compose([handler1], {
|
|
166
|
+
headers,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const response = await handler.fetch(
|
|
170
|
+
new Request('http://localhost/test', {
|
|
171
|
+
method: 'OPTIONS',
|
|
172
|
+
}),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
expect(response.status).toBe(200)
|
|
176
|
+
expect(await response.text()).toBe('')
|
|
177
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
178
|
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe(
|
|
179
|
+
'POST, OPTIONS',
|
|
180
|
+
)
|
|
181
|
+
expect(response.headers.get('Access-Control-Allow-Headers')).toBe(
|
|
182
|
+
'Content-Type, Authorization',
|
|
183
|
+
)
|
|
184
|
+
expect(response.headers.get('Access-Control-Max-Age')).toBe('86400')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('behavior: headers + 404', async () => {
|
|
188
|
+
const handler1 = Handler.from()
|
|
189
|
+
handler1.get('/test', () => new Response('test'))
|
|
190
|
+
|
|
191
|
+
const headers = new Headers({
|
|
192
|
+
'Access-Control-Allow-Origin': '*',
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const handler = Handler.compose([handler1], {
|
|
196
|
+
headers,
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
const response = await handler.fetch(
|
|
200
|
+
new Request('http://localhost/nonexistent'),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
expect(response.status).toBe(404)
|
|
204
|
+
expect(await response.text()).toBe('Not Found')
|
|
205
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test('behavior: headers propagation from child handlers', async () => {
|
|
209
|
+
const handler1 = Handler.from()
|
|
210
|
+
handler1.get('/test', () => {
|
|
211
|
+
const response = new Response('test')
|
|
212
|
+
response.headers.set('X-Custom-Header', 'custom-value')
|
|
213
|
+
return response
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
const headers = new Headers({
|
|
217
|
+
'Access-Control-Allow-Origin': '*',
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
const handler = Handler.compose([handler1], {
|
|
221
|
+
headers,
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const response = await handler.fetch(new Request('http://localhost/test'))
|
|
225
|
+
|
|
226
|
+
expect(response.status).toBe(200)
|
|
227
|
+
expect(await response.text()).toBe('test')
|
|
228
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
229
|
+
expect(response.headers.get('X-Custom-Header')).toBe('custom-value')
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
test('behavior: headers with child handler headers', async () => {
|
|
233
|
+
const childHeaders = new Headers({
|
|
234
|
+
'X-Child-Header': 'child-value',
|
|
235
|
+
})
|
|
236
|
+
const handler1 = Handler.from({ headers: childHeaders })
|
|
237
|
+
handler1.get('/test', () => new Response('test'))
|
|
238
|
+
|
|
239
|
+
const parentHeaders = new Headers({
|
|
240
|
+
'Access-Control-Allow-Origin': '*',
|
|
241
|
+
'X-Parent-Header': 'parent-value',
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
const handler = Handler.compose([handler1], {
|
|
245
|
+
headers: parentHeaders,
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const response = await handler.fetch(new Request('http://localhost/test'))
|
|
249
|
+
|
|
250
|
+
expect(response.status).toBe(200)
|
|
251
|
+
expect(await response.text()).toBe('test')
|
|
252
|
+
// Both parent and child headers should be present
|
|
253
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
254
|
+
expect(response.headers.get('X-Parent-Header')).toBe('parent-value')
|
|
255
|
+
expect(response.headers.get('X-Child-Header')).toBe('child-value')
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
test('behavior: headers as object', async () => {
|
|
259
|
+
const handler1 = Handler.from()
|
|
260
|
+
handler1.get('/test', () => new Response('test'))
|
|
261
|
+
|
|
262
|
+
const handler = Handler.compose([handler1], {
|
|
263
|
+
headers: {
|
|
264
|
+
'Access-Control-Allow-Origin': '*',
|
|
265
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
266
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
267
|
+
},
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const response = await handler.fetch(new Request('http://localhost/test'))
|
|
271
|
+
expect(response.status).toBe(200)
|
|
272
|
+
expect(await response.text()).toBe('test')
|
|
273
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
274
|
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe(
|
|
275
|
+
'POST, OPTIONS',
|
|
276
|
+
)
|
|
277
|
+
expect(response.headers.get('Access-Control-Allow-Headers')).toBe(
|
|
278
|
+
'Content-Type, Authorization',
|
|
279
|
+
)
|
|
280
|
+
})
|
|
281
|
+
|
|
77
282
|
describe('integration', () => {
|
|
78
283
|
const handler1 = Handler.from()
|
|
79
284
|
handler1.get('/foo', () => new Response('foo'))
|
|
@@ -256,6 +461,117 @@ describe('from', () => {
|
|
|
256
461
|
expect(data).toEqual({ message: 'hello from listener' })
|
|
257
462
|
})
|
|
258
463
|
|
|
464
|
+
test('behavior: headers', async () => {
|
|
465
|
+
const headers = new Headers({
|
|
466
|
+
'Access-Control-Allow-Origin': '*',
|
|
467
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
468
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
const handler = Handler.from({ headers })
|
|
472
|
+
handler.get('/test', () => new Response('test'))
|
|
473
|
+
|
|
474
|
+
const response = await handler.fetch(new Request('http://localhost/test'))
|
|
475
|
+
expect(response.status).toBe(200)
|
|
476
|
+
expect(await response.text()).toBe('test')
|
|
477
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
478
|
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe(
|
|
479
|
+
'POST, OPTIONS',
|
|
480
|
+
)
|
|
481
|
+
expect(response.headers.get('Access-Control-Allow-Headers')).toBe(
|
|
482
|
+
'Content-Type, Authorization',
|
|
483
|
+
)
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
test('behavior: headers + OPTIONS', async () => {
|
|
487
|
+
const headers = new Headers({
|
|
488
|
+
'Access-Control-Allow-Origin': '*',
|
|
489
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
490
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
491
|
+
'Access-Control-Max-Age': '86400',
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
const handler = Handler.from({ headers })
|
|
495
|
+
handler.get('/test', () => new Response('test'))
|
|
496
|
+
|
|
497
|
+
const response = await handler.fetch(
|
|
498
|
+
new Request('http://localhost/test', {
|
|
499
|
+
method: 'OPTIONS',
|
|
500
|
+
}),
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
expect(response.status).toBe(200)
|
|
504
|
+
expect(await response.text()).toBe('')
|
|
505
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
506
|
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe(
|
|
507
|
+
'POST, OPTIONS',
|
|
508
|
+
)
|
|
509
|
+
expect(response.headers.get('Access-Control-Allow-Headers')).toBe(
|
|
510
|
+
'Content-Type, Authorization',
|
|
511
|
+
)
|
|
512
|
+
expect(response.headers.get('Access-Control-Max-Age')).toBe('86400')
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
test('behavior: headers + 404', async () => {
|
|
516
|
+
const headers = new Headers({
|
|
517
|
+
'Access-Control-Allow-Origin': '*',
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
const handler = Handler.from({ headers })
|
|
521
|
+
handler.get('/test', () => new Response('test'))
|
|
522
|
+
|
|
523
|
+
const response = await handler.fetch(
|
|
524
|
+
new Request('http://localhost/nonexistent'),
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
expect(response.status).toBe(404)
|
|
528
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
test('behavior: headers propagation from routes', async () => {
|
|
532
|
+
const headers = new Headers({
|
|
533
|
+
'Access-Control-Allow-Origin': '*',
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
const handler = Handler.from({ headers })
|
|
537
|
+
handler.get('/test', () => {
|
|
538
|
+
const response = new Response('test')
|
|
539
|
+
response.headers.set('X-Custom-Header', 'custom-value')
|
|
540
|
+
response.headers.set('Content-Type', 'text/plain')
|
|
541
|
+
return response
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
const response = await handler.fetch(new Request('http://localhost/test'))
|
|
545
|
+
|
|
546
|
+
expect(response.status).toBe(200)
|
|
547
|
+
expect(await response.text()).toBe('test')
|
|
548
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
549
|
+
expect(response.headers.get('X-Custom-Header')).toBe('custom-value')
|
|
550
|
+
expect(response.headers.get('Content-Type')).toBe('text/plain')
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
test('behavior: headers as object', async () => {
|
|
554
|
+
const handler = Handler.from({
|
|
555
|
+
headers: {
|
|
556
|
+
'Access-Control-Allow-Origin': '*',
|
|
557
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
558
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
559
|
+
},
|
|
560
|
+
})
|
|
561
|
+
handler.get('/test', () => new Response('test'))
|
|
562
|
+
|
|
563
|
+
const response = await handler.fetch(new Request('http://localhost/test'))
|
|
564
|
+
expect(response.status).toBe(200)
|
|
565
|
+
expect(await response.text()).toBe('test')
|
|
566
|
+
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
567
|
+
expect(response.headers.get('Access-Control-Allow-Methods')).toBe(
|
|
568
|
+
'POST, OPTIONS',
|
|
569
|
+
)
|
|
570
|
+
expect(response.headers.get('Access-Control-Allow-Headers')).toBe(
|
|
571
|
+
'Content-Type, Authorization',
|
|
572
|
+
)
|
|
573
|
+
})
|
|
574
|
+
|
|
259
575
|
describe('integration', () => {
|
|
260
576
|
const handler = Handler.from()
|
|
261
577
|
handler.get('/foo', () => new Response('foo'))
|
|
@@ -785,5 +1101,30 @@ describe('feePayer', () => {
|
|
|
785
1101
|
}
|
|
786
1102
|
`)
|
|
787
1103
|
})
|
|
1104
|
+
|
|
1105
|
+
test('behavior: internal error', async () => {
|
|
1106
|
+
const response = await fetch(server.url, {
|
|
1107
|
+
method: 'POST',
|
|
1108
|
+
body: JSON.stringify({
|
|
1109
|
+
jsonrpc: '2.0',
|
|
1110
|
+
id: 1,
|
|
1111
|
+
method: 'eth_signRawTransaction',
|
|
1112
|
+
params: ['0xinvalid'],
|
|
1113
|
+
}),
|
|
1114
|
+
})
|
|
1115
|
+
|
|
1116
|
+
const data = await response.json()
|
|
1117
|
+
expect(data).toMatchInlineSnapshot(`
|
|
1118
|
+
{
|
|
1119
|
+
"error": {
|
|
1120
|
+
"code": -32603,
|
|
1121
|
+
"name": "RpcResponse.InternalError",
|
|
1122
|
+
"stack": "",
|
|
1123
|
+
},
|
|
1124
|
+
"id": 1,
|
|
1125
|
+
"jsonrpc": "2.0",
|
|
1126
|
+
}
|
|
1127
|
+
`)
|
|
1128
|
+
})
|
|
788
1129
|
})
|
|
789
1130
|
})
|
package/src/server/Handler.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createRouter,
|
|
3
|
+
type Middleware,
|
|
3
4
|
type Router,
|
|
4
5
|
type RouterOptions,
|
|
5
6
|
} from '@remix-run/fetch-router'
|
|
@@ -27,6 +28,7 @@ export function compose(
|
|
|
27
28
|
const path = options.path ?? '/'
|
|
28
29
|
|
|
29
30
|
return from({
|
|
31
|
+
...options,
|
|
30
32
|
async defaultHandler(context) {
|
|
31
33
|
const url = new URL(context.request.url)
|
|
32
34
|
if (!url.pathname.startsWith(path))
|
|
@@ -44,7 +46,7 @@ export function compose(
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export declare namespace compose {
|
|
47
|
-
export type Options = {
|
|
49
|
+
export type Options = from.Options & {
|
|
48
50
|
/** The path to use for the handler. */
|
|
49
51
|
path?: string | undefined
|
|
50
52
|
}
|
|
@@ -57,7 +59,11 @@ export declare namespace compose {
|
|
|
57
59
|
* @returns Handler instance
|
|
58
60
|
*/
|
|
59
61
|
export function from(options: from.Options = {}): Handler {
|
|
60
|
-
const router = createRouter(
|
|
62
|
+
const router = createRouter({
|
|
63
|
+
...options,
|
|
64
|
+
middleware: [headers(options.headers), preflight(options.headers)],
|
|
65
|
+
})
|
|
66
|
+
|
|
61
67
|
return {
|
|
62
68
|
...router,
|
|
63
69
|
listener: RequestListener.fromFetchHandler((request) => {
|
|
@@ -67,7 +73,10 @@ export function from(options: from.Options = {}): Handler {
|
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
export declare namespace from {
|
|
70
|
-
export type Options = RouterOptions
|
|
76
|
+
export type Options = RouterOptions & {
|
|
77
|
+
/** Headers to add to the response. */
|
|
78
|
+
headers?: Headers | Record<string, string> | undefined
|
|
79
|
+
}
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
/**
|
|
@@ -193,7 +202,7 @@ export function keyManager(options: keyManager.Options) {
|
|
|
193
202
|
return undefined
|
|
194
203
|
})()
|
|
195
204
|
|
|
196
|
-
const router = from()
|
|
205
|
+
const router = from(options)
|
|
197
206
|
|
|
198
207
|
// Get challenge for WebAuthn credential creation
|
|
199
208
|
router.get(`${path}/challenge`, async () => {
|
|
@@ -295,7 +304,7 @@ export function keyManager(options: keyManager.Options) {
|
|
|
295
304
|
}
|
|
296
305
|
|
|
297
306
|
export declare namespace keyManager {
|
|
298
|
-
export type Options = {
|
|
307
|
+
export type Options = from.Options & {
|
|
299
308
|
/** The KV store to use for key management. */
|
|
300
309
|
kv: Kv.Kv
|
|
301
310
|
/** The path to use for the handler. */
|
|
@@ -521,79 +530,93 @@ export function feePayer(options: feePayer.Options) {
|
|
|
521
530
|
router.post(path, async ({ request: req }) => {
|
|
522
531
|
const request = RpcRequest.from((await req.json()) as any)
|
|
523
532
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (request.method === 'eth_signTransaction') {
|
|
527
|
-
const transactionRequest = formatTransaction(request.params?.[0] as never)
|
|
533
|
+
try {
|
|
534
|
+
await onRequest?.(request)
|
|
528
535
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
feePayer: account,
|
|
534
|
-
})
|
|
536
|
+
if (request.method === 'eth_signTransaction') {
|
|
537
|
+
const transactionRequest = formatTransaction(
|
|
538
|
+
request.params?.[0] as never,
|
|
539
|
+
)
|
|
535
540
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
541
|
+
const serializedTransaction = await signTransaction(client, {
|
|
542
|
+
...transactionRequest,
|
|
543
|
+
account,
|
|
544
|
+
// @ts-expect-error
|
|
545
|
+
feePayer: account,
|
|
546
|
+
})
|
|
540
547
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
548
|
+
return Response.json(
|
|
549
|
+
RpcResponse.from({ result: serializedTransaction }, { request }),
|
|
550
|
+
)
|
|
551
|
+
}
|
|
544
552
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
// @ts-expect-error
|
|
549
|
-
feePayer: account,
|
|
550
|
-
})
|
|
553
|
+
if ((request as any).method === 'eth_signRawTransaction') {
|
|
554
|
+
const serialized = request.params?.[0] as `0x76${string}`
|
|
555
|
+
const transaction = Transaction.deserialize(serialized)
|
|
551
556
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
557
|
+
const serializedTransaction = await signTransaction(client, {
|
|
558
|
+
...transaction,
|
|
559
|
+
account,
|
|
560
|
+
// @ts-expect-error
|
|
561
|
+
feePayer: account,
|
|
562
|
+
})
|
|
556
563
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const serialized = request.params?.[0] as `0x76${string}`
|
|
562
|
-
const transaction = Transaction.deserialize(serialized)
|
|
563
|
-
|
|
564
|
-
const serializedTransaction = await signTransaction(client, {
|
|
565
|
-
...transaction,
|
|
566
|
-
account,
|
|
567
|
-
// @ts-expect-error
|
|
568
|
-
feePayer: account,
|
|
569
|
-
})
|
|
564
|
+
return Response.json(
|
|
565
|
+
RpcResponse.from({ result: serializedTransaction }, { request }),
|
|
566
|
+
)
|
|
567
|
+
}
|
|
570
568
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
569
|
+
if (
|
|
570
|
+
request.method === 'eth_sendRawTransaction' ||
|
|
571
|
+
request.method === 'eth_sendRawTransactionSync'
|
|
572
|
+
) {
|
|
573
|
+
const serialized = request.params?.[0] as `0x76${string}`
|
|
574
|
+
const transaction = Transaction.deserialize(serialized)
|
|
575
|
+
|
|
576
|
+
const serializedTransaction = await signTransaction(client, {
|
|
577
|
+
...transaction,
|
|
578
|
+
account,
|
|
579
|
+
// @ts-expect-error
|
|
580
|
+
feePayer: account,
|
|
581
|
+
})
|
|
582
|
+
|
|
583
|
+
const result = await client.request({
|
|
584
|
+
method: request.method,
|
|
585
|
+
params: [serializedTransaction],
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
return Response.json(RpcResponse.from({ result }, { request }))
|
|
589
|
+
}
|
|
575
590
|
|
|
576
|
-
return Response.json(
|
|
591
|
+
return Response.json(
|
|
592
|
+
RpcResponse.from(
|
|
593
|
+
{
|
|
594
|
+
error: new RpcResponse.MethodNotSupportedError({
|
|
595
|
+
message: `Method not supported: ${request.method}`,
|
|
596
|
+
}),
|
|
597
|
+
},
|
|
598
|
+
{ request },
|
|
599
|
+
),
|
|
600
|
+
)
|
|
601
|
+
} catch (error) {
|
|
602
|
+
return Response.json(
|
|
603
|
+
RpcResponse.from(
|
|
604
|
+
{
|
|
605
|
+
error: new RpcResponse.InternalError({
|
|
606
|
+
message: (error as Error).message,
|
|
607
|
+
}),
|
|
608
|
+
},
|
|
609
|
+
{ request },
|
|
610
|
+
),
|
|
611
|
+
)
|
|
577
612
|
}
|
|
578
|
-
|
|
579
|
-
return Response.json(
|
|
580
|
-
RpcResponse.from(
|
|
581
|
-
{
|
|
582
|
-
error: new RpcResponse.MethodNotSupportedError({
|
|
583
|
-
message: `Method not supported: ${request.method}`,
|
|
584
|
-
}),
|
|
585
|
-
},
|
|
586
|
-
{ request },
|
|
587
|
-
),
|
|
588
|
-
{ status: 400 },
|
|
589
|
-
)
|
|
590
613
|
})
|
|
591
614
|
|
|
592
615
|
return router
|
|
593
616
|
}
|
|
594
617
|
|
|
595
618
|
export declare namespace feePayer {
|
|
596
|
-
export type Options = {
|
|
619
|
+
export type Options = from.Options & {
|
|
597
620
|
/** Account to use as the fee payer. */
|
|
598
621
|
account: LocalAccount
|
|
599
622
|
/** Function to call before handling the request. */
|
|
@@ -601,15 +624,47 @@ export declare namespace feePayer {
|
|
|
601
624
|
/** Path to use for the handler. */
|
|
602
625
|
path?: string | undefined
|
|
603
626
|
} & OneOf<
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
627
|
+
| {
|
|
628
|
+
/** Client to use. */
|
|
629
|
+
client: Client
|
|
630
|
+
}
|
|
631
|
+
| {
|
|
632
|
+
/** Chain to use. */
|
|
633
|
+
chain: Chain
|
|
634
|
+
/** Transport to use. */
|
|
635
|
+
transport: Transport
|
|
636
|
+
}
|
|
637
|
+
>
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** @internal */
|
|
641
|
+
function normalizeHeaders(headers?: Headers | Record<string, string>): Headers {
|
|
642
|
+
if (!headers) return new Headers()
|
|
643
|
+
if (headers instanceof Headers) return headers
|
|
644
|
+
return new Headers(headers)
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** @internal */
|
|
648
|
+
function headers(headers?: Headers | Record<string, string>): Middleware {
|
|
649
|
+
const normalizedHeaders = normalizeHeaders(headers)
|
|
650
|
+
return async (_, next) => {
|
|
651
|
+
const response = await next()
|
|
652
|
+
const headers = new Headers(response.headers)
|
|
653
|
+
for (const [key, value] of normalizedHeaders.entries())
|
|
654
|
+
headers.set(key, value)
|
|
655
|
+
return new Response(response.body, {
|
|
656
|
+
headers,
|
|
657
|
+
status: response.status,
|
|
658
|
+
statusText: response.statusText,
|
|
659
|
+
})
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/** @internal */
|
|
664
|
+
function preflight(headers?: Headers | Record<string, string>): Middleware {
|
|
665
|
+
const normalizedHeaders = normalizeHeaders(headers)
|
|
666
|
+
return async (context) => {
|
|
667
|
+
if (context.request.method === 'OPTIONS')
|
|
668
|
+
return new Response(null, { headers: normalizedHeaders })
|
|
669
|
+
}
|
|
615
670
|
}
|