duto 0.260808.0 → 0.260915.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.
Files changed (98) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +35 -13
  3. package/package.json +54 -13
  4. package/src/cli.ts +45 -0
  5. package/src/commands/new.ts +147 -0
  6. package/src/commands/templates/base/.editorconfig +21 -0
  7. package/src/commands/templates/base/.env.dev.example +2 -0
  8. package/src/commands/templates/base/.env.prod.example +2 -0
  9. package/src/commands/templates/base/README.md +29 -0
  10. package/src/commands/templates/base/_gitignore +100 -0
  11. package/src/commands/templates/base/actions/blog/index.get.ts +9 -0
  12. package/src/commands/templates/base/actions/blog/post/[id].get.ts +9 -0
  13. package/src/commands/templates/base/actions/index.get.ts +5 -0
  14. package/src/commands/templates/base/configs/duto.ts +11 -0
  15. package/src/commands/templates/base/configs/roles.ts +5 -0
  16. package/src/commands/templates/base/package.json +24 -0
  17. package/src/commands/templates/base/tsconfig.json +42 -0
  18. package/src/commands/templates/workerd/wrangler.toml +47 -0
  19. package/src/commands/upgrade.ts +30 -0
  20. package/src/enum.ts +2 -0
  21. package/src/index.d.ts +61 -0
  22. package/src/index.ts +19 -23
  23. package/src/middleware.ts +2 -0
  24. package/src/{cli → pages/cli}/commands/build.ts +13 -7
  25. package/src/{cli → pages/cli}/commands/dev.ts +11 -4
  26. package/src/{cli → pages/cli}/commands/preview.ts +6 -4
  27. package/src/pages/cli/index.ts +10 -0
  28. package/src/{cli → pages/cli}/utils.ts +4 -5
  29. package/src/{client → pages/client}/app.tsx +1 -1
  30. package/src/{client → pages/client}/router.ts +1 -1
  31. package/src/{compiler → pages/compiler}/file.ts +1 -1
  32. package/src/{compiler → pages/compiler}/index.ts +2 -4
  33. package/src/{compiler → pages/compiler}/meta.ts +2 -2
  34. package/src/{compiler → pages/compiler}/routes.ts +1 -1
  35. package/src/{compiler → pages/compiler}/types.ts +10 -0
  36. package/src/{compiler → pages/compiler}/vite-plugin.ts +20 -45
  37. package/src/{i18n → pages/i18n}/client.ts +2 -2
  38. package/src/{i18n → pages/i18n}/element.ts +1 -1
  39. package/src/{i18n → pages/i18n}/server.ts +1 -1
  40. package/src/pages/index.ts +28 -0
  41. package/src/{preact → pages/preact}/server.ts +1 -1
  42. package/src/server/adapter/aws.ts +4 -0
  43. package/src/server/adapter/workerd.ts +4 -0
  44. package/src/server/app.ts +91 -0
  45. package/src/server/auth/ability.ts +72 -0
  46. package/src/server/auth/authnz.ts +86 -0
  47. package/src/server/auth/index.ts +5 -0
  48. package/src/server/auth/token.ts +147 -0
  49. package/src/server/auth/types.ts +2 -0
  50. package/src/server/cli/commands/build.ts +41 -0
  51. package/src/server/cli/commands/deploy.ts +81 -0
  52. package/src/server/cli/commands/dev.ts +279 -0
  53. package/src/server/cli/commands/make.ts +98 -0
  54. package/src/server/cli/commands/migrate.ts +93 -0
  55. package/src/server/cli/commands/routes.ts +78 -0
  56. package/src/server/cli/index.ts +27 -0
  57. package/src/server/cli/stubs.ts +39 -0
  58. package/src/server/cli/types.ts +3 -0
  59. package/src/server/cli/utils.ts +437 -0
  60. package/src/server/config.ts +19 -0
  61. package/src/server/db/d1.d.ts +1 -0
  62. package/src/server/db/d1.ts +1 -0
  63. package/src/server/db/dynamodb.d.ts +1 -0
  64. package/src/server/db/dynamodb.ts +1 -0
  65. package/src/server/db/index.ts +1 -0
  66. package/src/server/entry.ts +19 -0
  67. package/src/server/handle.ts +30 -0
  68. package/src/server/middlewares/auth.ts +40 -0
  69. package/src/server/middlewares/logger.ts +30 -0
  70. package/src/server/open-api/register.ts +29 -0
  71. package/src/server/open-api/spec.ts +114 -0
  72. package/src/server/request.ts +265 -0
  73. package/src/server/response.ts +142 -0
  74. package/src/server/routes.ts +473 -0
  75. package/src/server/types.ts +142 -0
  76. package/src/server/utils/environment.ts +17 -0
  77. package/src/server/utils.ts +71 -0
  78. package/src/server/validator.ts +70 -0
  79. package/src/utils.ts +15 -0
  80. package/src/cli/commands/dev-old.ts +0 -213
  81. package/src/cli/index.ts +0 -28
  82. package/src/compiler/utils.ts +0 -5
  83. /package/src/{client → pages/client}/hmr.ts +0 -0
  84. /package/src/{client → pages/client}/island.ts +0 -0
  85. /package/src/{client → pages/client}/link.ts +0 -0
  86. /package/src/{client → pages/client}/storage.ts +0 -0
  87. /package/src/{compiler → pages/compiler}/aliases.ts +0 -0
  88. /package/src/{compiler → pages/compiler}/assets.ts +0 -0
  89. /package/src/{compiler → pages/compiler}/ast.ts +0 -0
  90. /package/src/{compiler → pages/compiler}/constants.ts +0 -0
  91. /package/src/{compiler → pages/compiler}/island.ts +0 -0
  92. /package/src/{components → pages/components}/link.tsx +0 -0
  93. /package/src/{preact → pages/preact}/client-dev.ts +0 -0
  94. /package/src/{preact → pages/preact}/client.ts +0 -0
  95. /package/src/{preact → pages/preact}/context.ts +0 -0
  96. /package/src/{preact → pages/preact}/signals.ts +0 -0
  97. /package/src/{preact → pages/preact}/static-html.ts +0 -0
  98. /package/src/{preact → pages/preact}/types.ts +0 -0
