@h3ravel/http 0.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @h3ravel/http
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - a27f452: chore: fix all linting issues.
8
+ - c906050: chore: migrate tests suite to jest
9
+ - Updated dependencies [8ceb2c1]
10
+ - Updated dependencies [a27f452]
11
+ - Updated dependencies [c906050]
12
+ - @h3ravel/core@0.4.0
13
+ - @h3ravel/shared@0.4.0
14
+ - @h3ravel/support@0.4.0
15
+
16
+ ## 1.0.0
17
+
18
+ ### Minor Changes
19
+
20
+ - 3ff97bf: refactor: add a shared package to be extended by others to avoid cyclic dependency issues.
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies [3ff97bf]
25
+ - @h3ravel/core@0.3.0
26
+ - @h3ravel/shared@0.3.0
27
+ - @h3ravel/support@0.3.0
28
+
3
29
  ## 0.2.0
4
30
 
5
31
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -19,8 +19,8 @@ var __copyProps = (to, from, except, desc) => {
19
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
20
 
21
21
  // src/index.ts
22
- var index_exports = {};
23
- __export(index_exports, {
22
+ var src_exports = {};
23
+ __export(src_exports, {
24
24
  ApiResource: () => ApiResource,
25
25
  HttpServiceProvider: () => HttpServiceProvider,
26
26
  JsonResource: () => JsonResource,
@@ -29,7 +29,7 @@ __export(index_exports, {
29
29
  Request: () => Request,
30
30
  Response: () => Response
31
31
  });
32
- module.exports = __toCommonJS(index_exports);
32
+ module.exports = __toCommonJS(src_exports);
33
33
 
34
34
  // src/Middleware.ts
35
35
  var Middleware = class {
@@ -230,10 +230,10 @@ var JsonResource = class {
230
230
  Object.defineProperty(this, key, {
231
231
  enumerable: true,
232
232
  configurable: true,
233
- get: /* @__PURE__ */ __name(() => this.resource[key], "get"),
234
- set: /* @__PURE__ */ __name((value) => {
233
+ get: () => this.resource[key],
234
+ set: (value) => {
235
235
  this.resource[key] = value;
236
- }, "set")
236
+ }
237
237
  });
238
238
  }
239
239
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Middleware.ts","../src/Request.ts","../src/Response.ts","../src/Middleware/LogRequests.ts","../src/Providers/HttpServiceProvider.ts","../src/Resources/JsonResource.ts","../src/Resources/ApiResource.ts"],"sourcesContent":["/**\n * @file Automatically generated by barrelsby.\n */\n\nexport * from './Middleware';\nexport * from './Request';\nexport * from './Response';\nexport * from './Contracts/ControllerContracts';\nexport * from './Contracts/HttpContract';\nexport * from './Middleware/LogRequests';\nexport * from './Providers/HttpServiceProvider';\nexport * from './Resources/ApiResource';\nexport * from './Resources/JsonResource';\n","import { HttpContext } from './Contracts/HttpContract';\n\nexport abstract class Middleware {\n abstract handle (context: HttpContext, next: () => Promise<unknown>): Promise<unknown>\n}\n","import { getQuery, getRouterParams, readBody, type H3Event } from 'h3'\nimport { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\n\nexport class Request {\n private readonly event: H3Event\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Get all input data (query + body).\n */\n async all<T = Record<string, unknown>> (): Promise<T> {\n let data = {\n ...getRouterParams(this.event),\n ...getQuery(this.event),\n } as T\n\n if (this.event.req.method === 'POST') {\n data = Object.assign({}, data, Object.fromEntries((await this.event.req.formData()).entries()))\n } else if (this.event.req.method === 'PUT') {\n data = <never>Object.fromEntries(Object.entries(<never>await readBody(this.event)))\n }\n\n return data\n }\n\n /**\n * Get a single input field from query or body.\n */\n async input<T = unknown> (key: string, defaultValue?: T): Promise<T> {\n const data = await this.all<Record<string, T>>()\n return (data[key] ?? defaultValue) as T\n }\n\n /**\n * Get route parameters.\n */\n params<T = Record<string, string>> (): T {\n return getRouterParams(this.event) as T\n }\n\n /**\n * Get query parameters.\n */\n query<T = Record<string, string>> (): T {\n return getQuery(this.event) as T\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\nimport { html, redirect, } from 'h3'\n\nimport type { H3Event } from 'h3'\n\nexport class Response {\n private readonly event: H3Event\n private statusCode: number = 200\n private headers: Record<string, string> = {}\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Set HTTP status code.\n */\n setStatusCode (code: number): this {\n this.statusCode = code\n this.event.res.status = code\n return this\n }\n\n /**\n * Set a header.\n */\n setHeader (name: string, value: string): this {\n this.headers[name] = value\n return this\n }\n\n html (content: string): string {\n this.applyHeaders()\n return html(this.event, content)\n }\n\n /**\n * Send a JSON response.\n */\n json<T = unknown> (data: T): T {\n this.setHeader(\"content-type\", \"application/json; charset=utf-8\")\n this.applyHeaders()\n return data\n }\n\n /**\n * Send plain text.\n */\n text (data: string): string {\n this.setHeader(\"content-type\", \"text/plain; charset=utf-8\")\n this.applyHeaders()\n return data;\n }\n\n /**\n * Redirect to another URL.\n */\n redirect (url: string, status = 302): string {\n this.setStatusCode(status)\n return redirect(this.event, url, this.statusCode)\n }\n\n /**\n * Apply headers before sending response.\n */\n private applyHeaders (): void {\n Object.entries(this.headers).forEach(([key, value]) => {\n this.event.res.headers.set(key, value)\n })\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { HttpContext, Middleware } from '@h3ravel/http'\n\nexport class LogRequests extends Middleware {\n async handle ({ request }: HttpContext, next: () => Promise<unknown>): Promise<unknown> {\n const url = request.getEvent('url')\n console.log(`[${request.getEvent('method')}] ${url.pathname + url.search}`)\n return next()\n }\n}\n","import { H3, serve } from 'h3'\n\nimport { ServiceProvider } from '@h3ravel/core'\n\n/**\n * Sets up HTTP kernel and request lifecycle.\n * \n * Register Request, Response, and Middleware classes.\n * Configure global middleware stack.\n * Boot HTTP kernel.\n * \n * Auto-Registered\n */\nexport class HttpServiceProvider extends ServiceProvider {\n register () {\n this.app.singleton('http.app', () => {\n return new H3()\n })\n\n this.app.singleton('http.serve', () => serve)\n }\n}\n","import { EventHandlerRequest, H3Event } from \"h3\";\n\nexport interface Resource {\n [key: string]: any;\n pagination?: {\n from?: number | undefined;\n to?: number | undefined;\n perPage?: number | undefined;\n total?: number | undefined;\n } | undefined;\n}\n\ntype BodyResource = Resource & {\n data: Omit<Resource, 'pagination'>,\n meta?: {\n pagination?: Resource['pagination']\n } | undefined;\n}\n\n/**\n * Class to render API resource\n */\nexport class JsonResource<R extends Resource = any> {\n /**\n * The request instance\n */\n request: H3Event<EventHandlerRequest>['req'];\n /**\n * The response instance\n */\n response: H3Event['res'];\n /**\n * The data to send to the client\n */\n resource: R;\n /**\n * The final response data object\n */\n body: BodyResource = {\n data: {},\n };\n /**\n * Flag to track if response should be sent automatically\n */\n private shouldSend: boolean = false;\n /**\n * Flag to track if response has been sent\n */\n\n private responseSent: boolean = false;\n\n /**\n * Declare that this includes R's properties\n */\n [key: string]: any;\n\n /**\n * @param req The request instance\n * @param res The response instance\n * @param rsc The data to send to the client\n */\n constructor(private event: H3Event, rsc: R) {\n this.request = event.req;\n this.response = event.res;\n this.resource = rsc;\n\n // Copy all properties from rsc to this, avoiding conflicts\n for (const key of Object.keys(rsc)) {\n if (!(key in this)) {\n Object.defineProperty(this, key, {\n enumerable: true,\n configurable: true,\n get: () => this.resource[key],\n set: (value) => {\n (<any>this.resource)[key] = value;\n },\n });\n }\n }\n }\n\n /**\n * Return the data in the expected format\n * \n * @returns \n */\n data (): Resource {\n return this.resource\n }\n\n /**\n * Build the response object\n * @returns this\n */\n json () {\n // Indicate response should be sent automatically\n this.shouldSend = true;\n\n // Set default status code\n this.response.status = 200;\n\n // Prepare body\n const resource = this.data()\n let data: Resource = Array.isArray(resource) ? [...resource] : { ...resource };\n\n if (typeof data.data !== 'undefined') {\n data = data.data\n }\n\n if (!Array.isArray(resource)) {\n delete data.pagination;\n }\n\n this.body = {\n data,\n };\n\n // Set the pagination from the data() resource, if available\n if (!Array.isArray(resource) && resource.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = resource.pagination;\n this.body.meta = meta;\n }\n\n // If pagination is not available on the resource, then check and set it\n // if it's available on the base resource.\n if (this.resource.pagination && !this.body.meta?.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = this.resource.pagination;\n this.body.meta = meta;\n }\n\n return this;\n }\n\n /**\n * Add context data to the response object\n * @param data Context data\n * @returns this\n */\n additional<X extends { [key: string]: any }> (data: X) {\n\n // Allow automatic send after additional\n this.shouldSend = true;\n\n // Merge data with body\n delete data.data;\n delete data.pagination;\n\n this.body = {\n ...this.body,\n ...data,\n };\n\n return this;\n }\n\n /**\n * Send the output to the client\n * @returns this\n */\n send () {\n this.shouldSend = false; // Prevent automatic send\n if (!this.responseSent) {\n this.#send();\n }\n return this;\n }\n\n /**\n * Set the status code for this response\n * @param code Status code\n * @returns this\n */\n status (code: number) {\n this.response.status = code;\n return this;\n }\n\n /**\n * Private method to send the response\n */\n #send () {\n if (!this.responseSent) {\n this.event.context.\n this.response.json(this.body);\n\n // Mark response as sent\n this.responseSent = true;\n }\n }\n\n /**\n * Check if send should be triggered automatically\n */\n private checkSend () {\n if (this.shouldSend && !this.responseSent) {\n this.#send();\n }\n }\n}\n","import { JsonResource, Resource } from \"./JsonResource\";\n\nimport { H3Event } from \"h3\";\n\nexport function ApiResource (\n instance: JsonResource\n) {\n return new Proxy(instance, {\n get (target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === 'function') {\n // Intercept json, additional, and send methods\n if (prop === 'json' || prop === 'additional') {\n return (...args: any[]) => {\n const result = value.apply(target, args);\n // Schedule checkSend after json or additional\n setImmediate(() => target['checkSend']());\n return result;\n };\n } else if (prop === 'send') {\n return (...args: any[]) => {\n // Prevent checkSend from firing\n target['shouldSend'] = false;\n\n return value.apply(target, args);\n };\n }\n }\n return value;\n },\n });\n}\n\nexport default function BaseResource<R extends Resource> (\n evt: H3Event,\n rsc: R\n) {\n return ApiResource(new JsonResource<R>(evt, rsc))\n}\n"],"mappings":"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","names":["Middleware","Request","event","all","data","getRouterParams","getQuery","req","method","Object","assign","fromEntries","formData","entries","readBody","input","key","defaultValue","params","query","getEvent","safeDot","import_support","import_h3","Response","event","statusCode","headers","setStatusCode","code","res","status","setHeader","name","value","html","content","applyHeaders","json","data","text","redirect","url","Object","entries","forEach","key","set","getEvent","safeDot","LogRequests","Middleware","handle","request","next","url","getEvent","console","log","pathname","search","import_h3","HttpServiceProvider","ServiceProvider","register","app","singleton","H3","serve","JsonResource","request","response","resource","body","data","shouldSend","responseSent","event","rsc","req","res","key","Object","keys","defineProperty","enumerable","configurable","get","set","value","json","status","Array","isArray","pagination","meta","additional","send","code","context","this","checkSend","ApiResource","instance","Proxy","get","target","prop","receiver","value","Reflect","args","result","apply","setImmediate"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Middleware.ts","../src/Request.ts","../src/Response.ts","../src/Middleware/LogRequests.ts","../src/Providers/HttpServiceProvider.ts","../src/Resources/JsonResource.ts","../src/Resources/ApiResource.ts"],"sourcesContent":["/**\n * @file Automatically generated by barrelsby.\n */\n\nexport * from './Middleware';\nexport * from './Request';\nexport * from './Response';\nexport * from './Contracts/HttpContract';\nexport * from './Middleware/LogRequests';\nexport * from './Providers/HttpServiceProvider';\nexport * from './Resources/ApiResource';\nexport * from './Resources/JsonResource';\n","import { HttpContext } from './Contracts/HttpContract';\nimport { IMiddleware } from '@h3ravel/shared';\n\nexport abstract class Middleware implements IMiddleware {\n abstract handle (context: HttpContext, next: () => Promise<unknown>): Promise<unknown>\n}\n","import { getQuery, getRouterParams, readBody, type H3Event } from 'h3'\nimport { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\nimport { IRequest } from '@h3ravel/shared'\n\nexport class Request implements IRequest {\n private readonly event: H3Event\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Get all input data (query + body).\n */\n async all<T = Record<string, unknown>> (): Promise<T> {\n let data = {\n ...getRouterParams(this.event),\n ...getQuery(this.event),\n } as T\n\n if (this.event.req.method === 'POST') {\n data = Object.assign({}, data, Object.fromEntries((await this.event.req.formData()).entries()))\n } else if (this.event.req.method === 'PUT') {\n data = <never>Object.fromEntries(Object.entries(<never>await readBody(this.event)))\n }\n\n return data\n }\n\n /**\n * Get a single input field from query or body.\n */\n async input<T = unknown> (key: string, defaultValue?: T): Promise<T> {\n const data = await this.all<Record<string, T>>()\n return (data[key] ?? defaultValue) as T\n }\n\n /**\n * Get route parameters.\n */\n params<T = Record<string, string>> (): T {\n return getRouterParams(this.event) as T\n }\n\n /**\n * Get query parameters.\n */\n query<T = Record<string, string>> (): T {\n return getQuery(this.event) as T\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\nimport { html, redirect, } from 'h3'\n\nimport type { H3Event } from 'h3'\nimport { IResponse } from '@h3ravel/shared'\n\nexport class Response implements IResponse {\n private readonly event: H3Event\n private statusCode: number = 200\n private headers: Record<string, string> = {}\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Set HTTP status code.\n */\n setStatusCode (code: number): this {\n this.statusCode = code\n this.event.res.status = code\n return this\n }\n\n /**\n * Set a header.\n */\n setHeader (name: string, value: string): this {\n this.headers[name] = value\n return this\n }\n\n html (content: string): string {\n this.applyHeaders()\n return html(this.event, content)\n }\n\n /**\n * Send a JSON response.\n */\n json<T = unknown> (data: T): T {\n this.setHeader(\"content-type\", \"application/json; charset=utf-8\")\n this.applyHeaders()\n return data\n }\n\n /**\n * Send plain text.\n */\n text (data: string): string {\n this.setHeader(\"content-type\", \"text/plain; charset=utf-8\")\n this.applyHeaders()\n return data;\n }\n\n /**\n * Redirect to another URL.\n */\n redirect (url: string, status = 302): string {\n this.setStatusCode(status)\n return redirect(this.event, url, this.statusCode)\n }\n\n /**\n * Apply headers before sending response.\n */\n private applyHeaders (): void {\n Object.entries(this.headers).forEach(([key, value]) => {\n this.event.res.headers.set(key, value)\n })\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { HttpContext } from '@h3ravel/shared'\nimport { Middleware } from '../Middleware'\n\nexport class LogRequests extends Middleware {\n async handle ({ request }: HttpContext, next: () => Promise<unknown>): Promise<unknown> {\n const url = request.getEvent('url')\n console.log(`[${request.getEvent('method')}] ${url.pathname + url.search}`)\n return next()\n }\n}\n","import { H3, serve } from 'h3'\n\nimport { ServiceProvider } from '@h3ravel/core'\n\n/**\n * Sets up HTTP kernel and request lifecycle.\n * \n * Register Request, Response, and Middleware classes.\n * Configure global middleware stack.\n * Boot HTTP kernel.\n * \n * Auto-Registered\n */\nexport class HttpServiceProvider extends ServiceProvider {\n register () {\n this.app.singleton('http.app', () => {\n return new H3()\n })\n\n this.app.singleton('http.serve', () => serve)\n }\n}\n","import { EventHandlerRequest, H3Event } from \"h3\";\n\nexport interface Resource {\n [key: string]: any;\n pagination?: {\n from?: number | undefined;\n to?: number | undefined;\n perPage?: number | undefined;\n total?: number | undefined;\n } | undefined;\n}\n\ntype BodyResource = Resource & {\n data: Omit<Resource, 'pagination'>,\n meta?: {\n pagination?: Resource['pagination']\n } | undefined;\n}\n\n/**\n * Class to render API resource\n */\nexport class JsonResource<R extends Resource = any> {\n /**\n * The request instance\n */\n request: H3Event<EventHandlerRequest>['req'];\n /**\n * The response instance\n */\n response: H3Event['res'];\n /**\n * The data to send to the client\n */\n resource: R;\n /**\n * The final response data object\n */\n body: BodyResource = {\n data: {},\n };\n /**\n * Flag to track if response should be sent automatically\n */\n private shouldSend: boolean = false;\n /**\n * Flag to track if response has been sent\n */\n\n private responseSent: boolean = false;\n\n /**\n * Declare that this includes R's properties\n */\n [key: string]: any;\n\n /**\n * @param req The request instance\n * @param res The response instance\n * @param rsc The data to send to the client\n */\n constructor(protected event: H3Event, rsc: R) {\n this.request = event.req;\n this.response = event.res;\n this.resource = rsc;\n\n // Copy all properties from rsc to this, avoiding conflicts\n for (const key of Object.keys(rsc)) {\n if (!(key in this)) {\n Object.defineProperty(this, key, {\n enumerable: true,\n configurable: true,\n get: () => this.resource[key],\n set: (value) => {\n (<any>this.resource)[key] = value;\n },\n });\n }\n }\n }\n\n /**\n * Return the data in the expected format\n * \n * @returns \n */\n data (): Resource {\n return this.resource\n }\n\n /**\n * Build the response object\n * @returns this\n */\n json () {\n // Indicate response should be sent automatically\n this.shouldSend = true;\n\n // Set default status code\n this.response.status = 200;\n\n // Prepare body\n const resource = this.data()\n let data: Resource = Array.isArray(resource) ? [...resource] : { ...resource };\n\n if (typeof data.data !== 'undefined') {\n data = data.data\n }\n\n if (!Array.isArray(resource)) {\n delete data.pagination;\n }\n\n this.body = {\n data,\n };\n\n // Set the pagination from the data() resource, if available\n if (!Array.isArray(resource) && resource.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = resource.pagination;\n this.body.meta = meta;\n }\n\n // If pagination is not available on the resource, then check and set it\n // if it's available on the base resource.\n if (this.resource.pagination && !this.body.meta?.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = this.resource.pagination;\n this.body.meta = meta;\n }\n\n return this;\n }\n\n /**\n * Add context data to the response object\n * @param data Context data\n * @returns this\n */\n additional<X extends { [key: string]: any }> (data: X) {\n\n // Allow automatic send after additional\n this.shouldSend = true;\n\n // Merge data with body\n delete data.data;\n delete data.pagination;\n\n this.body = {\n ...this.body,\n ...data,\n };\n\n return this;\n }\n\n /**\n * Send the output to the client\n * @returns this\n */\n send () {\n this.shouldSend = false; // Prevent automatic send\n if (!this.responseSent) {\n this.#send();\n }\n return this;\n }\n\n /**\n * Set the status code for this response\n * @param code Status code\n * @returns this\n */\n status (code: number) {\n this.response.status = code;\n return this;\n }\n\n /**\n * Private method to send the response\n */\n #send () {\n if (!this.responseSent) {\n this.event.context.\n this.response.json(this.body);\n\n // Mark response as sent\n this.responseSent = true;\n }\n }\n\n /**\n * Check if send should be triggered automatically\n */\n private checkSend () {\n if (this.shouldSend && !this.responseSent) {\n this.#send();\n }\n }\n}\n","import { JsonResource, Resource } from \"./JsonResource\";\n\nimport { H3Event } from \"h3\";\n\nexport function ApiResource (\n instance: JsonResource\n) {\n return new Proxy(instance, {\n get (target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === 'function') {\n // Intercept json, additional, and send methods\n if (prop === 'json' || prop === 'additional') {\n return (...args: any[]) => {\n const result = value.apply(target, args);\n // Schedule checkSend after json or additional\n setImmediate(() => target['checkSend']());\n return result;\n };\n } else if (prop === 'send') {\n return (...args: any[]) => {\n // Prevent checkSend from firing\n target['shouldSend'] = false;\n\n return value.apply(target, args);\n };\n }\n }\n return value;\n },\n });\n}\n\nexport default function BaseResource<R extends Resource> (\n evt: H3Event,\n rsc: R\n) {\n return ApiResource(new JsonResource<R>(evt, rsc))\n}\n"],"mappings":"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","names":["Middleware","Request","event","all","data","getRouterParams","getQuery","req","method","Object","assign","fromEntries","formData","entries","readBody","input","key","defaultValue","params","query","getEvent","safeDot","import_support","import_h3","Response","event","statusCode","headers","setStatusCode","code","res","status","setHeader","name","value","html","content","applyHeaders","json","data","text","redirect","url","Object","entries","forEach","key","set","getEvent","safeDot","LogRequests","Middleware","handle","request","next","url","getEvent","console","log","pathname","search","import_h3","HttpServiceProvider","ServiceProvider","register","app","singleton","H3","serve","JsonResource","request","response","resource","body","data","shouldSend","responseSent","event","rsc","req","res","key","Object","keys","defineProperty","enumerable","configurable","get","set","value","json","status","Array","isArray","pagination","meta","additional","send","code","context","this","checkSend","ApiResource","instance","Proxy","get","target","prop","receiver","value","Reflect","args","result","apply","setImmediate"]}
package/dist/index.d.cts CHANGED
@@ -1,8 +1,15 @@
1
+ import { HttpContext as HttpContext$1, IMiddleware, IRequest, IResponse } from '@h3ravel/shared';
1
2
  import { H3Event, EventHandlerRequest } from 'h3';
2
3
  import { DotNestedKeys, DotNestedValue } from '@h3ravel/support';
3
4
  import { ServiceProvider } from '@h3ravel/core';
4
5
 
5
- declare class Request {
6
+ type HttpContext = HttpContext$1;
7
+
8
+ declare abstract class Middleware implements IMiddleware {
9
+ abstract handle(context: HttpContext, next: () => Promise<unknown>): Promise<unknown>;
10
+ }
11
+
12
+ declare class Request implements IRequest {
6
13
  private readonly event;
7
14
  constructor(event: H3Event);
8
15
  /**
@@ -28,7 +35,7 @@ declare class Request {
28
35
  getEvent<K extends DotNestedKeys<H3Event>>(key: K): DotNestedValue<H3Event, K>;
29
36
  }
30
37
 
31
- declare class Response {
38
+ declare class Response implements IResponse {
32
39
  private readonly event;
33
40
  private statusCode;
34
41
  private headers;
@@ -65,29 +72,8 @@ declare class Response {
65
72
  getEvent<K extends DotNestedKeys<H3Event>>(key: K): DotNestedValue<H3Event, K>;
66
73
  }
67
74
 
68
- interface HttpContext {
69
- request: Request;
70
- response: Response;
71
- }
72
-
73
- declare abstract class Middleware {
74
- abstract handle(context: HttpContext, next: () => Promise<unknown>): Promise<unknown>;
75
- }
76
-
77
- /**
78
- * Defines the contract for all controllers.
79
- * Any controller implementing this must define these methods.
80
- */
81
- interface IController {
82
- show(ctx: HttpContext): any;
83
- index(ctx: HttpContext): any;
84
- store(ctx: HttpContext): any;
85
- update(ctx: HttpContext): any;
86
- destroy(ctx: HttpContext): any;
87
- }
88
-
89
75
  declare class LogRequests extends Middleware {
90
- handle({ request }: HttpContext, next: () => Promise<unknown>): Promise<unknown>;
76
+ handle({ request }: HttpContext$1, next: () => Promise<unknown>): Promise<unknown>;
91
77
  }
92
78
 
93
79
  /**
@@ -123,7 +109,7 @@ type BodyResource = Resource & {
123
109
  */
124
110
  declare class JsonResource<R extends Resource = any> {
125
111
  #private;
126
- private event;
112
+ protected event: H3Event;
127
113
  /**
128
114
  * The request instance
129
115
  */
@@ -196,4 +182,4 @@ declare class JsonResource<R extends Resource = any> {
196
182
 
197
183
  declare function ApiResource(instance: JsonResource): JsonResource<any>;
198
184
 
199
- export { ApiResource, type HttpContext, HttpServiceProvider, type IController, JsonResource, LogRequests, Middleware, Request, type Resource, Response };
185
+ export { ApiResource, type HttpContext, HttpServiceProvider, JsonResource, LogRequests, Middleware, Request, type Resource, Response };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,15 @@
1
+ import { HttpContext as HttpContext$1, IMiddleware, IRequest, IResponse } from '@h3ravel/shared';
1
2
  import { H3Event, EventHandlerRequest } from 'h3';
2
3
  import { DotNestedKeys, DotNestedValue } from '@h3ravel/support';
3
4
  import { ServiceProvider } from '@h3ravel/core';
4
5
 
5
- declare class Request {
6
+ type HttpContext = HttpContext$1;
7
+
8
+ declare abstract class Middleware implements IMiddleware {
9
+ abstract handle(context: HttpContext, next: () => Promise<unknown>): Promise<unknown>;
10
+ }
11
+
12
+ declare class Request implements IRequest {
6
13
  private readonly event;
7
14
  constructor(event: H3Event);
8
15
  /**
@@ -28,7 +35,7 @@ declare class Request {
28
35
  getEvent<K extends DotNestedKeys<H3Event>>(key: K): DotNestedValue<H3Event, K>;
29
36
  }
30
37
 
31
- declare class Response {
38
+ declare class Response implements IResponse {
32
39
  private readonly event;
33
40
  private statusCode;
34
41
  private headers;
@@ -65,29 +72,8 @@ declare class Response {
65
72
  getEvent<K extends DotNestedKeys<H3Event>>(key: K): DotNestedValue<H3Event, K>;
66
73
  }
67
74
 
68
- interface HttpContext {
69
- request: Request;
70
- response: Response;
71
- }
72
-
73
- declare abstract class Middleware {
74
- abstract handle(context: HttpContext, next: () => Promise<unknown>): Promise<unknown>;
75
- }
76
-
77
- /**
78
- * Defines the contract for all controllers.
79
- * Any controller implementing this must define these methods.
80
- */
81
- interface IController {
82
- show(ctx: HttpContext): any;
83
- index(ctx: HttpContext): any;
84
- store(ctx: HttpContext): any;
85
- update(ctx: HttpContext): any;
86
- destroy(ctx: HttpContext): any;
87
- }
88
-
89
75
  declare class LogRequests extends Middleware {
90
- handle({ request }: HttpContext, next: () => Promise<unknown>): Promise<unknown>;
76
+ handle({ request }: HttpContext$1, next: () => Promise<unknown>): Promise<unknown>;
91
77
  }
92
78
 
93
79
  /**
@@ -123,7 +109,7 @@ type BodyResource = Resource & {
123
109
  */
124
110
  declare class JsonResource<R extends Resource = any> {
125
111
  #private;
126
- private event;
112
+ protected event: H3Event;
127
113
  /**
128
114
  * The request instance
129
115
  */
@@ -196,4 +182,4 @@ declare class JsonResource<R extends Resource = any> {
196
182
 
197
183
  declare function ApiResource(instance: JsonResource): JsonResource<any>;
198
184
 
199
- export { ApiResource, type HttpContext, HttpServiceProvider, type IController, JsonResource, LogRequests, Middleware, Request, type Resource, Response };
185
+ export { ApiResource, type HttpContext, HttpServiceProvider, JsonResource, LogRequests, Middleware, Request, type Resource, Response };
package/dist/index.js CHANGED
@@ -200,10 +200,10 @@ var JsonResource = class {
200
200
  Object.defineProperty(this, key, {
201
201
  enumerable: true,
202
202
  configurable: true,
203
- get: /* @__PURE__ */ __name(() => this.resource[key], "get"),
204
- set: /* @__PURE__ */ __name((value) => {
203
+ get: () => this.resource[key],
204
+ set: (value) => {
205
205
  this.resource[key] = value;
206
- }, "set")
206
+ }
207
207
  });
208
208
  }
209
209
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Middleware.ts","../src/Request.ts","../src/Response.ts","../src/Middleware/LogRequests.ts","../src/Providers/HttpServiceProvider.ts","../src/Resources/JsonResource.ts","../src/Resources/ApiResource.ts"],"sourcesContent":["import { HttpContext } from './Contracts/HttpContract';\n\nexport abstract class Middleware {\n abstract handle (context: HttpContext, next: () => Promise<unknown>): Promise<unknown>\n}\n","import { getQuery, getRouterParams, readBody, type H3Event } from 'h3'\nimport { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\n\nexport class Request {\n private readonly event: H3Event\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Get all input data (query + body).\n */\n async all<T = Record<string, unknown>> (): Promise<T> {\n let data = {\n ...getRouterParams(this.event),\n ...getQuery(this.event),\n } as T\n\n if (this.event.req.method === 'POST') {\n data = Object.assign({}, data, Object.fromEntries((await this.event.req.formData()).entries()))\n } else if (this.event.req.method === 'PUT') {\n data = <never>Object.fromEntries(Object.entries(<never>await readBody(this.event)))\n }\n\n return data\n }\n\n /**\n * Get a single input field from query or body.\n */\n async input<T = unknown> (key: string, defaultValue?: T): Promise<T> {\n const data = await this.all<Record<string, T>>()\n return (data[key] ?? defaultValue) as T\n }\n\n /**\n * Get route parameters.\n */\n params<T = Record<string, string>> (): T {\n return getRouterParams(this.event) as T\n }\n\n /**\n * Get query parameters.\n */\n query<T = Record<string, string>> (): T {\n return getQuery(this.event) as T\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\nimport { html, redirect, } from 'h3'\n\nimport type { H3Event } from 'h3'\n\nexport class Response {\n private readonly event: H3Event\n private statusCode: number = 200\n private headers: Record<string, string> = {}\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Set HTTP status code.\n */\n setStatusCode (code: number): this {\n this.statusCode = code\n this.event.res.status = code\n return this\n }\n\n /**\n * Set a header.\n */\n setHeader (name: string, value: string): this {\n this.headers[name] = value\n return this\n }\n\n html (content: string): string {\n this.applyHeaders()\n return html(this.event, content)\n }\n\n /**\n * Send a JSON response.\n */\n json<T = unknown> (data: T): T {\n this.setHeader(\"content-type\", \"application/json; charset=utf-8\")\n this.applyHeaders()\n return data\n }\n\n /**\n * Send plain text.\n */\n text (data: string): string {\n this.setHeader(\"content-type\", \"text/plain; charset=utf-8\")\n this.applyHeaders()\n return data;\n }\n\n /**\n * Redirect to another URL.\n */\n redirect (url: string, status = 302): string {\n this.setStatusCode(status)\n return redirect(this.event, url, this.statusCode)\n }\n\n /**\n * Apply headers before sending response.\n */\n private applyHeaders (): void {\n Object.entries(this.headers).forEach(([key, value]) => {\n this.event.res.headers.set(key, value)\n })\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { HttpContext, Middleware } from '@h3ravel/http'\n\nexport class LogRequests extends Middleware {\n async handle ({ request }: HttpContext, next: () => Promise<unknown>): Promise<unknown> {\n const url = request.getEvent('url')\n console.log(`[${request.getEvent('method')}] ${url.pathname + url.search}`)\n return next()\n }\n}\n","import { H3, serve } from 'h3'\n\nimport { ServiceProvider } from '@h3ravel/core'\n\n/**\n * Sets up HTTP kernel and request lifecycle.\n * \n * Register Request, Response, and Middleware classes.\n * Configure global middleware stack.\n * Boot HTTP kernel.\n * \n * Auto-Registered\n */\nexport class HttpServiceProvider extends ServiceProvider {\n register () {\n this.app.singleton('http.app', () => {\n return new H3()\n })\n\n this.app.singleton('http.serve', () => serve)\n }\n}\n","import { EventHandlerRequest, H3Event } from \"h3\";\n\nexport interface Resource {\n [key: string]: any;\n pagination?: {\n from?: number | undefined;\n to?: number | undefined;\n perPage?: number | undefined;\n total?: number | undefined;\n } | undefined;\n}\n\ntype BodyResource = Resource & {\n data: Omit<Resource, 'pagination'>,\n meta?: {\n pagination?: Resource['pagination']\n } | undefined;\n}\n\n/**\n * Class to render API resource\n */\nexport class JsonResource<R extends Resource = any> {\n /**\n * The request instance\n */\n request: H3Event<EventHandlerRequest>['req'];\n /**\n * The response instance\n */\n response: H3Event['res'];\n /**\n * The data to send to the client\n */\n resource: R;\n /**\n * The final response data object\n */\n body: BodyResource = {\n data: {},\n };\n /**\n * Flag to track if response should be sent automatically\n */\n private shouldSend: boolean = false;\n /**\n * Flag to track if response has been sent\n */\n\n private responseSent: boolean = false;\n\n /**\n * Declare that this includes R's properties\n */\n [key: string]: any;\n\n /**\n * @param req The request instance\n * @param res The response instance\n * @param rsc The data to send to the client\n */\n constructor(private event: H3Event, rsc: R) {\n this.request = event.req;\n this.response = event.res;\n this.resource = rsc;\n\n // Copy all properties from rsc to this, avoiding conflicts\n for (const key of Object.keys(rsc)) {\n if (!(key in this)) {\n Object.defineProperty(this, key, {\n enumerable: true,\n configurable: true,\n get: () => this.resource[key],\n set: (value) => {\n (<any>this.resource)[key] = value;\n },\n });\n }\n }\n }\n\n /**\n * Return the data in the expected format\n * \n * @returns \n */\n data (): Resource {\n return this.resource\n }\n\n /**\n * Build the response object\n * @returns this\n */\n json () {\n // Indicate response should be sent automatically\n this.shouldSend = true;\n\n // Set default status code\n this.response.status = 200;\n\n // Prepare body\n const resource = this.data()\n let data: Resource = Array.isArray(resource) ? [...resource] : { ...resource };\n\n if (typeof data.data !== 'undefined') {\n data = data.data\n }\n\n if (!Array.isArray(resource)) {\n delete data.pagination;\n }\n\n this.body = {\n data,\n };\n\n // Set the pagination from the data() resource, if available\n if (!Array.isArray(resource) && resource.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = resource.pagination;\n this.body.meta = meta;\n }\n\n // If pagination is not available on the resource, then check and set it\n // if it's available on the base resource.\n if (this.resource.pagination && !this.body.meta?.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = this.resource.pagination;\n this.body.meta = meta;\n }\n\n return this;\n }\n\n /**\n * Add context data to the response object\n * @param data Context data\n * @returns this\n */\n additional<X extends { [key: string]: any }> (data: X) {\n\n // Allow automatic send after additional\n this.shouldSend = true;\n\n // Merge data with body\n delete data.data;\n delete data.pagination;\n\n this.body = {\n ...this.body,\n ...data,\n };\n\n return this;\n }\n\n /**\n * Send the output to the client\n * @returns this\n */\n send () {\n this.shouldSend = false; // Prevent automatic send\n if (!this.responseSent) {\n this.#send();\n }\n return this;\n }\n\n /**\n * Set the status code for this response\n * @param code Status code\n * @returns this\n */\n status (code: number) {\n this.response.status = code;\n return this;\n }\n\n /**\n * Private method to send the response\n */\n #send () {\n if (!this.responseSent) {\n this.event.context.\n this.response.json(this.body);\n\n // Mark response as sent\n this.responseSent = true;\n }\n }\n\n /**\n * Check if send should be triggered automatically\n */\n private checkSend () {\n if (this.shouldSend && !this.responseSent) {\n this.#send();\n }\n }\n}\n","import { JsonResource, Resource } from \"./JsonResource\";\n\nimport { H3Event } from \"h3\";\n\nexport function ApiResource (\n instance: JsonResource\n) {\n return new Proxy(instance, {\n get (target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === 'function') {\n // Intercept json, additional, and send methods\n if (prop === 'json' || prop === 'additional') {\n return (...args: any[]) => {\n const result = value.apply(target, args);\n // Schedule checkSend after json or additional\n setImmediate(() => target['checkSend']());\n return result;\n };\n } else if (prop === 'send') {\n return (...args: any[]) => {\n // Prevent checkSend from firing\n target['shouldSend'] = false;\n\n return value.apply(target, args);\n };\n }\n }\n return value;\n },\n });\n}\n\nexport default function BaseResource<R extends Resource> (\n evt: H3Event,\n rsc: R\n) {\n return ApiResource(new JsonResource<R>(evt, rsc))\n}\n"],"mappings":";;;;AAEO,IAAeA,aAAf,MAAeA;EAAtB,OAAsBA;;;AAEtB;;;ACJA,SAASC,UAAUC,iBAAiBC,gBAA8B;AAClE,SAAwCC,eAAe;AAEhD,IAAMC,UAAN,MAAMA;EAHb,OAGaA;;;EACQC;EAEjB,YAAYA,OAAgB;AACxB,SAAKA,QAAQA;EACjB;;;;EAKA,MAAMC,MAAgD;AAClD,QAAIC,OAAO;MACP,GAAGC,gBAAgB,KAAKH,KAAK;MAC7B,GAAGI,SAAS,KAAKJ,KAAK;IAC1B;AAEA,QAAI,KAAKA,MAAMK,IAAIC,WAAW,QAAQ;AAClCJ,aAAOK,OAAOC,OAAO,CAAC,GAAGN,MAAMK,OAAOE,aAAa,MAAM,KAAKT,MAAMK,IAAIK,SAAQ,GAAIC,QAAO,CAAA,CAAA;IAC/F,WAAW,KAAKX,MAAMK,IAAIC,WAAW,OAAO;AACxCJ,aAAcK,OAAOE,YAAYF,OAAOI,QAAe,MAAMC,SAAS,KAAKZ,KAAK,CAAA,CAAA;IACpF;AAEA,WAAOE;EACX;;;;EAKA,MAAMW,MAAoBC,KAAaC,cAA8B;AACjE,UAAMb,OAAO,MAAM,KAAKD,IAAG;AAC3B,WAAQC,KAAKY,GAAAA,KAAQC;EACzB;;;;EAKAC,SAAyC;AACrC,WAAOb,gBAAgB,KAAKH,KAAK;EACrC;;;;EAKAiB,QAAwC;AACpC,WAAOb,SAAS,KAAKJ,KAAK;EAC9B;EAOAkB,SAA4CJ,KAAc;AACtD,WAAOK,QAAQ,KAAKnB,OAAOc,GAAAA;EAC/B;AACJ;;;AC1DA,SAAwCM,WAAAA,gBAAe;AACvD,SAASC,MAAMC,gBAAiB;AAIzB,IAAMC,WAAN,MAAMA;EALb,OAKaA;;;EACQC;EACTC,aAAqB;EACrBC,UAAkC,CAAC;EAE3C,YAAYF,OAAgB;AACxB,SAAKA,QAAQA;EACjB;;;;EAKAG,cAAeC,MAAoB;AAC/B,SAAKH,aAAaG;AAClB,SAAKJ,MAAMK,IAAIC,SAASF;AACxB,WAAO;EACX;;;;EAKAG,UAAWC,MAAcC,OAAqB;AAC1C,SAAKP,QAAQM,IAAAA,IAAQC;AACrB,WAAO;EACX;EAEAC,KAAMC,SAAyB;AAC3B,SAAKC,aAAY;AACjB,WAAOF,KAAK,KAAKV,OAAOW,OAAAA;EAC5B;;;;EAKAE,KAAmBC,MAAY;AAC3B,SAAKP,UAAU,gBAAgB,iCAAA;AAC/B,SAAKK,aAAY;AACjB,WAAOE;EACX;;;;EAKAC,KAAMD,MAAsB;AACxB,SAAKP,UAAU,gBAAgB,2BAAA;AAC/B,SAAKK,aAAY;AACjB,WAAOE;EACX;;;;EAKAE,SAAUC,KAAaX,SAAS,KAAa;AACzC,SAAKH,cAAcG,MAAAA;AACnB,WAAOU,SAAS,KAAKhB,OAAOiB,KAAK,KAAKhB,UAAU;EACpD;;;;EAKQW,eAAsB;AAC1BM,WAAOC,QAAQ,KAAKjB,OAAO,EAAEkB,QAAQ,CAAC,CAACC,KAAKZ,KAAAA,MAAM;AAC9C,WAAKT,MAAMK,IAAIH,QAAQoB,IAAID,KAAKZ,KAAAA;IACpC,CAAA;EACJ;EAOAc,SAA4CF,KAAc;AACtD,WAAOG,SAAQ,KAAKxB,OAAOqB,GAAAA;EAC/B;AACJ;;;AC7EO,IAAMI,cAAN,cAA0BC,WAAAA;EAFjC,OAEiCA;;;EAC7B,MAAMC,OAAQ,EAAEC,QAAO,GAAiBC,MAAgD;AACpF,UAAMC,MAAMF,QAAQG,SAAS,KAAA;AAC7BC,YAAQC,IAAI,IAAIL,QAAQG,SAAS,QAAA,CAAA,KAAcD,IAAII,WAAWJ,IAAIK,MAAM,EAAE;AAC1E,WAAON,KAAAA;EACX;AACJ;;;ACRA,SAASO,IAAIC,aAAa;AAE1B,SAASC,uBAAuB;AAWzB,IAAMC,sBAAN,cAAkCC,gBAAAA;EAbzC,OAayCA;;;EACrCC,WAAY;AACR,SAAKC,IAAIC,UAAU,YAAY,MAAA;AAC3B,aAAO,IAAIC,GAAAA;IACf,CAAA;AAEA,SAAKF,IAAIC,UAAU,cAAc,MAAME,KAAAA;EAC3C;AACJ;;;ACCO,IAAMC,eAAN,MAAMA;EAHb,OAGaA;;;;;;;EAITC;;;;EAIAC;;;;EAIAC;;;;EAIAC,OAAqB;IACjBC,MAAM,CAAC;EACX;;;;EAIQC,aAAsB;;;;EAKtBC,eAAwB;;;;;;EAYhC,YAAoBC,OAAgBC,KAAQ;SAAxBD,QAAAA;AAChB,SAAKP,UAAUO,MAAME;AACrB,SAAKR,WAAWM,MAAMG;AACtB,SAAKR,WAAWM;AAGhB,eAAWG,OAAOC,OAAOC,KAAKL,GAAAA,GAAM;AAChC,UAAI,EAAEG,OAAO,OAAO;AAChBC,eAAOE,eAAe,MAAMH,KAAK;UAC7BI,YAAY;UACZC,cAAc;UACdC,KAAK,6BAAM,KAAKf,SAASS,GAAAA,GAApB;UACLO,KAAK,wBAACC,UAAAA;AACI,iBAAKjB,SAAUS,GAAAA,IAAOQ;UAChC,GAFK;QAGT,CAAA;MACJ;IACJ;EACJ;;;;;;EAOAf,OAAkB;AACd,WAAO,KAAKF;EAChB;;;;;EAMAkB,OAAQ;AAEJ,SAAKf,aAAa;AAGlB,SAAKJ,SAASoB,SAAS;AAGvB,UAAMnB,WAAW,KAAKE,KAAI;AAC1B,QAAIA,OAAiBkB,MAAMC,QAAQrB,QAAAA,IAAY;SAAIA;QAAY;MAAE,GAAGA;IAAS;AAE7E,QAAI,OAAOE,KAAKA,SAAS,aAAa;AAClCA,aAAOA,KAAKA;IAChB;AAEA,QAAI,CAACkB,MAAMC,QAAQrB,QAAAA,GAAW;AAC1B,aAAOE,KAAKoB;IAChB;AAEA,SAAKrB,OAAO;MACRC;IACJ;AAGA,QAAI,CAACkB,MAAMC,QAAQrB,QAAAA,KAAaA,SAASsB,YAAY;AACjD,YAAMC,OAA6B,KAAKtB,KAAKsB,QAAQ,CAAC;AACtDA,WAAKD,aAAatB,SAASsB;AAC3B,WAAKrB,KAAKsB,OAAOA;IACrB;AAIA,QAAI,KAAKvB,SAASsB,cAAc,CAAC,KAAKrB,KAAKsB,MAAMD,YAAY;AACzD,YAAMC,OAA6B,KAAKtB,KAAKsB,QAAQ,CAAC;AACtDA,WAAKD,aAAa,KAAKtB,SAASsB;AAChC,WAAKrB,KAAKsB,OAAOA;IACrB;AAEA,WAAO;EACX;;;;;;EAOAC,WAA8CtB,MAAS;AAGnD,SAAKC,aAAa;AAGlB,WAAOD,KAAKA;AACZ,WAAOA,KAAKoB;AAEZ,SAAKrB,OAAO;MACR,GAAG,KAAKA;MACR,GAAGC;IACP;AAEA,WAAO;EACX;;;;;EAMAuB,OAAQ;AACJ,SAAKtB,aAAa;AAClB,QAAI,CAAC,KAAKC,cAAc;AACpB,WAAK,MAAK;IACd;AACA,WAAO;EACX;;;;;;EAOAe,OAAQO,MAAc;AAClB,SAAK3B,SAASoB,SAASO;AACvB,WAAO;EACX;;;;EAKA,QAAK;AACD,QAAI,CAAC,KAAKtB,cAAc;AACpB,WAAKC,MAAMsB,QACPC,KAAK7B,SAASmB,KAAK,KAAKjB,IAAI;AAGhC,WAAKG,eAAe;IACxB;EACJ;;;;EAKQyB,YAAa;AACjB,QAAI,KAAK1B,cAAc,CAAC,KAAKC,cAAc;AACvC,WAAK,MAAK;IACd;EACJ;AACJ;;;ACpMO,SAAS0B,YACZC,UAAsB;AAEtB,SAAO,IAAIC,MAAMD,UAAU;IACvBE,IAAKC,QAAQC,MAAMC,UAAQ;AACvB,YAAMC,QAAQC,QAAQL,IAAIC,QAAQC,MAAMC,QAAAA;AACxC,UAAI,OAAOC,UAAU,YAAY;AAE7B,YAAIF,SAAS,UAAUA,SAAS,cAAc;AAC1C,iBAAO,IAAII,SAAAA;AACP,kBAAMC,SAASH,MAAMI,MAAMP,QAAQK,IAAAA;AAEnCG,yBAAa,MAAMR,OAAO,WAAA,EAAY,CAAA;AACtC,mBAAOM;UACX;QACJ,WAAWL,SAAS,QAAQ;AACxB,iBAAO,IAAII,SAAAA;AAEPL,mBAAO,YAAA,IAAgB;AAEvB,mBAAOG,MAAMI,MAAMP,QAAQK,IAAAA;UAC/B;QACJ;MACJ;AACA,aAAOF;IACX;EACJ,CAAA;AACJ;AA3BgBP;","names":["Middleware","getQuery","getRouterParams","readBody","safeDot","Request","event","all","data","getRouterParams","getQuery","req","method","Object","assign","fromEntries","formData","entries","readBody","input","key","defaultValue","params","query","getEvent","safeDot","safeDot","html","redirect","Response","event","statusCode","headers","setStatusCode","code","res","status","setHeader","name","value","html","content","applyHeaders","json","data","text","redirect","url","Object","entries","forEach","key","set","getEvent","safeDot","LogRequests","Middleware","handle","request","next","url","getEvent","console","log","pathname","search","H3","serve","ServiceProvider","HttpServiceProvider","ServiceProvider","register","app","singleton","H3","serve","JsonResource","request","response","resource","body","data","shouldSend","responseSent","event","rsc","req","res","key","Object","keys","defineProperty","enumerable","configurable","get","set","value","json","status","Array","isArray","pagination","meta","additional","send","code","context","this","checkSend","ApiResource","instance","Proxy","get","target","prop","receiver","value","Reflect","args","result","apply","setImmediate"]}
1
+ {"version":3,"sources":["../src/Middleware.ts","../src/Request.ts","../src/Response.ts","../src/Middleware/LogRequests.ts","../src/Providers/HttpServiceProvider.ts","../src/Resources/JsonResource.ts","../src/Resources/ApiResource.ts"],"sourcesContent":["import { HttpContext } from './Contracts/HttpContract';\nimport { IMiddleware } from '@h3ravel/shared';\n\nexport abstract class Middleware implements IMiddleware {\n abstract handle (context: HttpContext, next: () => Promise<unknown>): Promise<unknown>\n}\n","import { getQuery, getRouterParams, readBody, type H3Event } from 'h3'\nimport { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\nimport { IRequest } from '@h3ravel/shared'\n\nexport class Request implements IRequest {\n private readonly event: H3Event\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Get all input data (query + body).\n */\n async all<T = Record<string, unknown>> (): Promise<T> {\n let data = {\n ...getRouterParams(this.event),\n ...getQuery(this.event),\n } as T\n\n if (this.event.req.method === 'POST') {\n data = Object.assign({}, data, Object.fromEntries((await this.event.req.formData()).entries()))\n } else if (this.event.req.method === 'PUT') {\n data = <never>Object.fromEntries(Object.entries(<never>await readBody(this.event)))\n }\n\n return data\n }\n\n /**\n * Get a single input field from query or body.\n */\n async input<T = unknown> (key: string, defaultValue?: T): Promise<T> {\n const data = await this.all<Record<string, T>>()\n return (data[key] ?? defaultValue) as T\n }\n\n /**\n * Get route parameters.\n */\n params<T = Record<string, string>> (): T {\n return getRouterParams(this.event) as T\n }\n\n /**\n * Get query parameters.\n */\n query<T = Record<string, string>> (): T {\n return getQuery(this.event) as T\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'\nimport { html, redirect, } from 'h3'\n\nimport type { H3Event } from 'h3'\nimport { IResponse } from '@h3ravel/shared'\n\nexport class Response implements IResponse {\n private readonly event: H3Event\n private statusCode: number = 200\n private headers: Record<string, string> = {}\n\n constructor(event: H3Event) {\n this.event = event\n }\n\n /**\n * Set HTTP status code.\n */\n setStatusCode (code: number): this {\n this.statusCode = code\n this.event.res.status = code\n return this\n }\n\n /**\n * Set a header.\n */\n setHeader (name: string, value: string): this {\n this.headers[name] = value\n return this\n }\n\n html (content: string): string {\n this.applyHeaders()\n return html(this.event, content)\n }\n\n /**\n * Send a JSON response.\n */\n json<T = unknown> (data: T): T {\n this.setHeader(\"content-type\", \"application/json; charset=utf-8\")\n this.applyHeaders()\n return data\n }\n\n /**\n * Send plain text.\n */\n text (data: string): string {\n this.setHeader(\"content-type\", \"text/plain; charset=utf-8\")\n this.applyHeaders()\n return data;\n }\n\n /**\n * Redirect to another URL.\n */\n redirect (url: string, status = 302): string {\n this.setStatusCode(status)\n return redirect(this.event, url, this.statusCode)\n }\n\n /**\n * Apply headers before sending response.\n */\n private applyHeaders (): void {\n Object.entries(this.headers).forEach(([key, value]) => {\n this.event.res.headers.set(key, value)\n })\n }\n\n /**\n * Get the base event\n */\n getEvent (): H3Event\n getEvent<K extends DotNestedKeys<H3Event>> (key: K): DotNestedValue<H3Event, K>\n getEvent<K extends DotNestedKeys<H3Event>> (key?: K): any {\n return safeDot(this.event, key)\n }\n}\n","import { HttpContext } from '@h3ravel/shared'\nimport { Middleware } from '../Middleware'\n\nexport class LogRequests extends Middleware {\n async handle ({ request }: HttpContext, next: () => Promise<unknown>): Promise<unknown> {\n const url = request.getEvent('url')\n console.log(`[${request.getEvent('method')}] ${url.pathname + url.search}`)\n return next()\n }\n}\n","import { H3, serve } from 'h3'\n\nimport { ServiceProvider } from '@h3ravel/core'\n\n/**\n * Sets up HTTP kernel and request lifecycle.\n * \n * Register Request, Response, and Middleware classes.\n * Configure global middleware stack.\n * Boot HTTP kernel.\n * \n * Auto-Registered\n */\nexport class HttpServiceProvider extends ServiceProvider {\n register () {\n this.app.singleton('http.app', () => {\n return new H3()\n })\n\n this.app.singleton('http.serve', () => serve)\n }\n}\n","import { EventHandlerRequest, H3Event } from \"h3\";\n\nexport interface Resource {\n [key: string]: any;\n pagination?: {\n from?: number | undefined;\n to?: number | undefined;\n perPage?: number | undefined;\n total?: number | undefined;\n } | undefined;\n}\n\ntype BodyResource = Resource & {\n data: Omit<Resource, 'pagination'>,\n meta?: {\n pagination?: Resource['pagination']\n } | undefined;\n}\n\n/**\n * Class to render API resource\n */\nexport class JsonResource<R extends Resource = any> {\n /**\n * The request instance\n */\n request: H3Event<EventHandlerRequest>['req'];\n /**\n * The response instance\n */\n response: H3Event['res'];\n /**\n * The data to send to the client\n */\n resource: R;\n /**\n * The final response data object\n */\n body: BodyResource = {\n data: {},\n };\n /**\n * Flag to track if response should be sent automatically\n */\n private shouldSend: boolean = false;\n /**\n * Flag to track if response has been sent\n */\n\n private responseSent: boolean = false;\n\n /**\n * Declare that this includes R's properties\n */\n [key: string]: any;\n\n /**\n * @param req The request instance\n * @param res The response instance\n * @param rsc The data to send to the client\n */\n constructor(protected event: H3Event, rsc: R) {\n this.request = event.req;\n this.response = event.res;\n this.resource = rsc;\n\n // Copy all properties from rsc to this, avoiding conflicts\n for (const key of Object.keys(rsc)) {\n if (!(key in this)) {\n Object.defineProperty(this, key, {\n enumerable: true,\n configurable: true,\n get: () => this.resource[key],\n set: (value) => {\n (<any>this.resource)[key] = value;\n },\n });\n }\n }\n }\n\n /**\n * Return the data in the expected format\n * \n * @returns \n */\n data (): Resource {\n return this.resource\n }\n\n /**\n * Build the response object\n * @returns this\n */\n json () {\n // Indicate response should be sent automatically\n this.shouldSend = true;\n\n // Set default status code\n this.response.status = 200;\n\n // Prepare body\n const resource = this.data()\n let data: Resource = Array.isArray(resource) ? [...resource] : { ...resource };\n\n if (typeof data.data !== 'undefined') {\n data = data.data\n }\n\n if (!Array.isArray(resource)) {\n delete data.pagination;\n }\n\n this.body = {\n data,\n };\n\n // Set the pagination from the data() resource, if available\n if (!Array.isArray(resource) && resource.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = resource.pagination;\n this.body.meta = meta;\n }\n\n // If pagination is not available on the resource, then check and set it\n // if it's available on the base resource.\n if (this.resource.pagination && !this.body.meta?.pagination) {\n const meta: BodyResource['meta'] = this.body.meta ?? {}\n meta.pagination = this.resource.pagination;\n this.body.meta = meta;\n }\n\n return this;\n }\n\n /**\n * Add context data to the response object\n * @param data Context data\n * @returns this\n */\n additional<X extends { [key: string]: any }> (data: X) {\n\n // Allow automatic send after additional\n this.shouldSend = true;\n\n // Merge data with body\n delete data.data;\n delete data.pagination;\n\n this.body = {\n ...this.body,\n ...data,\n };\n\n return this;\n }\n\n /**\n * Send the output to the client\n * @returns this\n */\n send () {\n this.shouldSend = false; // Prevent automatic send\n if (!this.responseSent) {\n this.#send();\n }\n return this;\n }\n\n /**\n * Set the status code for this response\n * @param code Status code\n * @returns this\n */\n status (code: number) {\n this.response.status = code;\n return this;\n }\n\n /**\n * Private method to send the response\n */\n #send () {\n if (!this.responseSent) {\n this.event.context.\n this.response.json(this.body);\n\n // Mark response as sent\n this.responseSent = true;\n }\n }\n\n /**\n * Check if send should be triggered automatically\n */\n private checkSend () {\n if (this.shouldSend && !this.responseSent) {\n this.#send();\n }\n }\n}\n","import { JsonResource, Resource } from \"./JsonResource\";\n\nimport { H3Event } from \"h3\";\n\nexport function ApiResource (\n instance: JsonResource\n) {\n return new Proxy(instance, {\n get (target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === 'function') {\n // Intercept json, additional, and send methods\n if (prop === 'json' || prop === 'additional') {\n return (...args: any[]) => {\n const result = value.apply(target, args);\n // Schedule checkSend after json or additional\n setImmediate(() => target['checkSend']());\n return result;\n };\n } else if (prop === 'send') {\n return (...args: any[]) => {\n // Prevent checkSend from firing\n target['shouldSend'] = false;\n\n return value.apply(target, args);\n };\n }\n }\n return value;\n },\n });\n}\n\nexport default function BaseResource<R extends Resource> (\n evt: H3Event,\n rsc: R\n) {\n return ApiResource(new JsonResource<R>(evt, rsc))\n}\n"],"mappings":"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","names":["Middleware","getQuery","getRouterParams","readBody","safeDot","Request","event","all","data","getRouterParams","getQuery","req","method","Object","assign","fromEntries","formData","entries","readBody","input","key","defaultValue","params","query","getEvent","safeDot","safeDot","html","redirect","Response","event","statusCode","headers","setStatusCode","code","res","status","setHeader","name","value","html","content","applyHeaders","json","data","text","redirect","url","Object","entries","forEach","key","set","getEvent","safeDot","LogRequests","Middleware","handle","request","next","url","getEvent","console","log","pathname","search","H3","serve","ServiceProvider","HttpServiceProvider","ServiceProvider","register","app","singleton","H3","serve","JsonResource","request","response","resource","body","data","shouldSend","responseSent","event","rsc","req","res","key","Object","keys","defineProperty","enumerable","configurable","get","set","value","json","status","Array","isArray","pagination","meta","additional","send","code","context","this","checkSend","ApiResource","instance","Proxy","get","target","prop","receiver","value","Reflect","args","result","apply","setImmediate"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h3ravel/http",
3
- "version": "0.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "HTTP kernel, middleware pipeline, request/response classes for H3ravel.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,8 +11,11 @@
11
11
  "dependencies": {
12
12
  "h3": "^2.0.0-beta.1",
13
13
  "srvx": "^0.8.2",
14
- "@h3ravel/core": "0.2.0",
15
- "@h3ravel/support": "0.2.0"
14
+ "@h3ravel/shared": "0.4.0",
15
+ "@h3ravel/support": "0.4.0"
16
+ },
17
+ "peerDependencies": {
18
+ "@h3ravel/core": "0.4.0"
16
19
  },
17
20
  "devDependencies": {
18
21
  "typescript": "^5.4.0"
@@ -23,6 +26,6 @@
23
26
  "dev": "tsx watch src/index.ts",
24
27
  "start": "node dist/index.js",
25
28
  "lint": "eslint . --ext .ts",
26
- "test": "vitest"
29
+ "test": "jest --passWithNoTests"
27
30
  }
28
31
  }
@@ -1,7 +1,3 @@
1
- import { Request } from "../Request"
2
- import { Response } from "../Response"
1
+ import { HttpContext as IHttpContext } from '@h3ravel/shared'
3
2
 
4
- export interface HttpContext {
5
- request: Request
6
- response: Response
7
- }
3
+ export type HttpContext = IHttpContext
@@ -1,4 +1,5 @@
1
- import { HttpContext, Middleware } from '@h3ravel/http'
1
+ import { HttpContext } from '@h3ravel/shared'
2
+ import { Middleware } from '../Middleware'
2
3
 
3
4
  export class LogRequests extends Middleware {
4
5
  async handle ({ request }: HttpContext, next: () => Promise<unknown>): Promise<unknown> {
package/src/Middleware.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { HttpContext } from './Contracts/HttpContract';
1
+ import { HttpContext } from './Contracts/HttpContract'
2
+ import { IMiddleware } from '@h3ravel/shared'
2
3
 
3
- export abstract class Middleware {
4
+ export abstract class Middleware implements IMiddleware {
4
5
  abstract handle (context: HttpContext, next: () => Promise<unknown>): Promise<unknown>
5
6
  }
package/src/Request.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { getQuery, getRouterParams, readBody, type H3Event } from 'h3'
2
2
  import { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'
3
+ import { IRequest } from '@h3ravel/shared'
3
4
 
4
- export class Request {
5
+ export class Request implements IRequest {
5
6
  private readonly event: H3Event
6
7
 
7
8
  constructor(event: H3Event) {
@@ -1,34 +1,34 @@
1
- import { JsonResource, Resource } from "./JsonResource";
1
+ import { JsonResource, Resource } from './JsonResource'
2
2
 
3
- import { H3Event } from "h3";
3
+ import { H3Event } from 'h3'
4
4
 
5
5
  export function ApiResource (
6
6
  instance: JsonResource
7
7
  ) {
8
8
  return new Proxy(instance, {
9
9
  get (target, prop, receiver) {
10
- const value = Reflect.get(target, prop, receiver);
10
+ const value = Reflect.get(target, prop, receiver)
11
11
  if (typeof value === 'function') {
12
12
  // Intercept json, additional, and send methods
13
13
  if (prop === 'json' || prop === 'additional') {
14
14
  return (...args: any[]) => {
15
- const result = value.apply(target, args);
15
+ const result = value.apply(target, args)
16
16
  // Schedule checkSend after json or additional
17
- setImmediate(() => target['checkSend']());
18
- return result;
19
- };
17
+ setImmediate(() => target['checkSend']())
18
+ return result
19
+ }
20
20
  } else if (prop === 'send') {
21
21
  return (...args: any[]) => {
22
22
  // Prevent checkSend from firing
23
- target['shouldSend'] = false;
23
+ target['shouldSend'] = false
24
24
 
25
- return value.apply(target, args);
26
- };
25
+ return value.apply(target, args)
26
+ }
27
27
  }
28
28
  }
29
- return value;
29
+ return value
30
30
  },
31
- });
31
+ })
32
32
  }
33
33
 
34
34
  export default function BaseResource<R extends Resource> (
@@ -1,4 +1,4 @@
1
- import { EventHandlerRequest, H3Event } from "h3";
1
+ import { EventHandlerRequest, H3Event } from 'h3'
2
2
 
3
3
  export interface Resource {
4
4
  [key: string]: any;
@@ -24,25 +24,25 @@ export class JsonResource<R extends Resource = any> {
24
24
  /**
25
25
  * The request instance
26
26
  */
27
- request: H3Event<EventHandlerRequest>['req'];
27
+ request: H3Event<EventHandlerRequest>['req']
28
28
  /**
29
29
  * The response instance
30
30
  */
31
- response: H3Event['res'];
31
+ response: H3Event['res']
32
32
  /**
33
33
  * The data to send to the client
34
34
  */
35
- resource: R;
35
+ resource: R
36
36
  /**
37
37
  * The final response data object
38
38
  */
39
39
  body: BodyResource = {
40
40
  data: {},
41
- };
41
+ }
42
42
  /**
43
43
  * Flag to track if response should be sent automatically
44
44
  */
45
- private shouldSend: boolean = false;
45
+ private shouldSend: boolean = false
46
46
  /**
47
47
  * Flag to track if response has been sent
48
48
  */
@@ -60,9 +60,9 @@ export class JsonResource<R extends Resource = any> {
60
60
  * @param rsc The data to send to the client
61
61
  */
62
62
  constructor(protected event: H3Event, rsc: R) {
63
- this.request = event.req;
64
- this.response = event.res;
65
- this.resource = rsc;
63
+ this.request = event.req
64
+ this.response = event.res
65
+ this.resource = rsc
66
66
 
67
67
  // Copy all properties from rsc to this, avoiding conflicts
68
68
  for (const key of Object.keys(rsc)) {
@@ -72,9 +72,9 @@ export class JsonResource<R extends Resource = any> {
72
72
  configurable: true,
73
73
  get: () => this.resource[key],
74
74
  set: (value) => {
75
- (<any>this.resource)[key] = value;
75
+ (<any>this.resource)[key] = value
76
76
  },
77
- });
77
+ })
78
78
  }
79
79
  }
80
80
  }
@@ -94,43 +94,43 @@ export class JsonResource<R extends Resource = any> {
94
94
  */
95
95
  json () {
96
96
  // Indicate response should be sent automatically
97
- this.shouldSend = true;
97
+ this.shouldSend = true
98
98
 
99
99
  // Set default status code
100
- this.response.status = 200;
100
+ this.response.status = 200
101
101
 
102
102
  // Prepare body
103
103
  const resource = this.data()
104
- let data: Resource = Array.isArray(resource) ? [...resource] : { ...resource };
104
+ let data: Resource = Array.isArray(resource) ? [...resource] : { ...resource }
105
105
 
106
106
  if (typeof data.data !== 'undefined') {
107
107
  data = data.data
108
108
  }
109
109
 
110
110
  if (!Array.isArray(resource)) {
111
- delete data.pagination;
111
+ delete data.pagination
112
112
  }
113
113
 
114
114
  this.body = {
115
115
  data,
116
- };
116
+ }
117
117
 
118
118
  // Set the pagination from the data() resource, if available
119
119
  if (!Array.isArray(resource) && resource.pagination) {
120
120
  const meta: BodyResource['meta'] = this.body.meta ?? {}
121
- meta.pagination = resource.pagination;
122
- this.body.meta = meta;
121
+ meta.pagination = resource.pagination
122
+ this.body.meta = meta
123
123
  }
124
124
 
125
125
  // If pagination is not available on the resource, then check and set it
126
126
  // if it's available on the base resource.
127
127
  if (this.resource.pagination && !this.body.meta?.pagination) {
128
128
  const meta: BodyResource['meta'] = this.body.meta ?? {}
129
- meta.pagination = this.resource.pagination;
130
- this.body.meta = meta;
129
+ meta.pagination = this.resource.pagination
130
+ this.body.meta = meta
131
131
  }
132
132
 
133
- return this;
133
+ return this
134
134
  }
135
135
 
136
136
  /**
@@ -141,18 +141,18 @@ export class JsonResource<R extends Resource = any> {
141
141
  additional<X extends { [key: string]: any }> (data: X) {
142
142
 
143
143
  // Allow automatic send after additional
144
- this.shouldSend = true;
144
+ this.shouldSend = true
145
145
 
146
146
  // Merge data with body
147
- delete data.data;
148
- delete data.pagination;
147
+ delete data.data
148
+ delete data.pagination
149
149
 
150
150
  this.body = {
151
151
  ...this.body,
152
152
  ...data,
153
- };
153
+ }
154
154
 
155
- return this;
155
+ return this
156
156
  }
157
157
 
158
158
  /**
@@ -160,11 +160,11 @@ export class JsonResource<R extends Resource = any> {
160
160
  * @returns this
161
161
  */
162
162
  send () {
163
- this.shouldSend = false; // Prevent automatic send
163
+ this.shouldSend = false // Prevent automatic send
164
164
  if (!this.responseSent) {
165
- this.#send();
165
+ this.#send()
166
166
  }
167
- return this;
167
+ return this
168
168
  }
169
169
 
170
170
  /**
@@ -173,8 +173,8 @@ export class JsonResource<R extends Resource = any> {
173
173
  * @returns this
174
174
  */
175
175
  status (code: number) {
176
- this.response.status = code;
177
- return this;
176
+ this.response.status = code
177
+ return this
178
178
  }
179
179
 
180
180
  /**
@@ -183,10 +183,10 @@ export class JsonResource<R extends Resource = any> {
183
183
  #send () {
184
184
  if (!this.responseSent) {
185
185
  this.event.context.
186
- this.response.json(this.body);
186
+ this.response.json(this.body)
187
187
 
188
188
  // Mark response as sent
189
- this.responseSent = true;
189
+ this.responseSent = true
190
190
  }
191
191
  }
192
192
 
@@ -195,7 +195,7 @@ export class JsonResource<R extends Resource = any> {
195
195
  */
196
196
  private checkSend () {
197
197
  if (this.shouldSend && !this.responseSent) {
198
- this.#send();
198
+ this.#send()
199
199
  }
200
200
  }
201
201
  }
package/src/Response.ts CHANGED
@@ -2,8 +2,9 @@ import { DotNestedKeys, DotNestedValue, safeDot } from '@h3ravel/support'
2
2
  import { html, redirect, } from 'h3'
3
3
 
4
4
  import type { H3Event } from 'h3'
5
+ import { IResponse } from '@h3ravel/shared'
5
6
 
6
- export class Response {
7
+ export class Response implements IResponse {
7
8
  private readonly event: H3Event
8
9
  private statusCode: number = 200
9
10
  private headers: Record<string, string> = {}
@@ -38,7 +39,7 @@ export class Response {
38
39
  * Send a JSON response.
39
40
  */
40
41
  json<T = unknown> (data: T): T {
41
- this.setHeader("content-type", "application/json; charset=utf-8")
42
+ this.setHeader('content-type', 'application/json; charset=utf-8')
42
43
  this.applyHeaders()
43
44
  return data
44
45
  }
@@ -47,9 +48,9 @@ export class Response {
47
48
  * Send plain text.
48
49
  */
49
50
  text (data: string): string {
50
- this.setHeader("content-type", "text/plain; charset=utf-8")
51
+ this.setHeader('content-type', 'text/plain; charset=utf-8')
51
52
  this.applyHeaders()
52
- return data;
53
+ return data
53
54
  }
54
55
 
55
56
  /**
package/src/index.ts CHANGED
@@ -5,7 +5,6 @@
5
5
  export * from './Middleware';
6
6
  export * from './Request';
7
7
  export * from './Response';
8
- export * from './Contracts/ControllerContracts';
9
8
  export * from './Contracts/HttpContract';
10
9
  export * from './Middleware/LogRequests';
11
10
  export * from './Providers/HttpServiceProvider';
package/tsconfig.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../tsconfig/tsconfig.json",
2
+ "extends": "../shared/tsconfig.json",
3
3
  "compilerOptions": {
4
4
  "outDir": "dist"
5
5
  },
@@ -1,13 +0,0 @@
1
- import { HttpContext } from "./HttpContract"
2
-
3
- /**
4
- * Defines the contract for all controllers.
5
- * Any controller implementing this must define these methods.
6
- */
7
- export interface IController {
8
- show (ctx: HttpContext): any
9
- index (ctx: HttpContext): any
10
- store (ctx: HttpContext): any
11
- update (ctx: HttpContext): any
12
- destroy (ctx: HttpContext): any
13
- }