arcway 0.4.7 → 0.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcway",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "A convention-based framework for building modular monoliths with strict domain boundaries.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,7 +10,7 @@ const DEFAULTS = {
10
10
 
11
11
  const DEFAULT_CORS = {
12
12
  origin: '*',
13
- methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
13
+ methods: ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
14
14
  allowedHeaders: ['Content-Type', 'Authorization'],
15
15
  exposedHeaders: [],
16
16
  credentials: false,
@@ -158,7 +158,14 @@ class S3FileDriver {
158
158
  });
159
159
  return {
160
160
  method: 'PUT',
161
- url: await getSignedUrl(this.client, command, { expiresIn }),
161
+ url: await getSignedUrl(this.client, command, {
162
+ expiresIn,
163
+ signableHeaders: new Set([
164
+ 'content-type',
165
+ ...(options.checksum ? ['x-amz-checksum-sha256'] : []),
166
+ ]),
167
+ ...(options.checksum ? { unhoistableHeaders: new Set(['x-amz-checksum-sha256']) } : {}),
168
+ }),
162
169
  headers,
163
170
  expiresAt: new Date(Date.now() + expiresIn * 1000).toISOString(),
164
171
  };
@@ -1,5 +1,5 @@
1
1
  function corsMiddleware(options) {
2
- const methods = options.methods ?? ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
2
+ const methods = options.methods ?? ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
3
3
  const allowedHeaders = options.allowedHeaders ?? ['Content-Type', 'Authorization'];
4
4
  const maxAge = options.maxAge ?? 86400;
5
5
  return (ctx) => {
@@ -377,7 +377,9 @@ class ApiRouter {
377
377
 
378
378
  // ── Serialize + send ──
379
379
  const statusCode = response.status ?? (response.error ? 400 : 200);
380
- await serializeResponse(res, response, responseHeaders, statusCode);
380
+ await serializeResponse(res, response, responseHeaders, statusCode, {
381
+ head: method === 'HEAD',
382
+ });
381
383
  this._emitCanonicalLog(ctx, {
382
384
  method,
383
385
  path: pathname,
@@ -68,10 +68,26 @@ function sendJson(res, statusCode, body, headers) {
68
68
  res.end(json);
69
69
  }
70
70
 
71
- async function serializeResponse(res, response, responseHeaders, statusCode) {
71
+ async function serializeResponse(
72
+ res,
73
+ response,
74
+ responseHeaders,
75
+ statusCode,
76
+ { head = false } = {},
77
+ ) {
72
78
  const customContentType = responseHeaders['Content-Type'] || responseHeaders['content-type'];
73
79
  if (!customContentType || customContentType.includes('application/json')) {
74
80
  const responseBody = response.error ? { error: response.error } : (response.data ?? null);
81
+ if (head) {
82
+ const json = JSON.stringify(responseBody);
83
+ res.writeHead(statusCode, {
84
+ 'Content-Type': 'application/json',
85
+ 'Content-Length': Buffer.byteLength(json),
86
+ ...responseHeaders,
87
+ });
88
+ res.end();
89
+ return;
90
+ }
75
91
  sendJson(res, statusCode, responseBody, responseHeaders);
76
92
  return;
77
93
  }
@@ -84,24 +100,39 @@ async function serializeResponse(res, response, responseHeaders, statusCode) {
84
100
 
85
101
  if (Buffer.isBuffer(body)) {
86
102
  res.writeHead(statusCode, { 'Content-Length': body.length, ...responseHeaders });
87
- res.end(body);
103
+ res.end(head ? void 0 : body);
88
104
  return;
89
105
  }
90
106
  if (body instanceof Readable) {
91
107
  res.writeHead(statusCode, responseHeaders);
108
+ if (head) {
109
+ body.destroy();
110
+ res.end();
111
+ return;
112
+ }
92
113
  await pipeline(body, res);
93
114
  return;
94
115
  }
95
116
  if (typeof body === 'object' && body !== null && typeof body.getReader === 'function') {
96
- const nodeStream = Readable.fromWeb(body);
97
117
  res.writeHead(statusCode, responseHeaders);
118
+ if (head) {
119
+ const reader = body.getReader();
120
+ try {
121
+ await reader.cancel();
122
+ } finally {
123
+ reader.releaseLock();
124
+ }
125
+ res.end();
126
+ return;
127
+ }
128
+ const nodeStream = Readable.fromWeb(body);
98
129
  await pipeline(nodeStream, res);
99
130
  return;
100
131
  }
101
132
 
102
133
  const raw = typeof body === 'string' ? body : String(body);
103
134
  res.writeHead(statusCode, { 'Content-Length': Buffer.byteLength(raw), ...responseHeaders });
104
- res.end(raw);
135
+ res.end(head ? void 0 : raw);
105
136
  }
106
137
 
107
138
  export { readBody, parseQuery, parseBody, sendJson, serializeResponse };
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import { discoverModules } from '../discovery.js';
3
3
  import { validateRequestSchema } from '../validation.js';
4
4
 
5
- const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
5
+ const HTTP_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
6
6
 
7
7
  function isMiddlewareConfig(value) {
8
8
  return (
@@ -78,7 +78,11 @@ async function discoverMiddleware(apiDir) {
78
78
  entry.methodFns = {};
79
79
  for (const method of HTTP_METHODS) {
80
80
  if (module[method] != null) {
81
- entry.methodFns[method] = parseMiddlewareItems(module[method], filePath, `${method} export`);
81
+ entry.methodFns[method] = parseMiddlewareItems(
82
+ module[method],
83
+ filePath,
84
+ `${method} export`,
85
+ );
82
86
  }
83
87
  }
84
88
  }
@@ -92,7 +96,8 @@ function getMiddlewareForRoute(allMiddleware, routePattern, method) {
92
96
  const matching = [];
93
97
  const upperMethod = method?.toUpperCase();
94
98
  for (const mw of allMiddleware) {
95
- const pathMatches = mw.pathPrefix === '/' ||
99
+ const pathMatches =
100
+ mw.pathPrefix === '/' ||
96
101
  routePattern === mw.pathPrefix ||
97
102
  routePattern.startsWith(mw.pathPrefix + '/');
98
103
  if (!pathMatches) continue;
@@ -1,7 +1,7 @@
1
1
  import { compileRoutePattern, extractRouteParams } from '#client/route-pattern.js';
2
2
  import { discoverModules } from '../discovery.js';
3
3
  import { parseRateLimit } from './ratelimit.js';
4
- const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
4
+ const HTTP_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
5
5
  function filePathToPattern(relativePath) {
6
6
  let route = relativePath.replace(/\\/g, '/');
7
7
  route = route.replace(/\.jsx?$/, '');