@@ -0,0 +1,114 @@
1
+ import { STATUS_CODES } from 'node:http'
2
+ import { generateSpecs, resolver } from 'hono-openapi'
3
+ import { array, object, string } from 'zod/mini'
4
+ import { getEnv } from 't0n'
5
+ import { Scalar } from '@scalar/hono-api-reference'
6
+ import { minify } from '@minify-html/node'
7
+ import type { Hono } from '$/types'
8
+ import { useAuth } from '$/middlewares/auth'
9
+
10
+ export function openAPIConfig(opts: any) {
11
+ const {
12
+ disable = false,
13
+ path = '/docs',
14
+ agent = false,
15
+ name = '',
16
+ title = '',
17
+ version = getEnv('APP_VERSION') || getEnv('VERSION_HASH') || '1.0.0',
18
+ description = getEnv('APP_DESCRIPTION', ''),
19
+ auth = {},
20
+ ...docs
21
+ } = opts?.docs ?? {}
22
+
23
+ return {
24
+ disable,
25
+ path,
26
+ auth,
27
+ agent: !!agent,
28
+ name: name || title || getEnv('APP_NAME', 'API Docs'),
29
+ version,
30
+ desc: description,
31
+ ...docs,
32
+ }
33
+ }
34
+
35
+ export async function generateOpenAPI(app: Hono, opts?: any) {
36
+ return await generateSpecs(app, {
37
+ documentation: {
38
+ info: {
39
+ title: opts.name,
40
+ version: opts.version,
41
+ description: opts.desc,
42
+ contact: opts.contact,
43
+ termsOfService: opts.terms || opts.termsOfService,
44
+ license: opts.license,
45
+ },
46
+ externalDocs: opts.externalDocs,
47
+ servers: opts.servers,
48
+ security: useAuth() ? [{'JWT':[]}] : opts.security,
49
+ tags: opts.tags,
50
+ components: {
51
+ ...(useAuth() ? { securitySchemes: {
52
+ JWT: {
53
+ type: 'http',
54
+ scheme: 'bearer',
55
+ bearerFormat: 'JWT',
56
+ },
57
+ } } : {}),
58
+ responses: {
59
+ 500: {
60
+ description: STATUS_CODES[500],
61
+ content: { // @ts-ignore
62
+ 'application/json': await resolver(object({
63
+ m: array(string()),
64
+ })).toOpenAPISchema(),
65
+ },
66
+ },
67
+ ...opts?.responses,
68
+ },
69
+ },
70
+ },
71
+ })
72
+ }
73
+
74
+ export async function generateOpenAPIClient(app: Hono, opts?: any) {
75
+ const customCss = `[href="https://www.scalar.com"],.scalar-mcp-layer{display:none!important}`
76
+ app.get(
77
+ opts.path,
78
+ Scalar({
79
+ theme: 'saturn',
80
+ url: opts.path +'/openapi',
81
+ showDeveloperTools: 'never',
82
+ telemetry: false,
83
+ documentDownloadType: 'json', //'direct',
84
+ isLoading: true,
85
+ persistAuth: true,
86
+ hideClientButton: true,
87
+ pageTitle: opts.name,
88
+ agent: { disabled: !opts.agent },
89
+ slug: opts.name?.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^\w\s_-]/g, '').replace(/[\s_-]+/g, '_').replace(/[^\x00-\x7F]/g, '') +'_'+ opts.version,
90
+ customCss,
91
+ // onLoaded: () => document?.querySelectorAll('[href="https://www.scalar.com"]')?.forEach(el => el.remove()),
92
+ // favicon: 'https://example.com/favicon.png',
93
+ // hideDownloadButton: true,
94
+ defaultHttpClient: {
95
+ targetKey: 'shell',
96
+ clientKey: 'curl',
97
+ },
98
+ hiddenClients: [
99
+ 'c', 'clojure', 'csharp', 'dart', 'fsharp', 'go', 'http', 'java', 'julia', 'kotlin', 'node', 'objc', 'ocaml', 'php', 'python', 'powershell', 'r', 'ruby', 'rust', 'swift'
100
+ ],
101
+ })
102
+ )
103
+
104
+ const html = await (await app.request('/docs')).text()
105
+ const result = minify(Buffer.from(html), {
106
+ minify_css: true,
107
+ minify_js: true,
108
+ keep_comments: false,
109
+ })
110
+
111
+ return result.toString()
112
+ .replace(',customCss:`' + customCss + '`', '')
113
+ .replace('_integration:`hono`,', '')
114
+ }
@@ -0,0 +1,265 @@
1
+ import { getCookie, getSignedCookie, setCookie, setSignedCookie, deleteCookie } from 'hono/cookie'
2
+ import { HTTPException } from 'hono/http-exception'
3
+ import { Authnz, Token } from './auth'
4
+ import { addEnv } from 't0n'
5
+
6
+ import type { Context } from 'hono'
7
+ import { routePath, matchedRoutes } from 'hono/route'
8
+ import type { RouterRoute } from 'hono/types'
9
+ import type { CookieOptions, CookiePrefixOptions } from 'hono/utils/cookie'
10
+ import type { CustomHeader, RequestHeader } from 'hono/utils/headers'
11
+ import type { BodyData, ParseBodyOptions } from 'hono/utils/body'
12
+
13
+ // function cookieWrapper(c: Context) {
14
+ // return {
15
+ // all: () => getCookie(c),
16
+ // allSigned: (secret: string) => getSignedCookie(c, secret),
17
+ // get: (name: string, prefixOptions?: CookiePrefixOptions) => prefixOptions ? getCookie(c, name, prefixOptions) : getCookie(c, name),
18
+ // getSigned: (secret: string, name: string, prefixOptions?: CookiePrefixOptions) => prefixOptions ? getSignedCookie(c, secret, name, prefixOptions) : getSignedCookie(c, secret, name),
19
+ // set: (name: string, value: string, opt?: CookieOptions) => setCookie(c, name, value, opt),
20
+ // setSigned: (name: string, value: string, secret: string, opt?: CookieOptions) => setSignedCookie(c, name, value, secret, opt),
21
+ // delete: (name: string, opt?: CookieOptions) => deleteCookie(c, name, opt)
22
+ // }
23
+ // }
24
+ class $Cookie {
25
+ #c!: Context
26
+ constructor(c: Context) {
27
+ this.#c = c
28
+ }
29
+
30
+ all() {
31
+ return getCookie(this.#c)
32
+ }
33
+ allSigned(secret: string) {
34
+ return getSignedCookie(this.#c, secret)
35
+ }
36
+ get(name: string, prefixOptions?: CookiePrefixOptions) {
37
+ return prefixOptions ? getCookie(this.#c, name, prefixOptions) : getCookie(this.#c, name)
38
+ }
39
+ getSigned(secret: string, name: string, prefixOptions?: CookiePrefixOptions) {
40
+ return prefixOptions ? getSignedCookie(this.#c, secret, name, prefixOptions) : getSignedCookie(this.#c, secret, name)
41
+ }
42
+ set(name: string, value: string, opt?: CookieOptions) {
43
+ setCookie(this.#c, name, value, opt)
44
+ }
45
+ setSigned(name: string, value: string, secret: string, opt?: CookieOptions) {
46
+ setSignedCookie(this.#c, name, value, secret, opt)
47
+ }
48
+ delete(name: string, opt?: CookieOptions) {
49
+ deleteCookie(this.#c, name, opt)
50
+ }
51
+ }
52
+
53
+ export const GET_REQUEST: unique symbol = Symbol()
54
+
55
+ export default class $Request {
56
+ #c!: Context
57
+ #cookie: $Cookie
58
+ // #cookie: ReturnType<typeof cookieWrapper>
59
+ #u: Authnz<any> | null = null
60
+
61
+ #host: string
62
+ #protocol: string
63
+ #routePath?: string
64
+ #matchedRoutes: RouterRoute[]
65
+
66
+ constructor(c: Context) {
67
+ this.#c = c
68
+ this.#cookie = new $Cookie(c)
69
+ // this.#cookie = cookieWrapper(c)
70
+
71
+ const url = new URL(c.req.raw.url)
72
+ this.#host = url.protocol +'//'+ url.host
73
+ this.#protocol = url.protocol
74
+
75
+ this.#matchedRoutes = matchedRoutes(c)
76
+ }
77
+
78
+ static async toContext(c: Context) {
79
+ const req = new $Request(c)
80
+ await req.setAuth()
81
+ c.set(GET_REQUEST as unknown as string, req)
82
+ if (c.env) addEnv(c.env)
83
+ }
84
+
85
+ async setAuth() {
86
+ this.#u = Authnz.fromToken(await Token.fromRequest(this))
87
+ }
88
+
89
+ get user() {
90
+ return this.#u ? this.#u?.data : null
91
+ }
92
+
93
+ get auth() {
94
+ return this.#u
95
+ }
96
+
97
+ can(...abilities: string[]) {
98
+ return this.#u ? this.#u.can(...abilities) : false
99
+ }
100
+
101
+ cant(...abilities: string[]) {
102
+ return !this.can(...abilities)
103
+ }
104
+
105
+ hasRole(...roles: string[]) {
106
+ return this.#u ? this.#u.hasRole(...roles) : false
107
+ }
108
+
109
+ // TODO: fazer funcionar dnv..
110
+ // has(prop: string, value: any = null) {
111
+ // return this.#u ? this.#u.has(prop, value) : false
112
+ // }
113
+ // hasValue(prop: string, value: any = null) {
114
+ // return this.has(prop, value)
115
+ // }
116
+
117
+ // createToken(data: any, exp: number) {
118
+ // return Token.create(this, data, exp)
119
+ // }
120
+ // parseToken(token: string) {
121
+ // return Token.parse(this, token)
122
+ // }
123
+
124
+ get cx() {
125
+ return this.#c
126
+ }
127
+
128
+ get cookie() {
129
+ return this.#cookie
130
+ }
131
+
132
+ get ip(): string | undefined {
133
+ return this.#c.req.header('cf-connecting-ip') // cf
134
+ || this.#c.req.header('x-forwarded-for')?.split(',').at(-1)?.trim() // aws lambda
135
+ || this.#c.env?.aws?.lambda?.event?.requestContext?.identity?.sourceIp
136
+ || this.#c.env?.event?.Records[0]?.cf?.request?.clientIp // aws lambda@edge
137
+ || this.#c.req.header('x-real-ip') // vercel
138
+ || this.#c.env?.context?.ip // netlify
139
+ || this.#c.env?.remoteAddr?.hostname // deno
140
+ }
141
+
142
+ get userAgent(): string | undefined {
143
+ return this.#c.req.header('user-agent')
144
+ }
145
+
146
+ get protocol() {
147
+ return this.#protocol
148
+ }
149
+
150
+ get url() {
151
+ return this.#c.req.raw.url
152
+ }
153
+
154
+ get host() {
155
+ return this.#host
156
+ }
157
+
158
+ get path() {
159
+ return this.#c.req.path
160
+ }
161
+
162
+ get fullPath() {
163
+ const url = this.url
164
+ return url.slice(url.indexOf('/', 8))
165
+ }
166
+
167
+ get method() {
168
+ return this.#c.req.raw.method
169
+ }
170
+
171
+ get routePath() {
172
+ this.#routePath ??= routePath(this.#c)
173
+ return this.#routePath
174
+ }
175
+
176
+ get routeIndex() {
177
+ return this.#c.req.routeIndex
178
+ }
179
+
180
+ get matchedRoutes() {
181
+ return this.#matchedRoutes
182
+ }
183
+
184
+ get raw() {
185
+ return this.#c.req.raw
186
+ }
187
+
188
+ header(name: RequestHeader): string | undefined
189
+ header(name: string): string | undefined
190
+ header(): Record<RequestHeader | (string & CustomHeader), string>
191
+ header(name?: string) { // @ts-ignore
192
+ return this.#c.req.header(name)
193
+ }
194
+
195
+ param(key?: string) { // @ts-ignore
196
+ return this.#c.req.param(key)
197
+ }
198
+
199
+ query(): Record<string, string>
200
+ query(key: string): string | undefined
201
+ query(key?: string) { // @ts-ignore
202
+ return this.#c.req.query(key)
203
+ }
204
+
205
+ queries(): Record<string, string[]>
206
+ queries(key: string):string[] | undefined
207
+ queries(key?: string) { // @ts-ignore
208
+ return this.#c.req.queries(key)
209
+ }
210
+
211
+ async body<E>() {
212
+ const cType = this.#c.req.header('Content-Type')
213
+ if (!cType) return {} as E
214
+
215
+ if (/^application\/([a-z-\.]+\+)?json(;\s*[a-zA-Z0-9\-]+\=([^;]+))*$/.test(cType))
216
+ return await this.json<E>()
217
+
218
+ if (
219
+ cType?.startsWith('multipart/form-data')
220
+ || cType?.startsWith('application/x-www-form-urlencoded')
221
+ ) {
222
+ return await this.parseBody() as E
223
+ }
224
+
225
+ return {} as E
226
+ }
227
+
228
+ async parseBody<Options extends Partial<ParseBodyOptions>, T extends BodyData<Options>>(
229
+ options?: Options
230
+ ): Promise<T>
231
+ async parseBody<T extends BodyData>(options?: Partial<ParseBodyOptions>): Promise<T>
232
+ async parseBody(options?: Partial<ParseBodyOptions>) {
233
+ try {
234
+ return await this.#c.req.parseBody(options)
235
+ } catch (e) {
236
+ throw new HTTPException(400, {
237
+ message: 'Malformed FormData request.'+ (e instanceof Error ? ` ${e.message}` : ` ${String(e)}`)
238
+ })
239
+ }
240
+ }
241
+
242
+ async json<E>() {
243
+ try {
244
+ return await this.#c.req.json<E>()
245
+ } catch {
246
+ throw new HTTPException(400, { message: 'Malformed JSON in request body' })
247
+ }
248
+ }
249
+
250
+ text() {
251
+ return this.#c.req.text()
252
+ }
253
+
254
+ arrayBuffer() {
255
+ return this.#c.req.arrayBuffer()
256
+ }
257
+
258
+ blob() {
259
+ return this.#c.req.blob()
260
+ }
261
+
262
+ formData() {
263
+ return this.#c.req.formData()
264
+ }
265
+ }
@@ -0,0 +1,142 @@
1
+ import { HtmlEscapedCallbackPhase, resolveCallback } from 'hono/utils/html'
2
+ import type {
3
+ ContentfulStatusCode, RedirectStatusCode, StatusCode,
4
+ BaseMime, HeaderRecord,
5
+ ErrorResponse, Errors,
6
+ } from './types'
7
+
8
+ type RBag = {
9
+ status?: StatusCode,
10
+ headers?: HeaderRecord,
11
+ }
12
+
13
+ export default class $Response {
14
+ static raw(
15
+ status?: StatusCode,
16
+ body?: any,
17
+ cType?: BaseMime,
18
+ headers?: HeaderRecord
19
+ ) {
20
+ const b: RBag = { status: status || 200 }
21
+
22
+ if (cType || headers) {
23
+ headers ??= {}
24
+ if (cType) headers['Content-Type'] = cType
25
+ b.headers = headers
26
+ }
27
+
28
+ return new Response(body ?? null, b)
29
+ }
30
+
31
+ static text(str?: string, status?: StatusCode, headers?: HeaderRecord) {
32
+ return this.raw(status, str, 'text/plain; charset=UTF-8' as BaseMime, headers)
33
+ }
34
+
35
+ static json<T>(data?: T, status?: StatusCode, headers?: HeaderRecord) {
36
+ if (data == null)
37
+ return this.raw(status, null, undefined, headers)
38
+
39
+ return this.raw(status, JSON.stringify(data), 'application/json', headers)
40
+ }
41
+
42
+ static redirect(
43
+ location: string | URL,
44
+ status?: RedirectStatusCode,
45
+ headers?: HeaderRecord
46
+ ) {
47
+ const loc = String(location)
48
+
49
+ return new Response(null, {
50
+ status: status || 302,
51
+ headers: { ...headers, 'Location': /[^\x00-\xFF]/.test(loc) ? encodeURI(loc) : loc }
52
+ })
53
+ }
54
+
55
+ static html(
56
+ html: string | Promise<string>,
57
+ status?: ContentfulStatusCode,
58
+ headers?: HeaderRecord
59
+ ): Response | Promise<Response> {
60
+ const res = (html: string) => this.raw(status, html, 'text/html; charset=UTF-8' as BaseMime, headers)
61
+ return typeof html === 'string'
62
+ ? res(html)
63
+ : resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res)
64
+ }
65
+
66
+ static ok(): Response
67
+ static ok<T>(data: T, headers?: HeaderRecord): Response
68
+ static ok<T>(data?: T, headers?: HeaderRecord) {
69
+ return this.json(data, 200, headers)
70
+ }
71
+
72
+ static created(): Response
73
+ static created<T>(data: T, headers?: HeaderRecord): Response
74
+ static created<T>(data?: T, headers?: HeaderRecord) {
75
+ return this.json(data, 201, headers)
76
+ }
77
+
78
+ static accepted(): Response
79
+ static accepted<T>(data: T, headers?: HeaderRecord): Response
80
+ static accepted<T>(data?: T, headers?: HeaderRecord) {
81
+ return this.json(data, 202, headers)
82
+ }
83
+
84
+ static deleted(headers?: HeaderRecord) {
85
+ return this.noContent(headers)
86
+ }
87
+
88
+ static noContent(headers?: HeaderRecord) {
89
+ return this.json(null, 204, headers)
90
+ }
91
+
92
+ static badRequest(): Response
93
+ static badRequest(errors?: Errors | null, msg?: string, headers?: HeaderRecord): Response
94
+ static badRequest(errors?: Errors | null, msg?: string, headers?: HeaderRecord) {
95
+ return this.error(errors, msg, 400, headers)
96
+ }
97
+
98
+ static unauthorized(): Response
99
+ static unauthorized<T>(data: T, headers?: HeaderRecord): Response
100
+ static unauthorized<T>(data?: T, headers?: HeaderRecord) {
101
+ return this.json(data, 401, headers)
102
+ }
103
+
104
+ static forbidden(): Response
105
+ static forbidden<T>(data: T, headers?: HeaderRecord): Response
106
+ static forbidden<T>(data?: T, headers?: HeaderRecord) {
107
+ return this.json(data, 403, headers)
108
+ }
109
+
110
+ static notFound(): Response
111
+ static notFound<T>(msg: T, headers?: HeaderRecord): Response
112
+ static notFound<T>(msg?: T, headers?: HeaderRecord) {
113
+ return this.json(msg, 404, headers)
114
+ }
115
+
116
+ static conflict(): Response
117
+ static conflict(errors?: Errors | null, msg?: string, headers?: HeaderRecord) {
118
+ return this.error(errors, msg, 409, headers)
119
+ }
120
+
121
+ static unsupportedMediaType(): Response
122
+ static unsupportedMediaType(errors?: Errors | null, msg?: string, headers?: HeaderRecord) {
123
+ return this.error(errors, msg, 415, headers)
124
+ }
125
+
126
+ static internalError(): Response
127
+ static internalError(errors?: Errors | null, msg?: string, headers?: HeaderRecord) {
128
+ return this.error(errors, msg, 500, headers)
129
+ }
130
+
131
+ static error(errors?: Errors | null, msg?: string, status?: ContentfulStatusCode, headers?: HeaderRecord) {
132
+ status ??= 500
133
+ if (!errors && !msg)
134
+ return this.raw(status, msg)
135
+
136
+ const resp: ErrorResponse = {}
137
+ if (msg) resp.m = msg
138
+ if (errors) resp.e = errors
139
+
140
+ return this.json(resp, status, headers)
141
+ }
142
+ }