k99 0.7.1 → 0.8.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/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * k99 v0.7.1
3
- * (c) 2019-2025 猛火Fierflame
2
+ * k99 v0.8.0
3
+ * (c) 2019-2026 猛火Fierflame
4
4
  * @license MIT
5
5
  */
6
6
  'use strict';
@@ -8,9 +8,10 @@
8
8
  /**
9
9
  *
10
10
  * @param {string} str
11
- * @returns {Uint8Array}
11
+ * @returns {Uint8Array<ArrayBuffer>}
12
12
  */
13
13
  function str2utf8bin(str) {
14
+ // @ts-ignore
14
15
  return new TextEncoder().encode(str);
15
16
  }
16
17
  /**
@@ -49,26 +50,15 @@ async function write(writer, chunk) {
49
50
  }
50
51
  }
51
52
 
52
- /**
53
- *
54
- * @param {any} k
55
- * @param {any} v
56
- * @returns {any}
57
- */
58
- function replacer(k, v) {
59
- if (typeof v === 'bigint') {
60
- return String(v);
61
- }
62
- return v;
63
- }
64
53
 
65
54
  /**
66
55
  *
67
56
  * @param {any} result
57
+ * @param {(this: any, key: string, value: any) => any} replacer
68
58
  * @param {Promise<never>} [aborted]
69
59
  * @returns {[BodyInit, number, string] | null}
70
60
  */
