better-call 0.0.0-experimental.01ff4b77

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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +265 -0
  3. package/dist/capability.cjs +417 -0
  4. package/dist/capability.cjs.map +1 -0
  5. package/dist/capability.d.cts +189 -0
  6. package/dist/capability.d.mts +189 -0
  7. package/dist/capability.mjs +401 -0
  8. package/dist/capability.mjs.map +1 -0
  9. package/dist/error.cjs +76 -0
  10. package/dist/error.cjs.map +1 -0
  11. package/dist/error.d.cts +51 -0
  12. package/dist/error.d.mts +51 -0
  13. package/dist/error.mjs +72 -0
  14. package/dist/error.mjs.map +1 -0
  15. package/dist/fn.cjs +350 -0
  16. package/dist/fn.cjs.map +1 -0
  17. package/dist/fn.d.cts +378 -0
  18. package/dist/fn.d.mts +378 -0
  19. package/dist/fn.mjs +350 -0
  20. package/dist/fn.mjs.map +1 -0
  21. package/dist/index.cjs +43 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.cts +50 -0
  24. package/dist/index.d.mts +50 -0
  25. package/dist/index.mjs +24 -0
  26. package/dist/index.mjs.map +1 -0
  27. package/dist/module.cjs +176 -0
  28. package/dist/module.cjs.map +1 -0
  29. package/dist/module.d.cts +283 -0
  30. package/dist/module.d.mts +283 -0
  31. package/dist/module.mjs +166 -0
  32. package/dist/module.mjs.map +1 -0
  33. package/dist/plugins/db.cjs +24 -0
  34. package/dist/plugins/db.cjs.map +1 -0
  35. package/dist/plugins/db.d.cts +18 -0
  36. package/dist/plugins/db.d.mts +18 -0
  37. package/dist/plugins/db.mjs +19 -0
  38. package/dist/plugins/db.mjs.map +1 -0
  39. package/dist/plugins/http/attrs.cjs +91 -0
  40. package/dist/plugins/http/attrs.cjs.map +1 -0
  41. package/dist/plugins/http/attrs.d.cts +26 -0
  42. package/dist/plugins/http/attrs.d.mts +26 -0
  43. package/dist/plugins/http/attrs.mjs +87 -0
  44. package/dist/plugins/http/attrs.mjs.map +1 -0
  45. package/dist/plugins/http/cookie.cjs +117 -0
  46. package/dist/plugins/http/cookie.cjs.map +1 -0
  47. package/dist/plugins/http/cookie.d.cts +637 -0
  48. package/dist/plugins/http/cookie.d.mts +637 -0
  49. package/dist/plugins/http/cookie.mjs +113 -0
  50. package/dist/plugins/http/cookie.mjs.map +1 -0
  51. package/dist/plugins/http/error.cjs +39 -0
  52. package/dist/plugins/http/error.cjs.map +1 -0
  53. package/dist/plugins/http/error.d.cts +38 -0
  54. package/dist/plugins/http/error.d.mts +38 -0
  55. package/dist/plugins/http/error.mjs +35 -0
  56. package/dist/plugins/http/error.mjs.map +1 -0
  57. package/dist/plugins/http/handle.cjs +73 -0
  58. package/dist/plugins/http/handle.cjs.map +1 -0
  59. package/dist/plugins/http/handle.d.cts +955 -0
  60. package/dist/plugins/http/handle.d.mts +955 -0
  61. package/dist/plugins/http/handle.mjs +72 -0
  62. package/dist/plugins/http/handle.mjs.map +1 -0
  63. package/dist/plugins/http/redirect.cjs +82 -0
  64. package/dist/plugins/http/redirect.cjs.map +1 -0
  65. package/dist/plugins/http/redirect.d.cts +67 -0
  66. package/dist/plugins/http/redirect.d.mts +67 -0
  67. package/dist/plugins/http/redirect.mjs +79 -0
  68. package/dist/plugins/http/redirect.mjs.map +1 -0
  69. package/dist/plugins/http/request.cjs +63 -0
  70. package/dist/plugins/http/request.cjs.map +1 -0
  71. package/dist/plugins/http/request.d.cts +142 -0
  72. package/dist/plugins/http/request.d.mts +142 -0
  73. package/dist/plugins/http/request.mjs +61 -0
  74. package/dist/plugins/http/request.mjs.map +1 -0
  75. package/dist/plugins/http/response.cjs +13 -0
  76. package/dist/plugins/http/response.cjs.map +1 -0
  77. package/dist/plugins/http/response.d.cts +18 -0
  78. package/dist/plugins/http/response.d.mts +18 -0
  79. package/dist/plugins/http/response.mjs +13 -0
  80. package/dist/plugins/http/response.mjs.map +1 -0
  81. package/dist/plugins/http.cjs +64 -0
  82. package/dist/plugins/http.cjs.map +1 -0
  83. package/dist/plugins/http.d.cts +945 -0
  84. package/dist/plugins/http.d.mts +945 -0
  85. package/dist/plugins/http.mjs +37 -0
  86. package/dist/plugins/http.mjs.map +1 -0
  87. package/dist/plugins/read-only.cjs +19 -0
  88. package/dist/plugins/read-only.cjs.map +1 -0
  89. package/dist/plugins/read-only.d.cts +17 -0
  90. package/dist/plugins/read-only.d.mts +17 -0
  91. package/dist/plugins/read-only.mjs +19 -0
  92. package/dist/plugins/read-only.mjs.map +1 -0
  93. package/dist/schema.cjs +245 -0
  94. package/dist/schema.cjs.map +1 -0
  95. package/dist/schema.d.cts +411 -0
  96. package/dist/schema.d.mts +411 -0
  97. package/dist/schema.mjs +235 -0
  98. package/dist/schema.mjs.map +1 -0
  99. package/dist/scope.d.cts +31 -0
  100. package/dist/scope.d.mts +31 -0
  101. package/dist/storage.cjs +308 -0
  102. package/dist/storage.cjs.map +1 -0
  103. package/dist/storage.d.cts +220 -0
  104. package/dist/storage.d.mts +220 -0
  105. package/dist/storage.mjs +302 -0
  106. package/dist/storage.mjs.map +1 -0
  107. package/dist/types.d.cts +8 -0
  108. package/dist/types.d.mts +8 -0
  109. package/dist/var.cjs +186 -0
  110. package/dist/var.cjs.map +1 -0
  111. package/dist/var.d.cts +46 -0
  112. package/dist/var.d.mts +46 -0
  113. package/dist/var.mjs +177 -0
  114. package/dist/var.mjs.map +1 -0
  115. package/package.json +114 -0
