resora 1.3.16 → 1.3.18

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/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  [![NPM Downloads](https://img.shields.io/npm/dt/resora.svg)](https://www.npmjs.com/package/resora)
4
4
  [![npm version](https://img.shields.io/npm/v/resora.svg)](https://www.npmjs.com/package/resora)
5
- [![License](https://img.shields.io/npm/l/resora.svg)](https://github.com/arkstack-tmp/resora/blob/main/LICENSE)
6
- [![CI](https://github.com/arkstack-tmp/resora/actions/workflows/ci.yml/badge.svg)](https://github.com/arkstack-tmp/resora/actions/workflows/ci.yml)
7
- [![Deploy Docs](https://github.com/arkstack-tmp/resora/actions/workflows/deploy-docs.yml/badge.svg)](https://github.com/arkstack-tmp/resora/actions/workflows/deploy-docs.yml)
8
- [![codecov](https://codecov.io/gh/arkstack-tmp/resora/graph/badge.svg?token=IBgFGJCoOr)](https://codecov.io/gh/arkstack-tmp/resora)
5
+ [![License](https://img.shields.io/npm/l/resora.svg)](https://github.com/arkstack-hq/resora/blob/main/LICENSE)
6
+ [![CI](https://github.com/arkstack-hq/resora/actions/workflows/ci.yml/badge.svg)](https://github.com/arkstack-hq/resora/actions/workflows/ci.yml)
7
+ [![Deploy Docs](https://github.com/arkstack-hq/resora/actions/workflows/deploy-docs.yml/badge.svg)](https://github.com/arkstack-hq/resora/actions/workflows/deploy-docs.yml)
8
+ [![codecov](https://codecov.io/gh/arkstack-hq/resora/graph/badge.svg?token=IBgFGJCoOr)](https://codecov.io/gh/arkstack-hq/resora)
9
9
 
10
10
  Resora is a structured API response layer for Node.js and TypeScript backends.
11
11
 
@@ -223,6 +223,24 @@ It works with:
223
223
 
224
224
  Adapters can be added without changing application logic.
225
225
 
226
+ Framework adapters that own response dispatch can use a non-thenable snapshot:
227
+
228
+ ```ts
229
+ const response = new UserResource(user)
230
+ .response()
231
+ .setStatusCode(201)
232
+ .setHeaders({ 'X-Resource': 'user' })
233
+ .toResponseData();
234
+
235
+ return framework.send(response.body, {
236
+ status: response.status,
237
+ headers: response.headers,
238
+ });
239
+ ```
240
+
241
+ This preserves transport metadata across async controller boundaries without
242
+ requiring Resora to write to the framework response directly.
243
+
226
244
  ## Plugin System
227
245
 
228
246
  Resora exposes a first-class plugin registry for opt-in integrations and lifecycle extensions.
@@ -242,7 +260,7 @@ Plugins can:
242
260
 
243
261
  Available plugins:
244
262
 
245
- - [`@resora/plugin-clear-router`](https://arkstack-tmp.github.io/resora/plugins/clear-router.md)
263
+ - [`@resora/plugin-clear-router`](https://arkstack-hq.github.io/resora/plugins/clear-router.md)
246
264
 
247
265
  ## Conditional Rendering Example
248
266
 
@@ -274,10 +292,10 @@ It is intentionally not opinionated about routing, validation, or persistence.
274
292
 
275
293
  ## Documentation
276
294
 
277
- - Getting Started: https://arkstack-tmp.github.io/resora/guide/getting-started
278
- - Configuration: https://arkstack-tmp.github.io/resora/guide/configuration
279
- - Conditional Rendering: https://arkstack-tmp.github.io/resora/guide/conditional-attributes
280
- - Pagination & Cursor Recipes: https://arkstack-tmp.github.io/resora/guide/pagination-cursor-recipes
295
+ - Getting Started: https://arkstack-hq.github.io/resora/guide/getting-started
296
+ - Configuration: https://arkstack-hq.github.io/resora/guide/configuration
297
+ - Conditional Rendering: https://arkstack-hq.github.io/resora/guide/conditional-attributes
298
+ - Pagination & Cursor Recipes: https://arkstack-hq.github.io/resora/guide/pagination-cursor-recipes
281
299
 
282
300
  ## License
283
301
 
package/dist/index.cjs CHANGED
@@ -1272,6 +1272,7 @@ const resetPluginsForTests = () => {
1272
1272
  var ServerResponse = class {
1273
1273
  _status = 200;
1274
1274
  sent = false;
1275
+ prepared = false;
1275
1276
  headers = {};
1276
1277
  constructor(response, body) {
1277
1278
  this.response = response;
@@ -1285,6 +1286,7 @@ var ServerResponse = class {
1285
1286
  */
1286
1287
  setStatusCode(status) {
1287
1288
  this._status = status;
1289
+ this.prepared = false;
1288
1290
  return this;
1289
1291
  }
1290
1292
  /**
@@ -1295,6 +1297,7 @@ var ServerResponse = class {
1295
1297
  */
1296
1298
  setBody(body) {
1297
1299
  this.body = body;
1300
+ this.prepared = false;
1298
1301
  return this;
1299
1302
  }
1300
1303
  /**
@@ -1311,8 +1314,8 @@ var ServerResponse = class {
1311
1314
  * @returns
1312
1315
  */
1313
1316
  statusText() {
1314
- if ("statusMessage" in this.response) return this.response.statusMessage;
1315
- else if ("statusText" in this.response) return this.response.statusText;
1317
+ if (this.response && "statusMessage" in this.response) return this.response.statusMessage;
1318
+ else if (this.response && "statusText" in this.response) return this.response.statusText;
1316
1319
  }
1317
1320
  /**
1318
1321
  * Set a cookie in the response header
@@ -1355,10 +1358,27 @@ var ServerResponse = class {
1355
1358
  */
1356
1359
  #addHeader(key, value) {
1357
1360
  this.headers[key] = value;
1358
- if ("headers" in this.response && this.response.headers && typeof this.response.headers.set === "function") this.response.headers.set(key, value);
1359
- else if ("setHeader" in this.response) this.response.setHeader(key, value);
1360
- else if ("set" in this.response && typeof this.response.set === "function") this.response.set(key, value);
1361
- else if ("header" in this.response && typeof this.response.header === "function") this.response.header(key, value);
1361
+ this.prepared = false;
1362
+ if (this.response && "headers" in this.response && this.response.headers && typeof this.response.headers.set === "function") this.response.headers.set(key, value);
1363
+ else if (this.response && "setHeader" in this.response) this.response.setHeader(key, value);
1364
+ else if (this.response && "set" in this.response && typeof this.response.set === "function") this.response.set(key, value);
1365
+ else if (this.response && "header" in this.response && typeof this.response.header === "function") this.response.header(key, value);
1366
+ }
1367
+ /**
1368
+ * Return the finalized response state without dispatching it.
1369
+ *
1370
+ * This is the preferred integration boundary for frameworks that own their
1371
+ * response lifecycle. The returned object is deliberately not thenable.
1372
+ */
1373
+ toResponseData() {
1374
+ this.#prepare();
1375
+ const statusText = this.statusText();
1376
+ return {
1377
+ body: this.body,
1378
+ status: this._status,
1379
+ ...statusText ? { statusText } : {},
1380
+ headers: { ...this.headers }
1381
+ };
1362
1382
  }
1363
1383
  /**
1364
1384
  * Dispatch the current body and apply any deferred transport state.
@@ -1368,17 +1388,15 @@ var ServerResponse = class {
1368
1388
  */
1369
1389
  send(body) {
1370
1390
  if (this.sent || this.#rawResponseSent()) return this.body;
1371
- if (typeof body !== "undefined") this.body = body;
1372
- const beforeSend = runPluginHook("beforeSend", {
1373
- response: this,
1374
- rawResponse: this.response,
1375
- body: this.body,
1376
- status: this._status,
1377
- headers: { ...this.headers }
1378
- });
1379
- this.body = beforeSend.body;
1380
- this._status = beforeSend.status;
1381
- this.headers = { ...beforeSend.headers };
1391
+ if (typeof body !== "undefined") {
1392
+ this.body = body;
1393
+ this.prepared = false;
1394
+ }
1395
+ this.#prepare();
1396
+ if (!this.response) {
1397
+ this.sent = true;
1398
+ return this.body;
1399
+ }
1382
1400
  if ("send" in this.response && typeof this.response.send === "function") {
1383
1401
  if ("statusCode" in this.response) this.response.statusCode = this._status;
1384
1402
  if ("status" in this.response && typeof this.response.status === "function") this.response.status(this._status);
@@ -1386,13 +1404,7 @@ var ServerResponse = class {
1386
1404
  this.response.__resoraStatus = this._status;
1387
1405
  this.response.send(this.body);
1388
1406
  this.sent = true;
1389
- runPluginHook("afterSend", {
1390
- response: this,
1391
- rawResponse: this.response,
1392
- body: this.body,
1393
- status: this._status,
1394
- headers: { ...this.headers }
1395
- });
1407
+ this.#runAfterSend();
1396
1408
  return this.body;
1397
1409
  }
1398
1410
  if ("status" in this.response && typeof this.response.status === "function") {
@@ -1404,6 +1416,24 @@ var ServerResponse = class {
1404
1416
  this.response.__resoraStatus = this._status;
1405
1417
  }
1406
1418
  this.sent = true;
1419
+ this.#runAfterSend();
1420
+ return this.body;
1421
+ }
1422
+ #prepare() {
1423
+ if (this.prepared) return;
1424
+ const beforeSend = runPluginHook("beforeSend", {
1425
+ response: this,
1426
+ rawResponse: this.response,
1427
+ body: this.body,
1428
+ status: this._status,
1429
+ headers: { ...this.headers }
1430
+ });
1431
+ this.body = beforeSend.body;
1432
+ this._status = beforeSend.status;
1433
+ this.headers = { ...beforeSend.headers };
1434
+ this.prepared = true;
1435
+ }
1436
+ #runAfterSend() {
1407
1437
  runPluginHook("afterSend", {
1408
1438
  response: this,
1409
1439
  rawResponse: this.response,
@@ -1411,7 +1441,6 @@ var ServerResponse = class {
1411
1441
  status: this._status,
1412
1442
  headers: { ...this.headers }
1413
1443
  });
1414
- return this.body;
1415
1444
  }
1416
1445
  #rawResponseSent() {
1417
1446
  const raw = this.response;
package/dist/index.d.cts CHANGED
@@ -399,6 +399,12 @@ declare class MakeResource extends Command<CliApp> {
399
399
  }
400
400
  //#endregion
401
401
  //#region src/ServerResponse.d.ts
402
+ interface ServerResponseData<R = any> {
403
+ body: R;
404
+ status: number;
405
+ statusText?: string;
406
+ headers: Record<string, string>;
407
+ }
402
408
  /**
403
409
  * ServerResponse class to handle HTTP response construction and sending, compatible
404
410
  * with both Express and H3 response objects.
@@ -412,9 +418,11 @@ declare class ServerResponse<R extends NonCollectible | Collectible | ResourceDa
412
418
  private body;
413
419
  private _status;
414
420
  private sent;
421
+ private prepared;
415
422
  headers: Record<string, string>;
416
423
  constructor(response: H3Event['res'], body: R);
417
424
  constructor(response: Response, body: R);
425
+ constructor(response: undefined, body: R);
418
426
  /**
419
427
  * Set the HTTP status code for the response
420
428
  *
@@ -465,6 +473,13 @@ declare class ServerResponse<R extends NonCollectible | Collectible | ResourceDa
465
473
  * @returns The current ServerResponse instance
466
474
  */
467
475
  header(key: string, value: string): this;
476
+ /**
477
+ * Return the finalized response state without dispatching it.
478
+ *
479
+ * This is the preferred integration boundary for frameworks that own their
480
+ * response lifecycle. The returned object is deliberately not thenable.
481
+ */
482
+ toResponseData(): ServerResponseData<R>;
468
483
  /**
469
484
  * Dispatch the current body and apply any deferred transport state.
470
485
  *
@@ -1593,4 +1608,4 @@ declare const extractResponseFromCtx: (ctx: unknown) => any | undefined;
1593
1608
  */
1594
1609
  declare const setCtx: (ctx: unknown) => void;
1595
1610
  //#endregion
1596
- export { ApiResource, CONDITIONAL_ATTRIBUTE_MISSING, CaseStyle, CliApp, Collectible, CollectionBody, CollectionLike, Config, Cursor, GenericBody, GenericResource, InitCommand, MakeResource, MetaData, NonCollectible, PaginatedMetaData, Pagination, PaginatorLike, ResoraConfig, ResoraPlugin, ResoraPluginApi, ResoraPluginUtility, Resource, ResourceBody, ResourceCollection, ResourceData, ResourceDef, ResourceLevelConfig, ResponseData, ResponseDataCollection, ResponseFactory, ResponseFactoryContext, ResponseKind, ResponsePluginEvent, ResponseStructureConfig, SendPluginEvent, SerializePluginEvent, ServerResponse, appendRootProperties, applyRuntimeConfig, buildPaginationExtras, buildResponseEnvelope, createArkormCurrentPageResolver, defineConfig, definePlugin, extractRequestUrl, extractResponseFromCtx, getCaseTransformer, getCtx, getDefaultConfig, getGlobalBaseUrl, getGlobalCase, getGlobalCursorMeta, getGlobalPageName, getGlobalPaginatedExtras, getGlobalPaginatedLinks, getGlobalPaginatedMeta, getGlobalResponseFactory, getGlobalResponseRootKey, getGlobalResponseStructure, getGlobalResponseWrap, getPaginationExtraKeys, getRegisteredPlugins, getRequestUrl, getUtility, hasPaginationLink, isArkormLikeCollection, isArkormLikeModel, isPlainObject, isResoraCollectionLike, loadRuntimeConfig, mergeMetadata, normalizeSerializableData, registerPlugin, registerUtility, resetPluginsForTests, resetRuntimeConfigForTests, resolveCurrentPage, resolveMergeWhen, resolveWhen, resolveWhenNotNull, resolveWithHookMetadata, runPluginHook, runWithCtx, sanitizeConditionalAttributes, setCtx, setGlobalBaseUrl, setGlobalCase, setGlobalCursorMeta, setGlobalPageName, setGlobalPaginatedExtras, setGlobalPaginatedLinks, setGlobalPaginatedMeta, setGlobalResponseFactory, setGlobalResponseRootKey, setGlobalResponseStructure, setGlobalResponseWrap, setRequestUrl, splitWords, toCamelCase, toKebabCase, toPascalCase, toSnakeCase, transformKeys };
1611
+ export { ApiResource, CONDITIONAL_ATTRIBUTE_MISSING, CaseStyle, CliApp, Collectible, CollectionBody, CollectionLike, Config, Cursor, GenericBody, GenericResource, InitCommand, MakeResource, MetaData, NonCollectible, PaginatedMetaData, Pagination, PaginatorLike, ResoraConfig, ResoraPlugin, ResoraPluginApi, ResoraPluginUtility, Resource, ResourceBody, ResourceCollection, ResourceData, ResourceDef, ResourceLevelConfig, ResponseData, ResponseDataCollection, ResponseFactory, ResponseFactoryContext, ResponseKind, ResponsePluginEvent, ResponseStructureConfig, SendPluginEvent, SerializePluginEvent, ServerResponse, ServerResponseData, appendRootProperties, applyRuntimeConfig, buildPaginationExtras, buildResponseEnvelope, createArkormCurrentPageResolver, defineConfig, definePlugin, extractRequestUrl, extractResponseFromCtx, getCaseTransformer, getCtx, getDefaultConfig, getGlobalBaseUrl, getGlobalCase, getGlobalCursorMeta, getGlobalPageName, getGlobalPaginatedExtras, getGlobalPaginatedLinks, getGlobalPaginatedMeta, getGlobalResponseFactory, getGlobalResponseRootKey, getGlobalResponseStructure, getGlobalResponseWrap, getPaginationExtraKeys, getRegisteredPlugins, getRequestUrl, getUtility, hasPaginationLink, isArkormLikeCollection, isArkormLikeModel, isPlainObject, isResoraCollectionLike, loadRuntimeConfig, mergeMetadata, normalizeSerializableData, registerPlugin, registerUtility, resetPluginsForTests, resetRuntimeConfigForTests, resolveCurrentPage, resolveMergeWhen, resolveWhen, resolveWhenNotNull, resolveWithHookMetadata, runPluginHook, runWithCtx, sanitizeConditionalAttributes, setCtx, setGlobalBaseUrl, setGlobalCase, setGlobalCursorMeta, setGlobalPageName, setGlobalPaginatedExtras, setGlobalPaginatedLinks, setGlobalPaginatedMeta, setGlobalResponseFactory, setGlobalResponseRootKey, setGlobalResponseStructure, setGlobalResponseWrap, setRequestUrl, splitWords, toCamelCase, toKebabCase, toPascalCase, toSnakeCase, transformKeys };
package/dist/index.d.mts CHANGED
@@ -399,6 +399,12 @@ declare class MakeResource extends Command<CliApp> {
399
399
  }
400
400
  //#endregion
401
401
  //#region src/ServerResponse.d.ts
402
+ interface ServerResponseData<R = any> {
403
+ body: R;
404
+ status: number;
405
+ statusText?: string;
406
+ headers: Record<string, string>;
407
+ }
402
408
  /**
403
409
  * ServerResponse class to handle HTTP response construction and sending, compatible
404
410
  * with both Express and H3 response objects.
@@ -412,9 +418,11 @@ declare class ServerResponse<R extends NonCollectible | Collectible | ResourceDa
412
418
  private body;
413
419
  private _status;
414
420
  private sent;
421
+ private prepared;
415
422
  headers: Record<string, string>;
416
423
  constructor(response: H3Event['res'], body: R);
417
424
  constructor(response: Response, body: R);
425
+ constructor(response: undefined, body: R);
418
426
  /**
419
427
  * Set the HTTP status code for the response
420
428
  *
@@ -465,6 +473,13 @@ declare class ServerResponse<R extends NonCollectible | Collectible | ResourceDa
465
473
  * @returns The current ServerResponse instance
466
474
  */
467
475
  header(key: string, value: string): this;
476
+ /**
477
+ * Return the finalized response state without dispatching it.
478
+ *
479
+ * This is the preferred integration boundary for frameworks that own their
480
+ * response lifecycle. The returned object is deliberately not thenable.
481
+ */
482
+ toResponseData(): ServerResponseData<R>;
468
483
  /**
469
484
  * Dispatch the current body and apply any deferred transport state.
470
485
  *
@@ -1593,4 +1608,4 @@ declare const extractResponseFromCtx: (ctx: unknown) => any | undefined;
1593
1608
  */
1594
1609
  declare const setCtx: (ctx: unknown) => void;
1595
1610
  //#endregion
1596
- export { ApiResource, CONDITIONAL_ATTRIBUTE_MISSING, CaseStyle, CliApp, Collectible, CollectionBody, CollectionLike, Config, Cursor, GenericBody, GenericResource, InitCommand, MakeResource, MetaData, NonCollectible, PaginatedMetaData, Pagination, PaginatorLike, ResoraConfig, ResoraPlugin, ResoraPluginApi, ResoraPluginUtility, Resource, ResourceBody, ResourceCollection, ResourceData, ResourceDef, ResourceLevelConfig, ResponseData, ResponseDataCollection, ResponseFactory, ResponseFactoryContext, ResponseKind, ResponsePluginEvent, ResponseStructureConfig, SendPluginEvent, SerializePluginEvent, ServerResponse, appendRootProperties, applyRuntimeConfig, buildPaginationExtras, buildResponseEnvelope, createArkormCurrentPageResolver, defineConfig, definePlugin, extractRequestUrl, extractResponseFromCtx, getCaseTransformer, getCtx, getDefaultConfig, getGlobalBaseUrl, getGlobalCase, getGlobalCursorMeta, getGlobalPageName, getGlobalPaginatedExtras, getGlobalPaginatedLinks, getGlobalPaginatedMeta, getGlobalResponseFactory, getGlobalResponseRootKey, getGlobalResponseStructure, getGlobalResponseWrap, getPaginationExtraKeys, getRegisteredPlugins, getRequestUrl, getUtility, hasPaginationLink, isArkormLikeCollection, isArkormLikeModel, isPlainObject, isResoraCollectionLike, loadRuntimeConfig, mergeMetadata, normalizeSerializableData, registerPlugin, registerUtility, resetPluginsForTests, resetRuntimeConfigForTests, resolveCurrentPage, resolveMergeWhen, resolveWhen, resolveWhenNotNull, resolveWithHookMetadata, runPluginHook, runWithCtx, sanitizeConditionalAttributes, setCtx, setGlobalBaseUrl, setGlobalCase, setGlobalCursorMeta, setGlobalPageName, setGlobalPaginatedExtras, setGlobalPaginatedLinks, setGlobalPaginatedMeta, setGlobalResponseFactory, setGlobalResponseRootKey, setGlobalResponseStructure, setGlobalResponseWrap, setRequestUrl, splitWords, toCamelCase, toKebabCase, toPascalCase, toSnakeCase, transformKeys };
1611
+ export { ApiResource, CONDITIONAL_ATTRIBUTE_MISSING, CaseStyle, CliApp, Collectible, CollectionBody, CollectionLike, Config, Cursor, GenericBody, GenericResource, InitCommand, MakeResource, MetaData, NonCollectible, PaginatedMetaData, Pagination, PaginatorLike, ResoraConfig, ResoraPlugin, ResoraPluginApi, ResoraPluginUtility, Resource, ResourceBody, ResourceCollection, ResourceData, ResourceDef, ResourceLevelConfig, ResponseData, ResponseDataCollection, ResponseFactory, ResponseFactoryContext, ResponseKind, ResponsePluginEvent, ResponseStructureConfig, SendPluginEvent, SerializePluginEvent, ServerResponse, ServerResponseData, appendRootProperties, applyRuntimeConfig, buildPaginationExtras, buildResponseEnvelope, createArkormCurrentPageResolver, defineConfig, definePlugin, extractRequestUrl, extractResponseFromCtx, getCaseTransformer, getCtx, getDefaultConfig, getGlobalBaseUrl, getGlobalCase, getGlobalCursorMeta, getGlobalPageName, getGlobalPaginatedExtras, getGlobalPaginatedLinks, getGlobalPaginatedMeta, getGlobalResponseFactory, getGlobalResponseRootKey, getGlobalResponseStructure, getGlobalResponseWrap, getPaginationExtraKeys, getRegisteredPlugins, getRequestUrl, getUtility, hasPaginationLink, isArkormLikeCollection, isArkormLikeModel, isPlainObject, isResoraCollectionLike, loadRuntimeConfig, mergeMetadata, normalizeSerializableData, registerPlugin, registerUtility, resetPluginsForTests, resetRuntimeConfigForTests, resolveCurrentPage, resolveMergeWhen, resolveWhen, resolveWhenNotNull, resolveWithHookMetadata, runPluginHook, runWithCtx, sanitizeConditionalAttributes, setCtx, setGlobalBaseUrl, setGlobalCase, setGlobalCursorMeta, setGlobalPageName, setGlobalPaginatedExtras, setGlobalPaginatedLinks, setGlobalPaginatedMeta, setGlobalResponseFactory, setGlobalResponseRootKey, setGlobalResponseStructure, setGlobalResponseWrap, setRequestUrl, splitWords, toCamelCase, toKebabCase, toPascalCase, toSnakeCase, transformKeys };
package/dist/index.mjs CHANGED
@@ -1243,6 +1243,7 @@ const resetPluginsForTests = () => {
1243
1243
  var ServerResponse = class {
1244
1244
  _status = 200;
1245
1245
  sent = false;
1246
+ prepared = false;
1246
1247
  headers = {};
1247
1248
  constructor(response, body) {
1248
1249
  this.response = response;
@@ -1256,6 +1257,7 @@ var ServerResponse = class {
1256
1257
  */
1257
1258
  setStatusCode(status) {
1258
1259
  this._status = status;
1260
+ this.prepared = false;
1259
1261
  return this;
1260
1262
  }
1261
1263
  /**
@@ -1266,6 +1268,7 @@ var ServerResponse = class {
1266
1268
  */
1267
1269
  setBody(body) {
1268
1270
  this.body = body;
1271
+ this.prepared = false;
1269
1272
  return this;
1270
1273
  }
1271
1274
  /**
@@ -1282,8 +1285,8 @@ var ServerResponse = class {
1282
1285
  * @returns
1283
1286
  */
1284
1287
  statusText() {
1285
- if ("statusMessage" in this.response) return this.response.statusMessage;
1286
- else if ("statusText" in this.response) return this.response.statusText;
1288
+ if (this.response && "statusMessage" in this.response) return this.response.statusMessage;
1289
+ else if (this.response && "statusText" in this.response) return this.response.statusText;
1287
1290
  }
1288
1291
  /**
1289
1292
  * Set a cookie in the response header
@@ -1326,10 +1329,27 @@ var ServerResponse = class {
1326
1329
  */
1327
1330
  #addHeader(key, value) {
1328
1331
  this.headers[key] = value;
1329
- if ("headers" in this.response && this.response.headers && typeof this.response.headers.set === "function") this.response.headers.set(key, value);
1330
- else if ("setHeader" in this.response) this.response.setHeader(key, value);
1331
- else if ("set" in this.response && typeof this.response.set === "function") this.response.set(key, value);
1332
- else if ("header" in this.response && typeof this.response.header === "function") this.response.header(key, value);
1332
+ this.prepared = false;
1333
+ if (this.response && "headers" in this.response && this.response.headers && typeof this.response.headers.set === "function") this.response.headers.set(key, value);
1334
+ else if (this.response && "setHeader" in this.response) this.response.setHeader(key, value);
1335
+ else if (this.response && "set" in this.response && typeof this.response.set === "function") this.response.set(key, value);
1336
+ else if (this.response && "header" in this.response && typeof this.response.header === "function") this.response.header(key, value);
1337
+ }
1338
+ /**
1339
+ * Return the finalized response state without dispatching it.
1340
+ *
1341
+ * This is the preferred integration boundary for frameworks that own their
1342
+ * response lifecycle. The returned object is deliberately not thenable.
1343
+ */
1344
+ toResponseData() {
1345
+ this.#prepare();
1346
+ const statusText = this.statusText();
1347
+ return {
1348
+ body: this.body,
1349
+ status: this._status,
1350
+ ...statusText ? { statusText } : {},
1351
+ headers: { ...this.headers }
1352
+ };
1333
1353
  }
1334
1354
  /**
1335
1355
  * Dispatch the current body and apply any deferred transport state.
@@ -1339,17 +1359,15 @@ var ServerResponse = class {
1339
1359
  */
1340
1360
  send(body) {
1341
1361
  if (this.sent || this.#rawResponseSent()) return this.body;
1342
- if (typeof body !== "undefined") this.body = body;
1343
- const beforeSend = runPluginHook("beforeSend", {
1344
- response: this,
1345
- rawResponse: this.response,
1346
- body: this.body,
1347
- status: this._status,
1348
- headers: { ...this.headers }
1349
- });
1350
- this.body = beforeSend.body;
1351
- this._status = beforeSend.status;
1352
- this.headers = { ...beforeSend.headers };
1362
+ if (typeof body !== "undefined") {
1363
+ this.body = body;
1364
+ this.prepared = false;
1365
+ }
1366
+ this.#prepare();
1367
+ if (!this.response) {
1368
+ this.sent = true;
1369
+ return this.body;
1370
+ }
1353
1371
  if ("send" in this.response && typeof this.response.send === "function") {
1354
1372
  if ("statusCode" in this.response) this.response.statusCode = this._status;
1355
1373
  if ("status" in this.response && typeof this.response.status === "function") this.response.status(this._status);
@@ -1357,13 +1375,7 @@ var ServerResponse = class {
1357
1375
  this.response.__resoraStatus = this._status;
1358
1376
  this.response.send(this.body);
1359
1377
  this.sent = true;
1360
- runPluginHook("afterSend", {
1361
- response: this,
1362
- rawResponse: this.response,
1363
- body: this.body,
1364
- status: this._status,
1365
- headers: { ...this.headers }
1366
- });
1378
+ this.#runAfterSend();
1367
1379
  return this.body;
1368
1380
  }
1369
1381
  if ("status" in this.response && typeof this.response.status === "function") {
@@ -1375,6 +1387,24 @@ var ServerResponse = class {
1375
1387
  this.response.__resoraStatus = this._status;
1376
1388
  }
1377
1389
  this.sent = true;
1390
+ this.#runAfterSend();
1391
+ return this.body;
1392
+ }
1393
+ #prepare() {
1394
+ if (this.prepared) return;
1395
+ const beforeSend = runPluginHook("beforeSend", {
1396
+ response: this,
1397
+ rawResponse: this.response,
1398
+ body: this.body,
1399
+ status: this._status,
1400
+ headers: { ...this.headers }
1401
+ });
1402
+ this.body = beforeSend.body;
1403
+ this._status = beforeSend.status;
1404
+ this.headers = { ...beforeSend.headers };
1405
+ this.prepared = true;
1406
+ }
1407
+ #runAfterSend() {
1378
1408
  runPluginHook("afterSend", {
1379
1409
  response: this,
1380
1410
  rawResponse: this.response,
@@ -1382,7 +1412,6 @@ var ServerResponse = class {
1382
1412
  status: this._status,
1383
1413
  headers: { ...this.headers }
1384
1414
  });
1385
- return this.body;
1386
1415
  }
1387
1416
  #rawResponseSent() {
1388
1417
  const raw = this.response;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resora",
3
- "version": "1.3.16",
3
+ "version": "1.3.18",
4
4
  "description": "A structured API response layer for Node.js and TypeScript with automatic JSON responses, collection support, and pagination handling.",
5
5
  "keywords": [
6
6
  "api",
@@ -14,13 +14,13 @@
14
14
  "json",
15
15
  "response"
16
16
  ],
17
- "homepage": "https://arkstack-tmp.github.io/resora",
17
+ "homepage": "https://arkstack-hq.github.io/resora",
18
18
  "bugs": {
19
- "url": "https://github.com/arkstack-tmp/resora/issues"
19
+ "url": "https://github.com/arkstack-hq/resora/issues"
20
20
  },
21
21
  "repository": {
22
22
  "type": "git",
23
- "url": "git+https://github.com/arkstack-tmp/resora.git"
23
+ "url": "git+https://github.com/arkstack-hq/resora.git"
24
24
  },
25
25
  "license": "MIT",
26
26
  "author": "3m1n1nce <3m1n1nce@toneflix.net>",
@@ -71,7 +71,7 @@
71
71
  "node": ">=20.0.0"
72
72
  },
73
73
  "dependencies": {
74
- "@h3ravel/musket": "^0.10.1"
74
+ "@h3ravel/musket": "^2.0.0"
75
75
  },
76
76
  "scripts": {
77
77
  "cmd": "tsx src/cli/index.ts",