@usetoki/toki 0.1.1

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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/dist/app.d.ts +137 -0
  4. package/dist/app.js +608 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/cookies.d.ts +17 -0
  7. package/dist/cookies.js +54 -0
  8. package/dist/cookies.js.map +1 -0
  9. package/dist/forms.d.ts +13 -0
  10. package/dist/forms.js +64 -0
  11. package/dist/forms.js.map +1 -0
  12. package/dist/group.d.ts +19 -0
  13. package/dist/group.js +51 -0
  14. package/dist/group.js.map +1 -0
  15. package/dist/index.d.ts +19 -0
  16. package/dist/index.js +11 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/inject.d.ts +19 -0
  19. package/dist/inject.js +56 -0
  20. package/dist/inject.js.map +1 -0
  21. package/dist/jwt.d.ts +57 -0
  22. package/dist/jwt.js +128 -0
  23. package/dist/jwt.js.map +1 -0
  24. package/dist/logger.d.ts +7 -0
  25. package/dist/logger.js +45 -0
  26. package/dist/logger.js.map +1 -0
  27. package/dist/middleware.d.ts +38 -0
  28. package/dist/middleware.js +133 -0
  29. package/dist/middleware.js.map +1 -0
  30. package/dist/native.d.ts +81 -0
  31. package/dist/native.js +38 -0
  32. package/dist/native.js.map +1 -0
  33. package/dist/pipeline.d.ts +16 -0
  34. package/dist/pipeline.js +125 -0
  35. package/dist/pipeline.js.map +1 -0
  36. package/dist/request.d.ts +65 -0
  37. package/dist/request.js +170 -0
  38. package/dist/request.js.map +1 -0
  39. package/dist/response.d.ts +33 -0
  40. package/dist/response.js +92 -0
  41. package/dist/response.js.map +1 -0
  42. package/dist/schema.d.ts +45 -0
  43. package/dist/schema.js +179 -0
  44. package/dist/schema.js.map +1 -0
  45. package/dist/static.d.ts +20 -0
  46. package/dist/static.js +105 -0
  47. package/dist/static.js.map +1 -0
  48. package/dist/types.d.ts +88 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +63 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../ts/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,IAAI,QAAQ,GAAG,CAAC,CAAC;AAEjB,MAAM,UAAU,GAAsC,EAAE,CAAC;AASzD;;;;GAIG;AACH,MAAM,OAAO,WAAW;IACtB,qCAAqC;IAC5B,MAAM,CAAc;IAE7B,8DAA8D;IACrD,IAAI,CAAS;IAEtB,0DAA0D;IACjD,IAAI,CAAoB;IAEjC,2BAA2B;IAClB,EAAE,CAAS;IAEpB,gFAAgF;IACvE,GAAG,CAAS;IAErB,wDAAwD;IAC/C,IAAI,CAAgB;IACpB,QAAQ,CAAoC;IAErD,YAAY,GAAkB,EAAE,OAAwB;QACtD,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAqB,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,YAAY,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,OAAO,IAAI,UAAU,CAAC;IACjD,CAAC;IAED,mEAAmE;IACnE,IAAI,QAAQ;QACV,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACpC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;IAED,kEAAkE;IAClE,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,MAAM,CAAC;IACzD,CAAC;IAED,GAAG,CAAU;IACb,OAAO,CAAgD;IACvD,MAAM,CAAmB;IACzB,QAAQ,CAAW;IACnB,QAAQ,CAAgD;IAExD,0EAA0E;IAC1E,IAAI,EAAE;QACJ,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,+FAA+F;IAC/F,IAAI,MAAM;QACR,OAAO,CAAC,IAAI,CAAC,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,+BAA+B;IAC/B,IAAI,KAAK;QACP,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,2BAA2B;IAC3B,IAAI,OAAO;QACT,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;YAC9B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAChC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;oBACjB,SAAS;gBACX,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;YAC1E,CAAC;YACD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,oFAAoF;IACpF,yCAAyC;IACzC,OAAO,CAA2B;IAElC,iFAAiF;IACjF,iBAAiB,CAAC,IAAY,EAAE,KAAa;QAC3C,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,IAAI,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,EAAE,CAAC;gBAC1C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC1B,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oFAAoF;IACpF,oBAAoB,CAAC,IAAY,EAAE,KAAa;QAC9C,CAAC,IAAI,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mFAAmF;IACnF,IAAI,OAAO;QACT,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,sEAAsE;IACtE,SAAS,CAAC,IAAY,EAAE,KAAa,EAAE,OAAuB;QAC5D,OAAO,IAAI,CAAC,oBAAoB,CAAC,YAAY,EAAE,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,4CAA4C;IAC5C,WAAW,CAAC,IAAY,EAAE,OAAuB;QAC/C,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,sEAAsE;IACtE,IAAI,qBAAqB;QACvB,OAAO,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,iEAAiE;IACjE,IAAI;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,CAAC;IAED,wFAAwF;IACxF,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAM,CAAC;IACtC,CAAC;IAED,KAAK,CAAqB;IAE1B,qFAAqF;IACrF,IAAI,IAAI;QACN,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/F,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,WAAW,GAAG,KAAK,CAAC;IACpB,YAAY,CAAU;IAEtB;;;OAGG;IACH,KAAK,CAAC,SAAS;QACb,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;YAC3C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC,YAAiB,CAAC;IAChC,CAAC;IAED,SAAS;QACP,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3E,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QACD,IAAI,WAAW,CAAC,UAAU,CAAC,kBAAkB,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAChF,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;QACD,IAAI,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;QACrB,CAAC;QACD,IACE,WAAW,CAAC,UAAU,CAAC,mCAAmC,CAAC;YAC3D,WAAW,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAC7C,CAAC;YACD,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,33 @@
