wooks 0.0.1-beta.4 → 0.0.1-beta.6

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.
@@ -10,6 +10,7 @@ var fs = require('fs');
10
10
  var path = require('path');
11
11
  var url = require('url');
12
12
  var net = require('net');
13
+ var nodeFetchNative = require('node-fetch-native');
13
14
 
14
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
15
16
 
@@ -18,8 +19,15 @@ var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
18
19
 
19
20
  const banner = () => `[wooks][${new Date().toISOString().replace('T', ' ').replace(/\.\d{3}z$/i, '')}] `;
20
21
 
21
- function panic(error) {
22
+ function warn(text) {
23
+ console.warn('' + banner() + text + '');
24
+ }
25
+ function logError(error) {
22
26
  console.error('' + '' + banner() + error + '');
27
+ }
28
+
29
+ function panic(error) {
30
+ logError(error);
23
31
  return new Error(error);
24
32
  }
25
33
 
@@ -195,6 +203,9 @@ class BaseWooksResponseRenderer {
195
203
  if (typeof response.body === 'undefined') {
196
204
  return '';
197
205
  }
206
+ if (response.body instanceof Uint8Array) {
207
+ return response.body;
208
+ }
198
209
  if (typeof response.body === 'object') {
199
210
  if (!response.getContentType())
200
211
  response.setContentType(contentTypes.application.json);
@@ -232,11 +243,10 @@ function useRequest() {
232
243
  return reqCache.reqId;
233
244
  }
234
245
  function getIp(options) {
235
- var _a, _b, _c;
236
- if (options === null || options === void 0 ? void 0 : options.trustProxy) {
246
+ if (options?.trustProxy) {
237
247
  if (typeof reqCache.forwardedIp !== 'string') {
238
248
  if (typeof req.headers[xForwardedFor] === 'string' && req.headers[xForwardedFor]) {
239
- reqCache.forwardedIp = (_a = req.headers[xForwardedFor].split(',').shift()) === null || _a === void 0 ? void 0 : _a.trim();
249
+ reqCache.forwardedIp = req.headers[xForwardedFor].split(',').shift()?.trim();
240
250
  }
241
251
  else {
242
252
  return getIp();
@@ -246,16 +256,15 @@ function useRequest() {
246
256
  }
247
257
  else {
248
258
  if (typeof reqCache.remoteIp !== 'string') {
249
- reqCache.remoteIp = ((_b = req.socket) === null || _b === void 0 ? void 0 : _b.remoteAddress) || ((_c = req.connection) === null || _c === void 0 ? void 0 : _c.remoteAddress) || '';
259
+ reqCache.remoteIp = req.socket?.remoteAddress || req.connection?.remoteAddress || '';
250
260
  }
251
261
  return reqCache.remoteIp;
252
262
  }
253
263
  }
254
264
  function getIpList() {
255
- var _a, _b;
256
265
  if (!reqCache.ipList) {
257
266
  reqCache.ipList = {
258
- remoteIp: ((_a = req.socket) === null || _a === void 0 ? void 0 : _a.remoteAddress) || ((_b = req.connection) === null || _b === void 0 ? void 0 : _b.remoteAddress) || '',
267
+ remoteIp: req.socket?.remoteAddress || req.connection?.remoteAddress || '',
259
268
  forwarded: (req.headers[xForwardedFor] || '').split(',').map(s => s.trim()),
260
269
  };
261
270
  }
@@ -318,10 +327,10 @@ function useSetHeaders() {
318
327
  delete cache[name];
319
328
  }
320
329
  function setContentType(value) {
321
- setHeader('Content-Type', value);
330
+ setHeader('content-type', value);
322
331
  }
323
332
  function enableCors(origin = '*') {
324
- setHeader('Access-Control-Allow-Origin', origin);
333
+ setHeader('access-control-allow-origin', origin);
325
334
  }
326
335
  return {
327
336
  setHeader,
@@ -378,13 +387,13 @@ function useAuthorization() {
378
387
  authorization,
379
388
  authType,
380
389
  authRawCredentials,
381
- isBasic: () => { var _a; return ((_a = authType()) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase()) === 'basic'; },
382
- isBearer: () => { var _a; return ((_a = authType()) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase()) === 'bearer'; },
390
+ isBasic: () => authType()?.toLocaleLowerCase() === 'basic',
391
+ isBearer: () => authType()?.toLocaleLowerCase() === 'bearer',
383
392
  basicCredentials: () => {
384
393
  if (authorization) {
385
394
  if (typeof cache.basicCredentials === 'undefined') {
386
395
  const type = authType();
387
- if ((type === null || type === void 0 ? void 0 : type.toLocaleLowerCase()) === 'basic') {
396
+ if (type?.toLocaleLowerCase() === 'basic') {
388
397
  const creds = Buffer.from(authRawCredentials() || '', 'base64').toString('ascii');
389
398
  const [username, password] = creds.split(':');
390
399
  cache.basicCredentials = { username, password };
@@ -455,16 +464,16 @@ const renderPragmaNoCache = (v) => v ? 'no-cache' : '';
455
464
  function useSetCacheControl() {
456
465
  const { setHeader } = useSetHeaders();
457
466
  const setAge = (value) => {
458
- setHeader('Age', renderAge(value));
467
+ setHeader('age', renderAge(value));
459
468
  };
460
469
  const setExpires = (value) => {
461
- setHeader('Expires', renderExpires(value));
470
+ setHeader('expires', renderExpires(value));
462
471
  };
463
472
  const setPragmaNoCache = (value = true) => {
464
- setHeader('Pragma', renderPragmaNoCache(value));
473
+ setHeader('pragma', renderPragmaNoCache(value));
465
474
  };
466
475
  const setCacheControl = (data) => {
467
- setHeader('Cache-Control', renderCacheControl(data));
476
+ setHeader('cache-control', renderCacheControl(data));
468
477
  };
469
478
  return {
470
479
  setExpires,
@@ -825,12 +834,12 @@ function useRespCookie(name) {
825
834
  attrs: {},
826
835
  };
827
836
  Object.defineProperty(hook, 'value', {
828
- get: () => { var _a; return (_a = getCookie(name)) === null || _a === void 0 ? void 0 : _a.value; },
829
- set: (value) => { var _a; return setCookie(name, value, (_a = getCookie(name)) === null || _a === void 0 ? void 0 : _a.attrs); },
837
+ get: () => getCookie(name)?.value,
838
+ set: (value) => setCookie(name, value, getCookie(name)?.attrs),
830
839
  });
831
840
  Object.defineProperty(hook, 'attrs', {
832
- get: () => { var _a; return (_a = getCookie(name)) === null || _a === void 0 ? void 0 : _a.attrs; },
833
- set: (attrs) => { var _a; return setCookie(name, ((_a = getCookie(name)) === null || _a === void 0 ? void 0 : _a.value) || '', attrs); },
841
+ get: () => getCookie(name)?.attrs,
842
+ set: (attrs) => setCookie(name, getCookie(name)?.value || '', attrs),
834
843
  });
835
844
  return hook;
836
845
  }
@@ -861,6 +870,135 @@ function useStatus() {
861
870
  return hook;
862
871
  }
863
872
 
873
+ class IterableRecords {
874
+ [Symbol.iterator]() {
875
+ return this;
876
+ }
877
+ index = 0;
878
+ next() {
879
+ return { value: undefined, done: true };
880
+ }
881
+ }
882
+ class CookiesIterable extends IterableRecords {
883
+ cookies;
884
+ constructor(cookiesString) {
885
+ super();
886
+ this.cookies = cookiesString.split(/,\s(?!\d{2}[\s-])/);
887
+ }
888
+ next() {
889
+ const str = this.cookies[this.index++];
890
+ const ind = str ? str.indexOf('=') : 0;
891
+ return this.index <= this.cookies.length ?
892
+ { value: [str.slice(0, ind), str.slice(ind + 1)], done: false } :
893
+ { value: undefined, done: true };
894
+ }
895
+ }
896
+ class HeadersIterable extends IterableRecords {
897
+ entries;
898
+ constructor(headers) {
899
+ super();
900
+ this.entries = Object.entries(headers);
901
+ }
902
+ next() {
903
+ return this.index < this.entries.length ?
904
+ { value: this.entries[this.index++], done: false } :
905
+ { value: undefined, done: true };
906
+ }
907
+ }
908
+ function applyProxyControls(records, controls, additionalBlockers) {
909
+ let result = {};
910
+ const { allow, block, overwrite } = controls;
911
+ const defaultedAllow = allow || '*';
912
+ if (defaultedAllow) {
913
+ for (const [name, value] of records) {
914
+ const add = block !== '*' && (defaultedAllow === '*' ||
915
+ (defaultedAllow.find(item => typeof item === 'string' && name.toLowerCase() === item.toLowerCase() || item instanceof RegExp && item.test(name)))) && (!block || !block.find(item => typeof item === 'string' && name.toLowerCase() === item.toLowerCase() || item instanceof RegExp && item.test(name))) && (!additionalBlockers || !additionalBlockers.includes(name));
916
+ if (add) {
917
+ result[name] = value;
918
+ }
919
+ }
920
+ }
921
+ if (overwrite) {
922
+ if (typeof overwrite === 'function') {
923
+ result = overwrite(result);
924
+ }
925
+ else {
926
+ result = { ...result, ...overwrite };
927
+ }
928
+ }
929
+ return result;
930
+ }
931
+
932
+ const reqHeadersToBlock = [
933
+ 'connection',
934
+ 'accept-encoding',
935
+ 'content-length',
936
+ 'upgrade-insecure-requests',
937
+ 'cookie',
938
+ ];
939
+ const resHeadersToBlock = [
940
+ 'transfer-encoding',
941
+ 'content-encoding',
942
+ 'set-cookie',
943
+ ];
944
+ function useProxy() {
945
+ const status = useStatus();
946
+ const { setHeader, headers: setHeadersObject } = useSetHeaders();
947
+ const { req } = useCurrentWooksContext().getCtx();
948
+ return async function proxy(target, opts) {
949
+ const targetUrl = new URL(target);
950
+ const path = targetUrl.pathname || '/';
951
+ const url = new URL(path, targetUrl.origin).toString() + (targetUrl.search);
952
+ const modifiedHeaders = { ...req.headers, host: targetUrl.hostname };
953
+ const headers = opts?.reqHeaders ? applyProxyControls(new HeadersIterable(modifiedHeaders), opts?.reqHeaders, reqHeadersToBlock) : {};
954
+ const cookies = opts?.reqCookies && req.headers.cookie ? applyProxyControls(new CookiesIterable(req.headers.cookie), opts?.reqCookies) : null;
955
+ if (cookies) {
956
+ headers.cookie = Object.entries(cookies).map(v => v.join('=')).join('; ');
957
+ }
958
+ const method = opts?.method || req.method;
959
+ if (opts?.debug) {
960
+ console.log();
961
+ warn(`[proxy] ${''}${req.method} ${req.url}${''} → ${''}${method} ${url}${''}`);
962
+ console.log('' + 'headers:', JSON.stringify(headers, null, ' '), '');
963
+ }
964
+ const resp = await nodeFetchNative.fetch(url, {
965
+ method,
966
+ body: ['GET', 'HEAD'].includes(method) ? undefined : req,
967
+ headers: headers,
968
+ });
969
+ status.value = resp.status;
970
+ if (opts?.debug) {
971
+ console.log();
972
+ warn(`[proxy] ${resp.status} ${''}${req.method} ${req.url}${''} → ${''}${method} ${url}${''}`);
973
+ console.log(`${''}response headers:${''}`);
974
+ }
975
+ const resHeaders = opts?.resHeaders ? applyProxyControls(resp.headers.entries(), opts?.resHeaders, resHeadersToBlock) : null;
976
+ const resCookies = opts?.resCookies ? applyProxyControls(new CookiesIterable(resp.headers.get('set-cookie') || ''), opts?.resCookies) : null;
977
+ if (resHeaders) {
978
+ for (const [name, value] of Object.entries(resHeaders)) {
979
+ if (name) {
980
+ setHeader(name, value);
981
+ if (opts?.debug) {
982
+ console.log(`\t${''}${name}=${''}${value}${''}`);
983
+ }
984
+ }
985
+ }
986
+ }
987
+ if (resCookies) {
988
+ setHeadersObject['set-cookie'] = (setHeadersObject['set-cookie'] || []);
989
+ for (const [name, value] of Object.entries(resCookies)) {
990
+ if (name) {
991
+ setHeadersObject['set-cookie'].push(`${name}=${value}`);
992
+ if (opts?.debug) {
993
+ console.log(`\t${''}${''}set-cookie=${''}${name}=${value}${''}`);
994
+ }
995
+ }
996
+ }
997
+ }
998
+ return resp;
999
+ };
1000
+ }
1001
+
864
1002
  const httpStatusCodes = {
865
1003
  [100]: 'Continue',
866
1004
  [101]: 'Switching protocols',
@@ -1001,12 +1139,15 @@ const defaultStatus = {
1001
1139
  DELETE: exports.EHttpStatusCode.Accepted,
1002
1140
  };
1003
1141
  const baseRenderer = new BaseWooksResponseRenderer();
1142
+ const FetchResponse = Response;
1004
1143
  class BaseWooksResponse {
1144
+ renderer;
1005
1145
  constructor(renderer = baseRenderer) {
1006
1146
  this.renderer = renderer;
1007
- this._status = 0;
1008
- this._headers = {};
1009
1147
  }
1148
+ _status = 0;
1149
+ _body;
1150
+ _headers = {};
1010
1151
  get status() {
1011
1152
  return this._status;
1012
1153
  }
@@ -1028,10 +1169,10 @@ class BaseWooksResponse {
1028
1169
  return this;
1029
1170
  }
1030
1171
  getContentType() {
1031
- return this._headers['Content-Type'];
1172
+ return this._headers['content-type'];
1032
1173
  }
1033
1174
  setContentType(value) {
1034
- this._headers['Content-Type'] = value;
1175
+ this._headers['content-type'] = value;
1035
1176
  return this;
1036
1177
  }
1037
1178
  enableCors(origin = '*') {
@@ -1039,15 +1180,15 @@ class BaseWooksResponse {
1039
1180
  return this;
1040
1181
  }
1041
1182
  setCookie(name, value, attrs) {
1042
- const cookies = this._headers['Set-Cookie'] = (this._headers['Set-Cookie'] || []);
1183
+ const cookies = this._headers['set-cookie'] = (this._headers['set-cookie'] || []);
1043
1184
  cookies.push(renderCookie(name, { value, attrs: attrs || {} }));
1044
1185
  return this;
1045
1186
  }
1046
1187
  setCacheControl(data) {
1047
- this.setHeader('Cache-Control', renderCacheControl(data));
1188
+ this.setHeader('cache-control', renderCacheControl(data));
1048
1189
  }
1049
1190
  setCookieRaw(rawValue) {
1050
- const cookies = this._headers['Set-Cookie'] = (this._headers['Set-Cookie'] || []);
1191
+ const cookies = this._headers['set-cookie'] = (this._headers['set-cookie'] || []);
1051
1192
  cookies.push(rawValue);
1052
1193
  return this;
1053
1194
  }
@@ -1064,17 +1205,18 @@ class BaseWooksResponse {
1064
1205
  mergeHeaders() {
1065
1206
  const { headers } = useSetHeaders();
1066
1207
  const { cookies, removeCookie } = useSetCookies();
1067
- const newCookies = (this._headers['Set-Cookie'] || []);
1208
+ const newCookies = (this._headers['set-cookie'] || []);
1068
1209
  for (const cookie of newCookies) {
1069
1210
  removeCookie(cookie.slice(0, cookie.indexOf('=')));
1070
1211
  }
1071
1212
  this._headers = {
1213
+ server: `Wooks v${"0.0.1-beta.5"}`,
1072
1214
  ...headers,
1073
1215
  ...this._headers,
1074
1216
  };
1075
1217
  const setCookie = [...newCookies, ...cookies()];
1076
1218
  if (setCookie && setCookie.length) {
1077
- this._headers['Set-Cookie'] = setCookie;
1219
+ this._headers['set-cookie'] = setCookie;
1078
1220
  }
1079
1221
  return this;
1080
1222
  }
@@ -1118,15 +1260,36 @@ class BaseWooksResponse {
1118
1260
  stream.pipe(res);
1119
1261
  }
1120
1262
  }
1263
+ else if (this.body instanceof FetchResponse) {
1264
+ this.mergeStatus('ok');
1265
+ if (method === 'HEAD') {
1266
+ res.end();
1267
+ }
1268
+ else {
1269
+ void respondWithFetch(this.body.body, res);
1270
+ }
1271
+ }
1121
1272
  else {
1122
1273
  const renderedBody = this.renderer.render(this);
1123
1274
  this.mergeStatus(renderedBody);
1124
1275
  res.writeHead(this.status, {
1125
- 'Content-Length': Buffer.byteLength(renderedBody),
1276
+ 'content-length': Buffer.byteLength(renderedBody),
1126
1277
  ...this._headers,
1127
1278
  }).end(method !== 'HEAD' ? renderedBody : '');
1128
1279
  }
1129
1280
  }
1281
+ }
1282
+ async function respondWithFetch(fetchBody, res) {
1283
+ if (fetchBody) {
1284
+ try {
1285
+ for await (const chunk of fetchBody) {
1286
+ res.write(chunk);
1287
+ }
1288
+ }
1289
+ catch (e) {
1290
+ }
1291
+ }
1292
+ res.end();
1130
1293
  }
1131
1294
 
1132
1295
  const preStyles = 'font-family: monospace;'
@@ -1146,7 +1309,7 @@ class WooksErrorRenderer extends BaseWooksResponseRenderer {
1146
1309
  `<head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head>` +
1147
1310
  `<body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center>` +
1148
1311
  `<center><h4>${data.message}</h1></center><hr color="#666">` +
1149
- `<center style="color: #666;"> wooks v${"0.0.1-beta.3"} </center>` +
1312
+ `<center style="color: #666;"> Wooks v${"0.0.1-beta.5"} </center>` +
1150
1313
  `${keys.length ? `<pre style="${preStyles}">${JSON.stringify({ ...data, statusCode: undefined, message: undefined, error: undefined }, null, ' ')}</pre>` : ''}` +
1151
1314
  '</body></html>';
1152
1315
  }
@@ -1167,9 +1330,8 @@ class WooksErrorRenderer extends BaseWooksResponseRenderer {
1167
1330
  + `${keys.length ? (',' + keys.map(k => `"${escapeQuotes(k)}":${JSON.stringify(data[k])}`).join(',')) : ''}}`;
1168
1331
  }
1169
1332
  render(response) {
1170
- var _a;
1171
1333
  const { acceptsJson, acceptsText, acceptsHtml } = useAccept();
1172
- response.status = ((_a = response.body) === null || _a === void 0 ? void 0 : _a.statusCode) || 500;
1334
+ response.status = response.body?.statusCode || 500;
1173
1335
  if (acceptsJson()) {
1174
1336
  return this.renderJson(response);
1175
1337
  }
@@ -1189,6 +1351,8 @@ function escapeQuotes(s) {
1189
1351
  }
1190
1352
 
1191
1353
  class WooksError extends Error {
1354
+ code;
1355
+ _body;
1192
1356
  constructor(code = 500, _body = '') {
1193
1357
  super(typeof _body === 'string' ? _body : _body.message);
1194
1358
  this.code = code;
@@ -1206,6 +1370,7 @@ class WooksError extends Error {
1206
1370
  error: httpStatusCodes[this.code],
1207
1371
  };
1208
1372
  }
1373
+ renderer;
1209
1374
  attachRenderer(renderer) {
1210
1375
  this.renderer = renderer;
1211
1376
  }
@@ -1239,6 +1404,10 @@ function createResponseFrom(data) {
1239
1404
  }
1240
1405
 
1241
1406
  class Wooks {
1407
+ options;
1408
+ router;
1409
+ server;
1410
+ _uncoughtExceptionHandler;
1242
1411
  constructor(options) {
1243
1412
  this.options = options;
1244
1413
  this.router = new router.ProstoRouter();
@@ -1251,22 +1420,20 @@ class Wooks {
1251
1420
  }
1252
1421
  listen(port, hostname, cb) {
1253
1422
  return new Promise((resolve, reject) => {
1254
- var _a;
1255
1423
  const myCb = () => {
1256
- var _a;
1257
1424
  const fn = typeof hostname === 'function' ? hostname : cb;
1258
1425
  process.on('uncaughtException', this._uncoughtExceptionHandler);
1259
1426
  if (fn) {
1260
1427
  fn();
1261
1428
  }
1262
- (_a = this.server) === null || _a === void 0 ? void 0 : _a.off('error', reject);
1429
+ this.server?.off('error', reject);
1263
1430
  resolve();
1264
1431
  };
1265
1432
  try {
1266
1433
  this.server = createServer({
1267
1434
  port,
1268
1435
  }, this.processRequest.bind(this), typeof hostname === 'string' ? hostname : '', myCb);
1269
- (_a = this.server) === null || _a === void 0 ? void 0 : _a.on('error', reject);
1436
+ this.server?.on('error', reject);
1270
1437
  }
1271
1438
  catch (e) {
1272
1439
  reject(e);
@@ -1275,8 +1442,7 @@ class Wooks {
1275
1442
  }
1276
1443
  close() {
1277
1444
  return new Promise((resolve, reject) => {
1278
- var _a;
1279
- (_a = this.server) === null || _a === void 0 ? void 0 : _a.close((err) => {
1445
+ this.server?.close((err) => {
1280
1446
  if (err)
1281
1447
  return reject(err);
1282
1448
  process.off('uncaughtException', this._uncoughtExceptionHandler);
@@ -1285,11 +1451,10 @@ class Wooks {
1285
1451
  });
1286
1452
  }
1287
1453
  processRequest(req, res) {
1288
- var _a;
1289
1454
  const found = this.router.lookup(req.method, req.url);
1290
1455
  const ctx = {
1291
1456
  __setHeader: {
1292
- Server: 'wooks v' + "0.0.1-beta.3",
1457
+ Server: 'wooks v' + "0.0.1-beta.5",
1293
1458
  },
1294
1459
  };
1295
1460
  if (found) {
@@ -1298,22 +1463,20 @@ class Wooks {
1298
1463
  const { restoreCtx } = useCurrentWooksContext();
1299
1464
  this.processHandlers(req, res, found)
1300
1465
  .catch((e) => {
1301
- var _a;
1302
1466
  this.printError('Internal error, please report: ', e);
1303
1467
  restoreCtx();
1304
- (_a = createResponseFrom(e)) === null || _a === void 0 ? void 0 : _a.respond();
1468
+ createResponseFrom(e)?.respond();
1305
1469
  clearCurrentWooksContext();
1306
1470
  console.error('' + '' + banner(), e, '');
1307
1471
  });
1308
1472
  }
1309
1473
  else {
1310
1474
  setCurrentWooksContext(req, res, {}, ctx);
1311
- (_a = createResponseFrom(new WooksError(404))) === null || _a === void 0 ? void 0 : _a.respond();
1475
+ createResponseFrom(new WooksError(404))?.respond();
1312
1476
  clearCurrentWooksContext();
1313
1477
  }
1314
1478
  }
1315
1479
  async processHandlers(req, res, found) {
1316
- var _a, _b;
1317
1480
  const { restoreCtx } = useCurrentWooksContext();
1318
1481
  for (const [i, handler] of found.route.handlers.entries()) {
1319
1482
  const isLastHandler = found.route.handlers.length === i + 1;
@@ -1323,7 +1486,7 @@ class Wooks {
1323
1486
  clearCurrentWooksContext();
1324
1487
  const result = await promise;
1325
1488
  restoreCtx();
1326
- (_a = createResponseFrom(result)) === null || _a === void 0 ? void 0 : _a.respond();
1489
+ createResponseFrom(result)?.respond();
1327
1490
  clearCurrentWooksContext();
1328
1491
  break;
1329
1492
  }
@@ -1331,7 +1494,7 @@ class Wooks {
1331
1494
  this.printError('Uncought route handler exception: ' + (req.url || '') + '\n', e);
1332
1495
  if (isLastHandler) {
1333
1496
  restoreCtx();
1334
- (_b = createResponseFrom(e)) === null || _b === void 0 ? void 0 : _b.respond();
1497
+ createResponseFrom(e)?.respond();
1335
1498
  clearCurrentWooksContext();
1336
1499
  }
1337
1500
  }
@@ -2587,7 +2750,7 @@ async function serveFile(filePath, options = {}) {
2587
2750
  return '';
2588
2751
  }
2589
2752
  setHeader('etag', etag);
2590
- setHeader('Last-Modified', lastModified.toUTCString());
2753
+ setHeader('last-modified', lastModified.toUTCString());
2591
2754
  if (options.cacheControl !== undefined) {
2592
2755
  setCacheControl(options.cacheControl);
2593
2756
  }
@@ -2604,7 +2767,7 @@ async function serveFile(filePath, options = {}) {
2604
2767
  }
2605
2768
  else if (options.index) {
2606
2769
  if (filePath[filePath.length - 1] !== '/' && url && url[url.length - 1] !== '/') {
2607
- return new BaseWooksResponse().setStatus(302).setHeader('Location', url + '/');
2770
+ return new BaseWooksResponse().setStatus(302).setHeader('location', url + '/');
2608
2771
  }
2609
2772
  restoreCtx();
2610
2773
  return serveFile(path__default.join(filePath, options.index), {
@@ -2613,7 +2776,7 @@ async function serveFile(filePath, options = {}) {
2613
2776
  });
2614
2777
  }
2615
2778
  removeHeader('etag');
2616
- removeHeader('Last-Modified');
2779
+ removeHeader('last-modified');
2617
2780
  throw new WooksError(404);
2618
2781
  }
2619
2782
  let range = headers.range;
@@ -2638,13 +2801,13 @@ async function serveFile(filePath, options = {}) {
2638
2801
  range = '';
2639
2802
  }
2640
2803
  else {
2641
- setHeader('Content-Range', `bytes ${start}-${end}/${fileStats.size}`);
2804
+ setHeader('content-range', `bytes ${start}-${end}/${fileStats.size}`);
2642
2805
  status(206);
2643
2806
  }
2644
2807
  }
2645
- setHeader('Accept-Ranges', 'bytes');
2646
- setHeader('Content-Type', getMimeType(normalizedPath) || 'application/octet-stream');
2647
- setHeader('Content-Length', size);
2808
+ setHeader('accept-ranges', 'bytes');
2809
+ setHeader('content-type', getMimeType(normalizedPath) || 'application/octet-stream');
2810
+ setHeader('content-length', size);
2648
2811
  if (options.headers) {
2649
2812
  for (const header of Object.keys(options.headers)) {
2650
2813
  setHeader(header, options.headers[header]);
@@ -2722,6 +2885,7 @@ exports.useCacheObject = useCacheObject;
2722
2885
  exports.useCookies = useCookies;
2723
2886
  exports.useCurrentWooksContext = useCurrentWooksContext;
2724
2887
  exports.useHeaders = useHeaders;
2888
+ exports.useProxy = useProxy;
2725
2889
  exports.useRequest = useRequest;
2726
2890
  exports.useRespCookie = useRespCookie;
2727
2891
  exports.useRespHeader = useRespHeader;
package/dist/wooks.d.ts CHANGED
@@ -35,12 +35,12 @@ export declare class BaseWooksResponse<BodyType = unknown> {
35
35
  setHeader(name: string, value: string): this;
36
36
  getHeader(name: string): string | string[];
37
37
  protected mergeHeaders(): this;
38
- protected mergeStatus(renderedBody: string): this;
38
+ protected mergeStatus(renderedBody: string | Uint8Array): this;
39
39
  respond(): void;
40
40
  }
41
41
 
42
42
  export declare class BaseWooksResponseRenderer<T = unknown> implements TWooksResponseRenderer<T> {
43
- render(response: BaseWooksResponse<T>): string;
43
+ render(response: BaseWooksResponse<T>): string | Uint8Array;
44
44
  }
45
45
 
46
46
  export declare function clearCacheObject(name: symbol | string): void;
@@ -423,8 +423,23 @@ export declare interface TWooksHook<T = string> {
423
423
  export declare interface TWooksOptions {
424
424
  }
425
425
 
426
+ export declare interface TWooksProxyControls {
427
+ overwrite?: Record<string, string> | ((data: Record<string, string>) => Record<string, string>);
428
+ allow?: (string | RegExp)[] | '*';
429
+ block?: (string | RegExp)[] | '*';
430
+ }
431
+
432
+ export declare interface TWooksProxyOptions {
433
+ method?: string;
434
+ reqHeaders?: TWooksProxyControls;
435
+ reqCookies?: TWooksProxyControls;
436
+ resHeaders?: TWooksProxyControls;
437
+ resCookies?: TWooksProxyControls;
438
+ debug?: boolean;
439
+ }
440
+
426
441
  export declare interface TWooksResponseRenderer<T = unknown> {
427
- render: (response: BaseWooksResponse<T>) => string;
442
+ render: (response: BaseWooksResponse<T>) => string | Uint8Array;
428
443
  }
429
444
 
430
445
  export declare function useAccept(): {
@@ -476,6 +491,8 @@ export declare function useCurrentWooksContext(): {
476
491
 
477
492
  export declare function useHeaders(): IncomingHttpHeaders;
478
493
 
494
+ export declare function useProxy(): (target: string, opts?: TWooksProxyOptions) => Promise<Response>;
495
+
479
496
  export declare function useRequest(): {
480
497
  rawRequest: IncomingMessage;
481
498
  url: string | undefined;
@@ -533,7 +550,7 @@ export declare function useSetHeaders(): {
533
550
  setHeader: (name: string, value: string | number) => void;
534
551
  removeHeader: (name: string) => void;
535
552
  setContentType: (value: string) => void;
536
- headers: Record<string, string>;
553
+ headers: Record<string, string | string[]>;
537
554
  enableCors: (origin?: string) => void;
538
555
  };
539
556