@@ -0,0 +1,113 @@
1
+ import { v } from "../../index.mjs";
2
+ import { res } from "./response.mjs";
3
+ import { req } from "./request.mjs";
4
+ //#region src/plugins/http/cookie.ts
5
+ /** Cookie attributes as a SCHEMA - the source of truth. The TS type
6
+ * falls out of it, and everything crossing a fn door validates against
7
+ * it (enum-checked sameSite, `instanceof`-checked expires). */
8
+ const cookieShape = {
9
+ maxAge: v.number({ optional: true }),
10
+ expires: v.date({ optional: true }),
11
+ path: v.string({ optional: true }),
12
+ domain: v.string({ optional: true }),
13
+ secure: v.boolean({ optional: true }),
14
+ httpOnly: v.boolean({ optional: true }),
15
+ sameSite: v.string({
16
+ enum: [
17
+ "strict",
18
+ "lax",
19
+ "none"
20
+ ],
21
+ optional: true
22
+ })
23
+ };
24
+ /**
25
+ * Scope-level cookie DEFAULTS, as a var: whatever sets it - app setup, a
26
+ * module, an interceptor - every `setCookie` below merges its per-call
27
+ * options ON TOP. Config that travels down the call tree is exactly what
28
+ * a var is, so "change the global cookie config" is just a `.set()` at
29
+ * any point in the scope.
30
+ */
31
+ const cookieOptions = v.var("cookieOptions", {
32
+ default: {},
33
+ schema: v.object(cookieShape)
34
+ });
35
+ /** The per-call options field: the same shape, optional to send. */
36
+ const cookieOptionsField = v.object(cookieShape, { optional: true });
37
+ const serialize = (name, value, options = {}) => {
38
+ const parts = [`${name}=${encodeURIComponent(value)}`];
39
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
40
+ if (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
41
+ if (options.path) parts.push(`Path=${options.path}`);
42
+ if (options.domain) parts.push(`Domain=${options.domain}`);
43
+ if (options.secure) parts.push("Secure");
44
+ if (options.httpOnly) parts.push("HttpOnly");
45
+ if (options.sameSite) parts.push(`SameSite=${options.sameSite.charAt(0).toUpperCase()}${options.sameSite.slice(1)}`);
46
+ return parts.join("; ");
47
+ };
48
+ const ck = v.fn("cookie.", { use: [{
49
+ req,
50
+ res,
51
+ cookieOptions
52
+ }] });
53
+ /** Read one cookie from the REQUEST jar. Positional: `getCookie("theme")`.
54
+ * Readonly - reading a cookie can never write the scope. */
55
+ const getCookie = ck.fn("get", {
56
+ input: [v.string()],
57
+ readonly: true
58
+ }, (c) => {
59
+ const [name] = c.input;
60
+ return c.req?.cookies[name] ?? null;
61
+ });
62
+ /** Queue a Set-Cookie on the scope's response headers: the scope's
63
+ * `cookieOptions` defaults, with per-call options merged on top. The
64
+ * written cookie also lands in the request jar, so a later `getCookie`
65
+ * in the same scope reads what was set. */
66
+ const setCookie = ck.fn("set", { input: {
67
+ name: v.string(),
68
+ value: v.string(),
69
+ options: cookieOptionsField
70
+ } }, (c) => {
71
+ const { name, value, options } = c.input;
72
+ let response = c.res;
73
+ if (!response) {
74
+ response = { headers: new Headers() };
75
+ c.res = response;
76
+ }
77
+ response.headers.append("set-cookie", serialize(name, value, {
78
+ ...c.cookieOptions,
79
+ ...options
80
+ }));
81
+ const current = c.req;
82
+ if (current) c.req = {
83
+ ...current,
84
+ cookies: {
85
+ ...current.cookies,
86
+ [name]: value
87
+ }
88
+ };
89
+ return { set: true };
90
+ });
91
+ /** Expire a cookie: a Set-Cookie with Max-Age=0 - the attributes (path,
92
+ * domain) must match the ones it was set with. */
93
+ const deleteCookie = ck.fn("delete", {
94
+ input: {
95
+ name: v.string(),
96
+ options: cookieOptionsField
97
+ },
98
+ use: [{ setCookie }]
99
+ }, (c) => {
100
+ const { name, options } = c.input;
101
+ return c.setCookie({
102
+ name,
103
+ value: "",
104
+ options: {
105
+ ...options,
106
+ maxAge: 0
107
+ }
108
+ });
109
+ });
110
+ //#endregion
111
+ export { cookieOptions, cookieShape, deleteCookie, getCookie, setCookie };
112
+
113
+ //# sourceMappingURL=cookie.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie.mjs","names":[],"sources":["../../../src/plugins/http/cookie.ts"],"sourcesContent":["import { v } from \"../../index\";\nimport type { InferArgs } from \"../../schema\";\nimport { req } from \"./request\";\nimport { res } from \"./response\";\n\n/** Cookie attributes as a SCHEMA - the source of truth. The TS type\n * falls out of it, and everything crossing a fn door validates against\n * it (enum-checked sameSite, `instanceof`-checked expires). */\nexport const cookieShape = {\n\tmaxAge: v.number({ optional: true }),\n\texpires: v.date({ optional: true }),\n\tpath: v.string({ optional: true }),\n\tdomain: v.string({ optional: true }),\n\tsecure: v.boolean({ optional: true }),\n\thttpOnly: v.boolean({ optional: true }),\n\tsameSite: v.string({ enum: [\"strict\", \"lax\", \"none\"], optional: true }),\n};\n\nexport type CookieOptions = InferArgs<typeof cookieShape>;\n\n/**\n * Scope-level cookie DEFAULTS, as a var: whatever sets it - app setup, a\n * module, an interceptor - every `setCookie` below merges its per-call\n * options ON TOP. Config that travels down the call tree is exactly what\n * a var is, so \"change the global cookie config\" is just a `.set()` at\n * any point in the scope.\n */\nexport const cookieOptions = v.var(\"cookieOptions\", {\n\tdefault: {},\n\tschema: v.object(cookieShape),\n});\n\n/** The per-call options field: the same shape, optional to send. */\nconst cookieOptionsField = v.object(cookieShape, { optional: true });\n\nconst serialize = (\n\tname: string,\n\tvalue: string,\n\toptions: CookieOptions = {},\n) => {\n\tconst parts = [`${name}=${encodeURIComponent(value)}`];\n\tif (options.maxAge !== undefined) parts.push(`Max-Age=${options.maxAge}`);\n\tif (options.expires) parts.push(`Expires=${options.expires.toUTCString()}`);\n\tif (options.path) parts.push(`Path=${options.path}`);\n\tif (options.domain) parts.push(`Domain=${options.domain}`);\n\tif (options.secure) parts.push(\"Secure\");\n\tif (options.httpOnly) parts.push(\"HttpOnly\");\n\tif (options.sameSite) {\n\t\tparts.push(\n\t\t\t`SameSite=${options.sameSite.charAt(0).toUpperCase()}${options.sameSite.slice(1)}`,\n\t\t);\n\t}\n\treturn parts.join(\"; \");\n};\n\nconst ck = v.fn(\"cookie.\", {\n\tuse: [{ req, res, cookieOptions }],\n});\n\n/** Read one cookie from the REQUEST jar. Positional: `getCookie(\"theme\")`.\n * Readonly - reading a cookie can never write the scope. */\nexport const getCookie = ck.fn(\n\t\"get\",\n\t{ input: [v.string()], readonly: true },\n\t(c): string | null => {\n\t\tconst [name] = c.input;\n\t\treturn c.req?.cookies[name] ?? null;\n\t},\n);\n\n/** Queue a Set-Cookie on the scope's response headers: the scope's\n * `cookieOptions` defaults, with per-call options merged on top. The\n * written cookie also lands in the request jar, so a later `getCookie`\n * in the same scope reads what was set. */\nexport const setCookie = ck.fn(\n\t\"set\",\n\t{\n\t\tinput: {\n\t\t\tname: v.string(),\n\t\t\tvalue: v.string(),\n\t\t\toptions: cookieOptionsField,\n\t\t},\n\t},\n\t(c) => {\n\t\tconst { name, value, options } = c.input;\n\t\tlet response = c.res;\n\t\tif (!response) {\n\t\t\tresponse = { headers: new Headers() };\n\t\t\tc.res = response;\n\t\t}\n\t\tresponse.headers.append(\n\t\t\t\"set-cookie\",\n\t\t\tserialize(name, value, { ...c.cookieOptions, ...options }),\n\t\t);\n\t\tconst current = c.req;\n\t\tif (current) {\n\t\t\tc.req = {\n\t\t\t\t...current,\n\t\t\t\tcookies: { ...current.cookies, [name]: value },\n\t\t\t};\n\t\t}\n\t\treturn { set: true };\n\t},\n);\n\n/** Expire a cookie: a Set-Cookie with Max-Age=0 - the attributes (path,\n * domain) must match the ones it was set with. */\nexport const deleteCookie = ck.fn(\n\t\"delete\",\n\t{\n\t\tinput: {\n\t\t\tname: v.string(),\n\t\t\toptions: cookieOptionsField,\n\t\t},\n\t\tuse: [{ setCookie }],\n\t},\n\t(c) => {\n\t\tconst { name, options } = c.input;\n\t\treturn c.setCookie({\n\t\t\tname,\n\t\t\tvalue: \"\",\n\t\t\toptions: { ...options, maxAge: 0 },\n\t\t});\n\t},\n);\n"],"mappings":";;;;;;;AAQA,MAAa,cAAc;CAC1B,QAAQ,EAAE,OAAO,EAAE,UAAU,KAAK,CAAC;CACnC,SAAS,EAAE,KAAK,EAAE,UAAU,KAAK,CAAC;CAClC,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,CAAC;CACjC,QAAQ,EAAE,OAAO,EAAE,UAAU,KAAK,CAAC;CACnC,QAAQ,EAAE,QAAQ,EAAE,UAAU,KAAK,CAAC;CACpC,UAAU,EAAE,QAAQ,EAAE,UAAU,KAAK,CAAC;CACtC,UAAU,EAAE,OAAO;EAAE,MAAM;GAAC;GAAU;GAAO;EAAM;EAAG,UAAU;CAAK,CAAC;AACvE;;;;;;;;AAWA,MAAa,gBAAgB,EAAE,IAAI,iBAAiB;CACnD,SAAS,CAAC;CACV,QAAQ,EAAE,OAAO,WAAW;AAC7B,CAAC;;AAGD,MAAM,qBAAqB,EAAE,OAAO,aAAa,EAAE,UAAU,KAAK,CAAC;AAEnE,MAAM,aACL,MACA,OACA,UAAyB,CAAC,MACtB;CACJ,MAAM,QAAQ,CAAC,GAAG,KAAK,GAAG,mBAAmB,KAAK,GAAG;CACrD,IAAI,QAAQ,WAAW,KAAA,GAAW,MAAM,KAAK,WAAW,QAAQ,QAAQ;CACxE,IAAI,QAAQ,SAAS,MAAM,KAAK,WAAW,QAAQ,QAAQ,YAAY,GAAG;CAC1E,IAAI,QAAQ,MAAM,MAAM,KAAK,QAAQ,QAAQ,MAAM;CACnD,IAAI,QAAQ,QAAQ,MAAM,KAAK,UAAU,QAAQ,QAAQ;CACzD,IAAI,QAAQ,QAAQ,MAAM,KAAK,QAAQ;CACvC,IAAI,QAAQ,UAAU,MAAM,KAAK,UAAU;CAC3C,IAAI,QAAQ,UACX,MAAM,KACL,YAAY,QAAQ,SAAS,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,QAAQ,SAAS,MAAM,CAAC,GAChF;CAED,OAAO,MAAM,KAAK,IAAI;AACvB;AAEA,MAAM,KAAK,EAAE,GAAG,WAAW,EAC1B,KAAK,CAAC;CAAE;CAAK;CAAK;AAAc,CAAC,EAClC,CAAC;;;AAID,MAAa,YAAY,GAAG,GAC3B,OACA;CAAE,OAAO,CAAC,EAAE,OAAO,CAAC;CAAG,UAAU;AAAK,IACrC,MAAqB;CACrB,MAAM,CAAC,QAAQ,EAAE;CACjB,OAAO,EAAE,KAAK,QAAQ,SAAS;AAChC,CACD;;;;;AAMA,MAAa,YAAY,GAAG,GAC3B,OACA,EACC,OAAO;CACN,MAAM,EAAE,OAAO;CACf,OAAO,EAAE,OAAO;CAChB,SAAS;AACV,EACD,IACC,MAAM;CACN,MAAM,EAAE,MAAM,OAAO,YAAY,EAAE;CACnC,IAAI,WAAW,EAAE;CACjB,IAAI,CAAC,UAAU;EACd,WAAW,EAAE,SAAS,IAAI,QAAQ,EAAE;EACpC,EAAE,MAAM;CACT;CACA,SAAS,QAAQ,OAChB,cACA,UAAU,MAAM,OAAO;EAAE,GAAG,EAAE;EAAe,GAAG;CAAQ,CAAC,CAC1D;CACA,MAAM,UAAU,EAAE;CAClB,IAAI,SACH,EAAE,MAAM;EACP,GAAG;EACH,SAAS;GAAE,GAAG,QAAQ;IAAU,OAAO;EAAM;CAC9C;CAED,OAAO,EAAE,KAAK,KAAK;AACpB,CACD;;;AAIA,MAAa,eAAe,GAAG,GAC9B,UACA;CACC,OAAO;EACN,MAAM,EAAE,OAAO;EACf,SAAS;CACV;CACA,KAAK,CAAC,EAAE,UAAU,CAAC;AACpB,IACC,MAAM;CACN,MAAM,EAAE,MAAM,YAAY,EAAE;CAC5B,OAAO,EAAE,UAAU;EAClB;EACA,OAAO;EACP,SAAS;GAAE,GAAG;GAAS,QAAQ;EAAE;CAClC,CAAC;AACF,CACD"}
@@ -0,0 +1,39 @@
1
+ const require_error = require("../../error.cjs");
2
+ //#region src/plugins/http/error.ts
3
+ /** Static HTTP metadata on an error declaration from `http.err`. Non-
4
+ * enumerable so `asType` / object validation never treat it as a field. */
5
+ const kHttpErr = Symbol.for("better-call:http.err");
6
+ function err(status, data = {}) {
7
+ if (!Number.isInteger(status) || status < 200 || status > 599) throw new require_error.ValidationError("http.err.status", `expected HTTP status 200-599, received ${status}`);
8
+ const schema = { ...data };
9
+ Object.defineProperty(schema, kHttpErr, {
10
+ value: { status },
11
+ enumerable: false,
12
+ configurable: true
13
+ });
14
+ return schema;
15
+ }
16
+ /** Status declared via `http.err` on a single error-schema value, if any. */
17
+ const statusOf = (decl) => {
18
+ if (decl === null || typeof decl !== "object") return void 0;
19
+ return decl[kHttpErr]?.status;
20
+ };
21
+ /** Status for a thrown tag, looked up on the fn's `errors` map. */
22
+ const errorStatus = (errors, tag) => statusOf(errors?.[tag]);
23
+ /**
24
+ * Write a declared error onto `res`: set `status` from `http.err`
25
+ * metadata when present. Returns the same `res` for chaining.
26
+ */
27
+ const applyError = (response, errors, error) => {
28
+ const status = errorStatus(errors, error.tag);
29
+ if (status !== void 0) response.status = status;
30
+ return response;
31
+ };
32
+ //#endregion
33
+ exports.applyError = applyError;
34
+ exports.err = err;
35
+ exports.errorStatus = errorStatus;
36
+ exports.kHttpErr = kHttpErr;
37
+ exports.statusOf = statusOf;
38
+
39
+ //# sourceMappingURL=error.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.cjs","names":["ValidationError"],"sources":["../../../src/plugins/http/error.ts"],"sourcesContent":["import { ValidationError } from \"../../error\";\nimport type { HttpResponse } from \"./response\";\n\n/** Static HTTP metadata on an error declaration from `http.err`. Non-\n * enumerable so `asType` / object validation never treat it as a field. */\nexport const kHttpErr = Symbol.for(\"better-call:http.err\");\n\nexport type HttpErrMeta = {\n\tstatus: number;\n};\n\n/**\n * Declare an HTTP status on a fn error tag. Returns a normal payload\n * schema (so `c.error(tag, data)` validates as today) with status stashed\n * on {@link kHttpErr}. Core never sees HTTP; the edge reads it back with\n * {@link errorStatus} / {@link applyError}.\n *\n * @example\n * ```ts\n * errors: {\n * invalid_credentials: http.err(401, { attempts: v.number() }),\n * gone: http.err(410),\n * }\n * ```\n */\nexport function err(status: number): Record<string, never>;\nexport function err<const S extends Record<string, unknown>>(\n\tstatus: number,\n\tdata: S,\n): S;\nexport function err(\n\tstatus: number,\n\tdata: Record<string, unknown> = {},\n): Record<string, unknown> {\n\tif (!Number.isInteger(status) || status < 200 || status > 599) {\n\t\tthrow new ValidationError(\n\t\t\t\"http.err.status\",\n\t\t\t`expected HTTP status 200-599, received ${status}`,\n\t\t);\n\t}\n\t// Copy so two tags can share a data shape without sharing metadata.\n\tconst schema = { ...data };\n\tObject.defineProperty(schema, kHttpErr, {\n\t\tvalue: { status } satisfies HttpErrMeta,\n\t\tenumerable: false,\n\t\tconfigurable: true,\n\t});\n\treturn schema;\n}\n\n/** Status declared via `http.err` on a single error-schema value, if any. */\nexport const statusOf = (decl: unknown): number | undefined => {\n\tif (decl === null || typeof decl !== \"object\") return undefined;\n\tconst meta = (decl as Record<symbol, HttpErrMeta | undefined>)[kHttpErr];\n\treturn meta?.status;\n};\n\n/** Status for a thrown tag, looked up on the fn's `errors` map. */\nexport const errorStatus = (\n\terrors: Record<string, unknown> | undefined | null,\n\ttag: string,\n): number | undefined => statusOf(errors?.[tag]);\n\n/**\n * Write a declared error onto `res`: set `status` from `http.err`\n * metadata when present. Returns the same `res` for chaining.\n */\nexport const applyError = (\n\tresponse: HttpResponse,\n\terrors: Record<string, unknown> | undefined | null,\n\terror: { tag: string },\n): HttpResponse => {\n\tconst status = errorStatus(errors, error.tag);\n\tif (status !== undefined) response.status = status;\n\treturn response;\n};\n"],"mappings":";;;;AAKA,MAAa,WAAW,OAAO,IAAI,sBAAsB;AAyBzD,SAAgB,IACf,QACA,OAAgC,CAAC,GACP;CAC1B,IAAI,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,OAAO,SAAS,KACzD,MAAM,IAAIA,cAAAA,gBACT,mBACA,0CAA0C,QAC3C;CAGD,MAAM,SAAS,EAAE,GAAG,KAAK;CACzB,OAAO,eAAe,QAAQ,UAAU;EACvC,OAAO,EAAE,OAAO;EAChB,YAAY;EACZ,cAAc;CACf,CAAC;CACD,OAAO;AACR;;AAGA,MAAa,YAAY,SAAsC;CAC9D,IAAI,SAAS,QAAQ,OAAO,SAAS,UAAU,OAAO,KAAA;CAEtD,OADc,KAAiD,SACpD,EAAE;AACd;;AAGA,MAAa,eACZ,QACA,QACwB,SAAS,SAAS,IAAI;;;;;AAM/C,MAAa,cACZ,UACA,QACA,UACkB;CAClB,MAAM,SAAS,YAAY,QAAQ,MAAM,GAAG;CAC5C,IAAI,WAAW,KAAA,GAAW,SAAS,SAAS;CAC5C,OAAO;AACR"}
@@ -0,0 +1,38 @@
1
+ import { HttpResponse } from "./response.cjs";
2
+ //#region src/plugins/http/error.d.ts
3
+ /** Static HTTP metadata on an error declaration from `http.err`. Non-
4
+ * enumerable so `asType` / object validation never treat it as a field. */
5
+ declare const kHttpErr: unique symbol;
6
+ type HttpErrMeta = {
7
+ status: number;
8
+ };
9
+ /**
10
+ * Declare an HTTP status on a fn error tag. Returns a normal payload
11
+ * schema (so `c.error(tag, data)` validates as today) with status stashed
12
+ * on {@link kHttpErr}. Core never sees HTTP; the edge reads it back with
13
+ * {@link errorStatus} / {@link applyError}.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * errors: {
18
+ * invalid_credentials: http.err(401, { attempts: v.number() }),
19
+ * gone: http.err(410),
20
+ * }
21
+ * ```
22
+ */
23
+ declare function err(status: number): Record<string, never>;
24
+ declare function err<const S extends Record<string, unknown>>(status: number, data: S): S;
25
+ /** Status declared via `http.err` on a single error-schema value, if any. */
26
+ declare const statusOf: (decl: unknown) => number | undefined;
27
+ /** Status for a thrown tag, looked up on the fn's `errors` map. */
28
+ declare const errorStatus: (errors: Record<string, unknown> | undefined | null, tag: string) => number | undefined;
29
+ /**
30
+ * Write a declared error onto `res`: set `status` from `http.err`
31
+ * metadata when present. Returns the same `res` for chaining.
32
+ */
33
+ declare const applyError: (response: HttpResponse, errors: Record<string, unknown> | undefined | null, error: {
34
+ tag: string;
35
+ }) => HttpResponse;
36
+ //#endregion
37
+ export { HttpErrMeta, applyError, err, errorStatus, kHttpErr, statusOf };
38
+ //# sourceMappingURL=error.d.cts.map
@@ -0,0 +1,38 @@
1
+ import { HttpResponse } from "./response.mjs";
2
+ //#region src/plugins/http/error.d.ts
3
+ /** Static HTTP metadata on an error declaration from `http.err`. Non-
4
+ * enumerable so `asType` / object validation never treat it as a field. */
5
+ declare const kHttpErr: unique symbol;
6
+ type HttpErrMeta = {
7
+ status: number;
8
+ };
9
+ /**
10
+ * Declare an HTTP status on a fn error tag. Returns a normal payload
11
+ * schema (so `c.error(tag, data)` validates as today) with status stashed
12
+ * on {@link kHttpErr}. Core never sees HTTP; the edge reads it back with
13
+ * {@link errorStatus} / {@link applyError}.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * errors: {
18
+ * invalid_credentials: http.err(401, { attempts: v.number() }),
19
+ * gone: http.err(410),
20
+ * }
21
+ * ```
22
+ */
23
+ declare function err(status: number): Record<string, never>;
24
+ declare function err<const S extends Record<string, unknown>>(status: number, data: S): S;
25
+ /** Status declared via `http.err` on a single error-schema value, if any. */
26
+ declare const statusOf: (decl: unknown) => number | undefined;
27
+ /** Status for a thrown tag, looked up on the fn's `errors` map. */
28
+ declare const errorStatus: (errors: Record<string, unknown> | undefined | null, tag: string) => number | undefined;
29
+ /**
30
+ * Write a declared error onto `res`: set `status` from `http.err`
31
+ * metadata when present. Returns the same `res` for chaining.
32
+ */
33
+ declare const applyError: (response: HttpResponse, errors: Record<string, unknown> | undefined | null, error: {
34
+ tag: string;
35
+ }) => HttpResponse;
36
+ //#endregion
37
+ export { HttpErrMeta, applyError, err, errorStatus, kHttpErr, statusOf };
38
+ //# sourceMappingURL=error.d.mts.map
@@ -0,0 +1,35 @@
1
+ import { ValidationError } from "../../error.mjs";
2
+ //#region src/plugins/http/error.ts
3
+ /** Static HTTP metadata on an error declaration from `http.err`. Non-
4
+ * enumerable so `asType` / object validation never treat it as a field. */
5
+ const kHttpErr = Symbol.for("better-call:http.err");
6
+ function err(status, data = {}) {
7
+ if (!Number.isInteger(status) || status < 200 || status > 599) throw new ValidationError("http.err.status", `expected HTTP status 200-599, received ${status}`);
8
+ const schema = { ...data };
9
+ Object.defineProperty(schema, kHttpErr, {
10
+ value: { status },
11
+ enumerable: false,
12
+ configurable: true
13
+ });
14
+ return schema;
15
+ }
16
+ /** Status declared via `http.err` on a single error-schema value, if any. */
17
+ const statusOf = (decl) => {
18
+ if (decl === null || typeof decl !== "object") return void 0;
19
+ return decl[kHttpErr]?.status;
20
+ };
21
+ /** Status for a thrown tag, looked up on the fn's `errors` map. */
22
+ const errorStatus = (errors, tag) => statusOf(errors?.[tag]);
23
+ /**
24
+ * Write a declared error onto `res`: set `status` from `http.err`
25
+ * metadata when present. Returns the same `res` for chaining.
26
+ */
27
+ const applyError = (response, errors, error) => {
28
+ const status = errorStatus(errors, error.tag);
29
+ if (status !== void 0) response.status = status;
30
+ return response;
31
+ };
32
+ //#endregion
33
+ export { applyError, err, errorStatus, kHttpErr, statusOf };
34
+
35
+ //# sourceMappingURL=error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.mjs","names":[],"sources":["../../../src/plugins/http/error.ts"],"sourcesContent":["import { ValidationError } from \"../../error\";\nimport type { HttpResponse } from \"./response\";\n\n/** Static HTTP metadata on an error declaration from `http.err`. Non-\n * enumerable so `asType` / object validation never treat it as a field. */\nexport const kHttpErr = Symbol.for(\"better-call:http.err\");\n\nexport type HttpErrMeta = {\n\tstatus: number;\n};\n\n/**\n * Declare an HTTP status on a fn error tag. Returns a normal payload\n * schema (so `c.error(tag, data)` validates as today) with status stashed\n * on {@link kHttpErr}. Core never sees HTTP; the edge reads it back with\n * {@link errorStatus} / {@link applyError}.\n *\n * @example\n * ```ts\n * errors: {\n * invalid_credentials: http.err(401, { attempts: v.number() }),\n * gone: http.err(410),\n * }\n * ```\n */\nexport function err(status: number): Record<string, never>;\nexport function err<const S extends Record<string, unknown>>(\n\tstatus: number,\n\tdata: S,\n): S;\nexport function err(\n\tstatus: number,\n\tdata: Record<string, unknown> = {},\n): Record<string, unknown> {\n\tif (!Number.isInteger(status) || status < 200 || status > 599) {\n\t\tthrow new ValidationError(\n\t\t\t\"http.err.status\",\n\t\t\t`expected HTTP status 200-599, received ${status}`,\n\t\t);\n\t}\n\t// Copy so two tags can share a data shape without sharing metadata.\n\tconst schema = { ...data };\n\tObject.defineProperty(schema, kHttpErr, {\n\t\tvalue: { status } satisfies HttpErrMeta,\n\t\tenumerable: false,\n\t\tconfigurable: true,\n\t});\n\treturn schema;\n}\n\n/** Status declared via `http.err` on a single error-schema value, if any. */\nexport const statusOf = (decl: unknown): number | undefined => {\n\tif (decl === null || typeof decl !== \"object\") return undefined;\n\tconst meta = (decl as Record<symbol, HttpErrMeta | undefined>)[kHttpErr];\n\treturn meta?.status;\n};\n\n/** Status for a thrown tag, looked up on the fn's `errors` map. */\nexport const errorStatus = (\n\terrors: Record<string, unknown> | undefined | null,\n\ttag: string,\n): number | undefined => statusOf(errors?.[tag]);\n\n/**\n * Write a declared error onto `res`: set `status` from `http.err`\n * metadata when present. Returns the same `res` for chaining.\n */\nexport const applyError = (\n\tresponse: HttpResponse,\n\terrors: Record<string, unknown> | undefined | null,\n\terror: { tag: string },\n): HttpResponse => {\n\tconst status = errorStatus(errors, error.tag);\n\tif (status !== undefined) response.status = status;\n\treturn response;\n};\n"],"mappings":";;;;AAKA,MAAa,WAAW,OAAO,IAAI,sBAAsB;AAyBzD,SAAgB,IACf,QACA,OAAgC,CAAC,GACP;CAC1B,IAAI,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,OAAO,SAAS,KACzD,MAAM,IAAI,gBACT,mBACA,0CAA0C,QAC3C;CAGD,MAAM,SAAS,EAAE,GAAG,KAAK;CACzB,OAAO,eAAe,QAAQ,UAAU;EACvC,OAAO,EAAE,OAAO;EAChB,YAAY;EACZ,cAAc;CACf,CAAC;CACD,OAAO;AACR;;AAGA,MAAa,YAAY,SAAsC;CAC9D,IAAI,SAAS,QAAQ,OAAO,SAAS,UAAU,OAAO,KAAA;CAEtD,OADc,KAAiD,SACpD,EAAE;AACd;;AAGA,MAAa,eACZ,QACA,QACwB,SAAS,SAAS,IAAI;;;;;AAM/C,MAAa,cACZ,UACA,QACA,UACkB;CAClB,MAAM,SAAS,YAAY,QAAQ,MAAM,GAAG;CAC5C,IAAI,WAAW,KAAA,GAAW,SAAS,SAAS;CAC5C,OAAO;AACR"}
@@ -0,0 +1,73 @@
1
+ const require_index = require("../../index.cjs");
2
+ const require_response = require("./response.cjs");
3
+ const require_request = require("./request.cjs");
4
+ const require_cookie = require("./cookie.cjs");
5
+ const require_redirect = require("./redirect.cjs");
6
+ //#region src/plugins/http/handle.ts
7
+ const base = {
8
+ req: require_request.req,
9
+ res: require_response.res,
10
+ fromRequest: require_request.fromRequest,
11
+ redirect: require_redirect.redirect,
12
+ getCookie: require_cookie.getCookie,
13
+ setCookie: require_cookie.setCookie,
14
+ deleteCookie: require_cookie.deleteCookie,
15
+ cookieOptions: require_cookie.cookieOptions
16
+ };
17
+ const isBodyInit = (value) => typeof value === "string" || value instanceof Blob || value instanceof ArrayBuffer || ArrayBuffer.isView(value) || value instanceof ReadableStream || value instanceof FormData || value instanceof URLSearchParams;
18
+ const resultToResponse = (response, out) => {
19
+ if (out instanceof Response) return out;
20
+ if (out === void 0 || out === null) return require_response.toResponse(response, null);
21
+ if (isBodyInit(out)) return require_response.toResponse(response, out);
22
+ if (!response.headers.has("content-type")) response.headers.set("content-type", "application/json");
23
+ return require_response.toResponse(response, JSON.stringify(out));
24
+ };
25
+ const catchRedirect = (thrown, response) => {
26
+ if (!(thrown instanceof require_redirect.Redirect)) throw thrown;
27
+ const current = response ?? { headers: new Headers() };
28
+ require_redirect.applyRedirect(current, thrown);
29
+ return require_response.toResponse(current, null);
30
+ };
31
+ /** Seed req/res, run `run` on the same `c`, settle Redirect / body. */
32
+ const settle = async (c, request, run) => {
33
+ await c.fromRequest({ request });
34
+ try {
35
+ const out = await run(c);
36
+ return resultToResponse(c.res ?? { headers: new Headers() }, out);
37
+ } catch (thrown) {
38
+ return catchRedirect(thrown, c.res);
39
+ }
40
+ };
41
+ /**
42
+ * Mounted web edge: seed `req`/`res` from `request`, run `run` in the
43
+ * same scope, and turn {@link Redirect} into a 3xx Response.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * return c.handler({
48
+ * request: c.input.request,
49
+ * run: (ctx) => {
50
+ * ctx.redirect({ url: "/dashboard" });
51
+ * },
52
+ * });
53
+ * ```
54
+ */
55
+ const handler = require_index.v.fn({ use: [base] }).fn("http.handler", { input: {
56
+ request: require_index.v.any(),
57
+ run: require_index.v.any()
58
+ } }, async (c) => settle(c, c.input.request, c.input.run));
59
+ /**
60
+ * Fetch adapter: `(request) => Response`. Modules in `options.use` are
61
+ * mounted on the same context as `req` / `res` / `redirect`, so
62
+ * `createHandler((c) => c.whoami(), { use: [{ whoami }] })` works.
63
+ */
64
+ function createHandler(run, options) {
65
+ const use = [base, ...options?.use ?? []];
66
+ const entry = require_index.v.fn({ use }).fn("http.create_handler", { input: { request: require_index.v.any() } }, async (c) => settle(c, c.input.request, run));
67
+ return (request) => entry({ request });
68
+ }
69
+ //#endregion
70
+ exports.createHandler = createHandler;
71
+ exports.handler = handler;
72
+
73
+ //# sourceMappingURL=handle.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle.cjs","names":["toResponse","Redirect","v"],"sources":["../../../src/plugins/http/handle.ts"],"sourcesContent":["import type { Context, Fn } from \"../../fn\";\nimport { v } from \"../../index\";\nimport type { ApplyOns, Module, ModuleFns } from \"../../module\";\nimport type { ScopeOf } from \"../../scope\";\nimport { cookieOptions, deleteCookie, getCookie, setCookie } from \"./cookie\";\nimport { applyRedirect, Redirect, redirect } from \"./redirect\";\nimport { fromRequest, req } from \"./request\";\nimport type { HttpResponse } from \"./response\";\nimport { res, toResponse } from \"./response\";\n\nconst base = {\n\treq,\n\tres,\n\tfromRequest,\n\tredirect,\n\tgetCookie,\n\tsetCookie,\n\tdeleteCookie,\n\tcookieOptions,\n};\n\ntype EdgeModules<PL extends readonly Module[]> = readonly [typeof base, ...PL];\n\n/** Context `run` receives: HTTP base plus anything in `options.use`. */\nexport type CreateHandlerContext<PL extends readonly Module[] = readonly []> =\n\tContext<\n\t\t{ request: Request },\n\t\tScopeOf<EdgeModules<PL>>,\n\t\tnever,\n\t\tApplyOns<ModuleFns<EdgeModules<PL>>, EdgeModules<PL>>,\n\t\tFn\n\t>;\n\nconst isBodyInit = (value: unknown): value is BodyInit =>\n\ttypeof value === \"string\" ||\n\tvalue instanceof Blob ||\n\tvalue instanceof ArrayBuffer ||\n\tArrayBuffer.isView(value) ||\n\tvalue instanceof ReadableStream ||\n\tvalue instanceof FormData ||\n\tvalue instanceof URLSearchParams;\n\nconst resultToResponse = (response: HttpResponse, out: unknown): Response => {\n\tif (out instanceof Response) return out;\n\tif (out === undefined || out === null) return toResponse(response, null);\n\tif (isBodyInit(out)) return toResponse(response, out);\n\tif (!response.headers.has(\"content-type\")) {\n\t\tresponse.headers.set(\"content-type\", \"application/json\");\n\t}\n\treturn toResponse(response, JSON.stringify(out));\n};\n\nconst catchRedirect = (\n\tthrown: unknown,\n\tresponse: HttpResponse | null | undefined,\n): Response => {\n\tif (!(thrown instanceof Redirect)) throw thrown;\n\tconst current = response ?? { headers: new Headers() };\n\tapplyRedirect(current, thrown);\n\treturn toResponse(current, null);\n};\n\n/** Seed req/res, run `run` on the same `c`, settle Redirect / body. */\nconst settle = async (\n\tc: any,\n\trequest: Request,\n\trun: (c: any) => unknown | Promise<unknown>,\n): Promise<Response> => {\n\tawait c.fromRequest({ request });\n\ttry {\n\t\tconst out = await run(c);\n\t\treturn resultToResponse(c.res ?? { headers: new Headers() }, out);\n\t} catch (thrown) {\n\t\treturn catchRedirect(thrown, c.res);\n\t}\n};\n\nconst h = v.fn({ use: [base] });\n\n/**\n * Mounted web edge: seed `req`/`res` from `request`, run `run` in the\n * same scope, and turn {@link Redirect} into a 3xx Response.\n *\n * @example\n * ```ts\n * return c.handler({\n * request: c.input.request,\n * run: (ctx) => {\n * ctx.redirect({ url: \"/dashboard\" });\n * },\n * });\n * ```\n */\nexport const handler = h.fn(\n\t\"http.handler\",\n\t{\n\t\tinput: {\n\t\t\trequest: v.any<Request>(),\n\t\t\trun: v.any<(c: any) => unknown | Promise<unknown>>(),\n\t\t},\n\t},\n\tasync (c) => settle(c, c.input.request, c.input.run),\n);\n\nexport type CreateHandlerOptions<PL extends readonly Module[] = readonly []> = {\n\t/** Extra modules mounted onto the same `c` that `run` receives. */\n\tuse?: PL;\n};\n\n/**\n * Fetch adapter: `(request) => Response`. Modules in `options.use` are\n * mounted on the same context as `req` / `res` / `redirect`, so\n * `createHandler((c) => c.whoami(), { use: [{ whoami }] })` works.\n */\nexport function createHandler<\n\tconst PL extends readonly Module[] = readonly [],\n\tR = unknown,\n>(\n\trun: (c: CreateHandlerContext<PL>) => R | Promise<R>,\n\toptions?: CreateHandlerOptions<PL>,\n): (request: Request) => Promise<Response> {\n\tconst use = [base, ...(options?.use ?? [])] as EdgeModules<PL>;\n\tconst entry = v\n\t\t.fn({ use })\n\t\t.fn(\n\t\t\t\"http.create_handler\",\n\t\t\t{ input: { request: v.any<Request>() } },\n\t\t\tasync (c) => settle(c, c.input.request, run),\n\t\t);\n\n\treturn (request) => entry({ request }) as Promise<Response>;\n}\n"],"mappings":";;;;;;AAUA,MAAM,OAAO;CACZ,KAAA,gBAAA;CACA,KAAA,iBAAA;CACA,aAAA,gBAAA;CACA,UAAA,iBAAA;CACA,WAAA,eAAA;CACA,WAAA,eAAA;CACA,cAAA,eAAA;CACA,eAAA,eAAA;AACD;AAcA,MAAM,cAAc,UACnB,OAAO,UAAU,YACjB,iBAAiB,QACjB,iBAAiB,eACjB,YAAY,OAAO,KAAK,KACxB,iBAAiB,kBACjB,iBAAiB,YACjB,iBAAiB;AAElB,MAAM,oBAAoB,UAAwB,QAA2B;CAC5E,IAAI,eAAe,UAAU,OAAO;CACpC,IAAI,QAAQ,KAAA,KAAa,QAAQ,MAAM,OAAOA,iBAAAA,WAAW,UAAU,IAAI;CACvE,IAAI,WAAW,GAAG,GAAG,OAAOA,iBAAAA,WAAW,UAAU,GAAG;CACpD,IAAI,CAAC,SAAS,QAAQ,IAAI,cAAc,GACvC,SAAS,QAAQ,IAAI,gBAAgB,kBAAkB;CAExD,OAAOA,iBAAAA,WAAW,UAAU,KAAK,UAAU,GAAG,CAAC;AAChD;AAEA,MAAM,iBACL,QACA,aACc;CACd,IAAI,EAAE,kBAAkBC,iBAAAA,WAAW,MAAM;CACzC,MAAM,UAAU,YAAY,EAAE,SAAS,IAAI,QAAQ,EAAE;CACrD,iBAAA,cAAc,SAAS,MAAM;CAC7B,OAAOD,iBAAAA,WAAW,SAAS,IAAI;AAChC;;AAGA,MAAM,SAAS,OACd,GACA,SACA,QACuB;CACvB,MAAM,EAAE,YAAY,EAAE,QAAQ,CAAC;CAC/B,IAAI;EACH,MAAM,MAAM,MAAM,IAAI,CAAC;EACvB,OAAO,iBAAiB,EAAE,OAAO,EAAE,SAAS,IAAI,QAAQ,EAAE,GAAG,GAAG;CACjE,SAAS,QAAQ;EAChB,OAAO,cAAc,QAAQ,EAAE,GAAG;CACnC;AACD;;;;;;;;;;;;;;;AAkBA,MAAa,UAhBHE,cAAAA,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,CAgBN,CAAA,CAAE,GACxB,gBACA,EACC,OAAO;CACN,SAASA,cAAAA,EAAE,IAAa;CACxB,KAAKA,cAAAA,EAAE,IAA4C;AACpD,EACD,GACA,OAAO,MAAM,OAAO,GAAG,EAAE,MAAM,SAAS,EAAE,MAAM,GAAG,CACpD;;;;;;AAYA,SAAgB,cAIf,KACA,SAC0C;CAC1C,MAAM,MAAM,CAAC,MAAM,GAAI,SAAS,OAAO,CAAC,CAAE;CAC1C,MAAM,QAAQA,cAAAA,EACZ,GAAG,EAAE,IAAI,CAAC,CAAC,CACX,GACA,uBACA,EAAE,OAAO,EAAE,SAASA,cAAAA,EAAE,IAAa,EAAE,EAAE,GACvC,OAAO,MAAM,OAAO,GAAG,EAAE,MAAM,SAAS,GAAG,CAC5C;CAED,QAAQ,YAAY,MAAM,EAAE,QAAQ,CAAC;AACtC"}