1
+ import type { NativeResponse } from "./native.js";
2
+ import type { HandlerResult, StreamResponse, StreamSource, TokiResponse } from "./types.js";
3
+ type StagedHeaders = ReadonlyArray<readonly [string, string]>;
4
+ export declare function isTokiResponse(value: unknown): value is TokiResponse;
5
+ export declare function isStreamResponse(value: unknown): value is StreamResponse;
6
+ /** Wrap an already-serialized JSON string as a response (schema serialization path). */
7
+ export declare function jsonResponse(serialized: string, status?: number): TokiResponse;
8
+ /** Build a fully-specified branded response (for middleware crafting its own). */
9
+ export declare function rawResponse(status: number, contentType: string, body: string | Uint8Array, headers?: ReadonlyArray<readonly [string, string]>): TokiResponse;
10
+ /** Builders for the common response shapes. */
11
+ export declare const reply: {
12
+ readonly text: (body: string, status?: number) => TokiResponse;
13
+ readonly html: (body: string, status?: number) => TokiResponse;
14
+ readonly json: <T>(data: T, status?: number) => TokiResponse;
15
+ readonly empty: (status?: number) => TokiResponse;
16
+ readonly redirect: (location: string, status?: number) => TokiResponse;
17
+ readonly bytes: (data: Uint8Array, contentType?: string, status?: number) => TokiResponse;
18
+ /**
19
+ * Chunked streaming response. `source` is an async/sync iterable or Node `Readable`;
20
+ * each chunk (bytes or utf-8 string) is written as it arrives. SSE: set
21
+ * `contentType: "text/event-stream"`.
22
+ */
23
+ readonly stream: (source: StreamSource, options?: {
24
+ status?: number;
25
+ contentType?: string;
26
+ headers?: ReadonlyArray<readonly [string, string]>;
27
+ }) => StreamResponse;
28
+ };
29
+ /** String becomes text/plain; any other plain value becomes JSON. */
30
+ export declare function normalize(result: HandlerResult): TokiResponse;
31
+ /** Flatten a handler result into the engine's wire shape, prepending staged headers. */
32
+ export declare function toNative(result: HandlerResult, staged?: StagedHeaders): NativeResponse;
33
+ export {};
@@ -0,0 +1,92 @@
1
+ const TOKI_RESPONSE = Symbol.for("toki.response");
2
+ const TOKI_STREAM = Symbol.for("toki.stream");
3
+ export function isTokiResponse(value) {
4
+ return (typeof value === "object" &&
5
+ value !== null &&
6
+ value[TOKI_RESPONSE] === true);
7
+ }
8
+ export function isStreamResponse(value) {
9
+ return (typeof value === "object" &&
10
+ value !== null &&
11
+ value[TOKI_STREAM] === true);
12
+ }
13
+ /** Wrap an already-serialized JSON string as a response (schema serialization path). */
14
+ export function jsonResponse(serialized, status = 200) {
15
+ return {
16
+ status,
17
+ contentType: JSON_TYPE,
18
+ headers: [],
19
+ body: serialized,
20
+ [TOKI_RESPONSE]: true,
21
+ };
22
+ }
23
+ /** Build a fully-specified branded response (for middleware crafting its own). */
24
+ export function rawResponse(status, contentType, body, headers = []) {
25
+ return make(status, contentType, body, headers);
26
+ }
27
+ const TEXT = "text/plain; charset=utf-8";
28
+ const HTML = "text/html; charset=utf-8";
29
+ const JSON_TYPE = "application/json; charset=utf-8";
30
+ function make(status, contentType, body, headers = []) {
31
+ return { status, contentType, headers, body, [TOKI_RESPONSE]: true };
32
+ }
33
+ /** Builders for the common response shapes. */
34
+ export const reply = {
35
+ text(body, status = 200) {
36
+ return make(status, TEXT, body);
37
+ },
38
+ html(body, status = 200) {
39
+ return make(status, HTML, body);
40
+ },
41
+ json(data, status = 200) {
42
+ return make(status, JSON_TYPE, JSON.stringify(data));
43
+ },
44
+ empty(status = 204) {
45
+ return make(status, TEXT, "");
46
+ },
47
+ redirect(location, status = 302) {
48
+ return make(status, TEXT, "", [["Location", location]]);
49
+ },
50
+ bytes(data, contentType = "application/octet-stream", status = 200) {
51
+ return make(status, contentType, data);
52
+ },
53
+ /**
54
+ * Chunked streaming response. `source` is an async/sync iterable or Node `Readable`;
55
+ * each chunk (bytes or utf-8 string) is written as it arrives. SSE: set
56
+ * `contentType: "text/event-stream"`.
57
+ */
58
+ stream(source, options = {}) {
59
+ return {
60
+ status: options.status ?? 200,
61
+ contentType: options.contentType ?? "application/octet-stream",
62
+ headers: options.headers ?? [],
63
+ source,
64
+ [TOKI_STREAM]: true,
65
+ };
66
+ },
67
+ };
68
+ /** String becomes text/plain; any other plain value becomes JSON. */
69
+ export function normalize(result) {
70
+ if (typeof result === "string") {
71
+ return reply.text(result);
72
+ }
73
+ if (isTokiResponse(result)) {
74
+ return result;
75
+ }
76
+ return reply.json(result);
77
+ }
78
+ /** Flatten a handler result into the engine's wire shape, prepending staged headers. */
79
+ export function toNative(result, staged = []) {
80
+ const response = normalize(result);
81
+ let headers = "";
82
+ // staged first: on a single-valued conflict the response's own header wins (written last)
83
+ for (const [name, value] of staged) {
84
+ headers += `${name}: ${value}\r\n`;
85
+ }
86
+ headers += `Content-Type: ${response.contentType}\r\n`;
87
+ for (const [name, value] of response.headers) {
88
+ headers += `${name}: ${value}\r\n`;
89
+ }
90
+ return { status: response.status, headers, body: response.body };
91
+ }
92
+ //# sourceMappingURL=response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response.js","sourceRoot":"","sources":["../ts/response.ts"],"names":[],"mappings":"AAKA,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AAClD,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAE9C,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAiC,CAAC,aAAa,CAAC,KAAK,IAAI,CAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAiC,CAAC,WAAW,CAAC,KAAK,IAAI,CACzD,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,MAAM,GAAG,GAAG;IAC3D,OAAO;QACL,MAAM;QACN,WAAW,EAAE,SAAS;QACtB,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,UAAU;QAChB,CAAC,aAAa,CAAC,EAAE,IAAI;KACN,CAAC;AACpB,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,WAAmB,EACnB,IAAyB,EACzB,UAAoD,EAAE;IAEtD,OAAO,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,IAAI,GAAG,2BAA2B,CAAC;AACzC,MAAM,IAAI,GAAG,0BAA0B,CAAC;AACxC,MAAM,SAAS,GAAG,iCAAiC,CAAC;AAEpD,SAAS,IAAI,CACX,MAAc,EACd,WAAmB,EACnB,IAAyB,EACzB,UAAoD,EAAE;IAEtD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAkB,CAAC;AACvF,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,IAAI,CAAC,IAAY,EAAE,MAAM,GAAG,GAAG;QAC7B,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,IAAY,EAAE,MAAM,GAAG,GAAG;QAC7B,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAI,IAAO,EAAE,MAAM,GAAG,GAAG;QAC3B,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,MAAM,GAAG,GAAG;QAChB,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,QAAQ,CAAC,QAAgB,EAAE,MAAM,GAAG,GAAG;QACrC,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,IAAgB,EAAE,WAAW,GAAG,0BAA0B,EAAE,MAAM,GAAG,GAAG;QAC5E,OAAO,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,MAAM,CACJ,MAAoB,EACpB,UAII,EAAE;QAEN,OAAO;YACL,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,GAAG;YAC7B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,0BAA0B;YAC9D,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,MAAM;YACN,CAAC,WAAW,CAAC,EAAE,IAAI;SACF,CAAC;IACtB,CAAC;CACO,CAAC;AAEX,qEAAqE;AACrE,MAAM,UAAU,SAAS,CAAC,MAAqB;IAC7C,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,QAAQ,CAAC,MAAqB,EAAE,SAAwB,EAAE;IACxE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,0FAA0F;IAC1F,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QACnC,OAAO,IAAI,GAAG,IAAI,KAAK,KAAK,MAAM,CAAC;IACrC,CAAC;IACD,OAAO,IAAI,iBAAiB,QAAQ,CAAC,WAAW,MAAM,CAAC;IACvD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QAC7C,OAAO,IAAI,GAAG,IAAI,KAAK,KAAK,MAAM,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;AACnE,CAAC"}
@@ -0,0 +1,45 @@
1
+ /** Custom error messages: one for the whole field, or per failing keyword. */
2
+ export interface ErrorMessages {
3
+ type?: string;
4
+ enum?: string;
5
+ minLength?: string;
6
+ maxLength?: string;
7
+ pattern?: string;
8
+ format?: string;
9
+ minimum?: string;
10
+ maximum?: string;
11
+ additionalProperties?: string;
12
+ /** Generic message, or one per required property name. */
13
+ required?: string | Record<string, string>;
14
+ }
15
+ export interface JSONSchema {
16
+ type?: "object" | "array" | "string" | "number" | "integer" | "boolean" | "null";
17
+ properties?: Record<string, JSONSchema>;
18
+ required?: string[];
19
+ items?: JSONSchema;
20
+ enum?: ReadonlyArray<unknown>;
21
+ /** Allow `null` in addition to `type`. */
22
+ nullable?: boolean;
23
+ additionalProperties?: boolean;
24
+ minimum?: number;
25
+ maximum?: number;
26
+ minLength?: number;
27
+ maxLength?: number;
28
+ pattern?: string;
29
+ format?: "email" | "uuid" | "date-time" | "uri";
30
+ default?: unknown;
31
+ /** Override the message(s) emitted when this schema fails. */
32
+ errorMessage?: string | ErrorMessages;
33
+ }
34
+ /** Per-route schema. `response` keys by status code. */
35
+ export interface RouteSchema {
36
+ body?: JSONSchema;
37
+ query?: JSONSchema;
38
+ params?: JSONSchema;
39
+ headers?: JSONSchema;
40
+ response?: Record<number, JSONSchema>;
41
+ }
42
+ /** Validates `value` against `schema`, returning human-readable error messages (empty = valid). */
43
+ export declare function validate(schema: JSONSchema, value: unknown, path?: string): string[];
44
+ /** schema-driven serialize: emits only declared props, falls back to JSON.stringify when type unknown */
45
+ export declare function serialize(schema: JSONSchema, value: unknown): string;
package/dist/schema.js ADDED
@@ -0,0 +1,179 @@
1
+ // A compact JSON Schema subset for request validation and response serialization.
2
+ // Dependency-free; covers the shapes real APIs use. Not a full draft implementation.
3
+ /** custom message for a failed keyword, else fallback */
4
+ function messageFor(schema, keyword, fallback) {
5
+ const em = schema.errorMessage;
6
+ if (typeof em === "string")
7
+ return em;
8
+ if (em && typeof em === "object") {
9
+ const m = em[keyword];
10
+ if (typeof m === "string")
11
+ return m;
12
+ }
13
+ return fallback;
14
+ }
15
+ const FORMATS = {
16
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
17
+ uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
18
+ "date-time": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/,
19
+ uri: /^[a-z][a-z0-9+.-]*:/i,
20
+ };
21
+ /** Validates `value` against `schema`, returning human-readable error messages (empty = valid). */
22
+ export function validate(schema, value, path = "") {
23
+ const errors = [];
24
+ check(schema, value, path || "value", errors);
25
+ return errors;
26
+ }
27
+ function check(schema, value, path, errors) {
28
+ if (value === null) {
29
+ if (schema.nullable || schema.type === "null") {
30
+ return;
31
+ }
32
+ }
33
+ if (schema.enum && !schema.enum.some((e) => e === value)) {
34
+ errors.push(messageFor(schema, "enum", `${path} must be one of ${JSON.stringify(schema.enum)}`));
35
+ return;
36
+ }
37
+ switch (schema.type) {
38
+ case "object":
39
+ checkObject(schema, value, path, errors);
40
+ break;
41
+ case "array":
42
+ checkArray(schema, value, path, errors);
43
+ break;
44
+ case "string":
45
+ checkString(schema, value, path, errors);
46
+ break;
47
+ case "number":
48
+ case "integer":
49
+ checkNumber(schema, value, path, errors);
50
+ break;
51
+ case "boolean":
52
+ if (typeof value !== "boolean")
53
+ errors.push(messageFor(schema, "type", `${path} must be a boolean`));
54
+ break;
55
+ default:
56
+ break;
57
+ }
58
+ }
59
+ // required-property message: generic override, per-property map, or fallback
60
+ function requiredMessage(schema, key, fallback) {
61
+ const em = schema.errorMessage;
62
+ if (typeof em === "string")
63
+ return em;
64
+ if (em && typeof em === "object" && em.required !== undefined) {
65
+ if (typeof em.required === "string")
66
+ return em.required;
67
+ return em.required[key] ?? fallback;
68
+ }
69
+ return fallback;
70
+ }
71
+ function checkObject(schema, value, path, errors) {
72
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
73
+ errors.push(messageFor(schema, "type", `${path} must be an object`));
74
+ return;
75
+ }
76
+ const obj = value;
77
+ for (const key of schema.required ?? []) {
78
+ if (!(key in obj)) {
79
+ errors.push(requiredMessage(schema, key, `${path}.${key} is required`));
80
+ }
81
+ }
82
+ if (schema.properties) {
83
+ for (const [key, sub] of Object.entries(schema.properties)) {
84
+ if (key in obj) {
85
+ check(sub, obj[key], `${path}.${key}`, errors);
86
+ }
87
+ }
88
+ if (schema.additionalProperties === false) {
89
+ for (const key of Object.keys(obj)) {
90
+ if (!(key in schema.properties)) {
91
+ errors.push(messageFor(schema, "additionalProperties", `${path}.${key} is not allowed`));
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ function checkArray(schema, value, path, errors) {
98
+ if (!Array.isArray(value)) {
99
+ errors.push(messageFor(schema, "type", `${path} must be an array`));
100
+ return;
101
+ }
102
+ if (schema.items) {
103
+ value.forEach((item, i) => check(schema.items, item, `${path}[${i}]`, errors));
104
+ }
105
+ }
106
+ function checkString(schema, value, path, errors) {
107
+ if (typeof value !== "string") {
108
+ errors.push(messageFor(schema, "type", `${path} must be a string`));
109
+ return;
110
+ }
111
+ if (schema.minLength !== undefined && value.length < schema.minLength) {
112
+ errors.push(messageFor(schema, "minLength", `${path} must be at least ${schema.minLength} characters`));
113
+ }
114
+ if (schema.maxLength !== undefined && value.length > schema.maxLength) {
115
+ errors.push(messageFor(schema, "maxLength", `${path} must be at most ${schema.maxLength} characters`));
116
+ }
117
+ if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
118
+ errors.push(messageFor(schema, "pattern", `${path} must match ${schema.pattern}`));
119
+ }
120
+ if (schema.format && FORMATS[schema.format] && !FORMATS[schema.format].test(value)) {
121
+ errors.push(messageFor(schema, "format", `${path} must be a valid ${schema.format}`));
122
+ }
123
+ }
124
+ function checkNumber(schema, value, path, errors) {
125
+ if (typeof value !== "number" || Number.isNaN(value)) {
126
+ errors.push(messageFor(schema, "type", `${path} must be a number`));
127
+ return;
128
+ }
129
+ if (schema.type === "integer" && !Number.isInteger(value)) {
130
+ errors.push(messageFor(schema, "type", `${path} must be an integer`));
131
+ }
132
+ if (schema.minimum !== undefined && value < schema.minimum) {
133
+ errors.push(messageFor(schema, "minimum", `${path} must be >= ${schema.minimum}`));
134
+ }
135
+ if (schema.maximum !== undefined && value > schema.maximum) {
136
+ errors.push(messageFor(schema, "maximum", `${path} must be <= ${schema.maximum}`));
137
+ }
138
+ }
139
+ /** schema-driven serialize: emits only declared props, falls back to JSON.stringify when type unknown */
140
+ export function serialize(schema, value) {
141
+ if (value === null || value === undefined) {
142
+ return "null";
143
+ }
144
+ switch (schema.type) {
145
+ case "object": {
146
+ if (typeof value !== "object")
147
+ return "null";
148
+ const obj = value;
149
+ let out = "{";
150
+ let first = true;
151
+ for (const [key, sub] of Object.entries(schema.properties ?? {})) {
152
+ if (obj[key] === undefined) {
153
+ continue;
154
+ }
155
+ if (!first)
156
+ out += ",";
157
+ out += `${JSON.stringify(key)}:${serialize(sub, obj[key])}`;
158
+ first = false;
159
+ }
160
+ return out + "}";
161
+ }
162
+ case "array": {
163
+ if (!Array.isArray(value))
164
+ return "[]";
165
+ const item = schema.items ?? {};
166
+ return `[${value.map((v) => serialize(item, v)).join(",")}]`;
167
+ }
168
+ case "string":
169
+ return JSON.stringify(String(value));
170
+ case "integer":
171
+ case "number":
172
+ return Number.isFinite(value) ? String(value) : "null";
173
+ case "boolean":
174
+ return value ? "true" : "false";
175
+ default:
176
+ return JSON.stringify(value);
177
+ }
178
+ }
179
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../ts/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { StaticEntry } from "./native.js";
2
+ /** Options for {@link Toki.static}. */
3
+ export interface StaticOptions {
4
+ /** `Cache-Control` for served files. Default `"public, max-age=3600"`. */
5
+ cacheControl?: string;
6
+ /** Index file served for a directory URL, or `false` to disable. Default `"index.html"`. */
7
+ index?: string | false;
8
+ /** Skip files larger than this many bytes (they stay in memory). Default 50 MiB. */
9
+ maxFileBytes?: number;
10
+ /** Pre-compress compressible files. Default `true`. */
11
+ compress?: boolean;
12
+ /** Only compress files at least this many bytes. Default 1024. */
13
+ compressMinBytes?: number;
14
+ /** gzip level (0–9). Default 9 (best — computed once at startup). */
15
+ gzipLevel?: number;
16
+ /** brotli quality (0–11). Default 11 (best — computed once at startup). */
17
+ brotliQuality?: number;
18
+ }
19
+ /** Builds static entries for `dir` served under `urlPrefix`; files read into memory once and pre-compressed at startup. */
20
+ export declare function buildStaticEntries(urlPrefix: string, dir: string, options?: StaticOptions): StaticEntry[];
package/dist/static.js ADDED
@@ -0,0 +1,105 @@
1
+ import { readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { extname, join, relative, sep } from "node:path";
3
+ import { brotliCompressSync, constants, gzipSync } from "node:zlib";
4
+ const DEFAULTS = {
5
+ cacheControl: "public, max-age=3600",
6
+ index: "index.html",
7
+ maxFileBytes: 50 * 1024 * 1024,
8
+ compress: true,
9
+ compressMinBytes: 1024,
10
+ gzipLevel: 9,
11
+ brotliQuality: 11,
12
+ };
13
+ // startup compression hint only; actual MIME is resolved natively
14
+ const COMPRESSIBLE_EXTS = new Set([
15
+ "html",
16
+ "htm",
17
+ "css",
18
+ "js",
19
+ "mjs",
20
+ "cjs",
21
+ "json",
22
+ "jsonld",
23
+ "webmanifest",
24
+ "map",
25
+ "xml",
26
+ "xhtml",
27
+ "xsl",
28
+ "svg",
29
+ "svgz",
30
+ "txt",
31
+ "md",
32
+ "markdown",
33
+ "csv",
34
+ "ics",
35
+ "wasm",
36
+ "atom",
37
+ "rss",
38
+ ]);
39
+ /** Builds static entries for `dir` served under `urlPrefix`; files read into memory once and pre-compressed at startup. */
40
+ export function buildStaticEntries(urlPrefix, dir, options = {}) {
41
+ const config = { ...DEFAULTS, ...options };
42
+ const prefix = urlPrefix.endsWith("/") ? urlPrefix.slice(0, -1) : urlPrefix;
43
+ const entries = [];
44
+ for (const abs of walk(dir)) {
45
+ const stats = statSync(abs);
46
+ if (stats.size > config.maxFileBytes) {
47
+ continue;
48
+ }
49
+ const rel = relative(dir, abs).split(sep).join("/");
50
+ const url = `${prefix}/${rel}`;
51
+ const body = readFileSync(abs);
52
+ const ext = extname(abs).slice(1).toLowerCase();
53
+ const variants = compressFile(ext, body, config);
54
+ const entry = {
55
+ path: url,
56
+ body,
57
+ mtimeMs: stats.mtimeMs,
58
+ cacheControl: config.cacheControl,
59
+ ...variants,
60
+ };
61
+ entries.push(entry);
62
+ // index file also answers its dir URL, with and without trailing slash
63
+ if (config.index && abs.endsWith(`${sep}${config.index}`)) {
64
+ const dirUrl = url.slice(0, url.length - (config.index.length + 1)) || "/";
65
+ entries.push({ ...entry, path: dirUrl });
66
+ if (dirUrl !== "/") {
67
+ entries.push({ ...entry, path: `${dirUrl}/` });
68
+ }
69
+ }
70
+ }
71
+ return entries;
72
+ }
73
+ // gzip/brotli variants; drop any that don't actually shrink the body
74
+ function compressFile(ext, body, config) {
75
+ if (!config.compress || !COMPRESSIBLE_EXTS.has(ext) || body.length < config.compressMinBytes) {
76
+ return {};
77
+ }
78
+ const out = {};
79
+ const gzip = gzipSync(body, { level: config.gzipLevel });
80
+ if (gzip.length < body.length) {
81
+ out.gzip = gzip;
82
+ }
83
+ const brotli = brotliCompressSync(body, {
84
+ params: {
85
+ [constants.BROTLI_PARAM_QUALITY]: config.brotliQuality,
86
+ [constants.BROTLI_PARAM_SIZE_HINT]: body.length,
87
+ },
88
+ });
89
+ if (brotli.length < body.length) {
90
+ out.brotli = brotli;
91
+ }
92
+ return out;
93
+ }
94
+ function* walk(dir) {
95
+ for (const item of readdirSync(dir, { withFileTypes: true })) {
96
+ const abs = join(dir, item.name);
97
+ if (item.isDirectory()) {
98
+ yield* walk(abs);
99
+ }
100
+ else if (item.isFile()) {
101
+ yield abs;
102
+ }
103
+ }
104
+ }
105
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.js","sourceRoot":"","sources":["../ts/static.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import type { TokiRequest } from "./request.js";
2
+ import type { RouteSchema } from "./schema.js";
3
+ /** Methods toki routes natively. Any token is accepted via {@link Toki.route}. */
4
+ export type RouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
5
+ /** A plain value a handler may return; it is sent as JSON (validated by the response schema). */
6
+ export type JsonResult = Record<string, unknown> | unknown[] | number | boolean | null;
7
+ /** Handler response; build with {@link reply}. Engine adds the status line, `Content-Length`, `Connection`. */
8
+ export interface TokiResponse {
9
+ readonly status: number;
10
+ readonly contentType: string;
11
+ /** Extra headers beyond `Content-Type`, e.g. `Set-Cookie` or `Location`. */
12
+ readonly headers: ReadonlyArray<readonly [name: string, value: string]>;
13
+ /** Text or raw bytes (e.g. an image or a compressed payload). */
14
+ readonly body: string | Uint8Array;
15
+ }
16
+ /** Bytes (or UTF-8 strings) a streaming response yields over time. */
17
+ export type StreamSource = AsyncIterable<Uint8Array | string> | Iterable<Uint8Array | string>;
18
+ /**
19
+ * Streaming response (`reply.stream`): head, then each chunk via `Transfer-Encoding: chunked`.
20
+ * Bypasses response hooks and serialization — no materialized body.
21
+ */
22
+ export interface StreamResponse {
23
+ readonly status: number;
24
+ readonly contentType: string;
25
+ readonly headers: ReadonlyArray<readonly [name: string, value: string]>;
26
+ readonly source: StreamSource;
27
+ }
28
+ /** A handler's result: a response, a stream, a string (`reply.text`), or a value sent as JSON. */
29
+ export type HandlerResult = TokiResponse | StreamResponse | string | JsonResult;
30
+ /** A request handler. May be synchronous or `async`. */
31
+ export type Handler = (req: TokiRequest) => HandlerResult | Promise<HandlerResult>;
32
+ /** Per-route options: validation schema and route-scoped hooks. */
33
+ export interface RouteOptions {
34
+ schema?: RouteSchema;
35
+ preValidation?: Middleware | Middleware[];
36
+ preHandler?: Middleware | Middleware[];
37
+ preSerialization?: SerializationHook | SerializationHook[];
38
+ /** Arbitrary per-route data, reachable via `req` decorators or hooks. */
39
+ config?: Record<string, unknown>;
40
+ }
41
+ /** App lifecycle callbacks (`onReady` at listen, `onClose` at shutdown). */
42
+ export type LifecycleHook = () => void | Promise<void>;
43
+ /** Options passed to `register`. `prefix` mounts the plugin's routes under a path. */
44
+ export interface PluginOptions {
45
+ prefix?: string;
46
+ [key: string]: unknown;
47
+ }
48
+ /** Pre-handler step. Return a response to short-circuit (skips handler + remaining steps); nothing to continue. */
49
+ export type Middleware = (req: TokiRequest) => HandlerResult | void | Promise<HandlerResult | void>;
50
+ /** Runs after the handler; may replace the response by returning a new one. */
51
+ export type ResponseHook = (req: TokiRequest, res: TokiResponse) => HandlerResult | void | Promise<HandlerResult | void>;
52
+ /** Runs on a plain handler return (not a built `reply`), just before JSON encoding. Returns the payload to serialize. */
53
+ export type SerializationHook = (req: TokiRequest, payload: unknown) => unknown | Promise<unknown>;
54
+ /** Handles an error thrown anywhere in the pipeline. */
55
+ export type ErrorHandler = (req: TokiRequest, error: unknown) => HandlerResult;
56
+ /** Runs when a request exceeds `requestTimeoutMs` (the engine then replies `408`). */
57
+ export type TimeoutHook = (req: TokiRequest) => void | Promise<void>;
58
+ /** Parses a raw request body (by content type) into a value, for `req.parseBody()`. */
59
+ export type BodyParser = (req: TokiRequest, body: Uint8Array) => unknown | Promise<unknown>;
60
+ /** A registered content-type parser: a content-type test and the parser to run. */
61
+ export interface ContentTypeParserEntry {
62
+ readonly test: (contentType: string) => boolean;
63
+ readonly parser: BodyParser;
64
+ }
65
+ /** Lifecycle hook points. `preParsing` runs right after `onRequest`; `preValidation`
66
+ * runs before schema validation; `preSerialization` runs before a plain value is
67
+ * JSON-encoded; `onSend` runs after `onResponse`, last before the bytes go out;
68
+ * `onTimeout` runs when a request exceeds `requestTimeoutMs`. */
69
+ export type HookName = "onRequest" | "preParsing" | "preValidation" | "preHandler" | "preSerialization" | "onResponse" | "onSend" | "onTimeout";
70
+ /** Minimal structured logger. */
71
+ export interface Logger {
72
+ debug(message: string, fields?: Record<string, unknown>): void;
73
+ info(message: string, fields?: Record<string, unknown>): void;
74
+ warn(message: string, fields?: Record<string, unknown>): void;
75
+ error(message: string, fields?: Record<string, unknown>): void;
76
+ child(bindings: Record<string, unknown>): Logger;
77
+ }
78
+ export type LogLevel = "debug" | "info" | "warn" | "error";
79
+ export interface TokiOptions {
80
+ /** A {@link Logger}, a level for the built-in console logger, or `false`/omitted for silence. */
81
+ logger?: Logger | LogLevel | false;
82
+ /** Reply `408` (and run `onTimeout` hooks) if an async handler runs longer than this (ms). 0/omitted = off. */
83
+ requestTimeoutMs?: number;
84
+ }
85
+ /** Returned by `listen`; `close()` stops the server. */
86
+ export interface ListenHandle {
87
+ close(): void;
88
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../ts/types.ts"],"names":[],"mappings":""}