@holo-js/kernel 0.3.28 → 0.4.1
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/LICENSE +21 -0
- package/dist/.holo-build-fingerprint +1 -0
- package/dist/chunk-CGVSGOEO.mjs +185 -0
- package/dist/chunk-K4RCM65I.mjs +13 -0
- package/dist/chunk-QQ5YD7HZ.mjs +52 -0
- package/dist/error-reporting.d.ts +19 -0
- package/dist/error-reporting.mjs +10 -0
- package/dist/file-content.d.ts +2 -1
- package/dist/http-errors.d.ts +2 -1
- package/dist/image-decoding.browser.d.ts +4 -1
- package/dist/image-decoding.browser.mjs +7 -2
- package/dist/image-decoding.d.ts +11 -2
- package/dist/image-decoding.mjs +5 -4
- package/dist/image-decoding.node.d.ts +4 -1
- package/dist/image-decoding.node.mjs +20 -9
- package/dist/index.d.ts +68 -6
- package/dist/index.mjs +282 -67
- package/dist/managed-resources.d.ts +24 -0
- package/dist/managed-resources.mjs +155 -0
- package/dist/request-body.d.ts +26 -0
- package/dist/request-body.mjs +28 -0
- package/package.json +34 -15
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Holo-JS contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
e3c435dda503d53760a65c69eb04a2a3f39e4e925d8d1a73214a0dd32e254fca
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// src/requestBody.ts
|
|
2
|
+
var DEFAULT_MAX_REQUEST_BODY_BYTES = 1024 * 1024;
|
|
3
|
+
var DEFAULT_MAX_UPLOAD_BODY_BYTES = 8 * 1024 * 1024;
|
|
4
|
+
var maxRequestBodyBytes = DEFAULT_MAX_REQUEST_BODY_BYTES;
|
|
5
|
+
var maxUploadBodyBytes = DEFAULT_MAX_UPLOAD_BODY_BYTES;
|
|
6
|
+
function isMultipartContentType(contentType) {
|
|
7
|
+
return contentType?.split(";", 1)[0]?.trim().toLowerCase() === "multipart/form-data";
|
|
8
|
+
}
|
|
9
|
+
function requestBodyLimitFor(contentType) {
|
|
10
|
+
return isMultipartContentType(contentType) ? maxUploadBodyBytes : maxRequestBodyBytes;
|
|
11
|
+
}
|
|
12
|
+
var HoloRequestBodyTooLargeError = class extends Error {
|
|
13
|
+
status = 413;
|
|
14
|
+
constructor(limit) {
|
|
15
|
+
super(`[@holo-js/kernel] Request body exceeds the ${limit}-byte limit.`);
|
|
16
|
+
this.name = "HoloRequestBodyTooLargeError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
function resolveLimit(maxBytes) {
|
|
20
|
+
return maxBytes ?? maxRequestBodyBytes;
|
|
21
|
+
}
|
|
22
|
+
function concatChunks(chunks, total) {
|
|
23
|
+
const bytes = new Uint8Array(total);
|
|
24
|
+
let offset = 0;
|
|
25
|
+
for (const chunk of chunks) {
|
|
26
|
+
bytes.set(chunk, offset);
|
|
27
|
+
offset += chunk.byteLength;
|
|
28
|
+
}
|
|
29
|
+
return bytes;
|
|
30
|
+
}
|
|
31
|
+
async function readBoundedRequestBody(request, maxBytes) {
|
|
32
|
+
const limit = maxBytes ?? requestBodyLimitFor(request.headers.get("content-type"));
|
|
33
|
+
const declared = request.headers.get("content-length");
|
|
34
|
+
if (declared !== null && Number(declared) > limit) {
|
|
35
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
36
|
+
}
|
|
37
|
+
if (!request.body) {
|
|
38
|
+
return new Uint8Array(0);
|
|
39
|
+
}
|
|
40
|
+
const reader = request.body.getReader();
|
|
41
|
+
const chunks = [];
|
|
42
|
+
let total = 0;
|
|
43
|
+
try {
|
|
44
|
+
for (; ; ) {
|
|
45
|
+
const { done, value } = await reader.read();
|
|
46
|
+
if (done) break;
|
|
47
|
+
total += value.byteLength;
|
|
48
|
+
if (total > limit) {
|
|
49
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
50
|
+
}
|
|
51
|
+
chunks.push(value);
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
void reader.cancel(error).catch(() => void 0);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
return concatChunks(chunks, total);
|
|
58
|
+
}
|
|
59
|
+
function isWebReadableStream(value) {
|
|
60
|
+
return typeof value === "object" && value !== null && typeof value.getReader === "function";
|
|
61
|
+
}
|
|
62
|
+
function isPlainBufferedObject(value) {
|
|
63
|
+
return typeof value === "object" && value !== null && value.constructor === Object;
|
|
64
|
+
}
|
|
65
|
+
async function readWebStreamBounded(stream, limit) {
|
|
66
|
+
const reader = stream.getReader();
|
|
67
|
+
const chunks = [];
|
|
68
|
+
let total = 0;
|
|
69
|
+
try {
|
|
70
|
+
for (; ; ) {
|
|
71
|
+
const { done, value } = await reader.read();
|
|
72
|
+
if (done) break;
|
|
73
|
+
total += value.byteLength;
|
|
74
|
+
if (total > limit) {
|
|
75
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
76
|
+
}
|
|
77
|
+
chunks.push(value);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
void reader.cancel(error).catch(() => void 0);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
return concatChunks(chunks, total);
|
|
84
|
+
}
|
|
85
|
+
async function readBoundedCachedBody(cached, maxBytes) {
|
|
86
|
+
return (await readBoundedBufferedBody(cached, maxBytes))?.bytes;
|
|
87
|
+
}
|
|
88
|
+
async function readBoundedBufferedBody(cached, maxBytes) {
|
|
89
|
+
const value = await cached;
|
|
90
|
+
const limit = resolveLimit(maxBytes);
|
|
91
|
+
if (value instanceof Uint8Array) {
|
|
92
|
+
if (value.byteLength > limit) {
|
|
93
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
94
|
+
}
|
|
95
|
+
return { bytes: new Uint8Array(value) };
|
|
96
|
+
}
|
|
97
|
+
if (value instanceof ArrayBuffer) {
|
|
98
|
+
if (value.byteLength > limit) {
|
|
99
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
100
|
+
}
|
|
101
|
+
return { bytes: new Uint8Array(value.slice(0)) };
|
|
102
|
+
}
|
|
103
|
+
if (typeof value === "string" || value instanceof URLSearchParams) {
|
|
104
|
+
const bytes = new TextEncoder().encode(value.toString());
|
|
105
|
+
if (bytes.byteLength > limit) {
|
|
106
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
107
|
+
}
|
|
108
|
+
return { bytes };
|
|
109
|
+
}
|
|
110
|
+
if (typeof FormData !== "undefined" && value instanceof FormData) {
|
|
111
|
+
const encoded = new Response(value);
|
|
112
|
+
const bytes = new Uint8Array(await encoded.arrayBuffer());
|
|
113
|
+
if (bytes.byteLength > limit) {
|
|
114
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
115
|
+
}
|
|
116
|
+
return { bytes, contentType: encoded.headers.get("content-type") ?? void 0 };
|
|
117
|
+
}
|
|
118
|
+
if (isWebReadableStream(value)) {
|
|
119
|
+
return { bytes: await readWebStreamBounded(value, limit) };
|
|
120
|
+
}
|
|
121
|
+
if (typeof value === "object" && value !== null && Symbol.asyncIterator in value) {
|
|
122
|
+
return { bytes: await readBoundedNodeRequestBody(value, limit) };
|
|
123
|
+
}
|
|
124
|
+
if (isPlainBufferedObject(value)) {
|
|
125
|
+
const bytes = new TextEncoder().encode(JSON.stringify(value));
|
|
126
|
+
if (bytes.byteLength > limit) {
|
|
127
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
128
|
+
}
|
|
129
|
+
return { bytes };
|
|
130
|
+
}
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
async function readBoundedRequestText(request, maxBytes) {
|
|
134
|
+
return new TextDecoder().decode(await readBoundedRequestBody(request, maxBytes));
|
|
135
|
+
}
|
|
136
|
+
async function readBoundedRequestJson(request, maxBytes) {
|
|
137
|
+
return JSON.parse(await readBoundedRequestText(request, maxBytes));
|
|
138
|
+
}
|
|
139
|
+
async function readBoundedRequestFormData(request, maxBytes) {
|
|
140
|
+
const bytes = await readBoundedRequestBody(request, maxBytes);
|
|
141
|
+
return new Response(bytes, { headers: { "content-type": request.headers.get("content-type") ?? "" } }).formData();
|
|
142
|
+
}
|
|
143
|
+
async function readBoundedNodeRequestBody(stream, maxBytes, declaredLength) {
|
|
144
|
+
const limit = resolveLimit(maxBytes);
|
|
145
|
+
if (declaredLength != null && Number(declaredLength) > limit) {
|
|
146
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
147
|
+
}
|
|
148
|
+
const chunks = [];
|
|
149
|
+
let total = 0;
|
|
150
|
+
for await (const chunk of stream) {
|
|
151
|
+
const bytes = typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk;
|
|
152
|
+
total += bytes.byteLength;
|
|
153
|
+
if (total > limit) {
|
|
154
|
+
throw new HoloRequestBodyTooLargeError(limit);
|
|
155
|
+
}
|
|
156
|
+
chunks.push(bytes);
|
|
157
|
+
}
|
|
158
|
+
return concatChunks(chunks, total);
|
|
159
|
+
}
|
|
160
|
+
var requestBodyInternals = Object.freeze({
|
|
161
|
+
configureMaxRequestBodyBytes(bytes) {
|
|
162
|
+
maxRequestBodyBytes = bytes ?? DEFAULT_MAX_REQUEST_BODY_BYTES;
|
|
163
|
+
},
|
|
164
|
+
configureMaxUploadBodyBytes(bytes) {
|
|
165
|
+
maxUploadBodyBytes = bytes ?? DEFAULT_MAX_UPLOAD_BODY_BYTES;
|
|
166
|
+
},
|
|
167
|
+
getMaxRequestBodyBytes() {
|
|
168
|
+
return maxRequestBodyBytes;
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
export {
|
|
173
|
+
DEFAULT_MAX_REQUEST_BODY_BYTES,
|
|
174
|
+
DEFAULT_MAX_UPLOAD_BODY_BYTES,
|
|
175
|
+
requestBodyLimitFor,
|
|
176
|
+
HoloRequestBodyTooLargeError,
|
|
177
|
+
readBoundedRequestBody,
|
|
178
|
+
readBoundedCachedBody,
|
|
179
|
+
readBoundedBufferedBody,
|
|
180
|
+
readBoundedRequestText,
|
|
181
|
+
readBoundedRequestJson,
|
|
182
|
+
readBoundedRequestFormData,
|
|
183
|
+
readBoundedNodeRequestBody,
|
|
184
|
+
requestBodyInternals
|
|
185
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/imageDecoding.ts
|
|
2
|
+
var imageDecodingLimits = Object.freeze({
|
|
3
|
+
maximumDecodedPixels: 4e7,
|
|
4
|
+
maximumFrames: 1e3
|
|
5
|
+
});
|
|
6
|
+
async function verifyDecodedImage(_contents, _mimeType, _aspectRatios = []) {
|
|
7
|
+
throw new Error("[@holo-js/kernel] Server-side image validation must run on Node.js, where images can be fully decoded.");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
imageDecodingLimits,
|
|
12
|
+
verifyDecodedImage
|
|
13
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/errorReporting.ts
|
|
2
|
+
function errorReportingState() {
|
|
3
|
+
const runtime = globalThis;
|
|
4
|
+
runtime.__holoErrorReporting__ ??= {};
|
|
5
|
+
return runtime.__holoErrorReporting__;
|
|
6
|
+
}
|
|
7
|
+
function describeContext(context) {
|
|
8
|
+
return `[holo:${context.capability}] ${context.operation}`;
|
|
9
|
+
}
|
|
10
|
+
function logReporterFailure(error, context, reporterError) {
|
|
11
|
+
console.error(describeContext(context), error, reporterError);
|
|
12
|
+
}
|
|
13
|
+
function isUnreported(error, exceptions) {
|
|
14
|
+
return exceptions.dontReport?.some((errorClass) => error instanceof errorClass) ?? false;
|
|
15
|
+
}
|
|
16
|
+
function deliver(exceptions, error, context) {
|
|
17
|
+
if (exceptions && isUnreported(error, exceptions)) return;
|
|
18
|
+
if (!exceptions?.report) {
|
|
19
|
+
console.error(describeContext(context), error);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
void Promise.resolve(exceptions.report(error, context)).catch((reporterError) => logReporterFailure(error, context, reporterError));
|
|
23
|
+
}
|
|
24
|
+
function reportWith(exceptions, error, context) {
|
|
25
|
+
try {
|
|
26
|
+
deliver(exceptions, error, context);
|
|
27
|
+
} catch (reporterError) {
|
|
28
|
+
logReporterFailure(error, context, reporterError);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function configure(exceptions) {
|
|
32
|
+
const state = errorReportingState();
|
|
33
|
+
const previous = state.exceptions;
|
|
34
|
+
state.exceptions = exceptions;
|
|
35
|
+
return previous;
|
|
36
|
+
}
|
|
37
|
+
function reportError(error, context) {
|
|
38
|
+
reportWith(errorReportingState().exceptions, error, context);
|
|
39
|
+
}
|
|
40
|
+
function captureErrorReporter() {
|
|
41
|
+
const exceptions = errorReportingState().exceptions;
|
|
42
|
+
return (error, context) => reportWith(exceptions, error, context);
|
|
43
|
+
}
|
|
44
|
+
var errorReportingInternals = {
|
|
45
|
+
configure
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export {
|
|
49
|
+
reportError,
|
|
50
|
+
captureErrorReporter,
|
|
51
|
+
errorReportingInternals
|
|
52
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface HoloErrorReportContext {
|
|
2
|
+
readonly capability: string;
|
|
3
|
+
readonly operation: string;
|
|
4
|
+
}
|
|
5
|
+
type HoloUnreportedErrorClass = new (...args: never[]) => Error;
|
|
6
|
+
interface HoloExceptionsConfig {
|
|
7
|
+
report?(error: unknown, context: HoloErrorReportContext): void | Promise<void>;
|
|
8
|
+
dontReport?: readonly HoloUnreportedErrorClass[];
|
|
9
|
+
}
|
|
10
|
+
type HoloErrorReporter = (error: unknown, context: HoloErrorReportContext) => void;
|
|
11
|
+
declare function configure(exceptions: HoloExceptionsConfig | undefined): HoloExceptionsConfig | undefined;
|
|
12
|
+
declare function reportError(error: unknown, context: HoloErrorReportContext): void;
|
|
13
|
+
declare function captureErrorReporter(): HoloErrorReporter;
|
|
14
|
+
declare const errorReportingInternals: {
|
|
15
|
+
configure: typeof configure;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { captureErrorReporter, errorReportingInternals, reportError };
|
|
19
|
+
export type { HoloErrorReportContext, HoloErrorReporter, HoloExceptionsConfig, HoloUnreportedErrorClass };
|
package/dist/file-content.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ declare const fileContentSampleBytes = 65536;
|
|
|
6
6
|
declare function mimeTypeFromFileName(fileName: string): string | null;
|
|
7
7
|
declare function inspectFileContent(sample: Uint8Array, fileName?: string): Promise<FileContentInspection>;
|
|
8
8
|
|
|
9
|
-
export {
|
|
9
|
+
export { fileContentSampleBytes, inspectFileContent, mimeTypeFromFileName };
|
|
10
|
+
export type { FileContentInspection };
|
package/dist/http-errors.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ type NormalizedHoloHttpError = {
|
|
|
8
8
|
declare function isHoloHttpErrorStatus(status: number): status is HoloHttpErrorStatus;
|
|
9
9
|
declare function normalizeHoloHttpError(error: unknown): NormalizedHoloHttpError | undefined;
|
|
10
10
|
|
|
11
|
-
export {
|
|
11
|
+
export { isHoloHttpErrorStatus, normalizeHoloHttpError };
|
|
12
|
+
export type { HoloHttpErrorStatus, NormalizedHoloHttpError };
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { DecodedImageVerification } from './image-decoding.js';
|
|
2
|
+
export { imageDecodingLimits } from './image-decoding.js';
|
|
3
|
+
|
|
4
|
+
declare function verifyDecodedImage(_contents: Uint8Array, _mimeType: string, aspectRatios?: readonly (readonly number[])[]): Promise<DecodedImageVerification>;
|
|
2
5
|
|
|
3
6
|
export { verifyDecodedImage };
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
imageDecodingLimits
|
|
3
|
+
} from "./chunk-K4RCM65I.mjs";
|
|
4
|
+
|
|
1
5
|
// src/imageDecoding.browser.ts
|
|
2
|
-
async function verifyDecodedImage(_contents, _mimeType) {
|
|
3
|
-
return true;
|
|
6
|
+
async function verifyDecodedImage(_contents, _mimeType, aspectRatios = []) {
|
|
7
|
+
return { valid: true, aspectRatios: aspectRatios.map(() => true) };
|
|
4
8
|
}
|
|
5
9
|
export {
|
|
10
|
+
imageDecodingLimits,
|
|
6
11
|
verifyDecodedImage
|
|
7
12
|
};
|
package/dist/image-decoding.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
declare
|
|
1
|
+
declare const imageDecodingLimits: Readonly<{
|
|
2
|
+
maximumDecodedPixels: 40000000;
|
|
3
|
+
maximumFrames: 1000;
|
|
4
|
+
}>;
|
|
5
|
+
interface DecodedImageVerification {
|
|
6
|
+
readonly valid: boolean;
|
|
7
|
+
readonly aspectRatios: readonly boolean[];
|
|
8
|
+
}
|
|
9
|
+
declare function verifyDecodedImage(_contents: Uint8Array, _mimeType: string, _aspectRatios?: readonly (readonly number[])[]): Promise<DecodedImageVerification>;
|
|
2
10
|
|
|
3
|
-
export { verifyDecodedImage };
|
|
11
|
+
export { imageDecodingLimits, verifyDecodedImage };
|
|
12
|
+
export type { DecodedImageVerification };
|
package/dist/image-decoding.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
imageDecodingLimits,
|
|
3
|
+
verifyDecodedImage
|
|
4
|
+
} from "./chunk-K4RCM65I.mjs";
|
|
5
5
|
export {
|
|
6
|
+
imageDecodingLimits,
|
|
6
7
|
verifyDecodedImage
|
|
7
8
|
};
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { DecodedImageVerification } from './image-decoding.js';
|
|
2
|
+
export { imageDecodingLimits } from './image-decoding.js';
|
|
3
|
+
|
|
4
|
+
declare function verifyDecodedImage(contents: Uint8Array, mimeType: string, aspectRatios?: readonly (readonly number[])[]): Promise<DecodedImageVerification>;
|
|
2
5
|
|
|
3
6
|
export { verifyDecodedImage };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
imageDecodingLimits
|
|
3
|
+
} from "./chunk-K4RCM65I.mjs";
|
|
4
|
+
|
|
1
5
|
// src/imageDecoding.node.ts
|
|
2
|
-
var maximumDecodedPixels = 4e7;
|
|
3
|
-
var maximumFrames = 1e3;
|
|
4
6
|
var decodedFormatMatchers = Object.freeze({
|
|
5
7
|
"image/avif": (metadata) => metadata.format === "heif" && metadata.compression === "av1",
|
|
6
8
|
"image/gif": (metadata) => metadata.format === "gif",
|
|
@@ -50,21 +52,30 @@ function hasCompleteGifStructure(bytes) {
|
|
|
50
52
|
}
|
|
51
53
|
return false;
|
|
52
54
|
}
|
|
53
|
-
async function verifyDecodedImage(contents, mimeType) {
|
|
55
|
+
async function verifyDecodedImage(contents, mimeType, aspectRatios = []) {
|
|
56
|
+
const invalid = { valid: false, aspectRatios: [] };
|
|
54
57
|
const matchesDecodedFormat = decodedFormatMatchers[mimeType];
|
|
55
|
-
if (!matchesDecodedFormat) return
|
|
56
|
-
if (mimeType === "image/gif" && !hasCompleteGifStructure(contents)) return
|
|
58
|
+
if (!matchesDecodedFormat) return invalid;
|
|
59
|
+
if (mimeType === "image/gif" && !hasCompleteGifStructure(contents)) return invalid;
|
|
57
60
|
const decode = await loadSharp();
|
|
58
61
|
try {
|
|
59
|
-
const image = decode(contents, { animated: true, failOn: "warning", limitInputPixels: maximumDecodedPixels });
|
|
62
|
+
const image = decode(contents, { animated: true, failOn: "warning", limitInputPixels: imageDecodingLimits.maximumDecodedPixels });
|
|
60
63
|
const metadata = await image.metadata();
|
|
61
|
-
if (!matchesDecodedFormat(metadata) || (metadata.pages ?? 1) > maximumFrames) return
|
|
64
|
+
if (!matchesDecodedFormat(metadata) || (metadata.pages ?? 1) > imageDecodingLimits.maximumFrames) return invalid;
|
|
62
65
|
await image.stats();
|
|
63
|
-
|
|
66
|
+
const width = metadata.width;
|
|
67
|
+
const height = metadata.pageHeight ?? metadata.height;
|
|
68
|
+
if (!width || !height) return invalid;
|
|
69
|
+
const precision = 1 / (Math.max((width + height) / 2, height) + 1);
|
|
70
|
+
return {
|
|
71
|
+
valid: true,
|
|
72
|
+
aspectRatios: aspectRatios.map((ratios) => ratios.some((ratio) => Math.abs(ratio - width / height) <= precision))
|
|
73
|
+
};
|
|
64
74
|
} catch {
|
|
65
|
-
return
|
|
75
|
+
return invalid;
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
export {
|
|
79
|
+
imageDecodingLimits,
|
|
69
80
|
verifyDecodedImage
|
|
70
81
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { HoloHttpErrorStatus, NormalizedHoloHttpError, isHoloHttpErrorStatus, normalizeHoloHttpError } from './http-errors.js';
|
|
2
|
+
export { BufferedRequestBody, DEFAULT_MAX_REQUEST_BODY_BYTES, DEFAULT_MAX_UPLOAD_BODY_BYTES, HoloRequestBodyTooLargeError, readBoundedBufferedBody, readBoundedCachedBody, readBoundedNodeRequestBody, readBoundedRequestBody, readBoundedRequestFormData, readBoundedRequestJson, readBoundedRequestText, requestBodyInternals, requestBodyLimitFor } from './request-body.js';
|
|
2
3
|
|
|
3
4
|
interface RuntimeLifecycleContext {
|
|
4
5
|
readonly projectRoot: string;
|
|
@@ -429,17 +430,78 @@ type HoloRequestInput = RequestUrlParts & {
|
|
|
429
430
|
readonly request?: RequestUrlParts;
|
|
430
431
|
};
|
|
431
432
|
};
|
|
432
|
-
type
|
|
433
|
-
|
|
434
|
-
|
|
433
|
+
type HoloForwardedRequest = {
|
|
434
|
+
readonly address?: string;
|
|
435
|
+
readonly protocol?: string;
|
|
436
|
+
readonly host?: string;
|
|
437
|
+
readonly port?: string;
|
|
438
|
+
readonly prefix?: string;
|
|
439
|
+
};
|
|
440
|
+
type HoloForwardedRequestResolver = (readHeader: (name: string) => string | undefined, connectionAddress: string | undefined) => HoloForwardedRequest;
|
|
441
|
+
type HoloConnectionAddressProvider = () => string | undefined;
|
|
442
|
+
declare function configureForwardedRequestResolver(resolver: HoloForwardedRequestResolver | undefined): void;
|
|
443
|
+
declare function provideConnectionAddress(provider: HoloConnectionAddressProvider | undefined): void;
|
|
444
|
+
declare function currentConnectionAddress(): string | undefined;
|
|
435
445
|
declare class HoloRequestUrlError extends TypeError {
|
|
436
446
|
readonly status = 400;
|
|
437
447
|
constructor(message: string);
|
|
438
448
|
}
|
|
439
449
|
declare function resolveRequestUrl(input: HoloRequestInput): URL;
|
|
450
|
+
declare function resolveRoutedRequestUrl(input: HoloRequestInput): URL;
|
|
451
|
+
declare function resolveClientAddress(input: HoloRequestInput): string | undefined;
|
|
440
452
|
declare const requestUrlInternals: Readonly<{
|
|
441
|
-
|
|
442
|
-
|
|
453
|
+
configureForwardedRequestResolver: typeof configureForwardedRequestResolver;
|
|
454
|
+
currentConnectionAddress: typeof currentConnectionAddress;
|
|
455
|
+
provideConnectionAddress: typeof provideConnectionAddress;
|
|
456
|
+
resolveClientAddress: typeof resolveClientAddress;
|
|
457
|
+
resolveRoutedRequestUrl: typeof resolveRoutedRequestUrl;
|
|
458
|
+
}>;
|
|
459
|
+
declare function sameOriginRedirectPath(path: string | undefined): string;
|
|
460
|
+
|
|
461
|
+
declare function encryptCookieValue(name: string, value: string, appKey: string | undefined): string;
|
|
462
|
+
declare function decryptCookieValue(name: string, value: string, appKey: string | undefined): string | undefined;
|
|
463
|
+
declare const cookieEncryptionInternals: Readonly<{
|
|
464
|
+
decryptCookieValue: typeof decryptCookieValue;
|
|
465
|
+
encryptCookieValue: typeof encryptCookieValue;
|
|
443
466
|
}>;
|
|
444
467
|
|
|
445
|
-
|
|
468
|
+
declare function windowsStorageNameProblem(name: string): string | null;
|
|
469
|
+
declare function segmentProblem(segment: string): string | null;
|
|
470
|
+
declare function assertValidSegment(segment: string): void;
|
|
471
|
+
declare function splitLocalStoragePath(path: string): string[];
|
|
472
|
+
declare function isReservedLocalStorageName(name: string | undefined): boolean;
|
|
473
|
+
declare const localStoragePathInternals: {
|
|
474
|
+
assertValidSegment: typeof assertValidSegment;
|
|
475
|
+
isReservedLocalStorageName: typeof isReservedLocalStorageName;
|
|
476
|
+
segmentProblem: typeof segmentProblem;
|
|
477
|
+
splitLocalStoragePath: typeof splitLocalStoragePath;
|
|
478
|
+
windowsStorageNameProblem: typeof windowsStorageNameProblem;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
declare const COOKIE_WRITE_UNAVAILABLE = "cookie_write_unavailable";
|
|
482
|
+
declare function createCookieWriteUnavailableError(message: string, cause?: unknown): Error;
|
|
483
|
+
|
|
484
|
+
declare function resolveRetryBackoffSeconds(backoff: number | readonly number[], attempt: number): number;
|
|
485
|
+
|
|
486
|
+
type ResponseCookieOptions = {
|
|
487
|
+
path: string;
|
|
488
|
+
domain?: string;
|
|
489
|
+
maxAge?: number;
|
|
490
|
+
expires?: Date;
|
|
491
|
+
secure?: boolean;
|
|
492
|
+
httpOnly?: boolean;
|
|
493
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
494
|
+
partitioned?: boolean;
|
|
495
|
+
};
|
|
496
|
+
type ParsedResponseCookie = {
|
|
497
|
+
readonly name: string;
|
|
498
|
+
readonly value: string;
|
|
499
|
+
readonly options: ResponseCookieOptions;
|
|
500
|
+
};
|
|
501
|
+
declare function parseResponseCookie(cookie: string): ParsedResponseCookie | null;
|
|
502
|
+
declare const responseCookieInternals: {
|
|
503
|
+
parseResponseCookie: typeof parseResponseCookie;
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
export { COOKIE_WRITE_UNAVAILABLE, DEFAULT_HOLO_PROJECT_PATHS, DEFAULT_REDIS_CONNECTION, DEFAULT_REDIS_DB, DEFAULT_REDIS_HOST, DEFAULT_REDIS_PORT, HOLO_PROJECT_PREPARE_API_VERSION, HoloProjectPrepareError, HoloRequestUrlError, RuntimeLifecycle, cookieEncryptionInternals, createCookieWriteUnavailableError, createRuntimeLifecycle, defineHoloPlugin, defineHoloProject, defineHoloProjectPreparer, defineRedisConfig, holoRedisDefaults, loadHoloPluginBootModules, loadHoloPluginContributionModules, loadHoloPluginDefinitions, localStoragePathInternals, normalizeHoloPluginDefinition, normalizeHoloProjectConfig, normalizeRedisConfig, requestUrlInternals, resolveHoloPluginModulePath, resolveNormalizedRedisConnection, resolveRequestUrl, resolveRetryBackoffSeconds, responseCookieInternals, sameOriginRedirectPath };
|
|
507
|
+
export type { ConcurrencyOptions, DatabaseLogger, HoloConnectionAddressProvider, HoloDatabaseDriver, HoloForwardedRequest, HoloForwardedRequestResolver, HoloGeneratedProjectArtifact, HoloManagedProjectArtifact, HoloPluginBroadcastContributions, HoloPluginCacheContributions, HoloPluginCliContributions, HoloPluginConfigContributions, HoloPluginContributions, HoloPluginDefinition, HoloPluginDependencyContributions, HoloPluginFrameworkContribution, HoloPluginFrameworkTsconfigKind, HoloPluginMailContributions, HoloPluginMigrationContributions, HoloPluginNotificationContributions, HoloPluginPackageManager, HoloPluginProjectContributions, HoloPluginQueueContributions, HoloPluginRuntimeContributions, HoloPluginRuntimeMapContributions, HoloPluginRuntimeModule, HoloProjectArtifactContents, HoloProjectConfig, HoloProjectConnectionConfig, HoloProjectDatabaseConfig, HoloProjectPaths, HoloProjectPrepareChange, HoloProjectPrepareChangeKind, HoloProjectPrepareCommand, HoloProjectPrepareContext, HoloProjectPrepareDiagnostic, HoloProjectPrepareFailure, HoloProjectPrepareFramework, HoloProjectPrepareFullRetry, HoloProjectPrepareLogger, HoloProjectPrepareModule, HoloProjectPreparePlugin, HoloProjectPrepareResult, HoloProjectPrepareRun, HoloProjectPrepareSource, HoloProjectPrepareWatch, HoloProjectPreparedResult, HoloProjectPreparer, HoloRedisClusterNodeConfig, HoloRedisConfig, HoloRedisConnectionConfig, HoloRequestHeaders, HoloRequestInput, LoadedHoloPluginDefinition, MigrationErrorLog, MigrationStartLog, MigrationSuccessLog, NormalizedHoloProjectConfig, NormalizedHoloRedisClusterNodeConfig, NormalizedHoloRedisConfig, NormalizedHoloRedisConnectionConfig, PluginLoadOptions, QueryErrorLog, QueryStartLog, QuerySuccessLog, RuntimeContribution, RuntimeLifecycleContext, SeederErrorLog, SeederStartLog, SeederSuccessLog, TransactionLog, TransactionScopeKind };
|
package/dist/index.mjs
CHANGED
|
@@ -2,6 +2,20 @@ import {
|
|
|
2
2
|
isHoloHttpErrorStatus,
|
|
3
3
|
normalizeHoloHttpError
|
|
4
4
|
} from "./chunk-UIGMROG3.mjs";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_MAX_REQUEST_BODY_BYTES,
|
|
7
|
+
DEFAULT_MAX_UPLOAD_BODY_BYTES,
|
|
8
|
+
HoloRequestBodyTooLargeError,
|
|
9
|
+
readBoundedBufferedBody,
|
|
10
|
+
readBoundedCachedBody,
|
|
11
|
+
readBoundedNodeRequestBody,
|
|
12
|
+
readBoundedRequestBody,
|
|
13
|
+
readBoundedRequestFormData,
|
|
14
|
+
readBoundedRequestJson,
|
|
15
|
+
readBoundedRequestText,
|
|
16
|
+
requestBodyInternals,
|
|
17
|
+
requestBodyLimitFor
|
|
18
|
+
} from "./chunk-CGVSGOEO.mjs";
|
|
5
19
|
|
|
6
20
|
// src/lifecycle.ts
|
|
7
21
|
function orderContributions(contributions) {
|
|
@@ -51,7 +65,7 @@ var RuntimeLifecycle = class {
|
|
|
51
65
|
try {
|
|
52
66
|
await this.dispose(context);
|
|
53
67
|
} catch (cleanupError) {
|
|
54
|
-
throw new AggregateError([error, cleanupError], "[Holo Runtime] Initialization and rollback both failed.");
|
|
68
|
+
throw new AggregateError([error, cleanupError], "[Holo Runtime] Initialization and rollback both failed.", { cause: cleanupError });
|
|
55
69
|
}
|
|
56
70
|
throw error;
|
|
57
71
|
}
|
|
@@ -168,7 +182,7 @@ async function loadHoloPluginDefinitions(projectRoot, packageNames, options = {}
|
|
|
168
182
|
try {
|
|
169
183
|
manifest = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
170
184
|
} catch (error) {
|
|
171
|
-
if (isRecord(error) && error.code === "ENOENT") throw new Error(`Cannot find module '${packageName}/package.json'
|
|
185
|
+
if (isRecord(error) && error.code === "ENOENT") throw new Error(`Cannot find module '${packageName}/package.json'`, { cause: error });
|
|
172
186
|
throw error;
|
|
173
187
|
}
|
|
174
188
|
if (!isRecord(manifest.holo) || !normalizeString(manifest.holo.plugin)) {
|
|
@@ -325,7 +339,7 @@ function normalizeUrl(value, label) {
|
|
|
325
339
|
if (error instanceof Error && error.message.startsWith("[Holo Redis]")) {
|
|
326
340
|
throw error;
|
|
327
341
|
}
|
|
328
|
-
throw new Error(`[Holo Redis] ${label} must be a valid redis:// or rediss:// URL
|
|
342
|
+
throw new Error(`[Holo Redis] ${label} must be a valid redis:// or rediss:// URL.`, { cause: error });
|
|
329
343
|
}
|
|
330
344
|
return normalized;
|
|
331
345
|
}
|
|
@@ -428,11 +442,23 @@ function defineRedisConfig(config) {
|
|
|
428
442
|
}
|
|
429
443
|
|
|
430
444
|
// src/requestUrl.ts
|
|
431
|
-
function
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
445
|
+
function requestForwardingState() {
|
|
446
|
+
const runtime = globalThis;
|
|
447
|
+
runtime.__holoRequestForwarding__ ??= {};
|
|
448
|
+
return runtime.__holoRequestForwarding__;
|
|
449
|
+
}
|
|
450
|
+
function configureForwardedRequestResolver(resolver) {
|
|
451
|
+
requestForwardingState().resolveForwardedRequest = resolver;
|
|
452
|
+
}
|
|
453
|
+
function provideConnectionAddress(provider) {
|
|
454
|
+
requestForwardingState().connectionAddress = provider;
|
|
455
|
+
}
|
|
456
|
+
function currentConnectionAddress() {
|
|
457
|
+
try {
|
|
458
|
+
return requestForwardingState().connectionAddress?.();
|
|
459
|
+
} catch {
|
|
460
|
+
return void 0;
|
|
461
|
+
}
|
|
436
462
|
}
|
|
437
463
|
function isHeaderTuples(headers) {
|
|
438
464
|
return Array.isArray(headers);
|
|
@@ -463,57 +489,6 @@ function readHeader(headers, name) {
|
|
|
463
489
|
}
|
|
464
490
|
return void 0;
|
|
465
491
|
}
|
|
466
|
-
function firstForwardedValue(value) {
|
|
467
|
-
return value?.split(",", 1)[0]?.trim().replace(/^"|"$/g, "");
|
|
468
|
-
}
|
|
469
|
-
function firstForwardedElement(value) {
|
|
470
|
-
const pairs = /* @__PURE__ */ new Map();
|
|
471
|
-
let key = "";
|
|
472
|
-
let current = "";
|
|
473
|
-
let readingValue = false;
|
|
474
|
-
let quoted = false;
|
|
475
|
-
let escaped = false;
|
|
476
|
-
const commit = () => {
|
|
477
|
-
const name = key.trim().toLowerCase();
|
|
478
|
-
if (readingValue && name && !pairs.has(name)) pairs.set(name, current.trim());
|
|
479
|
-
key = "";
|
|
480
|
-
current = "";
|
|
481
|
-
readingValue = false;
|
|
482
|
-
};
|
|
483
|
-
for (const character of value) {
|
|
484
|
-
if (escaped) {
|
|
485
|
-
current += character;
|
|
486
|
-
escaped = false;
|
|
487
|
-
} else if (quoted) {
|
|
488
|
-
if (character === "\\") escaped = true;
|
|
489
|
-
else if (character === '"') quoted = false;
|
|
490
|
-
else current += character;
|
|
491
|
-
} else if (character === ",") {
|
|
492
|
-
break;
|
|
493
|
-
} else if (character === ";") {
|
|
494
|
-
commit();
|
|
495
|
-
} else if (character === "=" && !readingValue) {
|
|
496
|
-
readingValue = true;
|
|
497
|
-
} else if (character === '"' && readingValue) {
|
|
498
|
-
quoted = true;
|
|
499
|
-
} else if (readingValue) {
|
|
500
|
-
current += character;
|
|
501
|
-
} else {
|
|
502
|
-
key += character;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
commit();
|
|
506
|
-
return pairs;
|
|
507
|
-
}
|
|
508
|
-
function readForwardedParameter(headers, name) {
|
|
509
|
-
const forwarded = readHeader(headers, "forwarded");
|
|
510
|
-
return forwarded === void 0 ? void 0 : firstForwardedElement(forwarded).get(name);
|
|
511
|
-
}
|
|
512
|
-
function readTrustedProxyValue(headers, trustedProxyHeaders, name) {
|
|
513
|
-
if (trustedProxyHeaders === "forwarded") return readForwardedParameter(headers, name);
|
|
514
|
-
if (trustedProxyHeaders === "x-forwarded") return firstForwardedValue(readHeader(headers, `x-forwarded-${name}`));
|
|
515
|
-
return void 0;
|
|
516
|
-
}
|
|
517
492
|
var HoloRequestUrlError = class extends TypeError {
|
|
518
493
|
status = 400;
|
|
519
494
|
constructor(message) {
|
|
@@ -522,25 +497,47 @@ var HoloRequestUrlError = class extends TypeError {
|
|
|
522
497
|
}
|
|
523
498
|
};
|
|
524
499
|
var invalidRequestHostMessage = "Request hosts must be a valid hostname and optional port.";
|
|
500
|
+
var invalidRequestPortMessage = "Request ports must be a number from 0 to 65535.";
|
|
501
|
+
var invalidRequestPrefixMessage = 'Request prefixes must be a path starting with a single "/".';
|
|
502
|
+
function validForwardedPort(port) {
|
|
503
|
+
if (!/^\d{1,5}$/u.test(port) || Number(port) > 65535) throw new HoloRequestUrlError(invalidRequestPortMessage);
|
|
504
|
+
return port;
|
|
505
|
+
}
|
|
506
|
+
function validForwardedPrefix(prefix) {
|
|
507
|
+
const trimmed = prefix.replace(/\/+$/u, "");
|
|
508
|
+
if (trimmed && (!trimmed.startsWith("/") || trimmed.startsWith("//") || /[\s?#\\]/u.test(trimmed))) {
|
|
509
|
+
throw new HoloRequestUrlError(invalidRequestPrefixMessage);
|
|
510
|
+
}
|
|
511
|
+
return trimmed;
|
|
512
|
+
}
|
|
525
513
|
function parseRequestUrl(value, invalidMessage) {
|
|
526
514
|
if (!URL.canParse(String(value), "http://localhost")) throw new HoloRequestUrlError(invalidMessage);
|
|
527
515
|
return new URL(value, "http://localhost");
|
|
528
516
|
}
|
|
529
|
-
function
|
|
530
|
-
|
|
531
|
-
|
|
517
|
+
function nestedRequest(input) {
|
|
518
|
+
return input instanceof Request ? void 0 : input.request ?? input.web?.request ?? input.req ?? input.node?.req;
|
|
519
|
+
}
|
|
520
|
+
function requestHeaders(input) {
|
|
521
|
+
return input.headers ?? nestedRequest(input)?.headers;
|
|
522
|
+
}
|
|
523
|
+
function resolveForwardedRequest(headers) {
|
|
524
|
+
return requestForwardingState().resolveForwardedRequest?.((name) => readHeader(headers, name), currentConnectionAddress());
|
|
525
|
+
}
|
|
526
|
+
function resolveUrl(input, applyPrefix) {
|
|
527
|
+
const nested = nestedRequest(input);
|
|
528
|
+
const headers = requestHeaders(input);
|
|
532
529
|
const value = input.url ?? nested?.url ?? input.path ?? (nested && "path" in nested ? nested.path : void 0) ?? "/";
|
|
533
530
|
const invalidUrlMessage = "Request URLs must use http or https without credentials.";
|
|
534
531
|
const url = parseRequestUrl(value, invalidUrlMessage);
|
|
535
532
|
if (url.username || url.password || url.protocol !== "http:" && url.protocol !== "https:") {
|
|
536
533
|
throw new HoloRequestUrlError(invalidUrlMessage);
|
|
537
534
|
}
|
|
538
|
-
const
|
|
539
|
-
const protocol = (
|
|
535
|
+
const forwarded = resolveForwardedRequest(headers);
|
|
536
|
+
const protocol = (forwarded?.protocol ?? url.protocol.slice(0, -1)).toLowerCase();
|
|
540
537
|
if (protocol !== "http" && protocol !== "https") {
|
|
541
538
|
throw new HoloRequestUrlError("Request protocols must be http or https.");
|
|
542
539
|
}
|
|
543
|
-
const host =
|
|
540
|
+
const host = forwarded?.host ?? readHeader(headers, "host") ?? url.host;
|
|
544
541
|
if (!host || /[\s,/@\\?#]/u.test(host)) {
|
|
545
542
|
throw new HoloRequestUrlError(invalidRequestHostMessage);
|
|
546
543
|
}
|
|
@@ -548,22 +545,227 @@ function resolveRequestUrl(input) {
|
|
|
548
545
|
if (origin.pathname !== "/" || origin.username || origin.password || origin.search || origin.hash) {
|
|
549
546
|
throw new HoloRequestUrlError(invalidRequestHostMessage);
|
|
550
547
|
}
|
|
548
|
+
if (forwarded?.port !== void 0) origin.port = validForwardedPort(forwarded.port);
|
|
551
549
|
url.protocol = origin.protocol;
|
|
552
550
|
url.host = origin.host;
|
|
553
551
|
url.port = origin.port;
|
|
552
|
+
const prefix = forwarded?.prefix === void 0 ? "" : validForwardedPrefix(forwarded.prefix);
|
|
553
|
+
if (applyPrefix) url.pathname = `${prefix}${url.pathname}`;
|
|
554
554
|
return url;
|
|
555
555
|
}
|
|
556
|
-
|
|
556
|
+
function resolveRequestUrl(input) {
|
|
557
|
+
return resolveUrl(input, true);
|
|
558
|
+
}
|
|
559
|
+
function resolveRoutedRequestUrl(input) {
|
|
560
|
+
return resolveUrl(input, false);
|
|
561
|
+
}
|
|
562
|
+
function resolveClientAddress(input) {
|
|
563
|
+
if (!requestForwardingState().resolveForwardedRequest) return currentConnectionAddress();
|
|
564
|
+
return resolveForwardedRequest(requestHeaders(input))?.address;
|
|
565
|
+
}
|
|
566
|
+
var requestUrlInternals = Object.freeze({
|
|
567
|
+
configureForwardedRequestResolver,
|
|
568
|
+
currentConnectionAddress,
|
|
569
|
+
provideConnectionAddress,
|
|
570
|
+
resolveClientAddress,
|
|
571
|
+
resolveRoutedRequestUrl
|
|
572
|
+
});
|
|
573
|
+
function hasControlCharacter(value) {
|
|
574
|
+
for (const character of value) {
|
|
575
|
+
const code = character.codePointAt(0) ?? 0;
|
|
576
|
+
if (code < 32 || code === 127) return true;
|
|
577
|
+
}
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
function sameOriginRedirectPath(path) {
|
|
581
|
+
if (!path || !path.startsWith("/") || path.startsWith("//") || path.includes("\\") || hasControlCharacter(path)) {
|
|
582
|
+
return "/";
|
|
583
|
+
}
|
|
584
|
+
return path;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// src/cookieEncryption.ts
|
|
588
|
+
import { createCipheriv, createDecipheriv, createHmac, randomBytes } from "crypto";
|
|
589
|
+
var IV_BYTES = 12;
|
|
590
|
+
var TAG_BYTES = 16;
|
|
591
|
+
function cookieEncryptionKey(appKey) {
|
|
592
|
+
const key = appKey?.trim();
|
|
593
|
+
if (!key) {
|
|
594
|
+
throw new Error('[@holo-js/kernel] Holo cookies are encrypted with the application key. Set APP_KEY or run "holo key:generate".');
|
|
595
|
+
}
|
|
596
|
+
return createHmac("sha256", key).update("holo-js:cookie-encryption").digest();
|
|
597
|
+
}
|
|
598
|
+
function encryptCookieValue(name, value, appKey) {
|
|
599
|
+
const iv = randomBytes(IV_BYTES);
|
|
600
|
+
const cipher = createCipheriv("aes-256-gcm", cookieEncryptionKey(appKey), iv);
|
|
601
|
+
cipher.setAAD(Buffer.from(name, "utf8"));
|
|
602
|
+
const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
603
|
+
return Buffer.concat([iv, cipher.getAuthTag(), encrypted]).toString("base64url");
|
|
604
|
+
}
|
|
605
|
+
function decryptCookieValue(name, value, appKey) {
|
|
606
|
+
const key = cookieEncryptionKey(appKey);
|
|
607
|
+
const payload = Buffer.from(value, "base64url");
|
|
608
|
+
if (payload.toString("base64url") !== value) return void 0;
|
|
609
|
+
if (payload.length < IV_BYTES + TAG_BYTES) return void 0;
|
|
610
|
+
try {
|
|
611
|
+
const decipher = createDecipheriv("aes-256-gcm", key, payload.subarray(0, IV_BYTES));
|
|
612
|
+
decipher.setAAD(Buffer.from(name, "utf8"));
|
|
613
|
+
decipher.setAuthTag(payload.subarray(IV_BYTES, IV_BYTES + TAG_BYTES));
|
|
614
|
+
return Buffer.concat([decipher.update(payload.subarray(IV_BYTES + TAG_BYTES)), decipher.final()]).toString("utf8");
|
|
615
|
+
} catch {
|
|
616
|
+
return void 0;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
var cookieEncryptionInternals = Object.freeze({ decryptCookieValue, encryptCookieValue });
|
|
620
|
+
|
|
621
|
+
// src/localStoragePath.ts
|
|
622
|
+
var WINDOWS_DEVICE_NAMES = /* @__PURE__ */ new Set([
|
|
623
|
+
"CON",
|
|
624
|
+
"PRN",
|
|
625
|
+
"AUX",
|
|
626
|
+
"NUL",
|
|
627
|
+
...["COM", "LPT"].flatMap((device) => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "\xB9", "\xB2", "\xB3"].map((digit) => `${device}${digit}`))
|
|
628
|
+
]);
|
|
629
|
+
function windowsStorageNameProblem(name) {
|
|
630
|
+
const hasControlCharacter2 = Array.from(name).some((character) => character.charCodeAt(0) < 32);
|
|
631
|
+
if (hasControlCharacter2 || /[<>:"|?*]/u.test(name)) {
|
|
632
|
+
return 'Windows file names cannot contain < > : " | ? * or control characters';
|
|
633
|
+
}
|
|
634
|
+
if (/[. ]$/u.test(name)) {
|
|
635
|
+
return "Windows file names cannot end with a dot or a space";
|
|
636
|
+
}
|
|
637
|
+
const baseName = (name.split(".", 1)[0] ?? "").replace(/ +$/u, "").toUpperCase();
|
|
638
|
+
if (WINDOWS_DEVICE_NAMES.has(baseName)) {
|
|
639
|
+
return "Windows reserves this device name";
|
|
640
|
+
}
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
function segmentProblem(segment) {
|
|
644
|
+
if (segment === "..") {
|
|
645
|
+
return 'Storage paths must not contain ".." segments';
|
|
646
|
+
}
|
|
647
|
+
if (segment === "" || segment === "." || /[\\/]/u.test(segment) || segment.includes("\0")) {
|
|
648
|
+
return "Storage path segments must be file or folder names";
|
|
649
|
+
}
|
|
650
|
+
return process.platform === "win32" ? windowsStorageNameProblem(segment) : null;
|
|
651
|
+
}
|
|
652
|
+
function assertValidSegment(segment) {
|
|
653
|
+
const problem = segmentProblem(segment);
|
|
654
|
+
if (problem) throw new Error(`[Holo Storage] ${problem}.`);
|
|
655
|
+
}
|
|
656
|
+
function splitLocalStoragePath(path) {
|
|
657
|
+
return path.trimStart().replace(/\\/g, "/").split("/").filter(Boolean);
|
|
658
|
+
}
|
|
659
|
+
function isReservedLocalStorageName(name) {
|
|
660
|
+
return name?.endsWith("$") === true;
|
|
661
|
+
}
|
|
662
|
+
var localStoragePathInternals = {
|
|
663
|
+
assertValidSegment,
|
|
664
|
+
isReservedLocalStorageName,
|
|
665
|
+
segmentProblem,
|
|
666
|
+
splitLocalStoragePath,
|
|
667
|
+
windowsStorageNameProblem
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
// src/cookieWriteUnavailable.ts
|
|
671
|
+
var COOKIE_WRITE_UNAVAILABLE = "cookie_write_unavailable";
|
|
672
|
+
function createCookieWriteUnavailableError(message, cause) {
|
|
673
|
+
return Object.assign(new Error(message, { cause }), {
|
|
674
|
+
code: COOKIE_WRITE_UNAVAILABLE
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// src/retryBackoff.ts
|
|
679
|
+
function resolveRetryBackoffSeconds(backoff, attempt) {
|
|
680
|
+
if (typeof backoff === "number") {
|
|
681
|
+
return backoff;
|
|
682
|
+
}
|
|
683
|
+
return backoff[Math.min(Math.max(attempt - 1, 0), backoff.length - 1)] ?? 0;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// src/responseCookie.ts
|
|
687
|
+
function safeDecodeCookieSegment(value) {
|
|
688
|
+
try {
|
|
689
|
+
return decodeURIComponent(value);
|
|
690
|
+
} catch {
|
|
691
|
+
return value;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
function parseResponseCookie(cookie) {
|
|
695
|
+
const [nameValue = "", ...attributes] = cookie.split(";");
|
|
696
|
+
const separator = nameValue.indexOf("=");
|
|
697
|
+
if (!nameValue || separator <= 0) {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
const options = { path: "/", secure: false, httpOnly: false };
|
|
701
|
+
for (const rawAttribute of attributes) {
|
|
702
|
+
const attribute = rawAttribute.trim();
|
|
703
|
+
if (!attribute) {
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
const attributeSeparator = attribute.indexOf("=");
|
|
707
|
+
const key = (attributeSeparator === -1 ? attribute : attribute.slice(0, attributeSeparator)).trim().toLowerCase();
|
|
708
|
+
const value = attributeSeparator === -1 ? "" : attribute.slice(attributeSeparator + 1).trim();
|
|
709
|
+
switch (key) {
|
|
710
|
+
case "path":
|
|
711
|
+
options.path = value || "/";
|
|
712
|
+
break;
|
|
713
|
+
case "domain":
|
|
714
|
+
options.domain = value;
|
|
715
|
+
break;
|
|
716
|
+
case "max-age": {
|
|
717
|
+
const maxAge = Number(value);
|
|
718
|
+
if (Number.isFinite(maxAge)) {
|
|
719
|
+
options.maxAge = maxAge;
|
|
720
|
+
}
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
case "expires": {
|
|
724
|
+
const expires = new Date(value);
|
|
725
|
+
if (!Number.isNaN(expires.getTime())) {
|
|
726
|
+
options.expires = expires;
|
|
727
|
+
}
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
case "secure":
|
|
731
|
+
options.secure = true;
|
|
732
|
+
break;
|
|
733
|
+
case "httponly":
|
|
734
|
+
options.httpOnly = true;
|
|
735
|
+
break;
|
|
736
|
+
case "samesite":
|
|
737
|
+
if (value.toLowerCase() === "lax" || value.toLowerCase() === "strict" || value.toLowerCase() === "none") {
|
|
738
|
+
options.sameSite = value.toLowerCase();
|
|
739
|
+
}
|
|
740
|
+
break;
|
|
741
|
+
case "partitioned":
|
|
742
|
+
options.partitioned = value ? value.toLowerCase() === "true" : true;
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
name: safeDecodeCookieSegment(nameValue.slice(0, separator)),
|
|
748
|
+
value: safeDecodeCookieSegment(nameValue.slice(separator + 1)),
|
|
749
|
+
options
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
var responseCookieInternals = { parseResponseCookie };
|
|
557
753
|
export {
|
|
754
|
+
COOKIE_WRITE_UNAVAILABLE,
|
|
558
755
|
DEFAULT_HOLO_PROJECT_PATHS,
|
|
756
|
+
DEFAULT_MAX_REQUEST_BODY_BYTES,
|
|
757
|
+
DEFAULT_MAX_UPLOAD_BODY_BYTES,
|
|
559
758
|
DEFAULT_REDIS_CONNECTION,
|
|
560
759
|
DEFAULT_REDIS_DB,
|
|
561
760
|
DEFAULT_REDIS_HOST,
|
|
562
761
|
DEFAULT_REDIS_PORT,
|
|
563
762
|
HOLO_PROJECT_PREPARE_API_VERSION,
|
|
564
763
|
HoloProjectPrepareError,
|
|
764
|
+
HoloRequestBodyTooLargeError,
|
|
565
765
|
HoloRequestUrlError,
|
|
566
766
|
RuntimeLifecycle,
|
|
767
|
+
cookieEncryptionInternals,
|
|
768
|
+
createCookieWriteUnavailableError,
|
|
567
769
|
createRuntimeLifecycle,
|
|
568
770
|
defineHoloPlugin,
|
|
569
771
|
defineHoloProject,
|
|
@@ -574,12 +776,25 @@ export {
|
|
|
574
776
|
loadHoloPluginBootModules,
|
|
575
777
|
loadHoloPluginContributionModules,
|
|
576
778
|
loadHoloPluginDefinitions,
|
|
779
|
+
localStoragePathInternals,
|
|
577
780
|
normalizeHoloHttpError,
|
|
578
781
|
normalizeHoloPluginDefinition,
|
|
579
782
|
normalizeHoloProjectConfig,
|
|
580
783
|
normalizeRedisConfig,
|
|
784
|
+
readBoundedBufferedBody,
|
|
785
|
+
readBoundedCachedBody,
|
|
786
|
+
readBoundedNodeRequestBody,
|
|
787
|
+
readBoundedRequestBody,
|
|
788
|
+
readBoundedRequestFormData,
|
|
789
|
+
readBoundedRequestJson,
|
|
790
|
+
readBoundedRequestText,
|
|
791
|
+
requestBodyInternals,
|
|
792
|
+
requestBodyLimitFor,
|
|
581
793
|
requestUrlInternals,
|
|
582
794
|
resolveHoloPluginModulePath,
|
|
583
795
|
resolveNormalizedRedisConnection,
|
|
584
|
-
resolveRequestUrl
|
|
796
|
+
resolveRequestUrl,
|
|
797
|
+
resolveRetryBackoffSeconds,
|
|
798
|
+
responseCookieInternals,
|
|
799
|
+
sameOriginRedirectPath
|
|
585
800
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
declare function throwCapabilityFailures(failures: readonly unknown[]): void;
|
|
2
|
+
interface ReleasableResource {
|
|
3
|
+
close?(): void | Promise<void>;
|
|
4
|
+
disconnect?(): void | Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
interface HeldResource {
|
|
7
|
+
hold<TResult>(operation: () => TResult): TResult;
|
|
8
|
+
}
|
|
9
|
+
interface ResourceLease<TValue> extends HeldResource {
|
|
10
|
+
readonly value: TValue;
|
|
11
|
+
}
|
|
12
|
+
interface ManagedResources<TConfig, TValue extends ReleasableResource> {
|
|
13
|
+
acquire(config: TConfig, create: (config: TConfig) => TValue): Promise<ResourceLease<TValue>>;
|
|
14
|
+
commit(): Promise<void>;
|
|
15
|
+
discard(): Promise<void>;
|
|
16
|
+
dispose(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
declare function createManagedResources<TConfig, TValue extends ReleasableResource>(options?: {
|
|
19
|
+
readonly connect?: (value: TValue) => void | Promise<void>;
|
|
20
|
+
}): ManagedResources<TConfig, TValue>;
|
|
21
|
+
declare function holdOperations<TTarget extends object>(target: TTarget, lease: HeldResource): TTarget;
|
|
22
|
+
|
|
23
|
+
export { createManagedResources, holdOperations, throwCapabilityFailures };
|
|
24
|
+
export type { HeldResource, ManagedResources, ReleasableResource, ResourceLease };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
reportError
|
|
3
|
+
} from "./chunk-QQ5YD7HZ.mjs";
|
|
4
|
+
|
|
5
|
+
// src/managedResources.ts
|
|
6
|
+
import { isDeepStrictEqual } from "util";
|
|
7
|
+
function throwCapabilityFailures(failures) {
|
|
8
|
+
if (failures.length === 1) throw failures[0];
|
|
9
|
+
if (failures.length > 1) throw new AggregateError(failures, "Optional capability cleanup failed.");
|
|
10
|
+
}
|
|
11
|
+
async function releaseManagedResources(adapters) {
|
|
12
|
+
const failures = [];
|
|
13
|
+
for (const adapter of [...new Set(adapters)].reverse()) {
|
|
14
|
+
try {
|
|
15
|
+
if (adapter.close) await adapter.close();
|
|
16
|
+
else await adapter.disconnect?.();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
failures.push(error);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
throwCapabilityFailures(failures);
|
|
22
|
+
}
|
|
23
|
+
function createManagedResources(options = {}) {
|
|
24
|
+
let active = [];
|
|
25
|
+
let retained = [];
|
|
26
|
+
let claimed = [];
|
|
27
|
+
let opened = [];
|
|
28
|
+
let disposing = false;
|
|
29
|
+
let disposalFailures = [];
|
|
30
|
+
const releasing = /* @__PURE__ */ new Set();
|
|
31
|
+
const findSameConfig = (entries, config) => entries.find((entry) => isDeepStrictEqual(entry.config, config));
|
|
32
|
+
const track = (promise) => {
|
|
33
|
+
releasing.add(promise);
|
|
34
|
+
promise.then(() => releasing.delete(promise));
|
|
35
|
+
};
|
|
36
|
+
const reportReplacedResourceFailure = (error) => reportError(error, {
|
|
37
|
+
capability: "runtime",
|
|
38
|
+
operation: "releaseReplacedResource"
|
|
39
|
+
});
|
|
40
|
+
const startClose = (entry, onFailure) => {
|
|
41
|
+
entry.state = "closing";
|
|
42
|
+
retained = retained.filter((candidate) => candidate !== entry);
|
|
43
|
+
return releaseManagedResources([entry.value]).catch(onFailure).finally(() => entry.onDrained?.());
|
|
44
|
+
};
|
|
45
|
+
const retireEntry = (entry, onFailure) => {
|
|
46
|
+
if (entry.pending === 0) return startClose(entry, onFailure);
|
|
47
|
+
entry.state = "draining";
|
|
48
|
+
entry.onFailure = onFailure;
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
entry.onDrained = resolve;
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const finishOperation = (entry) => {
|
|
54
|
+
entry.pending -= 1;
|
|
55
|
+
if (entry.pending !== 0 || entry.state === "closing") return;
|
|
56
|
+
const awaitingClose = entry.state === "draining" || retained.includes(entry) && !claimed.includes(entry);
|
|
57
|
+
if (awaitingClose) track(startClose(entry, entry.onFailure ?? reportReplacedResourceFailure));
|
|
58
|
+
};
|
|
59
|
+
const createEntry = (config, value) => {
|
|
60
|
+
const entry = {
|
|
61
|
+
config,
|
|
62
|
+
value,
|
|
63
|
+
pending: 0,
|
|
64
|
+
state: "open",
|
|
65
|
+
lease: {
|
|
66
|
+
value,
|
|
67
|
+
hold(operation) {
|
|
68
|
+
if (entry.state === "closing") throw new Error("[@holo-js/kernel] The managed resource is closed.");
|
|
69
|
+
entry.pending += 1;
|
|
70
|
+
let result;
|
|
71
|
+
try {
|
|
72
|
+
result = operation();
|
|
73
|
+
} catch (error) {
|
|
74
|
+
finishOperation(entry);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
if (result instanceof Promise) {
|
|
78
|
+
result.finally(() => finishOperation(entry)).catch(() => void 0);
|
|
79
|
+
} else {
|
|
80
|
+
finishOperation(entry);
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
return entry;
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
async acquire(config, create) {
|
|
90
|
+
if (disposing) throw new Error("[@holo-js/kernel] Managed resources are disposed; acquire a new registry instead.");
|
|
91
|
+
const reusable = findSameConfig(claimed, config) ?? findSameConfig(active, config) ?? findSameConfig(retained, config);
|
|
92
|
+
if (reusable) {
|
|
93
|
+
if (!claimed.includes(reusable)) claimed.push(reusable);
|
|
94
|
+
return reusable.lease;
|
|
95
|
+
}
|
|
96
|
+
const entry = createEntry(config, create(config));
|
|
97
|
+
opened.push(entry);
|
|
98
|
+
await options.connect?.(entry.value);
|
|
99
|
+
claimed.push(entry);
|
|
100
|
+
return entry.lease;
|
|
101
|
+
},
|
|
102
|
+
async commit() {
|
|
103
|
+
const previouslyOpen = [...active, ...retained];
|
|
104
|
+
active = claimed;
|
|
105
|
+
claimed = [];
|
|
106
|
+
opened = [];
|
|
107
|
+
retained = previouslyOpen.filter((entry) => !active.includes(entry));
|
|
108
|
+
const idle = retained.filter((entry) => entry.pending === 0);
|
|
109
|
+
retained = retained.filter((entry) => entry.pending > 0);
|
|
110
|
+
await Promise.all(idle.map((entry) => retireEntry(entry, reportReplacedResourceFailure)));
|
|
111
|
+
},
|
|
112
|
+
async discard() {
|
|
113
|
+
const revivedIdle = claimed.filter((entry) => retained.includes(entry) && entry.pending === 0);
|
|
114
|
+
retained = retained.filter((entry) => !revivedIdle.includes(entry));
|
|
115
|
+
const entries = [...opened, ...revivedIdle];
|
|
116
|
+
claimed = [];
|
|
117
|
+
opened = [];
|
|
118
|
+
const failures = [];
|
|
119
|
+
await Promise.all(entries.map((entry) => retireEntry(entry, (error) => {
|
|
120
|
+
failures.push(error);
|
|
121
|
+
})));
|
|
122
|
+
throwCapabilityFailures(failures);
|
|
123
|
+
},
|
|
124
|
+
async dispose() {
|
|
125
|
+
disposing = true;
|
|
126
|
+
disposalFailures = [];
|
|
127
|
+
const entries = /* @__PURE__ */ new Set([...active, ...retained, ...claimed, ...opened]);
|
|
128
|
+
active = [];
|
|
129
|
+
retained = [];
|
|
130
|
+
claimed = [];
|
|
131
|
+
opened = [];
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
track(retireEntry(entry, (error) => {
|
|
134
|
+
disposalFailures.push(error);
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
while (releasing.size > 0) await Promise.all([...releasing]);
|
|
138
|
+
throwCapabilityFailures(disposalFailures);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function holdOperations(target, lease) {
|
|
143
|
+
return new Proxy(target, {
|
|
144
|
+
get(source, property) {
|
|
145
|
+
const value = Reflect.get(source, property, source);
|
|
146
|
+
if (typeof value !== "function") return value;
|
|
147
|
+
return (...args) => lease.hold(() => Reflect.apply(value, source, args));
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
createManagedResources,
|
|
153
|
+
holdOperations,
|
|
154
|
+
throwCapabilityFailures
|
|
155
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare const DEFAULT_MAX_REQUEST_BODY_BYTES: number;
|
|
2
|
+
declare const DEFAULT_MAX_UPLOAD_BODY_BYTES: number;
|
|
3
|
+
declare function requestBodyLimitFor(contentType: string | null | undefined): number;
|
|
4
|
+
declare class HoloRequestBodyTooLargeError extends Error {
|
|
5
|
+
readonly status = 413;
|
|
6
|
+
constructor(limit: number);
|
|
7
|
+
}
|
|
8
|
+
declare function readBoundedRequestBody(request: Request, maxBytes?: number): Promise<Uint8Array<ArrayBuffer>>;
|
|
9
|
+
type BufferedRequestBody = {
|
|
10
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
11
|
+
readonly contentType?: string;
|
|
12
|
+
};
|
|
13
|
+
declare function readBoundedCachedBody(cached: unknown, maxBytes?: number): Promise<Uint8Array<ArrayBuffer> | undefined>;
|
|
14
|
+
declare function readBoundedBufferedBody(cached: unknown, maxBytes?: number): Promise<BufferedRequestBody | undefined>;
|
|
15
|
+
declare function readBoundedRequestText(request: Request, maxBytes?: number): Promise<string>;
|
|
16
|
+
declare function readBoundedRequestJson(request: Request, maxBytes?: number): Promise<unknown>;
|
|
17
|
+
declare function readBoundedRequestFormData(request: Request, maxBytes?: number): Promise<FormData>;
|
|
18
|
+
declare function readBoundedNodeRequestBody(stream: AsyncIterable<Uint8Array | string>, maxBytes?: number, declaredLength?: string | null): Promise<Uint8Array<ArrayBuffer>>;
|
|
19
|
+
declare const requestBodyInternals: Readonly<{
|
|
20
|
+
configureMaxRequestBodyBytes(bytes: number | undefined): void;
|
|
21
|
+
configureMaxUploadBodyBytes(bytes: number | undefined): void;
|
|
22
|
+
getMaxRequestBodyBytes(): number;
|
|
23
|
+
}>;
|
|
24
|
+
|
|
25
|
+
export { DEFAULT_MAX_REQUEST_BODY_BYTES, DEFAULT_MAX_UPLOAD_BODY_BYTES, HoloRequestBodyTooLargeError, readBoundedBufferedBody, readBoundedCachedBody, readBoundedNodeRequestBody, readBoundedRequestBody, readBoundedRequestFormData, readBoundedRequestJson, readBoundedRequestText, requestBodyInternals, requestBodyLimitFor };
|
|
26
|
+
export type { BufferedRequestBody };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_MAX_REQUEST_BODY_BYTES,
|
|
3
|
+
DEFAULT_MAX_UPLOAD_BODY_BYTES,
|
|
4
|
+
HoloRequestBodyTooLargeError,
|
|
5
|
+
readBoundedBufferedBody,
|
|
6
|
+
readBoundedCachedBody,
|
|
7
|
+
readBoundedNodeRequestBody,
|
|
8
|
+
readBoundedRequestBody,
|
|
9
|
+
readBoundedRequestFormData,
|
|
10
|
+
readBoundedRequestJson,
|
|
11
|
+
readBoundedRequestText,
|
|
12
|
+
requestBodyInternals,
|
|
13
|
+
requestBodyLimitFor
|
|
14
|
+
} from "./chunk-CGVSGOEO.mjs";
|
|
15
|
+
export {
|
|
16
|
+
DEFAULT_MAX_REQUEST_BODY_BYTES,
|
|
17
|
+
DEFAULT_MAX_UPLOAD_BODY_BYTES,
|
|
18
|
+
HoloRequestBodyTooLargeError,
|
|
19
|
+
readBoundedBufferedBody,
|
|
20
|
+
readBoundedCachedBody,
|
|
21
|
+
readBoundedNodeRequestBody,
|
|
22
|
+
readBoundedRequestBody,
|
|
23
|
+
readBoundedRequestFormData,
|
|
24
|
+
readBoundedRequestJson,
|
|
25
|
+
readBoundedRequestText,
|
|
26
|
+
requestBodyInternals,
|
|
27
|
+
requestBodyLimitFor
|
|
28
|
+
};
|
package/package.json
CHANGED
|
@@ -1,25 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/kernel",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Holo-JS Framework -
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Holo-JS Framework - contracts, plugins, and runtime lifecycle",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/cobraprojects/holo-js.git",
|
|
10
|
+
"directory": "packages/kernel"
|
|
11
|
+
},
|
|
7
12
|
"exports": {
|
|
8
13
|
".": {
|
|
9
14
|
"types": "./dist/index.d.ts",
|
|
10
15
|
"import": "./dist/index.mjs",
|
|
11
16
|
"default": "./dist/index.mjs"
|
|
12
17
|
},
|
|
18
|
+
"./error-reporting": {
|
|
19
|
+
"types": "./dist/error-reporting.d.ts",
|
|
20
|
+
"import": "./dist/error-reporting.mjs",
|
|
21
|
+
"default": "./dist/error-reporting.mjs"
|
|
22
|
+
},
|
|
13
23
|
"./http-errors": {
|
|
14
24
|
"types": "./dist/http-errors.d.ts",
|
|
15
25
|
"import": "./dist/http-errors.mjs",
|
|
16
26
|
"default": "./dist/http-errors.mjs"
|
|
17
27
|
},
|
|
28
|
+
"./managed-resources": {
|
|
29
|
+
"types": "./dist/managed-resources.d.ts",
|
|
30
|
+
"import": "./dist/managed-resources.mjs",
|
|
31
|
+
"default": "./dist/managed-resources.mjs"
|
|
32
|
+
},
|
|
18
33
|
"./file-content": {
|
|
19
34
|
"types": "./dist/file-content.d.ts",
|
|
20
35
|
"import": "./dist/file-content.mjs",
|
|
21
36
|
"default": "./dist/file-content.mjs"
|
|
22
37
|
},
|
|
38
|
+
"./request-body": {
|
|
39
|
+
"types": "./dist/request-body.d.ts",
|
|
40
|
+
"import": "./dist/request-body.mjs",
|
|
41
|
+
"default": "./dist/request-body.mjs"
|
|
42
|
+
},
|
|
23
43
|
"./image-decoding": {
|
|
24
44
|
"types": "./dist/image-decoding.d.ts",
|
|
25
45
|
"browser": "./dist/image-decoding.browser.mjs",
|
|
@@ -30,7 +50,8 @@
|
|
|
30
50
|
"main": "./dist/index.mjs",
|
|
31
51
|
"types": "./dist/index.d.ts",
|
|
32
52
|
"files": [
|
|
33
|
-
"dist"
|
|
53
|
+
"dist",
|
|
54
|
+
"LICENSE"
|
|
34
55
|
],
|
|
35
56
|
"scripts": {
|
|
36
57
|
"build": "tsup",
|
|
@@ -38,25 +59,23 @@
|
|
|
38
59
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
39
60
|
"test": "vitest --run"
|
|
40
61
|
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"file-type": "^22.2.0"
|
|
64
|
+
},
|
|
41
65
|
"devDependencies": {
|
|
42
|
-
"@types/node": "^22.
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"vitest": "^4.1.5"
|
|
66
|
+
"@types/node": "^22.20.5",
|
|
67
|
+
"sharp": "^0.35.5",
|
|
68
|
+
"tsup": "^9.0.0",
|
|
69
|
+
"typescript": "^6.0.3",
|
|
70
|
+
"vitest": "^5.0.5"
|
|
48
71
|
},
|
|
49
72
|
"engines": {
|
|
50
|
-
"node": "^22.
|
|
73
|
+
"node": "^22.18.0 || ^24.0.0 || ^26.0.0"
|
|
51
74
|
},
|
|
52
75
|
"peerDependencies": {
|
|
53
|
-
"
|
|
54
|
-
"sharp": "^0.35.4"
|
|
76
|
+
"sharp": "^0.35.5"
|
|
55
77
|
},
|
|
56
78
|
"peerDependenciesMeta": {
|
|
57
|
-
"file-type": {
|
|
58
|
-
"optional": true
|
|
59
|
-
},
|
|
60
79
|
"sharp": {
|
|
61
80
|
"optional": true
|
|
62
81
|
}
|