71
- function toBodyData(result, aborted) {
61
+ function toBodyData(result, replacer, aborted) {
72
62
  if (result instanceof ReadableStream) {
73
63
  return [result, 0, ''];
74
64
  }
@@ -82,11 +72,16 @@ function toBodyData(result, aborted) {
82
72
  return [result, 0, ''];
83
73
  }
84
74
  if (ArrayBuffer.isView(result) || result instanceof ArrayBuffer) {
75
+ // @ts-ignore
85
76
  return [result, result.byteLength, ''];
86
77
  }
87
78
  if (typeof result === 'string') {
88
79
  const body = str2utf8bin(result);
89
- return [body, body.byteLength, ''];
80
+ return [body, body.byteLength, 'text/plain'];
81
+ }
82
+ if (['bigint', 'boolean', 'number'].includes(typeof result)) {
83
+ const body = str2utf8bin(JSON.stringify(result, replacer));
84
+ return [body, body.byteLength, 'application/json'];
90
85
  }
91
86
  if (typeof result !== 'object') {
92
87
  return null;
@@ -113,11 +108,12 @@ function toBodyData(result, aborted) {
113
108
  *
114
109
  * @param {any} result
115
110
  * @param {Headers} headers
111
+ * @param {(this: any, key: string, value: any) => any} replacer
116
112
  * @param {Promise<never>} [aborted]
117
113
  * @returns
118
114
  */
119
- function toBody(result, headers, aborted) {
120
- const bodyData = toBodyData(result, aborted);
115
+ function toBody(result, headers, replacer, aborted) {
116
+ const bodyData = toBodyData(result, replacer, aborted);
121
117
  if (!bodyData) { return null; }
122
118
  const [body, size, type] = bodyData;
123
119
  if (type && !headers.get('Content-Type')) {
@@ -211,7 +207,7 @@ function clearCookie(
211
207
  }
212
208
  }
213
209
 
214
- /** @import { Context, Cookie, CookieOption, FindHandler, Method, Options, Params, Service } from './types' */
210
+ /** @import { Context, Cookie, CookieOption, FindHandler, HandlerResult, Method, Options, Params, Service } from './types' */
215
211
 
216
212
 
217
213
  const noBodyMethods = new Set(['GET', 'OPTIONS']);
@@ -264,6 +260,18 @@ function getMethod(request, toMethod) {
264
260
  }
265
261
  return /** @type {Method} */(methodStr.toUpperCase());
266
262
  }
263
+ /**
264
+ *
265
+ * @param {any} k
266
+ * @param {any} v
267
+ * @returns {any}
268
+ */
269
+ function defaultReplacer(k, v) {
270
+ if (typeof v === 'bigint') {
271
+ return String(v);
272
+ }
273
+ return v;
274
+ }
267
275
  /**
268
276
  *
269
277
  * @param {Request} request
@@ -271,10 +279,15 @@ function getMethod(request, toMethod) {
271
279
  * @param {Options} [options]
272
280
  * @returns {Promise<Response | null>}
273
281
  */
274
- function main(
275
- request, getHandler,
276
- { runner, error: echoError, catch: catchError, method: toMethod, environment } = {},
277
- ) {
282
+ function main(request, getHandler, {
283
+ runner,
284
+ error: echoError,
285
+ catch: catchError,
286
+ method: toMethod,
287
+ environment,
288
+ replacer: JSONReplacer,
289
+ } = {}) {
290
+ const replacer = typeof JSONReplacer === 'function' ? JSONReplacer : defaultReplacer;
278
291
  /**
279
292
  *
280
293
  * @param {Request} request
@@ -298,12 +311,12 @@ function main(
298
311
  /** @type {any} */
299
312
  let error = null;
300
313
 
301
- let resolve = () => {};
314
+ let resolve = () => { };
302
315
  /** @type {(error: unknown) => void} */
303
- let reject = () => {};
316
+ let reject = () => { };
304
317
  /** @type {Promise<void>} */
305
318
  const donePromise = new Promise((a, b) => { resolve = a; reject = b; });
306
- donePromise.catch(() => {});
319
+ donePromise.catch(() => { });
307
320
 
308
321
  /** @type {Params} */
309
322
  let params = {};
@@ -321,7 +334,7 @@ function main(
321
334
  if (!data || noBodyMethods.has(method.toUpperCase())) {
322
335
  return exec(new Request(fetchUrl, { method, headers, signal }), context);
323
336
  }
324
- const body = toBody(data, headers);
337
+ const body = toBody(data, headers, replacer);
325
338
  return exec(new Request(fetchUrl, { method, headers, signal, body }), context);
326
339
  },
327
340
  done(onfulfilled, onrejected) {
@@ -366,7 +379,7 @@ function main(
366
379
  set responseType(type) { setHeader(responseHeaders, 'content-type', type); },
367
380
  getCookie(name) { return getCookie(sentCookies, name); },
368
381
  setCookie(name, value, { expire, domain, path, secure, httpOnly } = {}) {
369
- sentCookies.push({name, value, expire, domain, path, secure, httpOnly});
382
+ sentCookies.push({ name, value, expire, domain, path, secure, httpOnly });
370
383
  setCookiesHeader(responseHeaders, sentCookies);
371
384
  },
372
385
  /**
@@ -384,16 +397,22 @@ function main(
384
397
  return Promise.race([
385
398
  aborted,
386
399
  Promise.resolve().then(() => getHandler(context, v => { params = v; })),
387
- ]).then(handler => handler ? Promise.race([aborted, handler(context)]).then(result => {
388
- if (result instanceof Response) {
389
- return result;
400
+ ]).then(async handlers => {
401
+ const allHandlers = [handlers].flat().filter(h => typeof h === 'function');
402
+ if (!allHandlers.length) { return null; }
403
+ /** @type {HandlerResult?} */
404
+ let result;
405
+ for (const handle of allHandlers) {
406
+ result = await Promise.race([aborted, handle(context)]);
407
+ if (result !== undefined) { break; }
390
408
  }
409
+ if (result instanceof Response) { return result; }
391
410
  const headers = new Headers(context.responseHeaders);
392
411
  const { status } = context;
393
412
  if (!result) { return new Response(null, { status, headers }); }
394
- const body = toBody(result, headers, aborted);
413
+ const body = toBody(result, headers, replacer, aborted);
395
414
  return new Response(body, { status, headers });
396
- }) : null).then(response => {
415
+ }).then(response => {
397
416
  destroyed = true;
398
417
  resolve();
399
418
  return response;
@@ -421,12 +440,12 @@ function make(getHandler, options) {
421
440
  return r => main(r, getHandler, options);
422
441
  }
423
442
 
424
- /** @import { Context, Handler } from './main/types' */
443
+ /** @import { Context, Handler, HandlerResult } from './main/types' */
425
444
  /**
426
445
  *
427
446
  * @param {Context} context
428
447
  * @param {Handler[]} handlers
429
- * @returns {Promise<string | boolean | object | undefined>}
448
+ * @returns {Promise<HandlerResult>}
430
449
  */
431
450
  async function runHandles(context, handlers) {
432
451
  for (const handle of handlers) {
@@ -628,9 +647,11 @@ function storeService(destroy, exec, options) {
628
647
  /** @import { Onionskin } from './onionskin.mjs' */
629
648
  /**
630
649
  * @callback Packer
631
- * @param {Handler} handler
632
- * @returns {Handler}
650
+ * @param {Handler | Handler[]} handler
651
+ * @returns {Handler | Handler[]}
633
652
  */
653
+
654
+
634
655
  /** @type {Packer} */
635
656
  const noop$1 = h => h;
636
657
  /**
@@ -642,7 +663,7 @@ const noop$1 = h => h;
642
663
  function packer(onionskin, packer = noop$1) {
643
664
  return h => {
644
665
  const handler = packer(h);
645
- return async (ctx) => onionskin(ctx, async () => handler(ctx));
666
+ return async (ctx) => onionskin(ctx, async () => runHandles(ctx, [handler].flat()));
646
667
  };
647
668
  }
648
669
 
@@ -655,7 +676,7 @@ function packer(onionskin, packer = noop$1) {
655
676
  * @returns {PromiseLike<boolean | Handler | void> | boolean | Handler | void}
656
677
  */
657
678
  /**
658
- * @typedef {[Handler | Router, Record<string | symbol, any>, string[]]} FindItem
679
+ * @typedef {[Handler | Handler[] | Router, Record<string | symbol, any>, string[]]} FindItem
659
680
  */
660
681
  /**
661
682
  * @callback Finder
@@ -666,55 +687,6 @@ function packer(onionskin, packer = noop$1) {
666
687
  * @returns {AsyncIterable<FindItem> | Iterable<FindItem>}
667
688
  */
668
689
 
669
- /**
670
- *
671
- * @param {Set<Guard>} guards
672
- * @param {Context} ctx
673
- * @param {(v: any) => void} setParams
674
- * @param {object} params
675
- * @returns {Promise<boolean | Handler>}
676
- */
677
- async function execGuard(guards, ctx, setParams, params) {
678
- if (!guards.size) { return true; }
679
- setParams(params);
680
- for (const guard of guards) {
681
- if (ctx.destroyed) { return false; }
682
- const ret = await guard(Object.create(ctx, {
683
- params: { value: { ...params } },
684
- }));
685
- if (ret === false) { return false; }
686
- // @ts-ignore
687
- if (typeof ret === 'function') { return ret; }
688
- }
689
- return true;
690
- }
691
-
692
- /**
693
- *
694
- * @param {Router | Handler} route
695
- * @param {string[]} path
696
- * @param {Context} ctx
697
- * @param {(v: Params) => void} setParams
698
- * @param {Params} params
699
- * @returns {Promise<Handler | null>}
700
- */
701
- async function find(route, path, ctx, setParams, params) {
702
- if (!(route instanceof Router)) {
703
- setParams(params);
704
- return route;
705
- }
706
- if (route.disabled) { return null; }
707
- const guardResult = await execGuard(route.guards, ctx, setParams, params);
708
- if (!guardResult) { return null; }
709
- if (typeof guardResult === 'function') { return guardResult; }
710
- if (ctx.destroyed) { return null; }
711
- for await (const [r, result, p] of route.find(ctx.method, path, ctx)) {
712
- if (ctx.destroyed) { return null; }
713
- const res = await find(r, p, ctx, setParams, { ...params, ...result });
714
- if (res) { return route.__onionskin(res); }
715
- }
716
- return null;
717
- }
718
690
 
719
691
  /**
720
692
  *
@@ -733,6 +705,29 @@ function uriDecode(t) {
733
705
  */
734
706
  class Router {
735
707
  disabled = false;
708
+ /**
709
+ *
710
+ * @param {Router | Handler[] | Handler} route
711
+ * @param {string[]} path
712
+ * @param {Context} ctx
713
+ * @param {(v: Params) => void} setParams
714
+ * @param {Params} params
715
+ * @returns {Promise<Handler[] | null>}
716
+ */
717
+ static async #find(route, path, ctx, setParams, params) {
718
+ if (!(route instanceof Router)) {
719
+ setParams(params);
720
+ return [route].flat();
721
+ }
722
+ if (route.disabled) { return null; }
723
+ if (ctx.destroyed) { return null; }
724
+ for await (const [r, result, p] of route.find(ctx.method, path, ctx)) {
725
+ if (ctx.destroyed) { return null; }
726
+ const res = await Router.#find(r, p, ctx, setParams, { ...params, ...result });
727
+ if (res) { return [route.#guards, route.#onionskin(res)].flat(); }
728
+ }
729
+ return null;
730
+ }
736
731
  /**
737
732
  * @abstract
738
733
  * @param {Method} method
@@ -751,13 +746,28 @@ class Router {
751
746
  const list = routers.flat();
752
747
  const path = ctx.url.pathname.split('/').filter(Boolean).map(uriDecode);
753
748
  for (const route of list) {
754
- const res = await find(route, path, ctx, setParams, {});
749
+ const res = await Router.#find(route, path, ctx, setParams, {});
755
750
  if (res) { return res; }
756
751
  }
757
752
  return null;
758
753
  };
759
754
  }
760
755
 
756
+
757
+ /** @type {Handler[]} */
758
+ #guards = [];
759
+ /**
760
+ *
761
+ * @param {...Handler | Handler[]} guards
762
+ */
763
+ guard(...guards) {
764
+ const list =this.#guards;
765
+ for (const guard of guards.flat()) {
766
+ if (typeof guard !== 'function') { continue; }
767
+ list.push(guard);
768
+ }
769
+ }
770
+
761
771
  /**
762
772
  *
763
773
  * @param {Finder} find
@@ -768,16 +778,14 @@ class Router {
768
778
  'find': { configurable: true, value: find, writable: true },
769
779
  });
770
780
  }
771
- /** @readonly @type {Set<Guard>} */
772
- guards = new Set();
773
781
  /**
774
782
  *
775
- * @param {Handler} h
776
- * @returns {Handler}
783
+ * @param {Handler | Handler[]} h
784
+ * @returns {Handler | Handler[]}
777
785
  */
778
- __onionskin = (h) => h;
786
+ #onionskin = (h) => h;
779
787
  /** @param {Onionskin} os */
780
- onionskin(os) { this.__onionskin = packer(os, this.__onionskin); }
788
+ onionskin(os) { this.#onionskin = packer(os, this.#onionskin); }
781
789
  }
782
790
 
783
791
  /** @import { Match } from './index.mjs' */
@@ -975,7 +983,7 @@ function toMatch(path, end) {
975
983
  */
976
984
  function bind(routes, methods, match, handlers) {
977
985
  /** @type {Route} */
978
- const route = { match, methods, handler: ctx => runHandles(ctx, handlers) };
986
+ const route = { match, methods, handlers: handlers };
979
987
  routes.push(route);
980
988
  let removed = false;
981
989
  return () => {
@@ -1057,8 +1065,7 @@ function getMethods(methods) {
1057
1065
  * @typedef {object} Route
1058
1066
  * @property {Match} [match] 路径匹配
1059
1067
  * @property {null} [router]
1060
- * @property {string} [plugin] 所属插件
1061
- * @property {Handler} handler 处理函数
1068
+ * @property {Handler[]} handlers 处理函数
1062
1069
  * @property {Set<Method>} methods 方法列表
1063
1070
  */
1064
1071
 
@@ -1145,14 +1152,14 @@ class ApiRouter extends Router {
1145
1152
  const {match} = route;
1146
1153
  if (!match) {
1147
1154
  if (route.router || !path.length) {
1148
- yield [route.router || route.handler, {}, path];
1155
+ yield [route.router || route.handlers, {}, path];
1149
1156
  }
1150
1157
  continue;
1151
1158
  }
1152
1159
  if (!path.length) { continue; }
1153
1160
  const result = match(path);
1154
1161
  if (!result) { continue; }
1155
- yield [route.router || route.handler, ...result];
1162
+ yield [route.router || route.handlers, ...result];
1156
1163
  }
1157
1164
  }
1158
1165
  /**
@@ -1180,25 +1187,25 @@ class ApiRouter extends Router {
1180
1187
  /**
1181
1188
  * @param {Method | Iterable<Method> | ArrayLike<Method>} methods
1182
1189
  * @param {string| Handler} [path]
1183
- * @param {Handler} [handler]
1190
+ * @param {...Handler} handler
1184
1191
  * @returns {Binder | (() => void)}
1185
1192
  */
1186
- verb(methods, path, handler) {
1193
+ verb(methods, path, ...handler) {
1187
1194
  const allMethods = getMethods(methods);
1188
1195
  if (!allMethods.length) { return () => {}; }
1189
- return verb(this.#routes, allMethods, [path, handler]);
1196
+ return verb(this.#routes, allMethods, [path, ...handler]);
1190
1197
  }
1191
1198
  /**
1192
1199
  * 注册 HTTP GET/POST/PUT/DELETE 处理函数
1193
1200
  * @overload
1194
- * @param {Handler} handler 要注册的处理函数
1201
+ * @param {...Handler} handlers 要注册的处理函数
1195
1202
  * @returns {() => void}
1196
1203
  */
1197
1204
  /**
1198
1205
  * 注册处理函数
1199
1206
  * @overload
1200
1207
  * @param {string} path 要注册的路径
1201
- * @param {Handler} handler 要注册的处理函数
1208
+ * @param {...Handler} handlers 要注册的处理函数
1202
1209
  * @returns {() => void}
1203
1210
  */
1204
1211
  /**
@@ -1224,14 +1231,14 @@ class ApiRouter extends Router {
1224
1231
  /**
1225
1232
  * 注册 HTTP GET 处理函数
1226
1233
  * @overload
1227
- * @param {Handler} handler 要注册的处理函数
1234
+ * @param {...Handler} handlers 要注册的处理函数
1228
1235
  * @returns {() => void}
1229
1236
  */
1230
1237
  /**
1231
1238
  * 注册 HTTP GET 处理函数
1232
1239
  * @overload
1233
1240
  * @param {string} path 要注册的路径
1234
- * @param {Handler} handler 要注册的处理函数
1241
+ * @param {...Handler} handlers 要注册的处理函数
1235
1242
  * @returns {() => void}
1236
1243
  */
1237
1244
  /**
@@ -1255,14 +1262,14 @@ class ApiRouter extends Router {
1255
1262
  /**
1256
1263
  * 注册 HTTP POST 处理函数
1257
1264
  * @overload
1258
- * @param {Handler} handler 要注册的处理函数
1265
+ * @param {...Handler} handlers 要注册的处理函数
1259
1266
  * @returns {() => void}
1260
1267
  */
1261
1268
  /**
1262
1269
  * 注册 HTTP POST 处理函数
1263
1270
  * @overload
1264
1271
  * @param {string} path 要注册的路径
1265
- * @param {Handler} handler 要注册的处理函数
1272
+ * @param {...Handler} handlers 要注册的处理函数
1266
1273
  * @returns {() => void}
1267
1274
  */
1268
1275
  /**
@@ -1286,14 +1293,14 @@ class ApiRouter extends Router {
1286
1293
  /**
1287
1294
  * 注册 HTTP PUT 处理函数
1288
1295
  * @overload
1289
- * @param {Handler} handler 要注册的处理函数
1296
+ * @param {...Handler} handlers 要注册的处理函数
1290
1297
  * @returns {() => void}
1291
1298
  */
1292
1299
  /**
1293
1300
  * 注册 HTTP PUT 处理函数
1294
1301
  * @overload
1295
1302
  * @param {string} path 要注册的路径
1296
- * @param {Handler} handler 要注册的处理函数
1303
+ * @param {...Handler} handlers 要注册的处理函数
1297
1304
  * @returns {() => void}
1298
1305
  */
1299
1306
  /**
@@ -1317,14 +1324,14 @@ class ApiRouter extends Router {
1317
1324
  /**
1318
1325
  * 注册 HTTP DELETE 处理函数
1319
1326
  * @overload
1320
- * @param {Handler} handler 要注册的处理函数
1327
+ * @param {...Handler} handlers 要注册的处理函数
1321
1328
  * @returns {() => void}
1322
1329
  */
1323
1330
  /**
1324
1331
  * 注册 HTTP DELETE 处理函数
1325
1332
  * @overload
1326
1333
  * @param {string} path 要注册的路径
1327
- * @param {Handler} handler 要注册的处理函数
1334
+ * @param {...Handler} handlers 要注册的处理函数
1328
1335
  * @returns {() => void}
1329
1336
  */
1330
1337
  /**
@@ -1348,14 +1355,14 @@ class ApiRouter extends Router {
1348
1355
  /**
1349
1356
  * 注册 HTTP HEAD 处理函数
1350
1357
  * @overload
1351
- * @param {Handler} handler 要注册的处理函数
1358
+ * @param {...Handler} handlers 要注册的处理函数
1352
1359
  * @returns {() => void}
1353
1360
  */
1354
1361
  /**
1355
1362
  * 注册 HTTP HEAD 处理函数
1356
1363
  * @overload
1357
1364
  * @param {string} path 要注册的路径
1358
- * @param {Handler} handler 要注册的处理函数
1365
+ * @param {...Handler} handlers 要注册的处理函数
1359
1366
  * @returns {() => void}
1360
1367
  */
1361
1368
  /**
@@ -1379,14 +1386,14 @@ class ApiRouter extends Router {
1379
1386
  /**
1380
1387
  * 注册 HTTP OPTIONS 处理函数
1381
1388
  * @overload
1382
- * @param {Handler} handler 要注册的处理函数
1389
+ * @param {...Handler} handlers 要注册的处理函数
1383
1390
  * @returns {() => void}
1384
1391
  */
1385
1392
  /**
1386
1393
  * 注册 HTTP OPTIONS 处理函数
1387
1394
  * @overload
1388
1395
  * @param {string} path 要注册的路径
1389
- * @param {Handler} handler 要注册的处理函数
1396
+ * @param {...Handler} handlers 要注册的处理函数
1390
1397
  * @returns {() => void}
1391
1398
  */
1392
1399
  /**