@velarscript/node 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -14
- package/dist/compiler.d.ts +3 -0
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +299 -76
- package/dist/compiler.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/node-host-runtime.d.ts.map +1 -1
- package/dist/node-host-runtime.js +30 -14
- package/dist/node-host-runtime.js.map +1 -1
- package/dist/node-host-worker-runtime.d.ts.map +1 -1
- package/dist/node-host-worker-runtime.js +495 -93
- package/dist/node-host-worker-runtime.js.map +1 -1
- package/dist/project-config.d.ts +17 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +66 -0
- package/dist/project-config.js.map +1 -0
- package/dist/serve-runtime.d.ts.map +1 -1
- package/dist/serve-runtime.js +2885 -157
- package/dist/serve-runtime.js.map +1 -1
- package/dist/server-analyzer.d.ts +34 -0
- package/dist/server-analyzer.d.ts.map +1 -0
- package/dist/server-analyzer.js +659 -0
- package/dist/server-analyzer.js.map +1 -0
- package/dist/server-ast.d.ts +42 -0
- package/dist/server-ast.d.ts.map +1 -0
- package/dist/server-ast.js +16 -0
- package/dist/server-ast.js.map +1 -0
- package/dist/server-emitter.d.ts +16 -0
- package/dist/server-emitter.d.ts.map +1 -0
- package/dist/server-emitter.js +166 -0
- package/dist/server-emitter.js.map +1 -0
- package/dist/server-inspection.d.ts +3 -0
- package/dist/server-inspection.d.ts.map +1 -0
- package/dist/server-inspection.js +12 -0
- package/dist/server-inspection.js.map +1 -0
- package/dist/server-lexer.d.ts +13 -0
- package/dist/server-lexer.d.ts.map +1 -0
- package/dist/server-lexer.js +65 -0
- package/dist/server-lexer.js.map +1 -0
- package/dist/server-parser.d.ts +16 -0
- package/dist/server-parser.d.ts.map +1 -0
- package/dist/server-parser.js +207 -0
- package/dist/server-parser.js.map +1 -0
- package/dist/server-semantic.d.ts +3 -0
- package/dist/server-semantic.d.ts.map +1 -0
- package/dist/server-semantic.js +31 -0
- package/dist/server-semantic.js.map +1 -0
- package/dist/server-types.d.ts +25 -0
- package/dist/server-types.d.ts.map +1 -0
- package/dist/server-types.js +60 -0
- package/dist/server-types.js.map +1 -0
- package/dist/websocket-runtime.d.ts.map +1 -1
- package/dist/websocket-runtime.js +169 -15
- package/dist/websocket-runtime.js.map +1 -1
- package/package.json +10 -2
- package/dist/sqlite-runtime.d.ts +0 -3
- package/dist/sqlite-runtime.d.ts.map +0 -1
- package/dist/sqlite-runtime.js +0 -92
- package/dist/sqlite-runtime.js.map +0 -1
package/dist/serve-runtime.js
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// this Realm owns only Velar values, handlers and strict JSON/type boundaries.
|
|
4
4
|
export const VELAR_NODE_SERVE_RUNTIME = String.raw `
|
|
5
5
|
import { __velarNodeHostInvoke, __velarNodeHostOn } from "velar/node-host-v1";
|
|
6
|
+
import { normalizeError as __velarServeNormalizeError } from "velar/compiler-runtime-errors-v1";
|
|
7
|
+
import { __velarValidateDenseList as __velarServeValidateDenseList } from "velar/compiler-runtime-collection-lowering-v1";
|
|
8
|
+
import { Bytes as __velarServeBytesType } from "velar/binary";
|
|
9
|
+
import { writeBytes as __velarServeWriteBytes } from "velar/fs";
|
|
10
|
+
import { Cancellation as __velarServeCancellation } from "velar/task";
|
|
6
11
|
|
|
7
12
|
const __velarServeArray = globalThis.Array;
|
|
8
13
|
const __velarServeError = globalThis.Error;
|
|
@@ -13,9 +18,23 @@ const __velarServeRangeError = globalThis.RangeError;
|
|
|
13
18
|
const __velarServeRegExp = globalThis.RegExp;
|
|
14
19
|
const __velarServeReflect = globalThis.Reflect;
|
|
15
20
|
const __velarServeString = globalThis.String;
|
|
21
|
+
const __velarServePromise = globalThis.Promise;
|
|
16
22
|
const __velarServeTypeError = globalThis.TypeError;
|
|
23
|
+
const __velarServeUint8Array = globalThis.Uint8Array;
|
|
24
|
+
const __velarServeWeakMap = globalThis.WeakMap;
|
|
25
|
+
const __velarServeDate = globalThis.Date;
|
|
26
|
+
const __velarServeMath = globalThis.Math;
|
|
27
|
+
const __velarServeTextDecoder = globalThis.TextDecoder;
|
|
28
|
+
const __velarServeTextEncoder = globalThis.TextEncoder;
|
|
17
29
|
const __velarServeOwnDescriptor = __velarServeObject.getOwnPropertyDescriptor;
|
|
18
30
|
const __velarServeApply = __velarServeReflect.apply;
|
|
31
|
+
const __velarServeConsole = globalThis.console;
|
|
32
|
+
const __velarServeConsoleErrorDescriptor = __velarServeConsole && (typeof __velarServeConsole === "object" || typeof __velarServeConsole === "function")
|
|
33
|
+
? __velarServeOwnDescriptor(__velarServeConsole, "error")
|
|
34
|
+
: null;
|
|
35
|
+
const __velarServeConsoleError = __velarServeConsoleErrorDescriptor && "value" in __velarServeConsoleErrorDescriptor
|
|
36
|
+
? __velarServeConsoleErrorDescriptor.value
|
|
37
|
+
: null;
|
|
19
38
|
function __velarServeDataOperation(target, name) {
|
|
20
39
|
const descriptor = __velarServeOwnDescriptor(target, name);
|
|
21
40
|
if (!descriptor || !("value" in descriptor) || typeof descriptor.value !== "function") {
|
|
@@ -24,42 +43,133 @@ function __velarServeDataOperation(target, name) {
|
|
|
24
43
|
return descriptor.value;
|
|
25
44
|
}
|
|
26
45
|
const __velarServeArrayIsArray = __velarServeDataOperation(__velarServeArray, "isArray");
|
|
46
|
+
const __velarServeArrayIncludes = __velarServeDataOperation(__velarServeArray.prototype, "includes");
|
|
47
|
+
const __velarServeArrayJoin = __velarServeDataOperation(__velarServeArray.prototype, "join");
|
|
48
|
+
const __velarServeTextDecode = __velarServeDataOperation(__velarServeTextDecoder.prototype, "decode");
|
|
49
|
+
const __velarServeUtf8Decoder = new __velarServeTextDecoder("utf-8", {fatal: true});
|
|
50
|
+
const __velarServeTextEncode = __velarServeDataOperation(__velarServeTextEncoder.prototype, "encode");
|
|
51
|
+
const __velarServeUtf8Encoder = new __velarServeTextEncoder();
|
|
27
52
|
const __velarServeNumberIsFinite = __velarServeDataOperation(__velarServeNumber, "isFinite");
|
|
28
53
|
const __velarServeNumberIsSafeInteger = __velarServeDataOperation(__velarServeNumber, "isSafeInteger");
|
|
54
|
+
const __velarServeNumberToString = __velarServeDataOperation(__velarServeNumber.prototype, "toString");
|
|
29
55
|
const __velarServeObjectCreate = __velarServeDataOperation(__velarServeObject, "create");
|
|
30
56
|
const __velarServeObjectDefineProperty = __velarServeDataOperation(__velarServeObject, "defineProperty");
|
|
31
57
|
const __velarServeObjectFreeze = __velarServeDataOperation(__velarServeObject, "freeze");
|
|
32
58
|
const __velarServeObjectGetPrototypeOf = __velarServeDataOperation(__velarServeObject, "getPrototypeOf");
|
|
59
|
+
const __velarServeTypedArrayPrototype = __velarServeApply(__velarServeObjectGetPrototypeOf, __velarServeObject, [__velarServeUint8Array.prototype]);
|
|
60
|
+
const __velarServeUint8Slice = __velarServeDataOperation(__velarServeTypedArrayPrototype, "slice");
|
|
61
|
+
const __velarServeUint8Subarray = __velarServeDataOperation(__velarServeTypedArrayPrototype, "subarray");
|
|
62
|
+
const __velarServeUint8Set = __velarServeDataOperation(__velarServeTypedArrayPrototype, "set");
|
|
33
63
|
const __velarServeOwnKeys = __velarServeDataOperation(__velarServeReflect, "ownKeys");
|
|
34
64
|
const __velarServeMapEntries = __velarServeDataOperation(__velarServeMap.prototype, "entries");
|
|
35
65
|
const __velarServeMapGet = __velarServeDataOperation(__velarServeMap.prototype, "get");
|
|
36
66
|
const __velarServeMapHas = __velarServeDataOperation(__velarServeMap.prototype, "has");
|
|
37
67
|
const __velarServeMapSet = __velarServeDataOperation(__velarServeMap.prototype, "set");
|
|
38
68
|
const __velarServeMapDelete = __velarServeDataOperation(__velarServeMap.prototype, "delete");
|
|
69
|
+
const __velarServeMapClear = __velarServeDataOperation(__velarServeMap.prototype, "clear");
|
|
39
70
|
const __velarServeMapSize = __velarServeOwnDescriptor(__velarServeMap.prototype, "size")?.get;
|
|
40
71
|
const __velarServeMapIterator = __velarServeApply(__velarServeMapEntries, new __velarServeMap(), []);
|
|
41
72
|
const __velarServeMapIteratorNext = __velarServeDataOperation(__velarServeApply(__velarServeObjectGetPrototypeOf, __velarServeObject, [__velarServeMapIterator]), "next");
|
|
73
|
+
const __velarServeWeakMapGet = __velarServeDataOperation(__velarServeWeakMap.prototype, "get");
|
|
74
|
+
const __velarServeWeakMapHas = __velarServeDataOperation(__velarServeWeakMap.prototype, "has");
|
|
75
|
+
const __velarServeWeakMapSet = __velarServeDataOperation(__velarServeWeakMap.prototype, "set");
|
|
76
|
+
const __velarServeWeakMapDelete = __velarServeDataOperation(__velarServeWeakMap.prototype, "delete");
|
|
42
77
|
const __velarServeStringIncludes = __velarServeDataOperation(__velarServeString.prototype, "includes");
|
|
78
|
+
const __velarServeStringEndsWith = __velarServeDataOperation(__velarServeString.prototype, "endsWith");
|
|
79
|
+
const __velarServeStringIndexOf = __velarServeDataOperation(__velarServeString.prototype, "indexOf");
|
|
80
|
+
const __velarServeStringSlice = __velarServeDataOperation(__velarServeString.prototype, "slice");
|
|
81
|
+
const __velarServeStringSplit = __velarServeDataOperation(__velarServeString.prototype, "split");
|
|
43
82
|
const __velarServeStringStartsWith = __velarServeDataOperation(__velarServeString.prototype, "startsWith");
|
|
44
83
|
const __velarServeStringToLowerCase = __velarServeDataOperation(__velarServeString.prototype, "toLowerCase");
|
|
84
|
+
const __velarServeStringTrim = __velarServeDataOperation(__velarServeString.prototype, "trim");
|
|
85
|
+
const __velarServeDateNow = __velarServeDataOperation(__velarServeDate, "now");
|
|
86
|
+
const __velarServeDateParse = __velarServeDataOperation(__velarServeDate, "parse");
|
|
87
|
+
const __velarServeMathFloor = __velarServeDataOperation(__velarServeMath, "floor");
|
|
88
|
+
const __velarServeMathMax = __velarServeDataOperation(__velarServeMath, "max");
|
|
89
|
+
const __velarServePromiseRace = __velarServeDataOperation(__velarServePromise, "race");
|
|
90
|
+
const __velarServePromiseThen = __velarServeDataOperation(__velarServePromise.prototype, "then");
|
|
91
|
+
const __velarServeSetTimeout = globalThis.setTimeout;
|
|
92
|
+
const __velarServeClearTimeout = globalThis.clearTimeout;
|
|
93
|
+
const __velarServeAtob = globalThis.atob;
|
|
94
|
+
const __velarServeDecodeURIComponent = globalThis.decodeURIComponent;
|
|
95
|
+
const __velarServeEncodeURIComponent = globalThis.encodeURIComponent;
|
|
45
96
|
const __velarServeRegExpTest = __velarServeDataOperation(__velarServeRegExp.prototype, "test");
|
|
46
97
|
const __velarServeHeaderNamePattern = /^[!#$%&'*+.^_|~0-9A-Za-z-]+$/u;
|
|
47
98
|
const __velarServeHeaderValuePattern = /[\0\r\n]/u;
|
|
48
99
|
const __velarServeMethodPattern = /^[!#$%&'*+.^_\x60|~0-9A-Z-]+$/u;
|
|
100
|
+
const __velarServeRouteCapturePattern = /^\{[A-Za-z_][A-Za-z0-9_]*:[A-Za-z_][A-Za-z0-9_]*\}$/u;
|
|
101
|
+
const __velarServeRouteNamePattern = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
102
|
+
const __velarServeOperationIdCharacterPattern = /^[A-Za-z0-9]$/u;
|
|
103
|
+
const __velarServeDecimalPattern = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u;
|
|
49
104
|
const __velarServeMaxBodyBytes = 16 * 1024 * 1024;
|
|
50
105
|
const __velarServeMaxPathCodeUnits = 4096;
|
|
106
|
+
const __velarServeMaxRoutes = 4096;
|
|
107
|
+
const __velarServeMaxLifecycles = 4096;
|
|
108
|
+
const __velarServeMaxRequestProviders = 256;
|
|
109
|
+
const __velarServeMaxAppProviders = 512;
|
|
110
|
+
const __velarServeMaxOutboundBytes = 128 * 1024 * 1024;
|
|
111
|
+
const __velarServeMaxBackgroundTasks = 1024;
|
|
112
|
+
const __velarServeMaxActiveTimeouts = 256;
|
|
113
|
+
const __velarServeDefaultShutdownGrace = 30_000;
|
|
51
114
|
const __velarServeFileMarker = Symbol("velar.serve.file-response");
|
|
115
|
+
const __velarServeAppMarker = Symbol("velar.serve.app");
|
|
116
|
+
const __velarServeRouteMarker = Symbol("velar.serve.route");
|
|
117
|
+
const __velarServeInputMarker = Symbol("velar.serve.input");
|
|
118
|
+
const __velarServeProviderMarker = Symbol("velar.serve.provider");
|
|
119
|
+
const __velarServeUploadMarker = Symbol("velar.serve.upload");
|
|
120
|
+
const __velarServeManagedResponseMarker = Symbol("velar.serve.managed-response");
|
|
121
|
+
const __velarServeMissing = Symbol("velar.serve.missing");
|
|
52
122
|
const __velarServeHandlers = new __velarServeMap();
|
|
53
|
-
const
|
|
123
|
+
const __velarServeHostCancellations = new __velarServeMap();
|
|
124
|
+
const __velarServeSerializedJson = new __velarServeWeakMap();
|
|
125
|
+
const __velarServeResponseCookies = new __velarServeWeakMap();
|
|
126
|
+
const __velarServeReservedBackground = new __velarServeWeakMap();
|
|
127
|
+
const __velarServeTimeoutSettlements = new __velarServeWeakMap();
|
|
128
|
+
const __velarServeResponseFields = __velarServeFieldMap(["status", "json", "text", "contentType", "stream", "headers", "background", "compression"]);
|
|
54
129
|
const __velarServeBodyFields = __velarServeFieldMap(["text", "bytes", "tooLarge"]);
|
|
130
|
+
const __velarServeBodyBytesFields = __velarServeFieldMap(["data", "bytes", "tooLarge"]);
|
|
55
131
|
const __velarServeRequestFields = __velarServeFieldMap(["token", "request", "method", "path", "query", "headers"]);
|
|
56
132
|
const __velarServeStartFields = __velarServeFieldMap(["handle", "port"]);
|
|
133
|
+
const __velarServeTestFileFields = __velarServeFieldMap(["data", "contentType"]);
|
|
134
|
+
const __velarServeTestUploadFields = __velarServeFieldMap(["filename", "contentType", "data"]);
|
|
135
|
+
const __velarServeRouteParameterFields = __velarServeFieldMap(["name", "source", "kind", "required", "check", "schema", "input"]);
|
|
136
|
+
const __velarServeRouteMetadataFields = __velarServeFieldMap(["responseSchema", "responseContentTypes", "maxBodyBytes", "middleware", "documented", "summary", "description", "tags", "status", "errors"]);
|
|
137
|
+
const __velarServeRouteDocumentationFields = __velarServeFieldMap(["summary", "description", "tags", "status", "errors", "documented"]);
|
|
138
|
+
const __velarServeErrorDocumentationFields = __velarServeFieldMap(["status", "description"]);
|
|
57
139
|
let __velarServeNextToken = 1;
|
|
140
|
+
let __velarServeNextRequestId = 1;
|
|
141
|
+
let __velarServeNextTestBoundary = 1;
|
|
142
|
+
let __velarServeActiveTimeouts = 0;
|
|
143
|
+
let __velarServeActiveBackgroundTasks = 0;
|
|
144
|
+
let __velarServeOutboundBytes = 0;
|
|
58
145
|
|
|
59
146
|
function __velarServeCall(operation, receiver, args) {
|
|
60
147
|
return __velarServeApply(operation, receiver, args);
|
|
61
148
|
}
|
|
62
149
|
|
|
150
|
+
function __velarServeFailureTrace(error) {
|
|
151
|
+
try {
|
|
152
|
+
const stack = __velarServeOwnDescriptor(error, "stack");
|
|
153
|
+
if (stack && "value" in stack && typeof stack.value === "string" && stack.value !== "") return stack.value;
|
|
154
|
+
} catch {}
|
|
155
|
+
try {
|
|
156
|
+
const message = __velarServeOwnDescriptor(error, "message");
|
|
157
|
+
if (message && "value" in message && typeof message.value === "string" && message.value !== "") return message.value;
|
|
158
|
+
} catch {}
|
|
159
|
+
return "The request failed without an Error value";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function __velarServeReportFailure(failure) {
|
|
163
|
+
try {
|
|
164
|
+
if (typeof __velarServeConsoleError !== "function") return null;
|
|
165
|
+
let error = null;
|
|
166
|
+
try { error = __velarServeNormalizeError(failure); } catch {}
|
|
167
|
+
const trace = error === null ? "The request failed without an Error value" : __velarServeFailureTrace(error);
|
|
168
|
+
__velarServeCall(__velarServeConsoleError, __velarServeConsole, ["Unhandled server request failed: " + trace]);
|
|
169
|
+
} catch {}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
63
173
|
function __velarServeFieldMap(names) {
|
|
64
174
|
const output = new __velarServeMap();
|
|
65
175
|
for (let index = 0; index < names.length; index += 1) __velarServeCall(__velarServeMapSet, output, [names[index], true]);
|
|
@@ -74,6 +184,30 @@ function __velarServeIsSafeInteger(value) {
|
|
|
74
184
|
return __velarServeCall(__velarServeNumberIsSafeInteger, __velarServeNumber, [value]);
|
|
75
185
|
}
|
|
76
186
|
|
|
187
|
+
function __velarServeReserveOutbound(bytes) {
|
|
188
|
+
if (!__velarServeIsSafeInteger(bytes) || bytes < 0 || __velarServeOutboundBytes + bytes > __velarServeMaxOutboundBytes) {
|
|
189
|
+
throw new __velarServeRangeError("ServeResponse aggregate outbound byte budget is exhausted");
|
|
190
|
+
}
|
|
191
|
+
__velarServeOutboundBytes += bytes;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function __velarServeReleaseOutbound(bytes) {
|
|
195
|
+
__velarServeOutboundBytes -= bytes;
|
|
196
|
+
if (__velarServeOutboundBytes < 0) __velarServeOutboundBytes = 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function __velarServeHeaderPairBytes(pairs) {
|
|
200
|
+
let bytes = 0;
|
|
201
|
+
for (let index = 0; index < pairs.length; index += 1) bytes += __velarUtf8ByteLength(pairs[index][0]) + __velarUtf8ByteLength(pairs[index][1]);
|
|
202
|
+
return bytes;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function __velarServeWithOutbound(bytes, action) {
|
|
206
|
+
__velarServeReserveOutbound(bytes);
|
|
207
|
+
try { return await action(); }
|
|
208
|
+
finally { __velarServeReleaseOutbound(bytes); }
|
|
209
|
+
}
|
|
210
|
+
|
|
77
211
|
function __velarServePlainRecord(value, name) {
|
|
78
212
|
if (!value || typeof value !== "object" || __velarServeIsArray(value)) throw new __velarServeTypeError(name + " must be a plain record");
|
|
79
213
|
const prototype = __velarServeCall(__velarServeObjectGetPrototypeOf, __velarServeObject, [value]);
|
|
@@ -157,12 +291,120 @@ function __velarServePairsMap(value, name) {
|
|
|
157
291
|
return output;
|
|
158
292
|
}
|
|
159
293
|
|
|
160
|
-
function
|
|
294
|
+
function __velarServePairsMaps(value, name) {
|
|
295
|
+
if (!__velarServeIsArray(value) || value.length > 1000) throw new __velarServeTypeError(name + " host result is invalid");
|
|
296
|
+
const values = new __velarServeMap();
|
|
297
|
+
const all = new __velarServeMap();
|
|
298
|
+
let units = 0;
|
|
299
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
300
|
+
const descriptor = __velarServeOwnDescriptor(value, __velarServeString(index));
|
|
301
|
+
if (!descriptor?.enumerable || !("value" in descriptor) || !__velarServeIsArray(descriptor.value) || descriptor.value.length !== 2) {
|
|
302
|
+
throw new __velarServeTypeError(name + " host result is invalid");
|
|
303
|
+
}
|
|
304
|
+
const key = __velarServeOwnDescriptor(descriptor.value, "0");
|
|
305
|
+
const item = __velarServeOwnDescriptor(descriptor.value, "1");
|
|
306
|
+
if (!key || !("value" in key) || !item || !("value" in item) || typeof key.value !== "string" || typeof item.value !== "string") {
|
|
307
|
+
throw new __velarServeTypeError(name + " host result is invalid");
|
|
308
|
+
}
|
|
309
|
+
units += key.value.length + item.value.length;
|
|
310
|
+
if (units > 1024 * 1024) throw new __velarServeRangeError(name + " host result exceeds 1 MiB");
|
|
311
|
+
if (!__velarServeCall(__velarServeMapHas, values, [key.value])) {
|
|
312
|
+
__velarServeCall(__velarServeMapSet, values, [key.value, item.value]);
|
|
313
|
+
__velarServeCall(__velarServeMapSet, all, [key.value, [item.value]]);
|
|
314
|
+
} else {
|
|
315
|
+
const previous = __velarServeCall(__velarServeMapGet, all, [key.value]);
|
|
316
|
+
if (previous.length >= 1000) throw new __velarServeRangeError(name + " contains too many repeated values");
|
|
317
|
+
previous[previous.length] = item.value;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const iterator = __velarServeCall(__velarServeMapEntries, all, []);
|
|
321
|
+
while (true) {
|
|
322
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, iterator, []);
|
|
323
|
+
if (step.done) break;
|
|
324
|
+
__velarServeCall(__velarServeObjectFreeze, __velarServeObject, [step.value[1]]);
|
|
325
|
+
}
|
|
326
|
+
return {values, all};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function __velarServeStringListMapSnapshot(value, name) {
|
|
330
|
+
let size;
|
|
331
|
+
let iterator;
|
|
332
|
+
try { size = __velarServeCall(__velarServeMapSize, value, []); iterator = __velarServeCall(__velarServeMapEntries, value, []); }
|
|
333
|
+
catch { throw new __velarServeTypeError(name + " must be a Map<string, List<string>>"); }
|
|
334
|
+
if (!__velarServeIsSafeInteger(size) || size < 0 || size > 1000) throw new __velarServeRangeError(name + " cannot contain more than 1000 fields");
|
|
335
|
+
let fields = 0;
|
|
336
|
+
let units = 0;
|
|
337
|
+
while (true) {
|
|
338
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, iterator, []);
|
|
339
|
+
if (step.done) break;
|
|
340
|
+
if (!__velarServeIsArray(step.value) || step.value.length !== 2 || typeof step.value[0] !== "string" || !__velarServeIsArray(step.value[1]) || step.value[1].length < 1 || step.value[1].length > 1000) {
|
|
341
|
+
throw new __velarServeTypeError(name + " must be a Map<string, List<string>>");
|
|
342
|
+
}
|
|
343
|
+
units += step.value[0].length;
|
|
344
|
+
for (let index = 0; index < step.value[1].length; index += 1) {
|
|
345
|
+
if (typeof step.value[1][index] !== "string") throw new __velarServeTypeError(name + " must be a Map<string, List<string>>");
|
|
346
|
+
units += step.value[1][index].length;
|
|
347
|
+
}
|
|
348
|
+
if (units > 1024 * 1024) throw new __velarServeRangeError(name + " cannot exceed 1 MiB of text");
|
|
349
|
+
fields += 1;
|
|
350
|
+
}
|
|
351
|
+
if (fields !== size) throw new __velarServeTypeError(name + " changed while it was being read");
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function __velarServeCanonicalPath(rawPath, name = "ServeRequest path") {
|
|
356
|
+
if (typeof rawPath !== "string" || rawPath.length === 0 || !__velarServeCall(__velarServeStringStartsWith, rawPath, ["/"])) {
|
|
357
|
+
throw new __velarServeTypeError(name + " must be an absolute URL path");
|
|
358
|
+
}
|
|
359
|
+
const source = __velarServeCall(__velarServeStringSplit, rawPath, ["/"]);
|
|
360
|
+
const decoded = [];
|
|
361
|
+
let units = 0;
|
|
362
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
363
|
+
let segment;
|
|
364
|
+
try { segment = __velarServeCall(__velarServeDecodeURIComponent, undefined, [source[index]]); }
|
|
365
|
+
catch { throw new __velarServeTypeError(name + " must use valid percent-encoded UTF-8"); }
|
|
366
|
+
if (__velarServeCall(__velarServeStringIncludes, segment, ["/"]) || __velarServeCall(__velarServeStringIncludes, segment, ["\\"])
|
|
367
|
+
|| __velarServeCall(__velarServeStringIncludes, segment, ["\0"]) || segment === "." || segment === "..") {
|
|
368
|
+
throw new __velarServeTypeError(name + " contains an unsafe encoded segment");
|
|
369
|
+
}
|
|
370
|
+
units += segment.length + (index === 0 ? 0 : 1);
|
|
371
|
+
if (units > __velarServeMaxPathCodeUnits) throw new __velarServeRangeError(name + " is too long");
|
|
372
|
+
decoded[decoded.length] = segment;
|
|
373
|
+
}
|
|
374
|
+
return __velarServeCall(__velarServeArrayJoin, decoded, ["/"]);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function __velarServeTargetParts(target, name) {
|
|
378
|
+
if (__velarServeCall(__velarServeStringIncludes, target, ["#"])) throw new __velarServeTypeError(name + " must not contain a URL fragment");
|
|
379
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, target, ["?"]);
|
|
380
|
+
const path = __velarServeCanonicalPath(separator < 0 ? target : __velarServeCall(__velarServeStringSlice, target, [0, separator]), name + " path");
|
|
381
|
+
const query = [];
|
|
382
|
+
const source = separator < 0 ? "" : __velarServeCall(__velarServeStringSlice, target, [separator + 1]);
|
|
383
|
+
if (source !== "") {
|
|
384
|
+
const fields = __velarServeCall(__velarServeStringSplit, source, ["&"]);
|
|
385
|
+
if (fields.length > 1000) throw new __velarServeRangeError(name + " query cannot contain more than 1000 fields");
|
|
386
|
+
for (let index = 0; index < fields.length; index += 1) {
|
|
387
|
+
const equals = __velarServeCall(__velarServeStringIndexOf, fields[index], ["="]);
|
|
388
|
+
const rawName = equals < 0 ? fields[index] : __velarServeCall(__velarServeStringSlice, fields[index], [0, equals]);
|
|
389
|
+
const rawValue = equals < 0 ? "" : __velarServeCall(__velarServeStringSlice, fields[index], [equals + 1]);
|
|
390
|
+
try {
|
|
391
|
+
query[query.length] = [
|
|
392
|
+
__velarServeCall(__velarServeDecodeURIComponent, undefined, [__velarServeCall(__velarServeArrayJoin, __velarServeCall(__velarServeStringSplit, rawName, ["+"]), [" "])]),
|
|
393
|
+
__velarServeCall(__velarServeDecodeURIComponent, undefined, [__velarServeCall(__velarServeArrayJoin, __velarServeCall(__velarServeStringSplit, rawValue, ["+"]), [" "])]),
|
|
394
|
+
];
|
|
395
|
+
} catch { throw new __velarServeTypeError(name + " query must use valid percent-encoded UTF-8"); }
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return {path, query};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function __velarServeTypeObject(check, message, internal = null, compilerBridge = null) {
|
|
161
402
|
const value = {
|
|
162
403
|
is(candidate) { try { return check(candidate); } catch { return false; } },
|
|
163
404
|
parse(candidate) { if (!check(candidate)) throw new __velarServeTypeError(message); return candidate; },
|
|
164
405
|
};
|
|
165
406
|
if (internal !== null) __velarServeCall(__velarServeObjectDefineProperty, __velarServeObject, [value, "__velarHandleNative", {value: internal, enumerable: false, configurable: false, writable: false}]);
|
|
407
|
+
if (compilerBridge !== null) __velarServeCall(__velarServeObjectDefineProperty, __velarServeObject, [value, "__velarCompilerBridge", {value: compilerBridge, enumerable: false, configurable: false, writable: false}]);
|
|
166
408
|
return __velarRegisterRuntimeType(__velarServeCall(__velarServeObjectFreeze, __velarServeObject, [value]));
|
|
167
409
|
}
|
|
168
410
|
|
|
@@ -174,6 +416,19 @@ export class RequestBodyTooLargeError extends __velarServeRangeError {
|
|
|
174
416
|
}
|
|
175
417
|
}
|
|
176
418
|
|
|
419
|
+
export class HttpError extends __velarServeError {
|
|
420
|
+
constructor(status, body = null, headers = null) {
|
|
421
|
+
if (!__velarServeIsSafeInteger(status) || status < 400 || status > 599) {
|
|
422
|
+
throw new __velarServeRangeError("HttpError status must be an HTTP error integer from 400 through 599");
|
|
423
|
+
}
|
|
424
|
+
super("HTTP " + status);
|
|
425
|
+
__velarServeCall(__velarServeObjectDefineProperty, __velarServeObject, [this, "name", {value: "HttpError", enumerable: false, configurable: true, writable: true}]);
|
|
426
|
+
__velarServeCall(__velarServeObjectDefineProperty, __velarServeObject, [this, "status", {value: status, enumerable: true, configurable: false, writable: false}]);
|
|
427
|
+
__velarServeCall(__velarServeObjectDefineProperty, __velarServeObject, [this, "body", {value: body ?? {error: "http_error"}, enumerable: true, configurable: false, writable: false}]);
|
|
428
|
+
__velarServeCall(__velarServeObjectDefineProperty, __velarServeObject, [this, "headers", {value: __velarServeHeaders(headers), enumerable: true, configurable: false, writable: false}]);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
177
432
|
function __velarServeIsFileResponse(value) {
|
|
178
433
|
if (!value || typeof value !== "object") return false;
|
|
179
434
|
const descriptor = __velarServeOwnDescriptor(value, __velarServeFileMarker);
|
|
@@ -182,31 +437,41 @@ function __velarServeIsFileResponse(value) {
|
|
|
182
437
|
|
|
183
438
|
function __velarServeResponse(value) {
|
|
184
439
|
if (__velarServeIsFileResponse(value)) return value;
|
|
440
|
+
const inheritedCookies = value && typeof value === "object" ? __velarServeCall(__velarServeWeakMapGet, __velarServeResponseCookies, [value]) : undefined;
|
|
185
441
|
const fields = __velarServeRecord(value, __velarServeResponseFields, "ServeResponse");
|
|
186
|
-
if (!__velarServeIsSafeInteger(fields.status) || fields.status <
|
|
187
|
-
throw new __velarServeRangeError("ServeResponse.status must be
|
|
442
|
+
if (!__velarServeIsSafeInteger(fields.status) || fields.status < 200 || fields.status > 599) {
|
|
443
|
+
throw new __velarServeRangeError("ServeResponse.status must be a final HTTP status integer from 200 through 599");
|
|
188
444
|
}
|
|
189
445
|
let bodies = 0;
|
|
190
446
|
const bodyNames = ["json", "text", "stream"];
|
|
191
447
|
for (let index = 0; index < bodyNames.length; index += 1) if (__velarServeOwnDescriptor(fields, bodyNames[index])) bodies += 1;
|
|
192
448
|
if (bodies !== 1) throw new __velarServeTypeError("ServeResponse requires exactly one of json, text, or stream");
|
|
193
|
-
if (__velarServeOwnDescriptor(fields, "json")
|
|
194
|
-
|
|
449
|
+
if (__velarServeOwnDescriptor(fields, "json")) {
|
|
450
|
+
const serialized = __velarJsonStringify(fields.json);
|
|
451
|
+
if (__velarUtf8ByteLength(serialized) > __velarServeMaxBodyBytes) throw new __velarServeRangeError("ServeResponse.json cannot exceed 16 MiB");
|
|
452
|
+
__velarServeCall(__velarServeWeakMapSet, __velarServeSerializedJson, [fields, serialized]);
|
|
195
453
|
}
|
|
196
454
|
if (__velarServeOwnDescriptor(fields, "text") && typeof fields.text !== "string") throw new __velarServeTypeError("ServeResponse.text must be a string");
|
|
197
455
|
if (__velarServeOwnDescriptor(fields, "text") && __velarUtf8ByteLength(fields.text) > __velarServeMaxBodyBytes) throw new __velarServeRangeError("ServeResponse.text cannot exceed 16 MiB");
|
|
198
456
|
if (__velarServeOwnDescriptor(fields, "stream") && typeof fields.stream !== "function") throw new __velarServeTypeError("ServeResponse.stream must be an async producer");
|
|
457
|
+
if (fields.background != null) {
|
|
458
|
+
if (!__velarServeIsArray(fields.background) || fields.background.length > 64) throw new __velarServeTypeError("ServeResponse.background must be a bounded task list");
|
|
459
|
+
for (let index = 0; index < fields.background.length; index += 1) if (typeof fields.background[index] !== "function") throw new __velarServeTypeError("ServeResponse.background tasks must be functions");
|
|
460
|
+
}
|
|
461
|
+
if (fields.compression != null && fields.compression !== "gzip" && fields.compression !== "br") throw new __velarServeTypeError("ServeResponse compression must be gzip, br, or null");
|
|
199
462
|
if (fields.contentType != null && (typeof fields.contentType !== "string" || fields.contentType.length === 0 || fields.contentType.length > 1024
|
|
200
463
|
|| __velarServeCall(__velarServeStringIncludes, fields.contentType, ["\0"]) || __velarServeCall(__velarServeStringIncludes, fields.contentType, ["\r"])
|
|
201
464
|
|| __velarServeCall(__velarServeStringIncludes, fields.contentType, ["\n"]))) {
|
|
202
465
|
throw new __velarServeTypeError("ServeResponse.contentType must be bounded single-line text");
|
|
203
466
|
}
|
|
204
467
|
__velarServeResponseHeaders(fields.headers);
|
|
468
|
+
if (inheritedCookies !== undefined) __velarServeCall(__velarServeWeakMapSet, __velarServeResponseCookies, [fields, inheritedCookies]);
|
|
205
469
|
return fields;
|
|
206
470
|
}
|
|
207
471
|
|
|
208
472
|
function __velarServeResponseHeaders(value) {
|
|
209
473
|
const pairs = value == null ? [] : __velarServeMapSnapshot(value, "ServeResponse.headers");
|
|
474
|
+
let bytes = 0;
|
|
210
475
|
for (let index = 0; index < pairs.length; index += 1) {
|
|
211
476
|
const name = pairs[index][0];
|
|
212
477
|
const item = pairs[index][1];
|
|
@@ -218,10 +483,73 @@ function __velarServeResponseHeaders(value) {
|
|
|
218
483
|
if (lower === "content-length" || lower === "transfer-encoding" || lower === "connection") {
|
|
219
484
|
throw new __velarServeTypeError("ServeResponse cannot set transport-owned header '" + name + "'");
|
|
220
485
|
}
|
|
486
|
+
bytes += __velarUtf8ByteLength(name) + __velarUtf8ByteLength(item);
|
|
487
|
+
if (bytes > 64 * 1024) throw new __velarServeRangeError("ServeResponse headers cannot exceed 64 KiB");
|
|
221
488
|
}
|
|
222
489
|
return pairs;
|
|
223
490
|
}
|
|
224
491
|
|
|
492
|
+
function __velarServeCookies(value) {
|
|
493
|
+
const cookies = __velarServeCall(__velarServeWeakMapGet, __velarServeResponseCookies, [value]);
|
|
494
|
+
if (cookies === undefined) return [];
|
|
495
|
+
let bytes = 0;
|
|
496
|
+
for (let index = 0; index < cookies.length; index += 1) {
|
|
497
|
+
if (typeof cookies[index] !== "string" || cookies[index].length === 0 || cookies[index].length > 8192 || /[\0\r\n]/u.test(cookies[index])) {
|
|
498
|
+
throw new __velarServeTypeError("ServeResponse cookies are invalid");
|
|
499
|
+
}
|
|
500
|
+
bytes += __velarUtf8ByteLength(cookies[index]);
|
|
501
|
+
}
|
|
502
|
+
if (bytes > 64 * 1024) throw new __velarServeRangeError("ServeResponse cookies cannot exceed 64 KiB");
|
|
503
|
+
return cookies;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
async function __velarServeRunBackground(tasks) {
|
|
507
|
+
if (tasks === null) return null;
|
|
508
|
+
for (let index = 0; index < tasks.length; index += 1) {
|
|
509
|
+
const reserved = __velarServeCall(__velarServeWeakMapHas, __velarServeReservedBackground, [tasks[index]]);
|
|
510
|
+
if (!reserved) {
|
|
511
|
+
if (__velarServeActiveBackgroundTasks >= __velarServeMaxBackgroundTasks - __velarServeMaxActiveTimeouts) {
|
|
512
|
+
__velarServeReportFailure(new __velarServeRangeError("ServeResponse background task capacity is exhausted"));
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
__velarServeActiveBackgroundTasks += 1;
|
|
516
|
+
}
|
|
517
|
+
try { await __velarServeCall(tasks[index], undefined, []); }
|
|
518
|
+
catch (error) { __velarServeReportFailure(error); }
|
|
519
|
+
finally { if (!reserved) __velarServeActiveBackgroundTasks -= 1; }
|
|
520
|
+
}
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function __velarServeRegisterTimeoutSettlement(request, settlement) {
|
|
525
|
+
let pending = __velarServeCall(__velarServeWeakMapGet, __velarServeTimeoutSettlements, [request]);
|
|
526
|
+
if (pending === undefined) {
|
|
527
|
+
pending = [];
|
|
528
|
+
__velarServeCall(__velarServeWeakMapSet, __velarServeTimeoutSettlements, [request, pending]);
|
|
529
|
+
}
|
|
530
|
+
pending[pending.length] = settlement;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async function __velarServeWaitTimeoutSettlements(request) {
|
|
534
|
+
const pending = __velarServeCall(__velarServeWeakMapGet, __velarServeTimeoutSettlements, [request]);
|
|
535
|
+
if (pending === undefined) return null;
|
|
536
|
+
__velarServeCall(__velarServeWeakMapDelete, __velarServeTimeoutSettlements, [request]);
|
|
537
|
+
for (let index = 0; index < pending.length; index += 1) await pending[index];
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function __velarServeHeaders(value, fixedName = null, fixedValue = null) {
|
|
542
|
+
const pairs = __velarServeResponseHeaders(value);
|
|
543
|
+
const output = new __velarServeMap();
|
|
544
|
+
for (let index = 0; index < pairs.length; index += 1) {
|
|
545
|
+
const name = pairs[index][0];
|
|
546
|
+
if (fixedName !== null && __velarServeCall(__velarServeStringToLowerCase, name, []) === fixedName) continue;
|
|
547
|
+
__velarServeCall(__velarServeMapSet, output, [name, pairs[index][1]]);
|
|
548
|
+
}
|
|
549
|
+
if (fixedName !== null) __velarServeCall(__velarServeMapSet, output, [fixedName, fixedValue]);
|
|
550
|
+
return output;
|
|
551
|
+
}
|
|
552
|
+
|
|
225
553
|
function __velarServeIsRequest(value) {
|
|
226
554
|
try {
|
|
227
555
|
value = __velarServePlainRecord(value, "ServeRequest");
|
|
@@ -232,17 +560,29 @@ function __velarServeIsRequest(value) {
|
|
|
232
560
|
&& typeof path === "string" && __velarServeCall(__velarServeStringStartsWith, path, ["/"]) && path.length <= __velarServeMaxPathCodeUnits
|
|
233
561
|
&& !__velarServeCall(__velarServeStringIncludes, path, ["\0"])
|
|
234
562
|
&& typeof __velarServeDataField(value, "text", "ServeRequest") === "function"
|
|
563
|
+
&& typeof __velarServeDataField(value, "bytes", "ServeRequest") === "function"
|
|
235
564
|
&& typeof __velarServeDataField(value, "json", "ServeRequest") === "function"
|
|
236
565
|
&& typeof __velarServeDataField(value, "parse", "ServeRequest") === "function";
|
|
237
566
|
if (!valid) return false;
|
|
238
567
|
__velarServeMapSnapshot(__velarServeDataField(value, "query", "ServeRequest"), "ServeRequest.query");
|
|
568
|
+
const queryAll = __velarServeOwnDescriptor(value, "queryAll");
|
|
569
|
+
if (queryAll !== undefined && (!queryAll.enumerable || !("value" in queryAll) || !__velarServeStringListMapSnapshot(queryAll.value, "ServeRequest.queryAll"))) return false;
|
|
570
|
+
const cancellation = __velarServeOwnDescriptor(value, "cancellation");
|
|
571
|
+
if (cancellation !== undefined && (!cancellation.enumerable || !("value" in cancellation) || !__velarServeCancellation.is(cancellation.value))) return false;
|
|
239
572
|
__velarServeMapSnapshot(__velarServeDataField(value, "headers", "ServeRequest"), "ServeRequest.headers");
|
|
240
573
|
return true;
|
|
241
574
|
} catch { return false; }
|
|
242
575
|
}
|
|
243
576
|
|
|
244
577
|
export const ServeRequest = __velarServeTypeObject(__velarServeIsRequest, "ServeRequest requires the request fields provided by velar/serve", __velarServeHandleNative);
|
|
578
|
+
export const Request = ServeRequest;
|
|
245
579
|
export const ServeResponse = __velarServeTypeObject(value => { __velarServeResponse(value); return true; }, "ServeResponse requires exactly one checked body");
|
|
580
|
+
export const ServeApp = __velarServeTypeObject(
|
|
581
|
+
value => __velarServeIsApp(value),
|
|
582
|
+
"ServeApp values are declared with 'server name:' or built by velar/serve composition functions",
|
|
583
|
+
null,
|
|
584
|
+
__velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{createApp: __velarCreateServeApp, createRoute: __velarCreateServeRoute, testClient: __velarServeTestClient, nativeApp: __velarServeNativeApp}]),
|
|
585
|
+
);
|
|
246
586
|
export const Server = __velarServeTypeObject(value => {
|
|
247
587
|
try {
|
|
248
588
|
value = __velarServePlainRecord(value, "Server");
|
|
@@ -251,6 +591,170 @@ export const Server = __velarServeTypeObject(value => {
|
|
|
251
591
|
} catch { return false; }
|
|
252
592
|
}, "Server requires port and stop fields");
|
|
253
593
|
|
|
594
|
+
function __velarServeIsUpload(value) {
|
|
595
|
+
if (!value || typeof value !== "object") return false;
|
|
596
|
+
const descriptor = __velarServeOwnDescriptor(value, __velarServeUploadMarker);
|
|
597
|
+
return descriptor?.enumerable === true && "value" in descriptor && descriptor.value === true;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export const Upload = __velarServeTypeObject(__velarServeIsUpload, "Upload values are created from multipart route inputs");
|
|
601
|
+
|
|
602
|
+
function __velarServeUploadValue(name, filename, contentType, data, states) {
|
|
603
|
+
if (!__velarServeBytesType.is(data)) throw new __velarServeTypeError("Upload data must be Bytes");
|
|
604
|
+
const state = {data};
|
|
605
|
+
states[states.length] = state;
|
|
606
|
+
const current = () => {
|
|
607
|
+
if (state.data === null) throw new __velarServeError("Upload lifetime ended with its request; copy bytes or save it inside the handler or a background task");
|
|
608
|
+
return state.data;
|
|
609
|
+
};
|
|
610
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
611
|
+
[__velarServeUploadMarker]: true,
|
|
612
|
+
name,
|
|
613
|
+
filename,
|
|
614
|
+
contentType,
|
|
615
|
+
size: data.byteLength,
|
|
616
|
+
text: async () => {
|
|
617
|
+
const value = current();
|
|
618
|
+
try { return __velarServeCall(__velarServeTextDecode, __velarServeUtf8Decoder, [value]); }
|
|
619
|
+
catch { throw new __velarServeTypeError("Upload is not valid UTF-8 text"); }
|
|
620
|
+
},
|
|
621
|
+
bytes: async () => __velarServeBytesType.parse(current()),
|
|
622
|
+
save: async path => { await __velarServeWriteBytes(path, current()); return null; },
|
|
623
|
+
}]);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function __velarServeIsInput(value) {
|
|
627
|
+
if (!value || typeof value !== "object") return false;
|
|
628
|
+
const descriptor = __velarServeOwnDescriptor(value, __velarServeInputMarker);
|
|
629
|
+
return descriptor?.enumerable === true && "value" in descriptor && descriptor.value === true;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function __velarServeIsProvider(value) {
|
|
633
|
+
if (!value || typeof value !== "object") return false;
|
|
634
|
+
const descriptor = __velarServeOwnDescriptor(value, __velarServeProviderMarker);
|
|
635
|
+
return descriptor?.enumerable === true && "value" in descriptor && descriptor.value === true;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export const Provider = __velarServeTypeObject(__velarServeIsProvider, "Provider values are declared with provide(...)");
|
|
639
|
+
export const RouteDocumentation = __velarServeTypeObject(value => {
|
|
640
|
+
value = __velarServeRecord(value, __velarServeRouteDocumentationFields, "RouteDocumentation");
|
|
641
|
+
if (value.documented !== undefined && typeof value.documented !== "boolean") throw new __velarServeTypeError("RouteDocumentation.documented must be bool");
|
|
642
|
+
if (value.status !== undefined && (!__velarServeIsSafeInteger(value.status) || value.status < 200 || value.status > 599)) throw new __velarServeRangeError("RouteDocumentation.status must be 200 through 599");
|
|
643
|
+
if (value.summary !== undefined) __velarServeDocumentationText(value.summary, "RouteDocumentation.summary", 1024);
|
|
644
|
+
if (value.description !== undefined) __velarServeDocumentationText(value.description, "RouteDocumentation.description", 16384);
|
|
645
|
+
if (value.tags !== undefined) __velarServeStringList(value.tags, "RouteDocumentation.tags", 32);
|
|
646
|
+
if (value.errors !== undefined) __velarServeErrorDocuments(value.errors);
|
|
647
|
+
return true;
|
|
648
|
+
}, "RouteDocumentation requires checked OpenAPI route metadata");
|
|
649
|
+
|
|
650
|
+
function __velarServeInputName(value, label) {
|
|
651
|
+
if (typeof value !== "string" || value.length > 256 || __velarServeCall(__velarServeStringIncludes, value, ["\0"])) {
|
|
652
|
+
throw new __velarServeTypeError(label + " name must be bounded text");
|
|
653
|
+
}
|
|
654
|
+
return value;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function __velarServeInputValue(source, name, fallback = __velarServeMissing, extra = null) {
|
|
658
|
+
name = __velarServeInputName(name, "input." + source);
|
|
659
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
660
|
+
[__velarServeInputMarker]: true,
|
|
661
|
+
source,
|
|
662
|
+
name,
|
|
663
|
+
hasDefault: fallback !== __velarServeMissing,
|
|
664
|
+
fallback: fallback === __velarServeMissing ? null : fallback,
|
|
665
|
+
extra,
|
|
666
|
+
}]);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function __velarServeQueryInput(name = "", fallback = __velarServeMissing) { return __velarServeInputValue("query", name, fallback); }
|
|
670
|
+
function __velarServeHeaderInput(name = "", fallback = __velarServeMissing) { return __velarServeInputValue("header", name, fallback); }
|
|
671
|
+
function __velarServeCookieInput(name = "", fallback = __velarServeMissing) { return __velarServeInputValue("cookie", name, fallback); }
|
|
672
|
+
function __velarServeFormInput(Type) {
|
|
673
|
+
Type = __velarRequireRuntimeType(Type, "input.form");
|
|
674
|
+
return __velarServeInputValue("form", "", __velarServeMissing, Type);
|
|
675
|
+
}
|
|
676
|
+
function __velarServeUploadInput(name = "", maxBytes = __velarServeMaxBodyBytes) {
|
|
677
|
+
if (!__velarServeIsSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > __velarServeMaxBodyBytes) {
|
|
678
|
+
throw new __velarServeRangeError("input.upload maxBytes must be an integer from 1 through 16777216");
|
|
679
|
+
}
|
|
680
|
+
return __velarServeInputValue("upload", name, __velarServeMissing, maxBytes);
|
|
681
|
+
}
|
|
682
|
+
function __velarServeDependencyInput(provider) {
|
|
683
|
+
if (!__velarServeIsProvider(provider)) throw new __velarServeTypeError("input.dependency requires a Provider");
|
|
684
|
+
return __velarServeInputValue("dependency", "", __velarServeMissing, provider);
|
|
685
|
+
}
|
|
686
|
+
function __velarServeRequestInput() { return __velarServeInputValue("request", ""); }
|
|
687
|
+
|
|
688
|
+
export const input = __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
689
|
+
query: __velarServeQueryInput,
|
|
690
|
+
header: __velarServeHeaderInput,
|
|
691
|
+
cookie: __velarServeCookieInput,
|
|
692
|
+
form: __velarServeFormInput,
|
|
693
|
+
upload: __velarServeUploadInput,
|
|
694
|
+
dependency: __velarServeDependencyInput,
|
|
695
|
+
request: __velarServeRequestInput,
|
|
696
|
+
}]);
|
|
697
|
+
|
|
698
|
+
function __velarServeSecurityValue(kind, details) {
|
|
699
|
+
return __velarServeInputValue("security", "", __velarServeMissing, __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{kind, ...details}]));
|
|
700
|
+
}
|
|
701
|
+
function __velarServeApiKey(name, source = "header") {
|
|
702
|
+
name = __velarServeInputName(name, "security.apiKey");
|
|
703
|
+
if (name.length === 0 || !__velarServeCall(__velarServeArrayIncludes, ["header", "query", "cookie"], [source])) {
|
|
704
|
+
throw new __velarServeTypeError("security.apiKey source must be header, query, or cookie");
|
|
705
|
+
}
|
|
706
|
+
return __velarServeSecurityValue("apiKey", {name, source});
|
|
707
|
+
}
|
|
708
|
+
function __velarServeBasic(realm = "") { return __velarServeSecurityValue("basic", {realm: __velarServeInputName(realm, "security.basic")}); }
|
|
709
|
+
function __velarServeBearer(scheme = "bearer") { return __velarServeSecurityValue("bearer", {scheme: __velarServeInputName(scheme, "security.bearer")}); }
|
|
710
|
+
function __velarServeOauth2(authorizationUrl, tokenUrl = "", scopes = []) {
|
|
711
|
+
authorizationUrl = __velarServeInputName(authorizationUrl, "security.oauth2 authorizationUrl");
|
|
712
|
+
tokenUrl = __velarServeInputName(tokenUrl, "security.oauth2 tokenUrl");
|
|
713
|
+
if (authorizationUrl.length === 0 || !__velarServeIsArray(scopes) || scopes.length > 128) throw new __velarServeTypeError("security.oauth2 requires bounded URLs and scopes");
|
|
714
|
+
const checked = [];
|
|
715
|
+
for (let index = 0; index < scopes.length; index += 1) checked[index] = __velarServeInputName(scopes[index], "security.oauth2 scope");
|
|
716
|
+
return __velarServeSecurityValue("oauth2", {authorizationUrl, tokenUrl, scopes: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [checked])});
|
|
717
|
+
}
|
|
718
|
+
function __velarServeOpenId(url) {
|
|
719
|
+
url = __velarServeInputName(url, "security.openId");
|
|
720
|
+
if (url.length === 0) throw new __velarServeTypeError("security.openId requires a discovery URL");
|
|
721
|
+
return __velarServeSecurityValue("openId", {url});
|
|
722
|
+
}
|
|
723
|
+
export const security = __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
724
|
+
apiKey: __velarServeApiKey,
|
|
725
|
+
basic: __velarServeBasic,
|
|
726
|
+
bearer: __velarServeBearer,
|
|
727
|
+
oauth2: __velarServeOauth2,
|
|
728
|
+
openId: __velarServeOpenId,
|
|
729
|
+
}]);
|
|
730
|
+
|
|
731
|
+
export function provide(inputs, resolve, scope = "request", release = null, eager = false) {
|
|
732
|
+
inputs = __velarServePlainRecord(inputs, "provide inputs");
|
|
733
|
+
if (typeof resolve !== "function" || release !== null && typeof release !== "function") throw new __velarServeTypeError("provide requires resolve and optional release functions");
|
|
734
|
+
if (scope !== "request" && scope !== "app") throw new __velarServeTypeError("provide scope must be 'request' or 'app'");
|
|
735
|
+
if (typeof eager !== "boolean") throw new __velarServeTypeError("provide eager must be bool");
|
|
736
|
+
const checked = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
737
|
+
const keys = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [inputs]);
|
|
738
|
+
if (keys.length > 64) throw new __velarServeRangeError("provide cannot declare more than 64 inputs");
|
|
739
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
740
|
+
const name = keys[index];
|
|
741
|
+
const descriptor = typeof name === "string" ? __velarServeOwnDescriptor(inputs, name) : null;
|
|
742
|
+
if (!descriptor?.enumerable || !("value" in descriptor) || !__velarServeIsInput(descriptor.value)) throw new __velarServeTypeError("provide inputs must be input or security descriptors");
|
|
743
|
+
if (scope === "app" && (descriptor.value.source !== "dependency" || descriptor.value.extra.scope !== "app")) {
|
|
744
|
+
throw new __velarServeTypeError("An app-scoped provider may depend only on app-scoped providers");
|
|
745
|
+
}
|
|
746
|
+
checked[name] = descriptor.value;
|
|
747
|
+
}
|
|
748
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
749
|
+
[__velarServeProviderMarker]: true,
|
|
750
|
+
inputs: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [checked]),
|
|
751
|
+
resolve,
|
|
752
|
+
scope,
|
|
753
|
+
release,
|
|
754
|
+
eager,
|
|
755
|
+
}]);
|
|
756
|
+
}
|
|
757
|
+
|
|
254
758
|
function __velarServeRequestPath(value) {
|
|
255
759
|
if (typeof value !== "string" || value.length === 0 || value.length > __velarServeMaxPathCodeUnits
|
|
256
760
|
|| __velarServeCall(__velarServeStringIncludes, value, ["\0"]) || __velarServeCall(__velarServeStringIncludes, value, ["\\"])) {
|
|
@@ -265,197 +769,2421 @@ export function fileResponse(root, path, fallback = null) {
|
|
|
265
769
|
}
|
|
266
770
|
path = __velarServeRequestPath(path);
|
|
267
771
|
if (fallback !== null) fallback = __velarServeRequestPath(fallback);
|
|
268
|
-
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{[__velarServeFileMarker]: true, root, path, fallback}]);
|
|
772
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{[__velarServeFileMarker]: true, root, path, fallback, headers: new __velarServeMap()}]);
|
|
269
773
|
}
|
|
270
774
|
|
|
271
|
-
function
|
|
272
|
-
|
|
273
|
-
if (!__velarServeIsSafeInteger(value.bytes) || value.bytes < 0 || value.bytes > __velarServeMaxBodyBytes || typeof value.tooLarge !== "boolean"
|
|
274
|
-
|| value.tooLarge && value.text !== null || !value.tooLarge && typeof value.text !== "string") {
|
|
275
|
-
throw new __velarServeTypeError("Node host returned an invalid request body");
|
|
276
|
-
}
|
|
277
|
-
if (value.tooLarge || value.bytes > maximum) throw new RequestBodyTooLargeError(maximum);
|
|
278
|
-
return value.text;
|
|
775
|
+
export function json(value, status = 200, headers = null) {
|
|
776
|
+
return __velarServeResponse({status, json: value, headers: __velarServeHeaders(headers)});
|
|
279
777
|
}
|
|
280
|
-
|
|
281
|
-
function
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
778
|
+
export function created(value, headers = null) { return json(value, 201, headers); }
|
|
779
|
+
export function noContent(completion = null, headers = null) {
|
|
780
|
+
if (completion !== null) throw new __velarServeTypeError("noContent completion must resolve to null");
|
|
781
|
+
return __velarServeResponse({status: 204, text: "", headers: __velarServeHeaders(headers)});
|
|
782
|
+
}
|
|
783
|
+
export function redirect(location, status = 302, headers = null) {
|
|
784
|
+
if (typeof location !== "string" || location.length === 0 || location.length > __velarServeMaxPathCodeUnits
|
|
785
|
+
|| __velarServeCall(__velarServeStringIncludes, location, ["\0"]) || __velarServeCall(__velarServeStringIncludes, location, ["\r"])
|
|
786
|
+
|| __velarServeCall(__velarServeStringIncludes, location, ["\n"])) throw new __velarServeTypeError("redirect location must be bounded single-line text");
|
|
787
|
+
if (!__velarServeIsSafeInteger(status) || status < 300 || status > 399) throw new __velarServeRangeError("redirect status must be an HTTP redirect integer from 300 through 399");
|
|
788
|
+
return __velarServeResponse({status, text: "", headers: __velarServeHeaders(headers, "location", location)});
|
|
789
|
+
}
|
|
790
|
+
export function text(value, status = 200, contentType = "text/plain; charset=utf-8", headers = null) {
|
|
791
|
+
return __velarServeResponse({status, text: value, contentType, headers: __velarServeHeaders(headers)});
|
|
792
|
+
}
|
|
793
|
+
export function stream(producer, status = 200, headers = null) {
|
|
794
|
+
return __velarServeResponse({status, stream: producer, headers: __velarServeHeaders(headers)});
|
|
795
|
+
}
|
|
796
|
+
export function sse(producer, headers = null) {
|
|
797
|
+
if (typeof producer !== "function") throw new __velarServeTypeError("sse requires an async producer");
|
|
798
|
+
const responseHeaders = __velarServeHeaders(headers, "content-type", "text/event-stream; charset=utf-8");
|
|
799
|
+
__velarServeCall(__velarServeMapSet, responseHeaders, ["cache-control", "no-cache"]);
|
|
800
|
+
return __velarServeResponse({
|
|
801
|
+
status: 200,
|
|
802
|
+
headers: responseHeaders,
|
|
803
|
+
stream: async write => {
|
|
804
|
+
const send = async event => {
|
|
805
|
+
const formatted = __velarServeSseEvent(event);
|
|
806
|
+
await write(formatted);
|
|
807
|
+
return null;
|
|
808
|
+
};
|
|
809
|
+
const result = await __velarServeCall(producer, undefined, [send]);
|
|
810
|
+
if (result !== null) throw new __velarServeTypeError("sse producer must resolve to null");
|
|
811
|
+
return null;
|
|
307
812
|
},
|
|
308
|
-
}
|
|
813
|
+
});
|
|
309
814
|
}
|
|
815
|
+
export function file(path, root = ".", fallback = null) { return fileResponse(root, path, fallback); }
|
|
310
816
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
817
|
+
function __velarServeSseEvent(value) {
|
|
818
|
+
const event = typeof value === "string" ? {data: value} : __velarServeRecord(value, __velarServeFieldMap(["data", "event", "id", "retry"]), "SSE event");
|
|
819
|
+
if (typeof event.data !== "string") throw new __velarServeTypeError("SSE event data must be text");
|
|
820
|
+
const output = [];
|
|
821
|
+
if (event.event != null) {
|
|
822
|
+
if (typeof event.event !== "string" || event.event.length === 0 || event.event.length > 1024 || /[\0\r\n]/u.test(event.event)) throw new __velarServeTypeError("SSE event name must be bounded single-line text");
|
|
823
|
+
output[output.length] = "event: " + event.event;
|
|
315
824
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const body = __velarJsonStringify(response.json);
|
|
320
|
-
if (__velarUtf8ByteLength(body) > __velarServeMaxBodyBytes) throw new __velarServeRangeError("ServeResponse.json cannot exceed 16 MiB");
|
|
321
|
-
await __velarNodeHostInvoke("serve.respond", [handle, response.status, headers, "json", body, null]);
|
|
322
|
-
return null;
|
|
825
|
+
if (event.id != null) {
|
|
826
|
+
if (typeof event.id !== "string" || event.id.length > 4096 || /[\0\r\n]/u.test(event.id)) throw new __velarServeTypeError("SSE event id must be bounded single-line text");
|
|
827
|
+
output[output.length] = "id: " + event.id;
|
|
323
828
|
}
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
-
|
|
829
|
+
if (event.retry != null) {
|
|
830
|
+
if (!__velarServeIsSafeInteger(event.retry) || event.retry < 0 || event.retry > 3_600_000) throw new __velarServeRangeError("SSE retry must be 0 through 3600000 milliseconds");
|
|
831
|
+
output[output.length] = "retry: " + event.retry;
|
|
327
832
|
}
|
|
328
|
-
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
};
|
|
335
|
-
const result = await response.stream(write);
|
|
336
|
-
if (result !== null) throw new __velarServeTypeError("ServeResponse.stream producer must resolve to null");
|
|
337
|
-
await __velarNodeHostInvoke("serve.streamEnd", [handle]);
|
|
338
|
-
return null;
|
|
833
|
+
const normalized = __velarServeCall(__velarServeArrayJoin, __velarServeCall(__velarServeStringSplit, __velarServeCall(__velarServeArrayJoin, __velarServeCall(__velarServeStringSplit, event.data, ["\r\n"]), ["\n"]), ["\r"]), ["\n"]);
|
|
834
|
+
const lines = __velarServeCall(__velarServeStringSplit, normalized, ["\n"]);
|
|
835
|
+
for (let index = 0; index < lines.length; index += 1) output[output.length] = "data: " + lines[index];
|
|
836
|
+
const formatted = __velarServeCall(__velarServeArrayJoin, output, ["\n"]) + "\n\n";
|
|
837
|
+
if (__velarUtf8ByteLength(formatted) > 1024 * 1024) throw new __velarServeRangeError("One SSE event cannot exceed 1 MiB");
|
|
838
|
+
return formatted;
|
|
339
839
|
}
|
|
340
840
|
|
|
341
|
-
function
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
841
|
+
function __velarServeResponseCopy(value, headers = null, backgroundTasks = null) {
|
|
842
|
+
value = __velarServeAutomaticResponse(value);
|
|
843
|
+
if (__velarServeIsFileResponse(value)) throw new __velarServeTypeError("This response operation does not support file responses");
|
|
844
|
+
const output = {status: value.status, headers: headers === null ? value.headers : headers};
|
|
845
|
+
if (__velarServeOwnDescriptor(value, "json")) output.json = value.json;
|
|
846
|
+
else if (__velarServeOwnDescriptor(value, "text")) { output.text = value.text; if (value.contentType != null) output.contentType = value.contentType; }
|
|
847
|
+
else output.stream = value.stream;
|
|
848
|
+
const tasks = backgroundTasks === null ? value.background : backgroundTasks;
|
|
849
|
+
if (tasks != null) output.background = tasks;
|
|
850
|
+
if (value.compression != null) output.compression = value.compression;
|
|
851
|
+
const copied = __velarServeResponse(output);
|
|
852
|
+
const cookies = __velarServeCall(__velarServeWeakMapGet, __velarServeResponseCookies, [value]);
|
|
853
|
+
if (cookies !== undefined) __velarServeCall(__velarServeWeakMapSet, __velarServeResponseCookies, [copied, cookies]);
|
|
854
|
+
return copied;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
export function background(response, task) {
|
|
858
|
+
if (typeof task !== "function") throw new __velarServeTypeError("background task must be a function");
|
|
859
|
+
response = __velarServeAutomaticResponse(response);
|
|
860
|
+
if (__velarServeIsFileResponse(response)) throw new __velarServeTypeError("background does not support file responses");
|
|
861
|
+
const tasks = [];
|
|
862
|
+
if (response.background != null) for (let index = 0; index < response.background.length; index += 1) tasks[tasks.length] = response.background[index];
|
|
863
|
+
if (tasks.length >= 64) throw new __velarServeRangeError("A response cannot have more than 64 background tasks");
|
|
864
|
+
tasks[tasks.length] = task;
|
|
865
|
+
return __velarServeResponseCopy(response, null, tasks);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
export function setCookie(response, name, value, path = "/", httpOnly = true, secure = true, sameSite = "lax", maxAge = null) {
|
|
869
|
+
if (typeof name !== "string" || name.length === 0 || name.length > 256 || !__velarServeCall(__velarServeRegExpTest, __velarServeHeaderNamePattern, [name])) throw new __velarServeTypeError("Cookie name must be a bounded token");
|
|
870
|
+
if (typeof value !== "string" || value.length > 4096 || /[\0\r\n]/u.test(value)) throw new __velarServeTypeError("Cookie value must be bounded text");
|
|
871
|
+
if (typeof path !== "string" || path.length === 0 || path.length > 4096 || !__velarServeCall(__velarServeStringStartsWith, path, ["/"]) || /[\0\r\n;]/u.test(path)) throw new __velarServeTypeError("Cookie path must be a bounded absolute path");
|
|
872
|
+
if (typeof httpOnly !== "boolean" || typeof secure !== "boolean" || !__velarServeCall(__velarServeArrayIncludes, ["lax", "strict", "none"], [sameSite])) throw new __velarServeTypeError("Cookie options are invalid");
|
|
873
|
+
if (sameSite === "none" && !secure) throw new __velarServeTypeError("SameSite=None cookies must be Secure");
|
|
874
|
+
if (maxAge !== null && (!__velarServeIsSafeInteger(maxAge) || maxAge < 0)) throw new __velarServeRangeError("Cookie maxAge must be a non-negative integer or null");
|
|
875
|
+
let cookie = name + "=" + __velarServeCall(__velarServeEncodeURIComponent, undefined, [value]) + "; Path=" + path + "; SameSite=" + (sameSite === "lax" ? "Lax" : sameSite === "strict" ? "Strict" : "None");
|
|
876
|
+
if (httpOnly) cookie += "; HttpOnly";
|
|
877
|
+
if (secure) cookie += "; Secure";
|
|
878
|
+
if (maxAge !== null) cookie += "; Max-Age=" + maxAge;
|
|
879
|
+
if (__velarUtf8ByteLength(cookie) > 8192) throw new __velarServeRangeError("Cookie cannot exceed 8 KiB");
|
|
880
|
+
response = __velarServeAutomaticResponse(response);
|
|
881
|
+
if (__velarServeIsFileResponse(response)) throw new __velarServeTypeError("setCookie does not support file responses");
|
|
882
|
+
const output = __velarServeResponseCopy(response);
|
|
883
|
+
const previous = __velarServeCall(__velarServeWeakMapGet, __velarServeResponseCookies, [response]);
|
|
884
|
+
const cookies = [];
|
|
885
|
+
if (previous !== undefined) for (let index = 0; index < previous.length; index += 1) cookies[index] = previous[index];
|
|
886
|
+
if (cookies.length >= 64) throw new __velarServeRangeError("A response cannot set more than 64 cookies");
|
|
887
|
+
cookies[cookies.length] = cookie;
|
|
888
|
+
__velarServeCall(__velarServeWeakMapSet, __velarServeResponseCookies, [output, __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [cookies])]);
|
|
351
889
|
return output;
|
|
352
890
|
}
|
|
353
891
|
|
|
354
|
-
function
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
for (const [name, value] of url.searchParams) query.set(name, value);
|
|
361
|
-
let bodyPromise = null;
|
|
362
|
-
const body = async (maxBytes = __velarServeMaxBodyBytes) => {
|
|
363
|
-
if (!__velarServeIsSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > __velarServeMaxBodyBytes) throw new __velarServeRangeError("Request body maxBytes must be an integer from 1 through 16777216");
|
|
364
|
-
if (bodyPromise === null) bodyPromise = (async () => {
|
|
365
|
-
const chunks = []; let total = 0;
|
|
366
|
-
for await (const chunk of request) { const bytes = chunk instanceof Uint8Array ? chunk : Buffer.from(chunk); total += bytes.byteLength; if (total > __velarServeMaxBodyBytes) { request.resume(); throw new RequestBodyTooLargeError(__velarServeMaxBodyBytes); } chunks.push(bytes); }
|
|
367
|
-
return Buffer.concat(chunks, total).toString("utf8");
|
|
368
|
-
})();
|
|
369
|
-
const text = await bodyPromise;
|
|
370
|
-
if (__velarUtf8ByteLength(text) > maxBytes) throw new RequestBodyTooLargeError(maxBytes);
|
|
371
|
-
return text;
|
|
372
|
-
};
|
|
373
|
-
const json = async (maxBytes = __velarServeMaxBodyBytes) => __velarJsonParse(await body(maxBytes), "ServeRequest JSON text");
|
|
374
|
-
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{method, path: url.pathname, query, headers: __velarServeNativeHeaders(request), text: body, json, parse: async (Type, maxBytes = __velarServeMaxBodyBytes) => { Type = __velarRequireRuntimeType(Type, "ServeRequest.parse"); return Type.parse(await json(maxBytes)); }}]);
|
|
892
|
+
export function clearCookie(response, name, path = "/") { return setCookie(response, name, "deleted", path, true, true, "lax", 0); }
|
|
893
|
+
|
|
894
|
+
function __velarServeIsRoute(value) {
|
|
895
|
+
if (!value || typeof value !== "object") return false;
|
|
896
|
+
const descriptor = __velarServeOwnDescriptor(value, __velarServeRouteMarker);
|
|
897
|
+
return descriptor?.enumerable === true && "value" in descriptor && descriptor.value === true;
|
|
375
898
|
}
|
|
376
899
|
|
|
377
|
-
function
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
const target = await operations.realpath(operations.resolve(root, path.startsWith("/") ? "." + path : path));
|
|
382
|
-
const relative = operations.relative(root, target);
|
|
383
|
-
if (relative.startsWith("..") || operations.isAbsolute(relative)) throw new __velarServeTypeError("fileResponse path escapes its root");
|
|
384
|
-
const info = await operations.stat(target);
|
|
385
|
-
if (!info.isFile() || info.size > 64 * 1024 * 1024) throw new __velarServeRangeError("fileResponse file exceeds 64 MiB");
|
|
386
|
-
return operations.readFile(target);
|
|
387
|
-
};
|
|
388
|
-
try { return await load(value.path); } catch (error) { if (value.fallback === null) throw error; return load(value.fallback); }
|
|
900
|
+
function __velarServeIsApp(value) {
|
|
901
|
+
if (!value || typeof value !== "object") return false;
|
|
902
|
+
const descriptor = __velarServeOwnDescriptor(value, __velarServeAppMarker);
|
|
903
|
+
return descriptor?.enumerable === true && "value" in descriptor && descriptor.value === true;
|
|
389
904
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const
|
|
401
|
-
|
|
905
|
+
|
|
906
|
+
function __velarServeRoutePath(path, name = "Route path") {
|
|
907
|
+
if (typeof path !== "string" || path.length === 0 || path.length > __velarServeMaxPathCodeUnits
|
|
908
|
+
|| !__velarServeCall(__velarServeStringStartsWith, path, ["/"]) || path.length > 1 && __velarServeCall(__velarServeStringEndsWith, path, ["/"])
|
|
909
|
+
|| __velarServeCall(__velarServeStringIncludes, path, ["//"]) || __velarServeCall(__velarServeStringIncludes, path, ["?"])
|
|
910
|
+
|| __velarServeCall(__velarServeStringIncludes, path, ["#"]) || __velarServeCall(__velarServeStringIncludes, path, ["\\"])) {
|
|
911
|
+
throw new __velarServeTypeError(name + " must be a normalized absolute URL path");
|
|
912
|
+
}
|
|
913
|
+
const segments = __velarServeCall(__velarServeStringSplit, path, ["/"]);
|
|
914
|
+
for (let index = 1; index < segments.length; index += 1) {
|
|
915
|
+
const segment = segments[index];
|
|
916
|
+
if (segment === "*" && index !== segments.length - 1) throw new __velarServeTypeError(name + " wildcard must be final");
|
|
917
|
+
if ((__velarServeCall(__velarServeStringStartsWith, segment, ["{"]) || __velarServeCall(__velarServeStringEndsWith, segment, ["}"]))
|
|
918
|
+
&& !__velarServeCall(__velarServeRegExpTest, __velarServeRouteCapturePattern, [segment])) {
|
|
919
|
+
throw new __velarServeTypeError(name + " captures use '{name:type}'");
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return path;
|
|
402
923
|
}
|
|
403
924
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
925
|
+
function __velarServeRouteShape(path) {
|
|
926
|
+
const segments = __velarServeCall(__velarServeStringSplit, path, ["/"]);
|
|
927
|
+
for (let index = 1; index < segments.length; index += 1) {
|
|
928
|
+
if (__velarServeCall(__velarServeStringStartsWith, segments[index], ["{"]) && __velarServeCall(__velarServeStringEndsWith, segments[index], ["}"])) segments[index] = "{}";
|
|
929
|
+
}
|
|
930
|
+
return __velarServeCall(__velarServeArrayJoin, segments, ["/"]);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
function __velarCreateServeRoute(method, path, parameters, handler, metadata = {}) {
|
|
934
|
+
if (typeof method !== "string" || !__velarServeCall(__velarServeArrayIncludes, ["GET", "POST", "PUT", "PATCH", "DELETE"], [method])) throw new __velarServeTypeError("Route method is invalid");
|
|
935
|
+
path = __velarServeRoutePath(path);
|
|
936
|
+
if (!__velarServeIsArray(parameters) || parameters.length > 64) throw new __velarServeTypeError("Route parameters must be a bounded list");
|
|
937
|
+
const checked = [];
|
|
938
|
+
const names = new __velarServeMap();
|
|
939
|
+
for (let index = 0; index < parameters.length; index += 1) {
|
|
940
|
+
const parameter = __velarServeRecord(parameters[index], __velarServeRouteParameterFields, "Route parameter");
|
|
941
|
+
if (typeof parameter.name !== "string" || !__velarServeCall(__velarServeRegExpTest, __velarServeRouteNamePattern, [parameter.name])
|
|
942
|
+
|| __velarServeCall(__velarServeMapHas, names, [parameter.name])) throw new __velarServeTypeError("Route parameter names must be unique identifiers");
|
|
943
|
+
if (!__velarServeCall(__velarServeArrayIncludes, ["path", "query", "body", "request", "header", "cookie", "form", "upload", "dependency", "security"], [parameter.source])
|
|
944
|
+
|| !__velarServeCall(__velarServeArrayIncludes, ["string", "number", "bool", "enum", "list", "data", "request", "upload", "dependency", "security"], [parameter.kind])
|
|
945
|
+
|| typeof parameter.required !== "boolean" || parameter.source !== "request" && typeof parameter.check !== "function") {
|
|
946
|
+
throw new __velarServeTypeError("Route parameter descriptor is invalid");
|
|
412
947
|
}
|
|
413
|
-
|
|
948
|
+
const routeInput = parameter.input == null ? null : parameter.input;
|
|
949
|
+
if (routeInput !== null && (!__velarServeIsInput(routeInput) || routeInput.source !== parameter.source)) {
|
|
950
|
+
throw new __velarServeTypeError("Route input descriptor does not agree with its compiled source");
|
|
951
|
+
}
|
|
952
|
+
const scalar = parameter.kind === "string" || parameter.kind === "number" || parameter.kind === "bool" || parameter.kind === "enum";
|
|
953
|
+
if (parameter.source === "path" && (!scalar || parameter.required !== true)
|
|
954
|
+
|| parameter.source === "query" && !scalar && parameter.kind !== "list"
|
|
955
|
+
|| parameter.source === "body" && parameter.kind !== "data"
|
|
956
|
+
|| parameter.source === "request" && (parameter.kind !== "request" || parameter.required !== true)
|
|
957
|
+
|| parameter.source === "header" && !scalar
|
|
958
|
+
|| parameter.source === "cookie" && !scalar
|
|
959
|
+
|| parameter.source === "form" && parameter.kind !== "data"
|
|
960
|
+
|| parameter.source === "upload" && parameter.kind !== "upload"
|
|
961
|
+
|| parameter.source === "dependency" && parameter.kind !== "dependency"
|
|
962
|
+
|| parameter.source === "security" && parameter.kind !== "security") {
|
|
963
|
+
throw new __velarServeTypeError("Route parameter source and kind do not agree");
|
|
964
|
+
}
|
|
965
|
+
__velarServeCall(__velarServeMapSet, names, [parameter.name, true]);
|
|
966
|
+
const schema = parameter.schema == null ? __velarServeDefaultSchema(parameter.kind) : __velarServeSchema(parameter.schema, "Route parameter schema");
|
|
967
|
+
const required = routeInput !== null && (routeInput.source === "query" || routeInput.source === "header" || routeInput.source === "cookie")
|
|
968
|
+
? !routeInput.hasDefault
|
|
969
|
+
: parameter.required;
|
|
970
|
+
checked[checked.length] = __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{name: parameter.name, source: parameter.source, kind: parameter.kind, required, check: parameter.check ?? null, schema, input: routeInput}]);
|
|
414
971
|
}
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
972
|
+
const captures = new __velarServeMap();
|
|
973
|
+
const segments = __velarServeCall(__velarServeStringSplit, path, ["/"]);
|
|
974
|
+
for (let index = 1; index < segments.length; index += 1) {
|
|
975
|
+
const segment = segments[index];
|
|
976
|
+
if (!__velarServeCall(__velarServeStringStartsWith, segment, ["{"])) continue;
|
|
977
|
+
const declaration = __velarServeCall(__velarServeStringSlice, segment, [1, -1]);
|
|
978
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, declaration, [":"]);
|
|
979
|
+
const capture = __velarServeCall(__velarServeStringSlice, declaration, [0, separator]);
|
|
980
|
+
if (__velarServeCall(__velarServeMapHas, captures, [capture])) throw new __velarServeTypeError("Route path capture names must be unique");
|
|
981
|
+
__velarServeCall(__velarServeMapSet, captures, [capture, false]);
|
|
982
|
+
}
|
|
983
|
+
for (let index = 0; index < checked.length; index += 1) {
|
|
984
|
+
const parameter = checked[index];
|
|
985
|
+
if (parameter.source !== "path") continue;
|
|
986
|
+
if (!__velarServeCall(__velarServeMapHas, captures, [parameter.name])) throw new __velarServeTypeError("A path parameter must match a path capture");
|
|
987
|
+
__velarServeCall(__velarServeMapSet, captures, [parameter.name, true]);
|
|
421
988
|
}
|
|
989
|
+
const captureIterator = __velarServeCall(__velarServeMapEntries, captures, []);
|
|
990
|
+
while (true) {
|
|
991
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, captureIterator, []);
|
|
992
|
+
if (step.done) break;
|
|
993
|
+
if (step.value[1] !== true) throw new __velarServeTypeError("Every path capture requires a checked path parameter");
|
|
994
|
+
}
|
|
995
|
+
if (typeof handler !== "function") throw new __velarServeTypeError("Route handler is invalid");
|
|
996
|
+
metadata = __velarServeRecord(metadata, __velarServeRouteMetadataFields, "Route metadata");
|
|
997
|
+
const responseSchema = metadata.responseSchema == null ? {} : __velarServeSchema(metadata.responseSchema, "Route response schema");
|
|
998
|
+
const responseContentTypes = metadata.responseContentTypes == null ? ["application/json"] : __velarServeStringList(metadata.responseContentTypes, "Route response content types", 8);
|
|
999
|
+
const maxBodyBytes = metadata.maxBodyBytes == null ? null : __velarServeBodyLimit(metadata.maxBodyBytes);
|
|
1000
|
+
const middleware = metadata.middleware == null ? [] : metadata.middleware;
|
|
1001
|
+
const documented = metadata.documented == null ? true : metadata.documented;
|
|
1002
|
+
if (typeof documented !== "boolean") throw new __velarServeTypeError("Route documented metadata must be bool");
|
|
1003
|
+
const summary = __velarServeDocumentationText(metadata.summary, "Route summary", 1024);
|
|
1004
|
+
const description = __velarServeDocumentationText(metadata.description, "Route description", 16384);
|
|
1005
|
+
const tags = metadata.tags == null ? [] : __velarServeStringList(metadata.tags, "Route documentation tags", 32);
|
|
1006
|
+
const status = metadata.status == null ? 200 : metadata.status;
|
|
1007
|
+
if (!__velarServeIsSafeInteger(status) || status < 200 || status > 599) throw new __velarServeRangeError("Route documentation status must be 200 through 599");
|
|
1008
|
+
const errors = __velarServeErrorDocuments(metadata.errors);
|
|
1009
|
+
if (!__velarServeIsArray(middleware) || middleware.length > 64) throw new __velarServeRangeError("A route cannot have more than 64 middleware functions");
|
|
1010
|
+
const checkedMiddleware = [];
|
|
1011
|
+
for (let index = 0; index < middleware.length; index += 1) {
|
|
1012
|
+
if (typeof middleware[index] !== "function") throw new __velarServeTypeError("Route middleware entries must be functions");
|
|
1013
|
+
checkedMiddleware[checkedMiddleware.length] = middleware[index];
|
|
1014
|
+
}
|
|
1015
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
1016
|
+
[__velarServeRouteMarker]: true,
|
|
1017
|
+
method,
|
|
1018
|
+
path,
|
|
1019
|
+
segments: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [segments]),
|
|
1020
|
+
parameters: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [checked]),
|
|
1021
|
+
handler,
|
|
1022
|
+
responseSchema,
|
|
1023
|
+
responseContentTypes: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [responseContentTypes]),
|
|
1024
|
+
maxBodyBytes,
|
|
1025
|
+
middleware: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [checkedMiddleware]),
|
|
1026
|
+
documented,
|
|
1027
|
+
summary,
|
|
1028
|
+
description,
|
|
1029
|
+
tags: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [tags]),
|
|
1030
|
+
status,
|
|
1031
|
+
errors,
|
|
1032
|
+
}]);
|
|
422
1033
|
}
|
|
423
1034
|
|
|
424
|
-
|
|
1035
|
+
function __velarServeDocumentationText(value, name, maximum) {
|
|
1036
|
+
if (value == null) return null;
|
|
1037
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maximum || /[\0]/u.test(value)) {
|
|
1038
|
+
throw new __velarServeTypeError(name + " must be bounded non-empty text");
|
|
1039
|
+
}
|
|
1040
|
+
return value;
|
|
1041
|
+
}
|
|
425
1042
|
|
|
426
|
-
|
|
427
|
-
if (
|
|
428
|
-
|
|
429
|
-
if (
|
|
430
|
-
throw new
|
|
1043
|
+
function __velarServeErrorDocuments(value) {
|
|
1044
|
+
if (value == null) return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [[]]);
|
|
1045
|
+
const output = [];
|
|
1046
|
+
if (__velarServeIsArray(value)) {
|
|
1047
|
+
if (value.length > 32) throw new __velarServeRangeError("Route documentation cannot declare more than 32 error responses");
|
|
1048
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1049
|
+
const item = __velarServeRecord(value[index], __velarServeErrorDocumentationFields, "Route error documentation");
|
|
1050
|
+
if (!__velarServeIsSafeInteger(item.status) || item.status < 400 || item.status > 599) throw new __velarServeRangeError("Route error status must be 400 through 599");
|
|
1051
|
+
output[output.length] = __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{status: item.status, description: __velarServeDocumentationText(item.description, "Route error description", 4096)}]);
|
|
1052
|
+
}
|
|
1053
|
+
} else {
|
|
1054
|
+
let size;
|
|
1055
|
+
let iterator;
|
|
1056
|
+
try { size = __velarServeCall(__velarServeMapSize, value, []); iterator = __velarServeCall(__velarServeMapEntries, value, []); }
|
|
1057
|
+
catch { throw new __velarServeTypeError("Route documentation errors must be a Map<number, string>"); }
|
|
1058
|
+
if (!__velarServeIsSafeInteger(size) || size < 0 || size > 32) throw new __velarServeRangeError("Route documentation cannot declare more than 32 error responses");
|
|
1059
|
+
while (true) {
|
|
1060
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, iterator, []);
|
|
1061
|
+
if (step.done) break;
|
|
1062
|
+
const item = step.value;
|
|
1063
|
+
if (!__velarServeIsArray(item) || item.length !== 2 || !__velarServeIsSafeInteger(item[0]) || item[0] < 400 || item[0] > 599) {
|
|
1064
|
+
throw new __velarServeTypeError("Route documentation errors must be a Map<number, string> with 400 through 599 status keys");
|
|
1065
|
+
}
|
|
1066
|
+
output[output.length] = __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{status: item[0], description: __velarServeDocumentationText(item[1], "Route error description", 4096)}]);
|
|
1067
|
+
}
|
|
1068
|
+
if (output.length !== size) throw new __velarServeTypeError("Route documentation errors changed while they were being read");
|
|
431
1069
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
1070
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [output]);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function __velarServeRouteMetadata(route, overrides = null) {
|
|
1074
|
+
const metadata = overrides ?? {};
|
|
1075
|
+
return {
|
|
1076
|
+
responseSchema: route.responseSchema,
|
|
1077
|
+
responseContentTypes: route.responseContentTypes,
|
|
1078
|
+
maxBodyBytes: route.maxBodyBytes,
|
|
1079
|
+
middleware: route.middleware,
|
|
1080
|
+
documented: metadata.documented ?? route.documented,
|
|
1081
|
+
summary: metadata.summary ?? route.summary,
|
|
1082
|
+
description: metadata.description ?? route.description,
|
|
1083
|
+
tags: metadata.tags ?? route.tags,
|
|
1084
|
+
status: metadata.status ?? route.status,
|
|
1085
|
+
errors: metadata.errors ?? route.errors,
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
function __velarServeSchema(value, name) {
|
|
1090
|
+
__velarServePlainRecord(value, name);
|
|
1091
|
+
const serialized = __velarJsonStringify(value);
|
|
1092
|
+
if (__velarUtf8ByteLength(serialized) > 1024 * 1024) throw new __velarServeRangeError(name + " cannot exceed 1 MiB");
|
|
1093
|
+
return __velarServeFreezeSchema(__velarJsonParse(serialized, name));
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
function __velarServeFreezeSchema(value) {
|
|
1097
|
+
if (!value || typeof value !== "object") return value;
|
|
1098
|
+
const keys = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [value]);
|
|
1099
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
1100
|
+
const descriptor = __velarServeOwnDescriptor(value, keys[index]);
|
|
1101
|
+
if (descriptor && "value" in descriptor) __velarServeFreezeSchema(descriptor.value);
|
|
1102
|
+
}
|
|
1103
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [value]);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
function __velarServeDefaultSchema(kind) {
|
|
1107
|
+
if (kind === "string" || kind === "enum") return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{type: "string"}]);
|
|
1108
|
+
if (kind === "number") return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{type: "number"}]);
|
|
1109
|
+
if (kind === "bool") return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{type: "boolean"}]);
|
|
1110
|
+
if (kind === "data") return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{type: "object"}]);
|
|
1111
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{}]);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
function __velarServeBodyLimit(value) {
|
|
1115
|
+
if (!__velarServeIsSafeInteger(value) || value < 1 || value > __velarServeMaxBodyBytes) {
|
|
1116
|
+
throw new __velarServeRangeError("bodyLimit maxBytes must be an integer from 1 through 16777216");
|
|
1117
|
+
}
|
|
1118
|
+
return value;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
function __velarServeAppValue(name, routes, lifecycles = []) {
|
|
1122
|
+
const router = __velarServeRouter(routes);
|
|
1123
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
1124
|
+
[__velarServeAppMarker]: true,
|
|
1125
|
+
name,
|
|
1126
|
+
routes: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [routes]),
|
|
1127
|
+
router,
|
|
1128
|
+
lifecycles: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [lifecycles]),
|
|
1129
|
+
}]);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
function __velarCreateServeApp(name, items) {
|
|
1133
|
+
if (typeof name !== "string" || name.length === 0 || name.length > 256) throw new __velarServeTypeError("ServeApp name must be bounded text");
|
|
1134
|
+
if (!__velarServeIsArray(items) || items.length > __velarServeMaxRoutes) throw new __velarServeTypeError("ServeApp items cannot exceed 4096 entries");
|
|
1135
|
+
const routes = [];
|
|
1136
|
+
const lifecycles = [];
|
|
1137
|
+
const shapes = new __velarServeMap();
|
|
1138
|
+
const append = route => {
|
|
1139
|
+
if (routes.length >= __velarServeMaxRoutes) throw new __velarServeRangeError("ServeApp cannot contain more than 4096 routes after composition");
|
|
1140
|
+
const key = route.method + " " + __velarServeRouteShape(route.path);
|
|
1141
|
+
if (__velarServeCall(__velarServeMapHas, shapes, [key])) throw new __velarServeTypeError("ServeApp contains conflicting route '" + key + "'");
|
|
1142
|
+
__velarServeCall(__velarServeMapSet, shapes, [key, true]);
|
|
1143
|
+
routes[routes.length] = route;
|
|
1144
|
+
};
|
|
1145
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
1146
|
+
const item = items[index];
|
|
1147
|
+
if (__velarServeIsRoute(item)) append(item);
|
|
1148
|
+
else if (__velarServeIsApp(item)) {
|
|
1149
|
+
for (let route = 0; route < item.routes.length; route += 1) append(item.routes[route]);
|
|
1150
|
+
for (let hook = 0; hook < item.lifecycles.length; hook += 1) {
|
|
1151
|
+
if (lifecycles.length >= __velarServeMaxLifecycles) throw new __velarServeRangeError("ServeApp cannot contain more than 4096 lifecycle pairs after composition");
|
|
1152
|
+
lifecycles[lifecycles.length] = item.lifecycles[hook];
|
|
1153
|
+
}
|
|
1154
|
+
} else throw new __velarServeTypeError("A server composition entry must be a ServeApp");
|
|
1155
|
+
}
|
|
1156
|
+
return __velarServeAppValue(name, routes, lifecycles);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
export function prefix(path, app) {
|
|
1160
|
+
path = __velarServeRoutePath(path, "prefix path");
|
|
1161
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("prefix requires a ServeApp");
|
|
1162
|
+
if (__velarServeCall(__velarServeStringIncludes, path, ["{"]) || __velarServeCall(__velarServeStringIncludes, path, ["*"])) {
|
|
1163
|
+
throw new __velarServeTypeError("prefix path must contain only literal path segments");
|
|
1164
|
+
}
|
|
1165
|
+
if (path === "/") return app;
|
|
1166
|
+
const routes = [];
|
|
1167
|
+
for (let index = 0; index < app.routes.length; index += 1) {
|
|
1168
|
+
const route = app.routes[index];
|
|
1169
|
+
routes[routes.length] = __velarCreateServeRoute(
|
|
1170
|
+
route.method,
|
|
1171
|
+
path + (route.path === "/" ? "" : route.path),
|
|
1172
|
+
route.parameters,
|
|
1173
|
+
route.handler,
|
|
1174
|
+
__velarServeRouteMetadata(route),
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
const output = __velarCreateServeApp(app.name, routes);
|
|
1178
|
+
return __velarServeAppValue(output.name, output.routes, app.lifecycles);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
export function staticFiles(path, root, fallback = null) {
|
|
1182
|
+
path = __velarServeRoutePath(path, "staticFiles path");
|
|
1183
|
+
if (path !== "/" && __velarServeCall(__velarServeStringEndsWith, path, ["/"])) throw new __velarServeTypeError("staticFiles path must not end with '/'");
|
|
1184
|
+
const pattern = path === "/" ? "/*" : path + "/*";
|
|
1185
|
+
const route = __velarCreateServeRoute("GET", pattern, [{name: "request", source: "request", kind: "request", required: true}], async request => {
|
|
1186
|
+
const relative = path === "/" ? request.path : __velarServeCall(__velarServeStringSlice, request.path, [path.length]);
|
|
1187
|
+
return fileResponse(root, relative || "/", fallback);
|
|
1188
|
+
}, {documented: false});
|
|
1189
|
+
return __velarCreateServeApp("static", [route]);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
export function use(app, middleware) {
|
|
1193
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("use requires a ServeApp");
|
|
1194
|
+
const additions = __velarServeIsArray(middleware) ? middleware : [middleware];
|
|
1195
|
+
if (additions.length === 0 || additions.length > 64) throw new __velarServeRangeError("use requires 1 through 64 middleware functions");
|
|
1196
|
+
for (let index = 0; index < additions.length; index += 1) if (typeof additions[index] !== "function") throw new __velarServeTypeError("use middleware entries must be functions");
|
|
1197
|
+
const routes = [];
|
|
1198
|
+
for (let index = 0; index < app.routes.length; index += 1) {
|
|
1199
|
+
const route = app.routes[index];
|
|
1200
|
+
const entries = [];
|
|
1201
|
+
for (let item = 0; item < route.middleware.length; item += 1) entries[entries.length] = route.middleware[item];
|
|
1202
|
+
for (let item = 0; item < additions.length; item += 1) entries[entries.length] = additions[item];
|
|
1203
|
+
routes[routes.length] = __velarCreateServeRoute(
|
|
1204
|
+
route.method,
|
|
1205
|
+
route.path,
|
|
1206
|
+
route.parameters,
|
|
1207
|
+
route.handler,
|
|
1208
|
+
{...__velarServeRouteMetadata(route), middleware: entries},
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
const output = __velarCreateServeApp(app.name, routes);
|
|
1212
|
+
return __velarServeAppValue(output.name, output.routes, app.lifecycles);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
export function bodyLimit(app, maxBytes) {
|
|
1216
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("bodyLimit requires a ServeApp");
|
|
1217
|
+
maxBytes = __velarServeBodyLimit(maxBytes);
|
|
1218
|
+
const routes = [];
|
|
1219
|
+
for (let index = 0; index < app.routes.length; index += 1) {
|
|
1220
|
+
const route = app.routes[index];
|
|
1221
|
+
routes[routes.length] = __velarCreateServeRoute(
|
|
1222
|
+
route.method,
|
|
1223
|
+
route.path,
|
|
1224
|
+
route.parameters,
|
|
1225
|
+
route.handler,
|
|
1226
|
+
{...__velarServeRouteMetadata(route), maxBodyBytes: maxBytes},
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
const output = __velarCreateServeApp(app.name, routes);
|
|
1230
|
+
return __velarServeAppValue(output.name, output.routes, app.lifecycles);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
export function lifecycle(app, startup = null, shutdown = null) {
|
|
1234
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("lifecycle requires a ServeApp");
|
|
1235
|
+
if (startup !== null && typeof startup !== "function" || shutdown !== null && typeof shutdown !== "function") throw new __velarServeTypeError("lifecycle hooks must be functions or null");
|
|
1236
|
+
if (app.lifecycles.length >= __velarServeMaxLifecycles) throw new __velarServeRangeError("ServeApp cannot contain more than 4096 lifecycle pairs");
|
|
1237
|
+
const lifecycles = [];
|
|
1238
|
+
for (let index = 0; index < app.lifecycles.length; index += 1) lifecycles[index] = app.lifecycles[index];
|
|
1239
|
+
lifecycles[lifecycles.length] = __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{startup, shutdown}]);
|
|
1240
|
+
return __velarServeAppValue(app.name, app.routes, lifecycles);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function __velarServeResponseWithHeaders(value, additions) {
|
|
1244
|
+
value = __velarServeAutomaticResponse(value);
|
|
1245
|
+
if (__velarServeIsFileResponse(value)) {
|
|
1246
|
+
const headers = __velarServeHeaders(value.headers);
|
|
1247
|
+
const pairs = __velarServeMapSnapshot(additions, "Middleware headers");
|
|
1248
|
+
for (let index = 0; index < pairs.length; index += 1) __velarServeMergeResponseHeader(headers, pairs[index][0], pairs[index][1]);
|
|
1249
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{[__velarServeFileMarker]: true, root: value.root, path: value.path, fallback: value.fallback, headers}]);
|
|
1250
|
+
}
|
|
1251
|
+
const headers = __velarServeHeaders(value.headers);
|
|
1252
|
+
const pairs = __velarServeMapSnapshot(additions, "Middleware headers");
|
|
1253
|
+
for (let index = 0; index < pairs.length; index += 1) __velarServeMergeResponseHeader(headers, pairs[index][0], pairs[index][1]);
|
|
1254
|
+
return __velarServeResponseCopy(value, headers);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function __velarServeMergeResponseHeader(headers, name, value) {
|
|
1258
|
+
const lower = __velarServeCall(__velarServeStringToLowerCase, name, []);
|
|
1259
|
+
const pairs = __velarServeMapSnapshot(headers, "ServeResponse.headers");
|
|
1260
|
+
let previous;
|
|
1261
|
+
for (let index = 0; index < pairs.length; index += 1) if (__velarServeCall(__velarServeStringToLowerCase, pairs[index][0], []) === lower) {
|
|
1262
|
+
previous = pairs[index][1];
|
|
1263
|
+
__velarServeCall(__velarServeMapDelete, headers, [pairs[index][0]]);
|
|
1264
|
+
}
|
|
1265
|
+
if (lower === "vary" && previous !== undefined) {
|
|
1266
|
+
const existing = __velarServeCall(__velarServeStringSplit, previous, [","]);
|
|
1267
|
+
const additions = __velarServeCall(__velarServeStringSplit, value, [","]);
|
|
1268
|
+
const normalized = [];
|
|
1269
|
+
for (let index = 0; index < existing.length; index += 1) normalized[normalized.length] = __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeStringTrim, existing[index], []), []);
|
|
1270
|
+
for (let index = 0; index < additions.length; index += 1) {
|
|
1271
|
+
const item = __velarServeCall(__velarServeStringTrim, additions[index], []);
|
|
1272
|
+
if (!__velarServeCall(__velarServeArrayIncludes, normalized, [__velarServeCall(__velarServeStringToLowerCase, item, [])])) {
|
|
1273
|
+
previous += (previous === "" ? "" : ", ") + item;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
value = previous;
|
|
1277
|
+
}
|
|
1278
|
+
__velarServeCall(__velarServeMapSet, headers, [name, value]);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
function __velarServeStringList(value, name, maximum = 128) {
|
|
1282
|
+
if (!__velarServeIsArray(value) || value.length > maximum) throw new __velarServeTypeError(name + " must be a bounded List<string>");
|
|
1283
|
+
const output = [];
|
|
1284
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1285
|
+
if (typeof value[index] !== "string" || value[index].length === 0 || value[index].length > 1024 || /[\0\r\n]/u.test(value[index])) throw new __velarServeTypeError(name + " must contain bounded single-line strings");
|
|
1286
|
+
output[index] = value[index];
|
|
1287
|
+
}
|
|
1288
|
+
return output;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function __velarServeCors(origins = ["*"], methods = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], headers = ["content-type", "authorization"], credentials = false, maxAge = 600) {
|
|
1292
|
+
origins = __velarServeStringList(origins, "middleware.cors origins");
|
|
1293
|
+
methods = __velarServeStringList(methods, "middleware.cors methods");
|
|
1294
|
+
headers = __velarServeStringList(headers, "middleware.cors headers");
|
|
1295
|
+
if (typeof credentials !== "boolean" || !__velarServeIsSafeInteger(maxAge) || maxAge < 0 || maxAge > 86400) throw new __velarServeTypeError("middleware.cors options are invalid");
|
|
1296
|
+
return async (request, next) => {
|
|
1297
|
+
const origin = __velarServeCall(__velarServeMapHas, request.headers, ["origin"]) ? __velarServeCall(__velarServeMapGet, request.headers, ["origin"]) : null;
|
|
1298
|
+
const wildcard = __velarServeCall(__velarServeArrayIncludes, origins, ["*"]);
|
|
1299
|
+
const allowed = origin !== null && (wildcard || __velarServeCall(__velarServeArrayIncludes, origins, [origin]));
|
|
1300
|
+
if (origin !== null && !allowed) return {status: 403, json: {error: "origin_not_allowed"}};
|
|
1301
|
+
const response = await next();
|
|
1302
|
+
if (!allowed) return response;
|
|
1303
|
+
const output = new __velarServeMap([
|
|
1304
|
+
["access-control-allow-origin", wildcard && !credentials ? "*" : origin],
|
|
1305
|
+
["access-control-allow-methods", __velarServeCall(__velarServeArrayJoin, methods, [", "])],
|
|
1306
|
+
["access-control-allow-headers", __velarServeCall(__velarServeArrayJoin, headers, [", "])],
|
|
1307
|
+
["access-control-max-age", __velarServeString(maxAge)],
|
|
1308
|
+
["vary", "Origin"],
|
|
1309
|
+
]);
|
|
1310
|
+
if (credentials) __velarServeCall(__velarServeMapSet, output, ["access-control-allow-credentials", "true"]);
|
|
1311
|
+
return __velarServeResponseWithHeaders(response, output);
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
function __velarServeTrustedHosts(hosts) {
|
|
1316
|
+
hosts = __velarServeStringList(hosts, "middleware.trustedHosts hosts");
|
|
1317
|
+
return async (request, next) => {
|
|
1318
|
+
const hostHeader = __velarServeCall(__velarServeMapHas, request.headers, ["host"]) ? __velarServeCall(__velarServeMapGet, request.headers, ["host"]) : "";
|
|
1319
|
+
let host;
|
|
1320
|
+
if (__velarServeCall(__velarServeStringStartsWith, hostHeader, ["["])) {
|
|
1321
|
+
const end = __velarServeCall(__velarServeStringIndexOf, hostHeader, ["]"]);
|
|
1322
|
+
host = end < 2 ? "" : __velarServeCall(__velarServeStringSlice, hostHeader, [1, end]);
|
|
1323
|
+
} else {
|
|
1324
|
+
const pieces = __velarServeCall(__velarServeStringSplit, hostHeader, [":"]);
|
|
1325
|
+
host = pieces.length === 2 ? pieces[0] : hostHeader;
|
|
1326
|
+
}
|
|
1327
|
+
host = __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeStringTrim, host, []), []);
|
|
1328
|
+
let accepted = false;
|
|
1329
|
+
for (let index = 0; index < hosts.length; index += 1) {
|
|
1330
|
+
let allowed = __velarServeCall(__velarServeStringToLowerCase, hosts[index], []);
|
|
1331
|
+
if (__velarServeCall(__velarServeStringStartsWith, allowed, ["["]) && __velarServeCall(__velarServeStringEndsWith, allowed, ["]"])) allowed = __velarServeCall(__velarServeStringSlice, allowed, [1, -1]);
|
|
1332
|
+
if (allowed === "*" || allowed === host || __velarServeCall(__velarServeStringStartsWith, allowed, ["*."]) && __velarServeCall(__velarServeStringEndsWith, host, [__velarServeCall(__velarServeStringSlice, allowed, [1])])) { accepted = true; break; }
|
|
1333
|
+
}
|
|
1334
|
+
return accepted ? await next() : {status: 400, json: {error: "untrusted_host"}};
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function __velarServeRequestId(header = "x-request-id") {
|
|
1339
|
+
header = __velarServeCall(__velarServeStringToLowerCase, __velarServeInputName(header, "middleware.requestId"), []);
|
|
1340
|
+
if (header.length === 0 || !__velarServeCall(__velarServeRegExpTest, __velarServeHeaderNamePattern, [header])) throw new __velarServeTypeError("middleware.requestId header is invalid");
|
|
1341
|
+
return async (request, next) => {
|
|
1342
|
+
let value = __velarServeCall(__velarServeMapHas, request.headers, [header]) ? __velarServeCall(__velarServeMapGet, request.headers, [header]) : null;
|
|
1343
|
+
if (value === null || value.length === 0 || value.length > 256 || /[\0\r\n]/u.test(value)) {
|
|
1344
|
+
if (!__velarServeIsSafeInteger(__velarServeNextRequestId)) __velarServeNextRequestId = 1;
|
|
1345
|
+
value = "velar-" + __velarServeCall(__velarServeDateNow, __velarServeDate, []) + "-" + __velarServeNextRequestId++;
|
|
1346
|
+
}
|
|
1347
|
+
return __velarServeResponseWithHeaders(await next(), new __velarServeMap([[header, value]]));
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
function __velarServeAccessLog(write) {
|
|
1352
|
+
if (typeof write !== "function") throw new __velarServeTypeError("middleware.accessLog requires a writer function");
|
|
1353
|
+
return async (request, next) => {
|
|
1354
|
+
const started = __velarServeCall(__velarServeDateNow, __velarServeDate, []);
|
|
1355
|
+
const response = await next();
|
|
1356
|
+
const normalized = __velarServeAutomaticResponse(response);
|
|
1357
|
+
const status = __velarServeIsFileResponse(normalized) ? 200 : normalized.status;
|
|
1358
|
+
await __velarServeCall(write, undefined, [{method: request.method, path: request.path, status, durationMs: __velarServeCall(__velarServeDateNow, __velarServeDate, []) - started}]);
|
|
1359
|
+
return response;
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
function __velarServeSecurityHeaders() {
|
|
1364
|
+
const headers = new __velarServeMap([
|
|
1365
|
+
["x-content-type-options", "nosniff"],
|
|
1366
|
+
["x-frame-options", "DENY"],
|
|
1367
|
+
["referrer-policy", "no-referrer"],
|
|
1368
|
+
["cross-origin-resource-policy", "same-origin"],
|
|
1369
|
+
]);
|
|
1370
|
+
return async (_request, next) => __velarServeResponseWithHeaders(await next(), headers);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
function __velarServeCompression(minimumBytes = 1024) {
|
|
1374
|
+
if (!__velarServeIsSafeInteger(minimumBytes) || minimumBytes < 0 || minimumBytes > __velarServeMaxBodyBytes) throw new __velarServeRangeError("middleware.compression minimumBytes is invalid");
|
|
1375
|
+
return async (request, next) => {
|
|
1376
|
+
const response = __velarServeAutomaticResponse(await next());
|
|
1377
|
+
if (__velarServeIsFileResponse(response) || __velarServeOwnDescriptor(response, "stream")) return response;
|
|
1378
|
+
const serialized = __velarServeOwnDescriptor(response, "json") ? __velarServeCall(__velarServeWeakMapGet, __velarServeSerializedJson, [response]) : null;
|
|
1379
|
+
const size = serialized !== null && serialized !== undefined ? __velarUtf8ByteLength(serialized) : __velarUtf8ByteLength(response.text);
|
|
1380
|
+
if (size < minimumBytes || !__velarServeCall(__velarServeMapHas, request.headers, ["accept-encoding"])) return response;
|
|
1381
|
+
const accepted = __velarServeCall(__velarServeStringSplit, __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeMapGet, request.headers, ["accept-encoding"]), []), [","]);
|
|
1382
|
+
let br = -1;
|
|
1383
|
+
let gzip = -1;
|
|
1384
|
+
let wildcard = -1;
|
|
1385
|
+
for (let index = 0; index < accepted.length; index += 1) {
|
|
1386
|
+
const parts = __velarServeCall(__velarServeStringSplit, __velarServeCall(__velarServeStringTrim, accepted[index], []), [";"]);
|
|
1387
|
+
const name = __velarServeCall(__velarServeStringTrim, parts[0], []);
|
|
1388
|
+
let quality = 1;
|
|
1389
|
+
for (let part = 1; part < parts.length; part += 1) {
|
|
1390
|
+
const option = __velarServeCall(__velarServeStringTrim, parts[part], []);
|
|
1391
|
+
if (__velarServeCall(__velarServeStringStartsWith, option, ["q="])) {
|
|
1392
|
+
quality = __velarServeNumber(__velarServeCall(__velarServeStringSlice, option, [2]));
|
|
1393
|
+
if (!__velarServeCall(__velarServeNumberIsFinite, __velarServeNumber, [quality]) || quality < 0 || quality > 1) quality = 0;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
if (name === "br") br = quality;
|
|
1397
|
+
else if (name === "gzip") gzip = quality;
|
|
1398
|
+
else if (name === "*") wildcard = quality;
|
|
1399
|
+
}
|
|
1400
|
+
if (br < 0) br = wildcard;
|
|
1401
|
+
if (gzip < 0) gzip = wildcard;
|
|
1402
|
+
const encoding = br > 0 && br >= gzip ? "br" : gzip > 0 ? "gzip" : null;
|
|
1403
|
+
if (encoding === null) return response;
|
|
1404
|
+
const headers = __velarServeHeaders(response.headers);
|
|
1405
|
+
__velarServeMergeResponseHeader(headers, "vary", "Accept-Encoding");
|
|
1406
|
+
const output = __velarServeResponseCopy(response, headers);
|
|
1407
|
+
const fields = {status: output.status, headers: output.headers, compression: encoding};
|
|
1408
|
+
if (__velarServeOwnDescriptor(output, "json")) fields.json = output.json;
|
|
1409
|
+
else { fields.text = output.text; if (output.contentType != null) fields.contentType = output.contentType; }
|
|
1410
|
+
if (output.background != null) fields.background = output.background;
|
|
1411
|
+
return __velarServeResponse(fields);
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
function __velarServeErrorMiddleware(handle) {
|
|
1416
|
+
if (typeof handle !== "function") throw new __velarServeTypeError("middleware.errors requires a handler function");
|
|
1417
|
+
return async (request, next) => { try { return await next(); } catch (error) { return await __velarServeCall(handle, undefined, [error, request]); } };
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
function __velarServeTimeout(milliseconds) {
|
|
1421
|
+
if (!__velarServeIsSafeInteger(milliseconds) || milliseconds < 1 || milliseconds > 3_600_000) throw new __velarServeRangeError("middleware.timeout must be 1 through 3600000 milliseconds");
|
|
1422
|
+
return async (request, next) => {
|
|
1423
|
+
if (__velarServeActiveTimeouts >= __velarServeMaxActiveTimeouts) {
|
|
1424
|
+
return {status: 503, json: {error: "server_busy"}, headers: new __velarServeMap([["retry-after", "1"]])};
|
|
1425
|
+
}
|
|
1426
|
+
__velarServeActiveTimeouts += 1;
|
|
1427
|
+
let timer = null;
|
|
1428
|
+
let detached = false;
|
|
1429
|
+
let pending;
|
|
1430
|
+
try { pending = next(); }
|
|
1431
|
+
catch (error) { __velarServeActiveTimeouts -= 1; throw error; }
|
|
1432
|
+
const expired = new __velarServePromise(resolve => { timer = __velarServeCall(__velarServeSetTimeout, globalThis, [() => resolve(__velarServeMissing), milliseconds]); });
|
|
1433
|
+
try {
|
|
1434
|
+
const result = await __velarServeCall(__velarServePromiseRace, __velarServePromise, [__velarServeCall(__velarServeObjectFreeze, __velarServeObject, [[pending, expired]])]);
|
|
1435
|
+
if (result !== __velarServeMissing) return result;
|
|
1436
|
+
detached = true;
|
|
1437
|
+
__velarServeCancellation.__velarCancel(request.cancellation, "Request timed out");
|
|
1438
|
+
__velarServeActiveBackgroundTasks += 1;
|
|
1439
|
+
const settlement = (async () => {
|
|
1440
|
+
try { await pending; }
|
|
1441
|
+
catch (error) { __velarServeReportFailure(error); }
|
|
1442
|
+
finally {
|
|
1443
|
+
__velarServeActiveTimeouts -= 1;
|
|
1444
|
+
__velarServeActiveBackgroundTasks -= 1;
|
|
1445
|
+
}
|
|
1446
|
+
return null;
|
|
1447
|
+
})();
|
|
1448
|
+
__velarServeRegisterTimeoutSettlement(request, settlement);
|
|
1449
|
+
const continuation = async () => {
|
|
1450
|
+
return await settlement;
|
|
1451
|
+
};
|
|
1452
|
+
__velarServeCall(__velarServeWeakMapSet, __velarServeReservedBackground, [continuation, true]);
|
|
1453
|
+
return {
|
|
1454
|
+
status: 504,
|
|
1455
|
+
json: {error: "request_timeout"},
|
|
1456
|
+
background: [continuation],
|
|
1457
|
+
};
|
|
1458
|
+
} finally {
|
|
1459
|
+
if (timer !== null) __velarServeCall(__velarServeClearTimeout, globalThis, [timer]);
|
|
1460
|
+
if (!detached) __velarServeActiveTimeouts -= 1;
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
function __velarServeConcurrency(maximum) {
|
|
1466
|
+
if (!__velarServeIsSafeInteger(maximum) || maximum < 1 || maximum > 4096) throw new __velarServeRangeError("middleware.concurrency maximum must be from 1 through 4096");
|
|
1467
|
+
let active = 0;
|
|
1468
|
+
return async (_request, next) => {
|
|
1469
|
+
if (active >= maximum) return {status: 503, json: {error: "server_busy"}, headers: new __velarServeMap([["retry-after", "1"]])};
|
|
1470
|
+
active += 1;
|
|
1471
|
+
try { return await next(); }
|
|
1472
|
+
finally { active -= 1; }
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
export const middleware = __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
1477
|
+
cors: __velarServeCors,
|
|
1478
|
+
trustedHosts: __velarServeTrustedHosts,
|
|
1479
|
+
requestId: __velarServeRequestId,
|
|
1480
|
+
accessLog: __velarServeAccessLog,
|
|
1481
|
+
securityHeaders: __velarServeSecurityHeaders,
|
|
1482
|
+
compression: __velarServeCompression,
|
|
1483
|
+
errors: __velarServeErrorMiddleware,
|
|
1484
|
+
timeout: __velarServeTimeout,
|
|
1485
|
+
concurrency: __velarServeConcurrency,
|
|
1486
|
+
}]);
|
|
1487
|
+
|
|
1488
|
+
function __velarServeMatch(route, actual) {
|
|
1489
|
+
const pattern = route.segments;
|
|
1490
|
+
const values = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
1491
|
+
let score = 0;
|
|
1492
|
+
for (let index = 1; index < pattern.length; index += 1) {
|
|
1493
|
+
const expected = pattern[index];
|
|
1494
|
+
if (expected === "*") return {values, score};
|
|
1495
|
+
if (index >= actual.length) return null;
|
|
1496
|
+
const received = actual[index];
|
|
1497
|
+
if (__velarServeCall(__velarServeStringStartsWith, expected, ["{"]) && __velarServeCall(__velarServeStringEndsWith, expected, ["}"])) {
|
|
1498
|
+
const declaration = __velarServeCall(__velarServeStringSlice, expected, [1, -1]);
|
|
1499
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, declaration, [":"]);
|
|
1500
|
+
values[__velarServeCall(__velarServeStringSlice, declaration, [0, separator])] = received;
|
|
1501
|
+
score += 1;
|
|
1502
|
+
} else {
|
|
1503
|
+
if (expected !== received) return null;
|
|
1504
|
+
score += 4;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return actual.length === pattern.length ? {values, score} : null;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
function __velarServeRouter(routes) {
|
|
1511
|
+
const methods = new __velarServeMap();
|
|
1512
|
+
for (let index = 0; index < routes.length; index += 1) {
|
|
1513
|
+
const route = routes[index];
|
|
1514
|
+
let buckets = __velarServeCall(__velarServeMapGet, methods, [route.method]);
|
|
1515
|
+
if (buckets === undefined) { buckets = new __velarServeMap(); __velarServeCall(__velarServeMapSet, methods, [route.method, buckets]); }
|
|
1516
|
+
const first = route.segments.length < 2 || route.segments[1] === "" ? "" : route.segments[1];
|
|
1517
|
+
const key = first === "*" || __velarServeCall(__velarServeStringStartsWith, first, ["{"]) ? "*" : first;
|
|
1518
|
+
let entries = __velarServeCall(__velarServeMapGet, buckets, [key]);
|
|
1519
|
+
if (entries === undefined) { entries = []; __velarServeCall(__velarServeMapSet, buckets, [key, entries]); }
|
|
1520
|
+
entries[entries.length] = route;
|
|
1521
|
+
}
|
|
1522
|
+
return methods;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function __velarServeRouterRoutes(app, method, actual) {
|
|
1526
|
+
const buckets = __velarServeCall(__velarServeMapGet, app.router, [method]);
|
|
1527
|
+
if (buckets === undefined) return [];
|
|
1528
|
+
const output = [];
|
|
1529
|
+
const first = actual.length < 2 ? "" : actual[1];
|
|
1530
|
+
const literal = __velarServeCall(__velarServeMapGet, buckets, [first]);
|
|
1531
|
+
if (literal !== undefined) for (let index = 0; index < literal.length; index += 1) output[output.length] = literal[index];
|
|
1532
|
+
const dynamic = __velarServeCall(__velarServeMapGet, buckets, ["*"]);
|
|
1533
|
+
if (dynamic !== undefined) for (let index = 0; index < dynamic.length; index += 1) output[output.length] = dynamic[index];
|
|
1534
|
+
return output;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
function __velarServeDecodeScalarValue(raw, kind, name) {
|
|
1538
|
+
let value = raw;
|
|
1539
|
+
if (kind === "number") {
|
|
1540
|
+
value = __velarServeNumber(raw);
|
|
1541
|
+
if (!__velarServeCall(__velarServeRegExpTest, __velarServeDecimalPattern, [raw])
|
|
1542
|
+
|| !__velarServeCall(__velarServeNumberIsFinite, __velarServeNumber, [value])) {
|
|
1543
|
+
throw new HttpError(422, {error: "invalid_request", parameter: name});
|
|
1544
|
+
}
|
|
1545
|
+
} else if (kind === "bool") {
|
|
1546
|
+
if (raw === "true") value = true;
|
|
1547
|
+
else if (raw === "false") value = false;
|
|
1548
|
+
else throw new HttpError(422, {error: "invalid_request", parameter: name});
|
|
1549
|
+
}
|
|
1550
|
+
return value;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
function __velarServeDecodeScalar(raw, parameter) {
|
|
1554
|
+
const value = __velarServeDecodeScalarValue(raw, parameter.kind, parameter.name);
|
|
1555
|
+
let valid = false;
|
|
1556
|
+
try { valid = parameter.check(value) === true; } catch {}
|
|
1557
|
+
if (!valid) throw new HttpError(422, {error: "invalid_request", parameter: parameter.name});
|
|
1558
|
+
return value;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
function __velarServeCookieValue(request, name) {
|
|
1562
|
+
if (!__velarServeCall(__velarServeMapHas, request.headers, ["cookie"])) return __velarServeMissing;
|
|
1563
|
+
const pieces = __velarServeCall(__velarServeStringSplit, __velarServeCall(__velarServeMapGet, request.headers, ["cookie"]), [";"]);
|
|
1564
|
+
for (let index = 0; index < pieces.length; index += 1) {
|
|
1565
|
+
const piece = __velarServeCall(__velarServeStringTrim, pieces[index], []);
|
|
1566
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, piece, ["="]);
|
|
1567
|
+
if (separator < 0 || __velarServeCall(__velarServeStringSlice, piece, [0, separator]) !== name) continue;
|
|
1568
|
+
const encoded = __velarServeCall(__velarServeStringSlice, piece, [separator + 1]);
|
|
1569
|
+
try { return __velarServeCall(__velarServeDecodeURIComponent, undefined, [encoded]); }
|
|
1570
|
+
catch { throw new HttpError(400, {error: "invalid_cookie", parameter: name}); }
|
|
1571
|
+
}
|
|
1572
|
+
return __velarServeMissing;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function __velarServeNamedInputRaw(descriptor, parameterName, request) {
|
|
1576
|
+
const name = descriptor.name === "" ? parameterName : descriptor.name;
|
|
1577
|
+
if (descriptor.source === "query") {
|
|
1578
|
+
if (!__velarServeCall(__velarServeMapHas, request.queryAll, [name])) return __velarServeMissing;
|
|
1579
|
+
const values = __velarServeCall(__velarServeMapGet, request.queryAll, [name]);
|
|
1580
|
+
if (values.length !== 1) throw new HttpError(422, {error: "duplicate_parameter", parameter: name});
|
|
1581
|
+
return values[0];
|
|
1582
|
+
}
|
|
1583
|
+
if (descriptor.source === "header") {
|
|
1584
|
+
const normalized = __velarServeCall(__velarServeStringToLowerCase, name, []);
|
|
1585
|
+
return __velarServeCall(__velarServeMapHas, request.headers, [normalized])
|
|
1586
|
+
? __velarServeCall(__velarServeMapGet, request.headers, [normalized])
|
|
1587
|
+
: __velarServeMissing;
|
|
1588
|
+
}
|
|
1589
|
+
if (descriptor.source === "cookie") return __velarServeCookieValue(request, name);
|
|
1590
|
+
return __velarServeMissing;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
function __velarServeUnauthorized(scheme) {
|
|
1594
|
+
throw new HttpError(401, {error: "not_authenticated"}, new __velarServeMap([["www-authenticate", scheme]]));
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
function __velarServeSecurityInput(descriptor, request) {
|
|
1598
|
+
const details = descriptor.extra;
|
|
1599
|
+
if (details.kind === "apiKey") {
|
|
1600
|
+
const carrier = __velarServeInputValue(details.source, details.name);
|
|
1601
|
+
const value = __velarServeNamedInputRaw(carrier, details.name, request);
|
|
1602
|
+
if (value === __velarServeMissing) __velarServeUnauthorized("ApiKey");
|
|
1603
|
+
return value;
|
|
1604
|
+
}
|
|
1605
|
+
const authorization = __velarServeCall(__velarServeMapHas, request.headers, ["authorization"])
|
|
1606
|
+
? __velarServeCall(__velarServeMapGet, request.headers, ["authorization"])
|
|
1607
|
+
: __velarServeMissing;
|
|
1608
|
+
if (authorization === __velarServeMissing) __velarServeUnauthorized(details.kind === "basic" ? "Basic" : "Bearer");
|
|
1609
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, authorization, [" "]);
|
|
1610
|
+
if (separator < 1) __velarServeUnauthorized(details.kind === "basic" ? "Basic" : "Bearer");
|
|
1611
|
+
const protocol = __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeStringSlice, authorization, [0, separator]), []);
|
|
1612
|
+
const credential = __velarServeCall(__velarServeStringTrim, __velarServeCall(__velarServeStringSlice, authorization, [separator + 1]), []);
|
|
1613
|
+
if (credential.length === 0) __velarServeUnauthorized(details.kind === "basic" ? "Basic" : "Bearer");
|
|
1614
|
+
if (details.kind !== "basic") {
|
|
1615
|
+
if (protocol !== "bearer") __velarServeUnauthorized("Bearer");
|
|
1616
|
+
return credential;
|
|
1617
|
+
}
|
|
1618
|
+
if (protocol !== "basic" || typeof __velarServeAtob !== "function") __velarServeUnauthorized("Basic");
|
|
1619
|
+
let decoded;
|
|
1620
|
+
try { decoded = __velarServeCall(__velarServeAtob, undefined, [credential]); }
|
|
1621
|
+
catch { __velarServeUnauthorized("Basic"); }
|
|
1622
|
+
const split = __velarServeCall(__velarServeStringIndexOf, decoded, [":"]);
|
|
1623
|
+
if (split < 0) __velarServeUnauthorized("Basic");
|
|
1624
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
1625
|
+
username: __velarServeCall(__velarServeStringSlice, decoded, [0, split]),
|
|
1626
|
+
password: __velarServeCall(__velarServeStringSlice, decoded, [split + 1]),
|
|
1627
|
+
}]);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
function __velarServeRequestContext(appState) {
|
|
1631
|
+
return {appState, cache: new __velarServeMap(), resolving: new __velarServeMap(), releases: [], providerCount: 0, form: null};
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
async function __velarServeCleanupReleases(releases) {
|
|
1635
|
+
let failure = null;
|
|
1636
|
+
for (let index = releases.length - 1; index >= 0; index -= 1) {
|
|
1637
|
+
const entry = releases[index];
|
|
1638
|
+
try { await __velarServeCall(entry.release, undefined, [entry.value]); }
|
|
1639
|
+
catch (error) { if (failure === null) failure = error; }
|
|
1640
|
+
}
|
|
1641
|
+
releases.length = 0;
|
|
1642
|
+
if (failure !== null) throw failure;
|
|
1643
|
+
return null;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
async function __velarServeCleanupRequestContext(context) {
|
|
1647
|
+
let failure = null;
|
|
1648
|
+
try { await __velarServeCleanupReleases(context.releases); }
|
|
1649
|
+
catch (error) { failure = error; }
|
|
1650
|
+
if (context.form !== null) {
|
|
1651
|
+
try { const form = await context.form; if (typeof form?.dispose === "function") form.dispose(); }
|
|
1652
|
+
catch {}
|
|
1653
|
+
context.form = null;
|
|
1654
|
+
}
|
|
1655
|
+
__velarServeCall(__velarServeMapClear, context.cache, []);
|
|
1656
|
+
__velarServeCall(__velarServeMapClear, context.resolving, []);
|
|
1657
|
+
context.providerCount = 0;
|
|
1658
|
+
if (failure !== null) throw failure;
|
|
1659
|
+
return null;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
async function __velarServeCleanupAppState(appState) {
|
|
1663
|
+
if (appState.activeRequests !== 0) throw new __velarServeError("ServeApp providers cannot close while requests are active");
|
|
1664
|
+
let failure = null;
|
|
1665
|
+
try { await __velarServeCleanupReleases(appState.releases); }
|
|
1666
|
+
catch (error) { failure = error; }
|
|
1667
|
+
__velarServeCall(__velarServeMapClear, appState.cache, []);
|
|
1668
|
+
__velarServeCall(__velarServeMapClear, appState.resolving, []);
|
|
1669
|
+
__velarServeCall(__velarServeMapClear, appState.cancellations, []);
|
|
1670
|
+
appState.providerCount = 0;
|
|
1671
|
+
appState.phase = "closed";
|
|
1672
|
+
appState.drain = null;
|
|
1673
|
+
appState.resolveDrain = null;
|
|
1674
|
+
if (failure !== null) throw failure;
|
|
1675
|
+
return null;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
async function __velarServeResolveProvider(provider, request, maxBodyBytes, context) {
|
|
1679
|
+
if (context.appState.overrides !== null && __velarServeCall(__velarServeMapHas, context.appState.overrides, [provider])) {
|
|
1680
|
+
return __velarServeCall(__velarServeMapGet, context.appState.overrides, [provider]);
|
|
1681
|
+
}
|
|
1682
|
+
const appScoped = provider.scope === "app";
|
|
1683
|
+
const cache = appScoped ? context.appState.cache : context.cache;
|
|
1684
|
+
const resolving = appScoped ? context.appState.resolving : context.resolving;
|
|
1685
|
+
const owner = appScoped ? context.appState : context;
|
|
1686
|
+
const providerLimit = appScoped ? __velarServeMaxAppProviders : __velarServeMaxRequestProviders;
|
|
1687
|
+
if (__velarServeCall(__velarServeMapHas, cache, [provider])) return await __velarServeCall(__velarServeMapGet, cache, [provider]);
|
|
1688
|
+
if (__velarServeCall(__velarServeMapHas, resolving, [provider])) throw new HttpError(500, {error: "provider_cycle"});
|
|
1689
|
+
if (owner.providerCount >= providerLimit) throw new HttpError(503, {error: "provider_budget_exhausted"});
|
|
1690
|
+
owner.providerCount += 1;
|
|
1691
|
+
__velarServeCall(__velarServeMapSet, resolving, [provider, true]);
|
|
1692
|
+
const pending = (async () => {
|
|
1693
|
+
const values = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
1694
|
+
const keys = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [provider.inputs]);
|
|
1695
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
1696
|
+
const name = keys[index];
|
|
1697
|
+
values[name] = await __velarServeResolveInput(provider.inputs[name], name, null, request, maxBodyBytes, context);
|
|
1698
|
+
}
|
|
1699
|
+
const result = await __velarServeCall(provider.resolve, undefined, [__velarServeCall(__velarServeObjectFreeze, __velarServeObject, [values])]);
|
|
1700
|
+
if (provider.release !== null) {
|
|
1701
|
+
const releases = appScoped ? context.appState.releases : context.releases;
|
|
1702
|
+
releases[releases.length] = {release: provider.release, value: result};
|
|
1703
|
+
}
|
|
1704
|
+
return result;
|
|
1705
|
+
})();
|
|
1706
|
+
__velarServeCall(__velarServeMapSet, cache, [provider, pending]);
|
|
1707
|
+
try { return await pending; }
|
|
1708
|
+
catch (error) { __velarServeCall(__velarServeMapDelete, cache, [provider]); owner.providerCount -= 1; throw error; }
|
|
1709
|
+
finally { __velarServeCall(__velarServeMapDelete, resolving, [provider]); }
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
function __velarServeBytePattern(text) {
|
|
1713
|
+
const output = new __velarServeUint8Array(text.length);
|
|
1714
|
+
for (let index = 0; index < text.length; index += 1) output[index] = text.charCodeAt(index);
|
|
1715
|
+
return output;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
function __velarServeFindBytes(data, pattern, start) {
|
|
1719
|
+
if (pattern.length === 0) return start;
|
|
1720
|
+
const prefix = new __velarServeUint8Array(pattern.length);
|
|
1721
|
+
for (let index = 1, matched = 0; index < pattern.length;) {
|
|
1722
|
+
if (pattern[index] === pattern[matched]) prefix[index++] = ++matched;
|
|
1723
|
+
else if (matched > 0) matched = prefix[matched - 1];
|
|
1724
|
+
else prefix[index++] = 0;
|
|
1725
|
+
}
|
|
1726
|
+
for (let index = start, matched = 0; index < data.length;) {
|
|
1727
|
+
if (data[index] === pattern[matched]) { index += 1; matched += 1; if (matched === pattern.length) return index - matched; }
|
|
1728
|
+
else if (matched > 0) matched = prefix[matched - 1];
|
|
1729
|
+
else index += 1;
|
|
1730
|
+
}
|
|
1731
|
+
return -1;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
function __velarServeDecodeBytes(data, start = 0, end = data.length) {
|
|
1735
|
+
try { return __velarServeCall(__velarServeTextDecode, __velarServeUtf8Decoder, [__velarServeCall(__velarServeUint8Slice, data, [start, end])]); }
|
|
1736
|
+
catch { throw new HttpError(400, {error: "invalid_form_encoding"}); }
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
function __velarServeDispositionValue(value, key) {
|
|
1740
|
+
const pieces = __velarServeCall(__velarServeStringSplit, value, [";"]);
|
|
1741
|
+
for (let index = 1; index < pieces.length; index += 1) {
|
|
1742
|
+
const item = __velarServeCall(__velarServeStringTrim, pieces[index], []);
|
|
1743
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, item, ["="]);
|
|
1744
|
+
if (separator < 1 || __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeStringSlice, item, [0, separator]), []) !== key) continue;
|
|
1745
|
+
let output = __velarServeCall(__velarServeStringTrim, __velarServeCall(__velarServeStringSlice, item, [separator + 1]), []);
|
|
1746
|
+
if (__velarServeCall(__velarServeStringStartsWith, output, ["\""]) && __velarServeCall(__velarServeStringEndsWith, output, ["\""]) && output.length >= 2) output = __velarServeCall(__velarServeStringSlice, output, [1, -1]);
|
|
1747
|
+
return output;
|
|
1748
|
+
}
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
function __velarServeAddFormField(fields, name, value) {
|
|
1753
|
+
const existing = __velarServeOwnDescriptor(fields, name);
|
|
1754
|
+
if (existing === undefined) { fields[name] = value; return; }
|
|
1755
|
+
if (!existing.enumerable || !("value" in existing)) throw new HttpError(400, {error: "invalid_form"});
|
|
1756
|
+
if (__velarServeIsArray(existing.value)) existing.value[existing.value.length] = value;
|
|
1757
|
+
else fields[name] = [existing.value, value];
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
function __velarServeMultipartHeaders(text) {
|
|
1761
|
+
const output = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
1762
|
+
const lines = __velarServeCall(__velarServeStringSplit, text, ["\r\n"]);
|
|
1763
|
+
if (lines.length > 64) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1764
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1765
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, lines[index], [":"]);
|
|
1766
|
+
if (separator < 1) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1767
|
+
const name = __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeStringTrim, __velarServeCall(__velarServeStringSlice, lines[index], [0, separator]), []), []);
|
|
1768
|
+
output[name] = __velarServeCall(__velarServeStringTrim, __velarServeCall(__velarServeStringSlice, lines[index], [separator + 1]), []);
|
|
1769
|
+
}
|
|
1770
|
+
return output;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
function __velarServeMultipart(data, boundary) {
|
|
1774
|
+
const opening = __velarServeBytePattern("--" + boundary);
|
|
1775
|
+
const separator = __velarServeBytePattern("\r\n\r\n");
|
|
1776
|
+
const delimiter = __velarServeBytePattern("\r\n--" + boundary);
|
|
1777
|
+
if (__velarServeFindBytes(data, opening, 0) !== 0) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1778
|
+
const fields = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
1779
|
+
const files = new __velarServeMap();
|
|
1780
|
+
const uploadStates = [];
|
|
1781
|
+
let position = opening.length;
|
|
1782
|
+
let parts = 0;
|
|
1783
|
+
while (position < data.length) {
|
|
1784
|
+
if (data[position] === 45 && data[position + 1] === 45) break;
|
|
1785
|
+
if (data[position] !== 13 || data[position + 1] !== 10) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1786
|
+
position += 2;
|
|
1787
|
+
const headerEnd = __velarServeFindBytes(data, separator, position);
|
|
1788
|
+
if (headerEnd < 0 || headerEnd - position > 16 * 1024) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1789
|
+
const headers = __velarServeMultipartHeaders(__velarServeDecodeBytes(data, position, headerEnd));
|
|
1790
|
+
const contentStart = headerEnd + separator.length;
|
|
1791
|
+
const contentEnd = __velarServeFindBytes(data, delimiter, contentStart);
|
|
1792
|
+
if (contentEnd < 0) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1793
|
+
const disposition = headers["content-disposition"];
|
|
1794
|
+
if (typeof disposition !== "string" || !__velarServeCall(__velarServeStringStartsWith, __velarServeCall(__velarServeStringToLowerCase, disposition, []), ["form-data"])) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1795
|
+
const name = __velarServeDispositionValue(disposition, "name");
|
|
1796
|
+
const filename = __velarServeDispositionValue(disposition, "filename");
|
|
1797
|
+
if (name === null || name.length === 0 || name.length > 256) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1798
|
+
const part = __velarServeCall(__velarServeUint8Subarray, data, [contentStart, contentEnd]);
|
|
1799
|
+
if (filename === null) {
|
|
1800
|
+
if (part.length > 1024 * 1024) throw new HttpError(413, {error: "form_field_too_large", parameter: name});
|
|
1801
|
+
__velarServeAddFormField(fields, name, __velarServeDecodeBytes(part));
|
|
1802
|
+
} else {
|
|
1803
|
+
if (filename.length > 1024 || __velarServeCall(__velarServeMapHas, files, [name])) throw new HttpError(400, {error: "invalid_multipart"});
|
|
1804
|
+
const contentType = typeof headers["content-type"] === "string" ? headers["content-type"] : "application/octet-stream";
|
|
1805
|
+
__velarServeCall(__velarServeMapSet, files, [name, __velarServeUploadValue(name, filename, contentType, part, uploadStates)]);
|
|
1806
|
+
}
|
|
1807
|
+
parts += 1;
|
|
1808
|
+
if (parts > 128) throw new HttpError(413, {error: "too_many_form_parts"});
|
|
1809
|
+
position = contentEnd + delimiter.length;
|
|
1810
|
+
}
|
|
1811
|
+
return {fields, files, dispose() { for (let index = 0; index < uploadStates.length; index += 1) uploadStates[index].data = null; data = null; }};
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
function __velarServeFormComponent(value) {
|
|
1815
|
+
const spaced = __velarServeCall(__velarServeArrayJoin, __velarServeCall(__velarServeStringSplit, value, ["+"]), [" "]);
|
|
1816
|
+
try { return __velarServeCall(__velarServeDecodeURIComponent, undefined, [spaced]); }
|
|
1817
|
+
catch { throw new HttpError(400, {error: "invalid_form_encoding"}); }
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
function __velarServeUrlEncoded(text) {
|
|
1821
|
+
const fields = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
1822
|
+
if (text === "") return {fields, files: new __velarServeMap(), dispose() {}};
|
|
1823
|
+
const pairs = __velarServeCall(__velarServeStringSplit, text, ["&"]);
|
|
1824
|
+
if (pairs.length > 128) throw new HttpError(413, {error: "too_many_form_parts"});
|
|
1825
|
+
for (let index = 0; index < pairs.length; index += 1) {
|
|
1826
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, pairs[index], ["="]);
|
|
1827
|
+
const name = __velarServeFormComponent(separator < 0 ? pairs[index] : __velarServeCall(__velarServeStringSlice, pairs[index], [0, separator]));
|
|
1828
|
+
const value = __velarServeFormComponent(separator < 0 ? "" : __velarServeCall(__velarServeStringSlice, pairs[index], [separator + 1]));
|
|
1829
|
+
if (name.length === 0 || name.length > 256 || value.length > 1024 * 1024) throw new HttpError(400, {error: "invalid_form"});
|
|
1830
|
+
__velarServeAddFormField(fields, name, value);
|
|
1831
|
+
}
|
|
1832
|
+
return {fields, files: new __velarServeMap(), dispose() {}};
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
function __velarServeMediaType(header) {
|
|
1836
|
+
const pieces = __velarServeCall(__velarServeStringSplit, header, [";"]);
|
|
1837
|
+
return {type: __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeStringTrim, pieces[0], []), []), pieces};
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
async function __velarServeRequestForm(request, maxBodyBytes, context) {
|
|
1841
|
+
if (context.form !== null) return await context.form;
|
|
1842
|
+
context.form = (async () => {
|
|
1843
|
+
const header = __velarServeCall(__velarServeMapHas, request.headers, ["content-type"]) ? __velarServeCall(__velarServeMapGet, request.headers, ["content-type"]) : "";
|
|
1844
|
+
const media = __velarServeMediaType(header);
|
|
1845
|
+
try {
|
|
1846
|
+
if (media.type === "application/x-www-form-urlencoded") return __velarServeUrlEncoded(await request.text(maxBodyBytes));
|
|
1847
|
+
if (media.type === "multipart/form-data") {
|
|
1848
|
+
let boundary = null;
|
|
1849
|
+
for (let index = 1; index < media.pieces.length; index += 1) {
|
|
1850
|
+
const item = __velarServeCall(__velarServeStringTrim, media.pieces[index], []);
|
|
1851
|
+
if (!__velarServeCall(__velarServeStringStartsWith, __velarServeCall(__velarServeStringToLowerCase, item, []), ["boundary="])) continue;
|
|
1852
|
+
boundary = __velarServeCall(__velarServeStringSlice, item, [9]);
|
|
1853
|
+
if (__velarServeCall(__velarServeStringStartsWith, boundary, ["\""]) && __velarServeCall(__velarServeStringEndsWith, boundary, ["\""])) boundary = __velarServeCall(__velarServeStringSlice, boundary, [1, -1]);
|
|
1854
|
+
}
|
|
1855
|
+
if (boundary === null || boundary.length === 0 || boundary.length > 128 || /[\0\r\n]/u.test(boundary)) throw new HttpError(400, {error: "invalid_multipart_boundary"});
|
|
1856
|
+
return __velarServeMultipart(await request.bytes(maxBodyBytes), boundary);
|
|
1857
|
+
}
|
|
1858
|
+
} catch (error) { if (error instanceof RequestBodyTooLargeError) throw new HttpError(413, {error: "request_too_large"}); throw error; }
|
|
1859
|
+
throw new HttpError(415, {error: "unsupported_media_type", expected: "multipart/form-data or application/x-www-form-urlencoded"});
|
|
1860
|
+
})();
|
|
1861
|
+
return await context.form;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
function __velarServeCoerceForm(fields, schema) {
|
|
1865
|
+
const output = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
1866
|
+
const keys = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [fields]);
|
|
1867
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
1868
|
+
const name = keys[index];
|
|
1869
|
+
const value = fields[name];
|
|
1870
|
+
const property = schema?.properties?.[name];
|
|
1871
|
+
const values = __velarServeIsArray(value) ? value : [value];
|
|
1872
|
+
if (__velarServeIsArray(value) && property?.type !== "array") throw new HttpError(422, {error: "duplicate_parameter", parameter: name});
|
|
1873
|
+
const item = property?.type === "array" ? property.items : property;
|
|
1874
|
+
const converted = [];
|
|
1875
|
+
for (let itemIndex = 0; itemIndex < values.length; itemIndex += 1) {
|
|
1876
|
+
if (item?.type === "number") {
|
|
1877
|
+
const number = __velarServeCall(__velarServeNumber, undefined, [values[itemIndex]]);
|
|
1878
|
+
if (!__velarServeCall(__velarServeRegExpTest, __velarServeDecimalPattern, [values[itemIndex]]) || !__velarServeCall(__velarServeNumberIsFinite, __velarServeNumber, [number])) throw new HttpError(422, {error: "invalid_request", parameter: name});
|
|
1879
|
+
converted[converted.length] = number;
|
|
1880
|
+
} else if (item?.type === "boolean") {
|
|
1881
|
+
if (values[itemIndex] !== "true" && values[itemIndex] !== "false") throw new HttpError(422, {error: "invalid_request", parameter: name});
|
|
1882
|
+
converted[converted.length] = values[itemIndex] === "true";
|
|
1883
|
+
} else converted[converted.length] = values[itemIndex];
|
|
1884
|
+
}
|
|
1885
|
+
output[name] = property?.type === "array" ? __velarServeValidateDenseList(converted, "Form List input") : converted[0];
|
|
1886
|
+
}
|
|
1887
|
+
return output;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
async function __velarServeResolveInput(descriptor, parameterName, parameter, request, maxBodyBytes, context) {
|
|
1891
|
+
if (descriptor.source === "request") return request;
|
|
1892
|
+
if (descriptor.source === "dependency") return await __velarServeResolveProvider(descriptor.extra, request, maxBodyBytes, context);
|
|
1893
|
+
if (descriptor.source === "security") return __velarServeSecurityInput(descriptor, request);
|
|
1894
|
+
if (descriptor.source === "form") {
|
|
1895
|
+
const form = await __velarServeRequestForm(request, maxBodyBytes, context);
|
|
1896
|
+
const value = __velarServeCoerceForm(form.fields, parameter?.schema ?? null);
|
|
1897
|
+
try { return descriptor.extra.parse(value); }
|
|
1898
|
+
catch { throw new HttpError(422, {error: "invalid_request", parameter: parameterName}); }
|
|
1899
|
+
}
|
|
1900
|
+
if (descriptor.source === "upload") {
|
|
1901
|
+
const form = await __velarServeRequestForm(request, maxBodyBytes, context);
|
|
1902
|
+
const name = descriptor.name === "" ? parameterName : descriptor.name;
|
|
1903
|
+
if (!__velarServeCall(__velarServeMapHas, form.files, [name])) throw new HttpError(422, {error: "missing_parameter", parameter: name});
|
|
1904
|
+
const upload = __velarServeCall(__velarServeMapGet, form.files, [name]);
|
|
1905
|
+
if (upload.size > descriptor.extra) throw new HttpError(413, {error: "upload_too_large", parameter: name});
|
|
1906
|
+
return upload;
|
|
1907
|
+
}
|
|
1908
|
+
const raw = __velarServeNamedInputRaw(descriptor, parameterName, request);
|
|
1909
|
+
if (raw === __velarServeMissing) {
|
|
1910
|
+
if (descriptor.hasDefault) return descriptor.fallback;
|
|
1911
|
+
throw new HttpError(422, {error: "missing_parameter", parameter: descriptor.name === "" ? parameterName : descriptor.name});
|
|
1912
|
+
}
|
|
1913
|
+
return parameter === null ? raw : __velarServeDecodeScalar(raw, parameter);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
async function __velarServeRouteArguments(route, match, request, maxBodyBytes, context) {
|
|
1917
|
+
const values = [];
|
|
1918
|
+
let body = undefined;
|
|
1919
|
+
for (let index = 0; index < route.parameters.length; index += 1) {
|
|
1920
|
+
const parameter = route.parameters[index];
|
|
1921
|
+
if (parameter.source === "request") { values[values.length] = request; continue; }
|
|
1922
|
+
if (parameter.input !== null) {
|
|
1923
|
+
values[values.length] = await __velarServeResolveInput(parameter.input, parameter.name, parameter, request, maxBodyBytes, context);
|
|
1924
|
+
continue;
|
|
1925
|
+
}
|
|
1926
|
+
if (parameter.source === "body") {
|
|
1927
|
+
if (body === undefined) {
|
|
1928
|
+
if (!__velarServeJsonContentType(request.headers)) {
|
|
1929
|
+
throw new HttpError(415, {error: "unsupported_media_type", expected: "application/json"});
|
|
1930
|
+
}
|
|
1931
|
+
try { body = await request.json(maxBodyBytes); }
|
|
1932
|
+
catch (error) {
|
|
1933
|
+
if (error instanceof RequestBodyTooLargeError) throw new HttpError(413, {error: "request_too_large"});
|
|
1934
|
+
throw new HttpError(400, {error: "invalid_json"});
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
let valid = false;
|
|
1938
|
+
try { valid = parameter.check(body) === true; } catch {}
|
|
1939
|
+
if (!valid) throw new HttpError(422, {error: "invalid_request", parameter: parameter.name});
|
|
1940
|
+
values[values.length] = body;
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
const present = parameter.source === "path"
|
|
1944
|
+
? __velarServeOwnDescriptor(match.values, parameter.name) !== undefined
|
|
1945
|
+
: __velarServeCall(__velarServeMapHas, request.queryAll, [parameter.name]);
|
|
1946
|
+
if (!present) {
|
|
1947
|
+
if (parameter.required) throw new HttpError(422, {error: "missing_parameter", parameter: parameter.name});
|
|
1948
|
+
values[values.length] = undefined;
|
|
1949
|
+
continue;
|
|
1950
|
+
}
|
|
1951
|
+
if (parameter.source === "query") {
|
|
1952
|
+
const queryValues = __velarServeCall(__velarServeMapGet, request.queryAll, [parameter.name]);
|
|
1953
|
+
if (parameter.kind === "list") {
|
|
1954
|
+
const decoded = [];
|
|
1955
|
+
const itemSchema = parameter.schema?.items ?? {};
|
|
1956
|
+
const itemKind = itemSchema.type === "number" ? "number" : itemSchema.type === "boolean" ? "bool" : "string";
|
|
1957
|
+
for (let item = 0; item < queryValues.length; item += 1) decoded[item] = __velarServeDecodeScalarValue(queryValues[item], itemKind, parameter.name);
|
|
1958
|
+
__velarServeValidateDenseList(decoded, "Query List input");
|
|
1959
|
+
let valid = false;
|
|
1960
|
+
try { valid = parameter.check(decoded) === true; } catch {}
|
|
1961
|
+
if (!valid) throw new HttpError(422, {error: "invalid_request", parameter: parameter.name});
|
|
1962
|
+
values[values.length] = decoded;
|
|
1963
|
+
continue;
|
|
1964
|
+
}
|
|
1965
|
+
if (queryValues.length !== 1) throw new HttpError(422, {error: "duplicate_parameter", parameter: parameter.name});
|
|
1966
|
+
values[values.length] = __velarServeDecodeScalar(queryValues[0], parameter);
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
values[values.length] = __velarServeDecodeScalar(match.values[parameter.name], parameter);
|
|
1970
|
+
}
|
|
1971
|
+
return values;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
function __velarServeJsonContentType(headers) {
|
|
1975
|
+
if (!__velarServeCall(__velarServeMapHas, headers, ["content-type"])) return true;
|
|
1976
|
+
const header = __velarServeCall(__velarServeMapGet, headers, ["content-type"]);
|
|
1977
|
+
const mediaType = __velarServeCall(__velarServeStringToLowerCase, __velarServeCall(__velarServeStringTrim, header, []), []);
|
|
1978
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, mediaType, [";"]);
|
|
1979
|
+
const value = separator < 0 ? mediaType : __velarServeCall(__velarServeStringTrim, __velarServeCall(__velarServeStringSlice, mediaType, [0, separator]), []);
|
|
1980
|
+
return value === "application/json"
|
|
1981
|
+
|| __velarServeCall(__velarServeStringStartsWith, value, ["application/"]) && __velarServeCall(__velarServeStringEndsWith, value, ["+json"]);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
function __velarServeAutomaticResponse(value) {
|
|
1985
|
+
if (__velarServeIsFileResponse(value)) return value;
|
|
1986
|
+
try { return __velarServeResponse(value); }
|
|
1987
|
+
catch { return __velarServeResponse({status: 200, json: value}); }
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
async function __velarServeHandleAppResponse(app, request, maxBodyBytes, context) {
|
|
1991
|
+
try {
|
|
1992
|
+
const actual = __velarServeCall(__velarServeStringSplit, request.path, ["/"]);
|
|
1993
|
+
const candidates = [];
|
|
1994
|
+
const allowed = new __velarServeMap();
|
|
1995
|
+
let pathOwner = null;
|
|
1996
|
+
const declared = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
1997
|
+
for (let methodIndex = 0; methodIndex < declared.length; methodIndex += 1) {
|
|
1998
|
+
const routes = __velarServeRouterRoutes(app, declared[methodIndex], actual);
|
|
1999
|
+
for (let index = 0; index < routes.length; index += 1) {
|
|
2000
|
+
const route = routes[index];
|
|
2001
|
+
const match = __velarServeMatch(route, actual);
|
|
2002
|
+
if (!match) continue;
|
|
2003
|
+
if (pathOwner === null || match.score > pathOwner.match.score) pathOwner = {route, match};
|
|
2004
|
+
__velarServeCall(__velarServeMapSet, allowed, [route.method, true]);
|
|
2005
|
+
if (route.method === request.method || request.method === "HEAD" && route.method === "GET") candidates[candidates.length] = {route, match};
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
if (request.method === "OPTIONS" && __velarServeCall(__velarServeMapSize, allowed, []) > 0) {
|
|
2009
|
+
const methods = __velarServeAllowedMethods(allowed);
|
|
2010
|
+
return await __velarServeApplyMiddleware(pathOwner.route, request, async () => ({status: 204, text: "", headers: new __velarServeMap([["allow", __velarServeCall(__velarServeArrayJoin, methods, [", "])]])}));
|
|
2011
|
+
}
|
|
2012
|
+
if (candidates.length === 0) {
|
|
2013
|
+
if (__velarServeCall(__velarServeMapSize, allowed, []) > 0) {
|
|
2014
|
+
const methods = __velarServeAllowedMethods(allowed);
|
|
2015
|
+
return await __velarServeApplyMiddleware(pathOwner.route, request, async () => ({status: 405, json: {error: "method_not_allowed"}, headers: new __velarServeMap([["allow", __velarServeCall(__velarServeArrayJoin, methods, [", "])]])}));
|
|
2016
|
+
}
|
|
2017
|
+
return {status: 404, json: {error: "not_found"}};
|
|
2018
|
+
}
|
|
2019
|
+
let selected = candidates[0];
|
|
2020
|
+
for (let index = 1; index < candidates.length; index += 1) if (candidates[index].match.score > selected.match.score) selected = candidates[index];
|
|
2021
|
+
const routeBodyBytes = selected.route.maxBodyBytes === null || selected.route.maxBodyBytes > maxBodyBytes
|
|
2022
|
+
? maxBodyBytes
|
|
2023
|
+
: selected.route.maxBodyBytes;
|
|
2024
|
+
const invokeRoute = async () => __velarServeAutomaticResponse(await __velarServeCall(selected.route.handler, undefined, await __velarServeRouteArguments(selected.route, selected.match, request, routeBodyBytes, context)));
|
|
2025
|
+
return await __velarServeApplyMiddleware(selected.route, request, invokeRoute);
|
|
2026
|
+
} catch (error) {
|
|
2027
|
+
if (error instanceof HttpError) return {status: error.status, json: error.body, headers: error.headers};
|
|
2028
|
+
throw error;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
function __velarServeAppState(overrides = null) {
|
|
2033
|
+
return {
|
|
2034
|
+
cache: new __velarServeMap(), resolving: new __velarServeMap(), releases: [], providerCount: 0, overrides,
|
|
2035
|
+
phase: "open", activeRequests: 0, cancellations: new __velarServeMap(), drain: null, resolveDrain: null,
|
|
2036
|
+
startedLifecycles: 0,
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
function __velarServeBeginAppRequest(appState, cancellation) {
|
|
2041
|
+
if (appState.phase !== "open") return false;
|
|
2042
|
+
appState.activeRequests += 1;
|
|
2043
|
+
__velarServeCall(__velarServeMapSet, appState.cancellations, [cancellation, true]);
|
|
2044
|
+
return true;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
function __velarServeEndAppRequest(appState, cancellation) {
|
|
2048
|
+
if (appState.activeRequests < 1) throw new __velarServeError("ServeApp request ownership is unbalanced");
|
|
2049
|
+
appState.activeRequests -= 1;
|
|
2050
|
+
__velarServeCall(__velarServeMapDelete, appState.cancellations, [cancellation]);
|
|
2051
|
+
if (appState.activeRequests === 0 && appState.resolveDrain !== null) {
|
|
2052
|
+
const resolve = appState.resolveDrain;
|
|
2053
|
+
appState.resolveDrain = null;
|
|
2054
|
+
resolve(null);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
async function __velarServeDrainAppState(appState, grace = __velarServeDefaultShutdownGrace) {
|
|
2059
|
+
if (!__velarServeIsSafeInteger(grace) || grace < 1 || grace > 120_000) throw new __velarServeRangeError("Server shutdown grace must be 1 through 120000 milliseconds");
|
|
2060
|
+
if (appState.phase === "closed") return null;
|
|
2061
|
+
appState.phase = "closing";
|
|
2062
|
+
const cancellations = __velarServeCall(__velarServeMapEntries, appState.cancellations, []);
|
|
2063
|
+
while (true) {
|
|
2064
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, cancellations, []);
|
|
2065
|
+
if (step.done) break;
|
|
2066
|
+
__velarServeCancellation.__velarCancel(step.value[0], "Server is stopping");
|
|
2067
|
+
}
|
|
2068
|
+
if (appState.activeRequests === 0) return null;
|
|
2069
|
+
if (appState.drain === null) {
|
|
2070
|
+
appState.drain = new __velarServePromise(resolve => { appState.resolveDrain = resolve; });
|
|
2071
|
+
}
|
|
2072
|
+
let timer = null;
|
|
2073
|
+
const deadline = new __velarServePromise((_resolve, reject) => { timer = __velarServeCall(__velarServeSetTimeout, globalThis, [() => reject(new __velarServeError("ServeApp requests did not drain before the shutdown deadline")), grace]); });
|
|
2074
|
+
try { await __velarServeCall(__velarServePromiseRace, __velarServePromise, [__velarServeCall(__velarServeObjectFreeze, __velarServeObject, [[appState.drain, deadline]])]); }
|
|
2075
|
+
finally { if (timer !== null) __velarServeCall(__velarServeClearTimeout, globalThis, [timer]); }
|
|
2076
|
+
return null;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
function __velarServeCollectEagerProvider(provider, providers, seen) {
|
|
2080
|
+
if (__velarServeCall(__velarServeMapHas, seen, [provider])) return;
|
|
2081
|
+
__velarServeCall(__velarServeMapSet, seen, [provider, true]);
|
|
2082
|
+
const keys = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [provider.inputs]);
|
|
2083
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
2084
|
+
const descriptor = provider.inputs[keys[index]];
|
|
2085
|
+
if (descriptor.source === "dependency") __velarServeCollectEagerProvider(descriptor.extra, providers, seen);
|
|
2086
|
+
}
|
|
2087
|
+
if (provider.scope === "app" && provider.eager) providers[providers.length] = provider;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
async function __velarServeInitializeEagerProviders(app, maxBodyBytes, appState) {
|
|
2091
|
+
const providers = [];
|
|
2092
|
+
const seen = new __velarServeMap();
|
|
2093
|
+
for (let routeIndex = 0; routeIndex < app.routes.length; routeIndex += 1) {
|
|
2094
|
+
const parameters = app.routes[routeIndex].parameters;
|
|
2095
|
+
for (let parameterIndex = 0; parameterIndex < parameters.length; parameterIndex += 1) {
|
|
2096
|
+
const descriptor = parameters[parameterIndex].input;
|
|
2097
|
+
if (descriptor !== null && descriptor.source === "dependency") __velarServeCollectEagerProvider(descriptor.extra, providers, seen);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
const context = __velarServeRequestContext(appState);
|
|
2101
|
+
for (let index = 0; index < providers.length; index += 1) await __velarServeResolveProvider(providers[index], null, maxBodyBytes, context);
|
|
2102
|
+
return null;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
async function __velarServeRunStartup(app, appState) {
|
|
2106
|
+
for (let index = appState.startedLifecycles; index < app.lifecycles.length; index += 1) {
|
|
2107
|
+
const hook = app.lifecycles[index].startup;
|
|
2108
|
+
if (hook !== null) {
|
|
2109
|
+
const result = await __velarServeCall(hook, undefined, []);
|
|
2110
|
+
if (result !== null) throw new __velarServeTypeError("A lifecycle startup hook must resolve to null");
|
|
2111
|
+
}
|
|
2112
|
+
appState.startedLifecycles = index + 1;
|
|
2113
|
+
}
|
|
2114
|
+
return null;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
async function __velarServeRunShutdown(app, appState) {
|
|
2118
|
+
let failure = null;
|
|
2119
|
+
for (let index = appState.startedLifecycles - 1; index >= 0; index -= 1) {
|
|
2120
|
+
appState.startedLifecycles = index;
|
|
2121
|
+
const hook = app.lifecycles[index].shutdown;
|
|
2122
|
+
if (hook === null) continue;
|
|
2123
|
+
try {
|
|
2124
|
+
const result = await __velarServeCall(hook, undefined, []);
|
|
2125
|
+
if (result !== null) throw new __velarServeTypeError("A lifecycle shutdown hook must resolve to null");
|
|
2126
|
+
} catch (error) { if (failure === null) failure = error; }
|
|
2127
|
+
}
|
|
2128
|
+
if (failure !== null) throw failure;
|
|
2129
|
+
return null;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
async function __velarServeFinishApp(app, appState) {
|
|
2133
|
+
let failure = null;
|
|
2134
|
+
try { await __velarServeCleanupAppState(appState); } catch (error) { failure = error; }
|
|
2135
|
+
if (__velarServeIsApp(app)) {
|
|
2136
|
+
try { await __velarServeRunShutdown(app, appState); } catch (error) { if (failure === null) failure = error; }
|
|
2137
|
+
}
|
|
2138
|
+
if (failure !== null) throw failure;
|
|
2139
|
+
return null;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
async function __velarServeFinishAppAfterDrain(app, appState) {
|
|
2143
|
+
if (appState.activeRequests > 0) {
|
|
2144
|
+
if (appState.drain === null) appState.drain = new __velarServePromise(resolve => { appState.resolveDrain = resolve; });
|
|
2145
|
+
await appState.drain;
|
|
2146
|
+
}
|
|
2147
|
+
return await __velarServeFinishApp(app, appState);
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
async function __velarServeHandleApp(app, request, maxBodyBytes, appState) {
|
|
2151
|
+
if (!__velarServeBeginAppRequest(appState, request.cancellation)) {
|
|
2152
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
2153
|
+
[__velarServeManagedResponseMarker]: true,
|
|
2154
|
+
response: {status: 503, json: {error: "server_stopping"}, headers: new __velarServeMap([["connection", "close"]])},
|
|
2155
|
+
cleanup: async () => null,
|
|
2156
|
+
}]);
|
|
2157
|
+
}
|
|
2158
|
+
const context = __velarServeRequestContext(appState);
|
|
2159
|
+
try {
|
|
2160
|
+
const response = await __velarServeHandleAppResponse(app, request, maxBodyBytes, context);
|
|
2161
|
+
let cleaned = false;
|
|
2162
|
+
const cleanup = async () => {
|
|
2163
|
+
if (cleaned) return null;
|
|
2164
|
+
cleaned = true;
|
|
2165
|
+
try {
|
|
2166
|
+
await __velarServeWaitTimeoutSettlements(request);
|
|
2167
|
+
return await __velarServeCleanupRequestContext(context);
|
|
2168
|
+
}
|
|
2169
|
+
finally { __velarServeEndAppRequest(appState, request.cancellation); }
|
|
2170
|
+
};
|
|
2171
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{[__velarServeManagedResponseMarker]: true, response, cleanup}]);
|
|
2172
|
+
} catch (error) {
|
|
2173
|
+
try {
|
|
2174
|
+
await __velarServeWaitTimeoutSettlements(request);
|
|
2175
|
+
await __velarServeCleanupRequestContext(context);
|
|
2176
|
+
}
|
|
2177
|
+
catch (cleanupError) { __velarServeReportFailure(cleanupError); }
|
|
2178
|
+
finally { __velarServeEndAppRequest(appState, request.cancellation); }
|
|
2179
|
+
throw error;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
async function __velarServeHandleFunction(handler, request, appState) {
|
|
2184
|
+
if (!__velarServeBeginAppRequest(appState, request.cancellation)) return {status: 503, json: {error: "server_stopping"}};
|
|
2185
|
+
try {
|
|
2186
|
+
const response = await __velarServeCall(handler, undefined, [request]);
|
|
2187
|
+
let cleaned = false;
|
|
2188
|
+
const cleanup = async () => { if (cleaned) return null; cleaned = true; __velarServeEndAppRequest(appState, request.cancellation); return null; };
|
|
2189
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{[__velarServeManagedResponseMarker]: true, response, cleanup}]);
|
|
2190
|
+
} catch (error) {
|
|
2191
|
+
__velarServeEndAppRequest(appState, request.cancellation);
|
|
2192
|
+
throw error;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
async function __velarServeNativeApp(app, maxBodyBytes = __velarServeMaxBodyBytes) {
|
|
2197
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("Native HTTP composition requires a ServeApp");
|
|
2198
|
+
maxBodyBytes = __velarServeBodyLimit(maxBodyBytes);
|
|
2199
|
+
const appState = __velarServeAppState();
|
|
2200
|
+
try {
|
|
2201
|
+
await __velarServeRunStartup(app, appState);
|
|
2202
|
+
await __velarServeInitializeEagerProviders(app, maxBodyBytes, appState);
|
|
2203
|
+
} catch (error) {
|
|
2204
|
+
try { await __velarServeCleanupAppState(appState); } catch (cleanupError) { __velarServeReportFailure(cleanupError); }
|
|
2205
|
+
try { await __velarServeRunShutdown(app, appState); } catch (shutdownError) { __velarServeReportFailure(shutdownError); }
|
|
2206
|
+
throw error;
|
|
2207
|
+
}
|
|
2208
|
+
let closing = null;
|
|
2209
|
+
let finalized = null;
|
|
2210
|
+
const close = async (grace = __velarServeDefaultShutdownGrace) => {
|
|
2211
|
+
if (finalized !== null) return finalized;
|
|
2212
|
+
if (closing !== null) return closing;
|
|
2213
|
+
const attempt = (async () => {
|
|
2214
|
+
try { await __velarServeDrainAppState(appState, grace); }
|
|
2215
|
+
catch (error) {
|
|
2216
|
+
if (finalized === null) finalized = __velarServeFinishAppAfterDrain(app, appState);
|
|
2217
|
+
__velarServeCall(__velarServePromiseThen, finalized, [() => null, failure => __velarServeReportFailure(failure)]);
|
|
2218
|
+
throw error;
|
|
2219
|
+
}
|
|
2220
|
+
finalized = __velarServeFinishApp(app, appState);
|
|
2221
|
+
return await finalized;
|
|
2222
|
+
})();
|
|
2223
|
+
closing = attempt;
|
|
2224
|
+
try { return await attempt; }
|
|
2225
|
+
finally { if (closing === attempt) closing = null; }
|
|
2226
|
+
};
|
|
2227
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
2228
|
+
handle: request => __velarServeHandleApp(app, request, maxBodyBytes, appState),
|
|
2229
|
+
close,
|
|
2230
|
+
}]);
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
async function __velarServeApplyMiddleware(route, request, handler) {
|
|
2234
|
+
let next = handler;
|
|
2235
|
+
for (let index = route.middleware.length - 1; index >= 0; index -= 1) {
|
|
2236
|
+
const downstream = next;
|
|
2237
|
+
const current = route.middleware[index];
|
|
2238
|
+
next = async () => {
|
|
2239
|
+
let called = false;
|
|
2240
|
+
const guarded = async () => {
|
|
2241
|
+
if (called) throw new __velarServeError("A middleware next function can be called only once per request");
|
|
2242
|
+
called = true;
|
|
2243
|
+
return downstream();
|
|
2244
|
+
};
|
|
2245
|
+
return __velarServeAutomaticResponse(await current(request, guarded));
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
return await next();
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
function __velarServeAllowedMethods(allowed) {
|
|
2252
|
+
const methods = [];
|
|
2253
|
+
const declared = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
2254
|
+
for (let index = 0; index < declared.length; index += 1) {
|
|
2255
|
+
if (__velarServeCall(__velarServeMapHas, allowed, [declared[index]])) methods[methods.length] = declared[index];
|
|
2256
|
+
}
|
|
2257
|
+
if (__velarServeCall(__velarServeMapHas, allowed, ["GET"])) methods[methods.length] = "HEAD";
|
|
2258
|
+
methods[methods.length] = "OPTIONS";
|
|
2259
|
+
return methods;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
export function openapi(app, title = null, version = "1.0.0") {
|
|
2263
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("openapi requires a ServeApp");
|
|
2264
|
+
if (title === null) title = app.name;
|
|
2265
|
+
if (typeof title !== "string" || title.length === 0 || title.length > 256
|
|
2266
|
+
|| typeof version !== "string" || version.length === 0 || version.length > 256) {
|
|
2267
|
+
throw new __velarServeTypeError("openapi title and version must be bounded text");
|
|
2268
|
+
}
|
|
2269
|
+
const paths = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
2270
|
+
const operationIds = new __velarServeMap();
|
|
2271
|
+
const securitySchemes = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
2272
|
+
const securityNames = new __velarServeMap();
|
|
2273
|
+
for (let index = 0; index < app.routes.length; index += 1) {
|
|
2274
|
+
const route = app.routes[index];
|
|
2275
|
+
if (!route.documented) continue;
|
|
2276
|
+
const parameters = [];
|
|
2277
|
+
let requestBody = null;
|
|
2278
|
+
const formProperties = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
2279
|
+
const formRequired = [];
|
|
2280
|
+
let hasUpload = false;
|
|
2281
|
+
const operationSecurity = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
2282
|
+
const securitySeen = new __velarServeMap();
|
|
2283
|
+
let documentsValidationFailure = false;
|
|
2284
|
+
let documentsMalformedInput = false;
|
|
2285
|
+
let documentsBoundedBody = false;
|
|
2286
|
+
let documentsMediaType = false;
|
|
2287
|
+
for (let item = 0; item < route.parameters.length; item += 1) {
|
|
2288
|
+
const parameter = route.parameters[item];
|
|
2289
|
+
if (parameter.source === "request") continue;
|
|
2290
|
+
if (parameter.source !== "dependency" && parameter.source !== "security") documentsValidationFailure = true;
|
|
2291
|
+
if (parameter.source === "cookie" || parameter.source === "body" || parameter.source === "form" || parameter.source === "upload") documentsMalformedInput = true;
|
|
2292
|
+
if (parameter.source === "body" || parameter.source === "form" || parameter.source === "upload") {
|
|
2293
|
+
documentsBoundedBody = true;
|
|
2294
|
+
documentsMediaType = true;
|
|
2295
|
+
}
|
|
2296
|
+
const schema = __velarServeSchema(parameter.schema, "OpenAPI parameter schema");
|
|
2297
|
+
if (parameter.source === "body") requestBody = {required: parameter.required, content: {"application/json": {schema}}};
|
|
2298
|
+
else if (parameter.source === "form" || parameter.source === "upload") {
|
|
2299
|
+
if (parameter.source === "upload") hasUpload = true;
|
|
2300
|
+
const name = parameter.input?.name || parameter.name;
|
|
2301
|
+
if (parameter.source === "form" && schema.type === "object" && schema.properties && typeof schema.properties === "object") {
|
|
2302
|
+
const propertyNames = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [schema.properties]);
|
|
2303
|
+
for (let propertyIndex = 0; propertyIndex < propertyNames.length; propertyIndex += 1) {
|
|
2304
|
+
const propertyName = propertyNames[propertyIndex];
|
|
2305
|
+
if (__velarServeOwnDescriptor(formProperties, propertyName)) throw new __velarServeTypeError("OpenAPI form inputs contain conflicting field '" + propertyName + "'");
|
|
2306
|
+
formProperties[propertyName] = schema.properties[propertyName];
|
|
2307
|
+
}
|
|
2308
|
+
if (__velarServeIsArray(schema.required)) {
|
|
2309
|
+
for (let requiredIndex = 0; requiredIndex < schema.required.length; requiredIndex += 1) {
|
|
2310
|
+
if (!__velarServeCall(__velarServeArrayIncludes, formRequired, [schema.required[requiredIndex]])) formRequired[formRequired.length] = schema.required[requiredIndex];
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
} else {
|
|
2314
|
+
if (__velarServeOwnDescriptor(formProperties, name)) throw new __velarServeTypeError("OpenAPI form inputs contain conflicting field '" + name + "'");
|
|
2315
|
+
formProperties[name] = parameter.source === "upload" ? {type: "string", format: "binary"} : schema;
|
|
2316
|
+
if (parameter.required) formRequired[formRequired.length] = name;
|
|
2317
|
+
}
|
|
2318
|
+
} else if (parameter.source === "security" || parameter.source === "dependency") {
|
|
2319
|
+
if (parameter.input !== null) __velarServeOpenApiInputSecurity(parameter.input, securitySchemes, securityNames, operationSecurity, securitySeen);
|
|
2320
|
+
} else {
|
|
2321
|
+
const name = parameter.input?.name || parameter.name;
|
|
2322
|
+
parameters[parameters.length] = {name, in: parameter.source, required: parameter.source === "path" || parameter.required, schema, ...(parameter.source === "query" && schema.type === "array" ? {style: "form", explode: true} : {})};
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
if (__velarServeCall(__velarServeOwnKeys, __velarServeReflect, [formProperties]).length > 0) {
|
|
2326
|
+
const schema = {type: "object", properties: formProperties};
|
|
2327
|
+
if (formRequired.length > 0) schema.required = formRequired;
|
|
2328
|
+
requestBody = {required: formRequired.length > 0, content: hasUpload ? {"multipart/form-data": {schema}} : {"multipart/form-data": {schema}, "application/x-www-form-urlencoded": {schema}}};
|
|
2329
|
+
}
|
|
2330
|
+
const documentPath = __velarServeOpenApiPath(route.path);
|
|
2331
|
+
const operationBase = __velarServeOperationId(route.method, documentPath);
|
|
2332
|
+
const operationCount = __velarServeCall(__velarServeMapGet, operationIds, [operationBase]) ?? 0;
|
|
2333
|
+
__velarServeCall(__velarServeMapSet, operationIds, [operationBase, operationCount + 1]);
|
|
2334
|
+
const operation = {
|
|
2335
|
+
operationId: operationCount === 0 ? operationBase : operationBase + "_" + (operationCount + 1),
|
|
2336
|
+
parameters,
|
|
2337
|
+
responses: __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]),
|
|
2338
|
+
};
|
|
2339
|
+
const responseSchema = __velarServeSchema(route.responseSchema, "OpenAPI response schema");
|
|
2340
|
+
const responseContent = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]);
|
|
2341
|
+
for (let contentIndex = 0; contentIndex < route.responseContentTypes.length; contentIndex += 1) responseContent[route.responseContentTypes[contentIndex]] = {schema: responseSchema};
|
|
2342
|
+
operation.responses[__velarServeString(route.status)] = route.status === 204 || route.status === 304
|
|
2343
|
+
? {description: "Successful response"}
|
|
2344
|
+
: {description: "Successful response", content: responseContent};
|
|
2345
|
+
if (__velarServeCall(__velarServeOwnKeys, __velarServeReflect, [operationSecurity]).length > 0) {
|
|
2346
|
+
operation.responses["401"] = __velarServeOpenApiFailure("Authentication required");
|
|
2347
|
+
}
|
|
2348
|
+
if (documentsMalformedInput) operation.responses["400"] = __velarServeOpenApiFailure("Malformed request input");
|
|
2349
|
+
if (documentsBoundedBody) operation.responses["413"] = __velarServeOpenApiFailure("Request body or upload is too large");
|
|
2350
|
+
if (documentsMediaType) operation.responses["415"] = __velarServeOpenApiFailure("Unsupported request content type");
|
|
2351
|
+
if (documentsValidationFailure) operation.responses["422"] = __velarServeOpenApiFailure("Request validation failed");
|
|
2352
|
+
for (let errorIndex = 0; errorIndex < route.errors.length; errorIndex += 1) {
|
|
2353
|
+
const error = route.errors[errorIndex];
|
|
2354
|
+
operation.responses[__velarServeString(error.status)] = {description: error.description};
|
|
2355
|
+
}
|
|
2356
|
+
if (route.summary !== null) operation.summary = route.summary;
|
|
2357
|
+
if (route.description !== null) operation.description = route.description;
|
|
2358
|
+
if (route.tags.length > 0) operation.tags = route.tags;
|
|
2359
|
+
if (requestBody) operation.requestBody = requestBody;
|
|
2360
|
+
if (__velarServeCall(__velarServeOwnKeys, __velarServeReflect, [operationSecurity]).length > 0) operation.security = [operationSecurity];
|
|
2361
|
+
const entry = paths[documentPath] ?? (paths[documentPath] = __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null]));
|
|
2362
|
+
entry[__velarServeCall(__velarServeStringToLowerCase, route.method, [])] = operation;
|
|
2363
|
+
}
|
|
2364
|
+
const document = {openapi: "3.1.0", info: {title, version}, paths};
|
|
2365
|
+
if (__velarServeCall(__velarServeOwnKeys, __velarServeReflect, [securitySchemes]).length > 0) document.components = {securitySchemes};
|
|
2366
|
+
if (__velarUtf8ByteLength(__velarJsonStringify(document)) > __velarServeMaxBodyBytes) throw new __velarServeRangeError("OpenAPI document cannot exceed 16 MiB");
|
|
2367
|
+
return document;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
function __velarServeOpenApiFailure(description) {
|
|
2371
|
+
return {
|
|
2372
|
+
description,
|
|
2373
|
+
content: {"application/json": {schema: {
|
|
2374
|
+
type: "object",
|
|
2375
|
+
properties: {error: {type: "string"}},
|
|
2376
|
+
required: ["error"],
|
|
2377
|
+
additionalProperties: true,
|
|
2378
|
+
}}},
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
function __velarServeOpenApiInputSecurity(descriptor, schemes, names, requirements, seen) {
|
|
2383
|
+
if (descriptor.source === "dependency") {
|
|
2384
|
+
const provider = descriptor.extra;
|
|
2385
|
+
if (__velarServeCall(__velarServeMapHas, seen, [provider])) return;
|
|
2386
|
+
__velarServeCall(__velarServeMapSet, seen, [provider, true]);
|
|
2387
|
+
const keys = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [provider.inputs]);
|
|
2388
|
+
for (let index = 0; index < keys.length; index += 1) __velarServeOpenApiInputSecurity(provider.inputs[keys[index]], schemes, names, requirements, seen);
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
if (descriptor.source !== "security") return;
|
|
2392
|
+
const details = descriptor.extra;
|
|
2393
|
+
const identity = __velarJsonStringify(details);
|
|
2394
|
+
let name = __velarServeCall(__velarServeMapGet, names, [identity]);
|
|
2395
|
+
if (name == null) {
|
|
2396
|
+
name = "security" + (__velarServeCall(__velarServeMapSize, names, []) + 1);
|
|
2397
|
+
__velarServeCall(__velarServeMapSet, names, [identity, name]);
|
|
2398
|
+
if (details.kind === "apiKey") schemes[name] = {type: "apiKey", name: details.name, in: details.source};
|
|
2399
|
+
else if (details.kind === "basic") schemes[name] = {type: "http", scheme: "basic"};
|
|
2400
|
+
else if (details.kind === "bearer") schemes[name] = {type: "http", scheme: "bearer"};
|
|
2401
|
+
else if (details.kind === "oauth2") {
|
|
2402
|
+
const flow = {authorizationUrl: details.authorizationUrl, scopes: __velarServeCall(__velarServeObjectCreate, __velarServeObject, [null])};
|
|
2403
|
+
if (details.tokenUrl !== "") flow.tokenUrl = details.tokenUrl;
|
|
2404
|
+
for (let index = 0; index < details.scopes.length; index += 1) flow.scopes[details.scopes[index]] = details.scopes[index];
|
|
2405
|
+
schemes[name] = {type: "oauth2", flows: {authorizationCode: flow}};
|
|
2406
|
+
} else schemes[name] = {type: "openIdConnect", openIdConnectUrl: details.url};
|
|
2407
|
+
}
|
|
2408
|
+
requirements[name] = descriptor.extra.kind === "oauth2" ? descriptor.extra.scopes : [];
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
export function docs(app, title = null, version = "1.0.0", path = "/docs", openapiPath = "/openapi.json", routes = null) {
|
|
2412
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("docs requires a ServeApp");
|
|
2413
|
+
path = __velarServeRoutePath(path, "docs path");
|
|
2414
|
+
openapiPath = __velarServeRoutePath(openapiPath, "docs OpenAPI path");
|
|
2415
|
+
if (path === openapiPath || /[{}*<>]/u.test(path) || /[{}*<>]/u.test(openapiPath)) throw new __velarServeTypeError("docs paths must be distinct literal URL paths");
|
|
2416
|
+
app = __velarServeDocumentRoutes(app, routes);
|
|
2417
|
+
const document = openapi(app, title, version);
|
|
2418
|
+
const schemaRoute = __velarCreateServeRoute("GET", openapiPath, [], async () => json(document), {documented: false});
|
|
2419
|
+
const html = "<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Velar API Docs</title><style>body{font:15px system-ui;margin:0;background:#0b1020;color:#e8ecf4}main{max-width:960px;margin:auto;padding:32px}article{background:#151c31;border:1px solid #2a3555;border-radius:12px;padding:16px;margin:12px 0}code{color:#8ed7ff}.method{font-weight:700;color:#9cf0b3}button{background:#5b7cff;color:white;border:0;border-radius:7px;padding:8px 12px}</style></head><body><main><h1 id=\"title\">API</h1><p>OpenAPI 3.1 · bundled offline documentation</p><section id=\"routes\"></section></main><script>fetch(" + __velarJsonStringify(openapiPath) + ").then(function(r){return r.json()}).then(function(d){document.getElementById('title').textContent=d.info.title+' '+d.info.version;var root=document.getElementById('routes');Object.keys(d.paths).forEach(function(p){Object.keys(d.paths[p]).forEach(function(m){var a=document.createElement('article');var h=document.createElement('div');h.innerHTML='<span class=\"method\">'+m.toUpperCase()+'</span> <code></code>';h.querySelector('code').textContent=p;a.appendChild(h);var b=document.createElement('button');b.textContent='Try request';b.onclick=function(){fetch(p,{method:m.toUpperCase()}).then(function(r){return r.text().then(function(t){alert(r.status+' '+t)})})};a.appendChild(b);root.appendChild(a)})})}).catch(function(e){document.getElementById('routes').textContent=String(e)});</script></body></html>";
|
|
2420
|
+
const uiRoute = __velarCreateServeRoute("GET", path, [], async () => text(html, 200, "text/html; charset=utf-8"), {documented: false});
|
|
2421
|
+
return __velarCreateServeApp(app.name, [app, schemaRoute, uiRoute]);
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
function __velarServeDocumentRoutes(app, documentation) {
|
|
2425
|
+
if (documentation == null) return app;
|
|
2426
|
+
let size;
|
|
2427
|
+
let iterator;
|
|
2428
|
+
try { size = __velarServeCall(__velarServeMapSize, documentation, []); iterator = __velarServeCall(__velarServeMapEntries, documentation, []); }
|
|
2429
|
+
catch { throw new __velarServeTypeError("docs routes must be a Map<string, route documentation>"); }
|
|
2430
|
+
if (!__velarServeIsSafeInteger(size) || size < 0 || size > app.routes.length || size > __velarServeMaxRoutes) throw new __velarServeRangeError("docs routes cannot exceed the application route count or 4096 entries");
|
|
2431
|
+
const configured = new __velarServeMap();
|
|
2432
|
+
while (true) {
|
|
2433
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, iterator, []);
|
|
2434
|
+
if (step.done) break;
|
|
2435
|
+
if (!__velarServeIsArray(step.value) || step.value.length !== 2 || typeof step.value[0] !== "string" || step.value[0].length > 4352) {
|
|
2436
|
+
throw new __velarServeTypeError("docs routes must be a Map with bounded 'METHOD /path' string keys");
|
|
2437
|
+
}
|
|
2438
|
+
const metadata = __velarServeRecord(step.value[1], __velarServeRouteDocumentationFields, "Route documentation");
|
|
2439
|
+
if (metadata.documented !== undefined && typeof metadata.documented !== "boolean") throw new __velarServeTypeError("Route documentation documented must be bool");
|
|
2440
|
+
if (metadata.status !== undefined && (!__velarServeIsSafeInteger(metadata.status) || metadata.status < 200 || metadata.status > 599)) throw new __velarServeRangeError("Route documentation status must be 200 through 599");
|
|
2441
|
+
const normalized = {
|
|
2442
|
+
...(metadata.summary === undefined ? {} : {summary: __velarServeDocumentationText(metadata.summary, "Route summary", 1024)}),
|
|
2443
|
+
...(metadata.description === undefined ? {} : {description: __velarServeDocumentationText(metadata.description, "Route description", 16384)}),
|
|
2444
|
+
...(metadata.tags === undefined ? {} : {tags: __velarServeStringList(metadata.tags, "Route documentation tags", 32)}),
|
|
2445
|
+
...(metadata.status === undefined ? {} : {status: metadata.status}),
|
|
2446
|
+
...(metadata.errors === undefined ? {} : {errors: __velarServeErrorDocuments(metadata.errors)}),
|
|
2447
|
+
...(metadata.documented === undefined ? {} : {documented: metadata.documented}),
|
|
2448
|
+
};
|
|
2449
|
+
__velarServeCall(__velarServeMapSet, configured, [step.value[0], normalized]);
|
|
2450
|
+
}
|
|
2451
|
+
if (__velarServeCall(__velarServeMapSize, configured, []) !== size) throw new __velarServeTypeError("docs routes changed while they were being read");
|
|
2452
|
+
const seen = new __velarServeMap();
|
|
2453
|
+
const output = [];
|
|
2454
|
+
for (let index = 0; index < app.routes.length; index += 1) {
|
|
2455
|
+
const route = app.routes[index];
|
|
2456
|
+
const internalKey = route.method + " " + route.path;
|
|
2457
|
+
const publicKey = route.method + " " + __velarServeOpenApiPath(route.path);
|
|
2458
|
+
const key = __velarServeCall(__velarServeMapHas, configured, [internalKey]) ? internalKey
|
|
2459
|
+
: __velarServeCall(__velarServeMapHas, configured, [publicKey]) ? publicKey : null;
|
|
2460
|
+
if (key === null) { output[output.length] = route; continue; }
|
|
2461
|
+
__velarServeCall(__velarServeMapSet, seen, [key, true]);
|
|
2462
|
+
output[output.length] = __velarCreateServeRoute(route.method, route.path, route.parameters, route.handler, __velarServeRouteMetadata(route, __velarServeCall(__velarServeMapGet, configured, [key])));
|
|
2463
|
+
}
|
|
2464
|
+
if (__velarServeCall(__velarServeMapSize, seen, []) !== size) throw new __velarServeTypeError("docs routes contains a route that the application does not declare");
|
|
2465
|
+
return __velarServeAppValue(app.name, output, app.lifecycles);
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
function __velarServeOpenApiPath(path) {
|
|
2469
|
+
const segments = __velarServeCall(__velarServeStringSplit, path, ["/"]);
|
|
2470
|
+
for (let index = 1; index < segments.length; index += 1) {
|
|
2471
|
+
const segment = segments[index];
|
|
2472
|
+
if (!__velarServeCall(__velarServeStringStartsWith, segment, ["{"])) continue;
|
|
2473
|
+
const declaration = __velarServeCall(__velarServeStringSlice, segment, [1, -1]);
|
|
2474
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, declaration, [":"]);
|
|
2475
|
+
segments[index] = "{" + __velarServeCall(__velarServeStringSlice, declaration, [0, separator]) + "}";
|
|
2476
|
+
}
|
|
2477
|
+
return __velarServeCall(__velarServeArrayJoin, segments, ["/"]);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
function __velarServeOperationId(method, path) {
|
|
2481
|
+
let output = __velarServeCall(__velarServeStringToLowerCase, method, []);
|
|
2482
|
+
let separated = false;
|
|
2483
|
+
for (let index = 0; index < path.length; index += 1) {
|
|
2484
|
+
const character = path[index];
|
|
2485
|
+
if (__velarServeCall(__velarServeRegExpTest, __velarServeOperationIdCharacterPattern, [character])) {
|
|
2486
|
+
output += character;
|
|
2487
|
+
separated = false;
|
|
2488
|
+
} else if (!separated) {
|
|
2489
|
+
output += "_";
|
|
2490
|
+
separated = true;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
return output;
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
function __velarServeBodyResult(value, maximum) {
|
|
2497
|
+
value = __velarServeRecord(value, __velarServeBodyFields, "ServeRequest body result");
|
|
2498
|
+
if (!__velarServeIsSafeInteger(value.bytes) || value.bytes < 0 || value.bytes > __velarServeMaxBodyBytes || typeof value.tooLarge !== "boolean"
|
|
2499
|
+
|| value.tooLarge && value.text !== null || !value.tooLarge && typeof value.text !== "string") {
|
|
2500
|
+
throw new __velarServeTypeError("Node host returned an invalid request body");
|
|
2501
|
+
}
|
|
2502
|
+
if (value.tooLarge || value.bytes > maximum) throw new RequestBodyTooLargeError(maximum);
|
|
2503
|
+
return value.text;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
function __velarServeBodyBytesResult(value, maximum) {
|
|
2507
|
+
value = __velarServeRecord(value, __velarServeBodyBytesFields, "ServeRequest byte body result");
|
|
2508
|
+
if (!__velarServeIsSafeInteger(value.bytes) || value.bytes < 0 || value.bytes > __velarServeMaxBodyBytes || typeof value.tooLarge !== "boolean"
|
|
2509
|
+
|| value.tooLarge && value.data !== null || !value.tooLarge && !__velarServeBytesType.is(value.data)) {
|
|
2510
|
+
throw new __velarServeTypeError("Node host returned an invalid request byte body");
|
|
2511
|
+
}
|
|
2512
|
+
if (value.tooLarge || value.bytes > maximum) throw new RequestBodyTooLargeError(maximum);
|
|
2513
|
+
return value.data;
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
function __velarServeRequest(value) {
|
|
2517
|
+
value = __velarServeRecord(value, __velarServeRequestFields, "Node serve request event");
|
|
2518
|
+
if (!__velarServeIsSafeInteger(value.token) || value.token < 1 || !__velarServeIsSafeInteger(value.request) || value.request < 1
|
|
2519
|
+
|| typeof value.method !== "string" || value.method.length === 0 || value.method.length > 32
|
|
2520
|
+
|| !__velarServeCall(__velarServeRegExpTest, __velarServeMethodPattern, [value.method])
|
|
2521
|
+
|| typeof value.path !== "string" || !__velarServeCall(__velarServeStringStartsWith, value.path, ["/"]) || value.path.length > __velarServeMaxPathCodeUnits) {
|
|
2522
|
+
throw new __velarServeTypeError("Node host returned an invalid serve request");
|
|
2523
|
+
}
|
|
2524
|
+
const handle = value.request;
|
|
2525
|
+
const cancellation = __velarServeCancellation.__velarCreate();
|
|
2526
|
+
__velarServeCall(__velarServeMapSet, __velarServeHostCancellations, [handle, {token: value.token, cancellation}]);
|
|
2527
|
+
const query = __velarServePairsMaps(value.query, "ServeRequest.query");
|
|
2528
|
+
let bytesPromise = null;
|
|
2529
|
+
const bytes = async (maxBytes = __velarServeMaxBodyBytes) => {
|
|
2530
|
+
if (!__velarServeIsSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > __velarServeMaxBodyBytes) {
|
|
2531
|
+
throw new __velarServeRangeError("Request body maxBytes must be an integer from 1 through 16777216");
|
|
2532
|
+
}
|
|
2533
|
+
if (bytesPromise === null) bytesPromise = __velarNodeHostInvoke("serve.bodyBytes", [handle, maxBytes]);
|
|
2534
|
+
return __velarServeBodyBytesResult(await bytesPromise, maxBytes);
|
|
2535
|
+
};
|
|
2536
|
+
const body = async (maxBytes = __velarServeMaxBodyBytes) => {
|
|
2537
|
+
const data = await bytes(maxBytes);
|
|
2538
|
+
try { return __velarServeCall(__velarServeTextDecode, __velarServeUtf8Decoder, [data]); }
|
|
2539
|
+
catch { throw new __velarServeTypeError("Request body must be valid UTF-8 text"); }
|
|
2540
|
+
};
|
|
2541
|
+
const json = async (maxBytes = __velarServeMaxBodyBytes) => __velarJsonParse(await body(maxBytes), "ServeRequest JSON text");
|
|
2542
|
+
return {token: value.token, handle, request: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
2543
|
+
method: value.method,
|
|
2544
|
+
path: value.path,
|
|
2545
|
+
query: query.values,
|
|
2546
|
+
queryAll: query.all,
|
|
2547
|
+
headers: __velarServePairsMap(value.headers, "ServeRequest.headers"),
|
|
2548
|
+
cancellation,
|
|
2549
|
+
text: body,
|
|
2550
|
+
bytes,
|
|
2551
|
+
json,
|
|
2552
|
+
parse: async (Type, maxBytes = __velarServeMaxBodyBytes) => {
|
|
2553
|
+
Type = __velarRequireRuntimeType(Type, "ServeRequest.parse");
|
|
2554
|
+
return Type.parse(await json(maxBytes));
|
|
2555
|
+
},
|
|
2556
|
+
}])};
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
async function __velarServeWriteResponse(handle, value) {
|
|
2560
|
+
let cleanup = null;
|
|
2561
|
+
let backgroundTasks = null;
|
|
2562
|
+
const managed = value && typeof value === "object" ? __velarServeOwnDescriptor(value, __velarServeManagedResponseMarker) : null;
|
|
2563
|
+
if (managed?.enumerable === true && "value" in managed && managed.value === true) {
|
|
2564
|
+
cleanup = value.cleanup;
|
|
2565
|
+
value = value.response;
|
|
2566
|
+
}
|
|
2567
|
+
try {
|
|
2568
|
+
if (__velarServeIsFileResponse(value)) {
|
|
2569
|
+
const headers = __velarServeResponseHeaders(value.headers);
|
|
2570
|
+
await __velarServeWithOutbound(__velarServeHeaderPairBytes(headers), () => __velarNodeHostInvoke("serve.respondFile", [handle, value.root, value.path, value.fallback, headers, []]));
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
const response = __velarServeResponse(value);
|
|
2574
|
+
backgroundTasks = response.background ?? null;
|
|
2575
|
+
const headers = __velarServeResponseHeaders(response.headers);
|
|
2576
|
+
const cookies = __velarServeCookies(response);
|
|
2577
|
+
let metadataBytes = __velarServeHeaderPairBytes(headers);
|
|
2578
|
+
for (let index = 0; index < cookies.length; index += 1) metadataBytes += __velarUtf8ByteLength(cookies[index]);
|
|
2579
|
+
if (__velarServeOwnDescriptor(response, "json")) {
|
|
2580
|
+
const body = __velarServeCall(__velarServeWeakMapGet, __velarServeSerializedJson, [response]);
|
|
2581
|
+
if (__velarUtf8ByteLength(body) > __velarServeMaxBodyBytes) throw new __velarServeRangeError("ServeResponse.json cannot exceed 16 MiB");
|
|
2582
|
+
await __velarServeWithOutbound(metadataBytes + __velarUtf8ByteLength(body), () => __velarNodeHostInvoke("serve.respond", [handle, response.status, headers, "json", body, null, response.compression ?? null, cookies]));
|
|
2583
|
+
return null;
|
|
2584
|
+
}
|
|
2585
|
+
if (__velarServeOwnDescriptor(response, "text")) {
|
|
2586
|
+
await __velarServeWithOutbound(metadataBytes + __velarUtf8ByteLength(response.text), () => __velarNodeHostInvoke("serve.respond", [handle, response.status, headers, "text", response.text, response.contentType ?? null, response.compression ?? null, cookies]));
|
|
2587
|
+
return null;
|
|
2588
|
+
}
|
|
2589
|
+
await __velarServeWithOutbound(metadataBytes, () => __velarNodeHostInvoke("serve.streamStart", [handle, response.status, headers, cookies]));
|
|
2590
|
+
let writing = false;
|
|
2591
|
+
const write = async chunk => {
|
|
2592
|
+
if (writing) throw new __velarServeError("ServeResponse allows only one active stream write");
|
|
2593
|
+
writing = true;
|
|
2594
|
+
try {
|
|
2595
|
+
if (typeof chunk !== "string") throw new __velarServeTypeError("ServeResponse.stream chunks must be strings");
|
|
2596
|
+
if (__velarUtf8ByteLength(chunk) > 1024 * 1024) throw new __velarServeRangeError("ServeResponse.stream chunks cannot exceed 1 MiB");
|
|
2597
|
+
await __velarServeWithOutbound(__velarUtf8ByteLength(chunk), () => __velarNodeHostInvoke("serve.streamWrite", [handle, chunk]));
|
|
2598
|
+
return null;
|
|
2599
|
+
} finally { writing = false; }
|
|
2600
|
+
};
|
|
2601
|
+
const result = await response.stream(write);
|
|
2602
|
+
if (result !== null) throw new __velarServeTypeError("ServeResponse.stream producer must resolve to null");
|
|
2603
|
+
if (writing) throw new __velarServeError("ServeResponse stream producer returned before its write completed");
|
|
2604
|
+
await __velarNodeHostInvoke("serve.streamEnd", [handle]);
|
|
2605
|
+
return null;
|
|
2606
|
+
} finally {
|
|
2607
|
+
await __velarServeRunBackground(backgroundTasks);
|
|
2608
|
+
if (typeof cleanup === "function") {
|
|
2609
|
+
try { await cleanup(); }
|
|
2610
|
+
catch (error) { __velarServeReportFailure(error); }
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
function __velarServeNativeHeaders(request) {
|
|
2616
|
+
const output = new __velarServeMap();
|
|
2617
|
+
let units = 0;
|
|
2618
|
+
for (const [name, value] of __velarServeObject.entries(request.headers)) {
|
|
2619
|
+
if (value === undefined) continue;
|
|
2620
|
+
const text = __velarServeIsArray(value) ? value.join(", ") : __velarServeString(value);
|
|
2621
|
+
units += name.length + text.length;
|
|
2622
|
+
if (units > 64 * 1024) throw new __velarServeRangeError("ServeRequest headers cannot exceed 64 KiB");
|
|
2623
|
+
output.set(name, text);
|
|
2624
|
+
}
|
|
2625
|
+
return output;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
function __velarServeNativeRequest(request) {
|
|
2629
|
+
const method = request.method ?? "GET";
|
|
2630
|
+
if (typeof method !== "string" || !__velarServeCall(__velarServeRegExpTest, __velarServeMethodPattern, [method])) throw new __velarServeTypeError("Native HTTP method is invalid");
|
|
2631
|
+
const target = request.url ?? "/";
|
|
2632
|
+
if (typeof target !== "string" || target.length === 0 || __velarUtf8ByteLength(target) > 64 * 1024) throw new __velarServeRangeError("Native HTTP request target is too long");
|
|
2633
|
+
const targetParts = __velarServeTargetParts(target, "Native HTTP request target");
|
|
2634
|
+
const path = targetParts.path;
|
|
2635
|
+
const query = __velarServePairsMaps(targetParts.query, "Native ServeRequest.query");
|
|
2636
|
+
const cancellation = __velarServeCancellation.__velarCreate();
|
|
2637
|
+
let bodyPromise = null;
|
|
2638
|
+
let reservedBodyBytes = 0;
|
|
2639
|
+
const rawBody = async () => {
|
|
2640
|
+
if (bodyPromise === null) bodyPromise = (async () => {
|
|
2641
|
+
const chunks = []; let total = 0;
|
|
2642
|
+
try {
|
|
2643
|
+
for await (const chunk of request) {
|
|
2644
|
+
const data = chunk instanceof __velarServeUint8Array ? chunk : __velarServeCall(__velarServeTextEncode, __velarServeUtf8Encoder, [__velarServeString(chunk)]);
|
|
2645
|
+
if (total + data.byteLength > __velarServeMaxBodyBytes) { request.resume(); throw new RequestBodyTooLargeError(__velarServeMaxBodyBytes); }
|
|
2646
|
+
__velarServeReserveOutbound(data.byteLength);
|
|
2647
|
+
total += data.byteLength;
|
|
2648
|
+
reservedBodyBytes += data.byteLength;
|
|
2649
|
+
chunks[chunks.length] = data;
|
|
2650
|
+
}
|
|
2651
|
+
__velarServeReserveOutbound(total);
|
|
2652
|
+
try {
|
|
2653
|
+
const output = new __velarServeUint8Array(total);
|
|
2654
|
+
let offset = 0;
|
|
2655
|
+
for (let index = 0; index < chunks.length; index += 1) { __velarServeCall(__velarServeUint8Set, output, [chunks[index], offset]); offset += chunks[index].byteLength; }
|
|
2656
|
+
chunks.length = 0;
|
|
2657
|
+
return output;
|
|
2658
|
+
}
|
|
2659
|
+
finally { __velarServeReleaseOutbound(total); }
|
|
2660
|
+
} catch (error) {
|
|
2661
|
+
__velarServeReleaseOutbound(reservedBodyBytes);
|
|
2662
|
+
reservedBodyBytes = 0;
|
|
2663
|
+
throw error;
|
|
2664
|
+
}
|
|
2665
|
+
})();
|
|
2666
|
+
return await bodyPromise;
|
|
2667
|
+
};
|
|
2668
|
+
const bytes = async (maxBytes = __velarServeMaxBodyBytes) => {
|
|
2669
|
+
if (!__velarServeIsSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > __velarServeMaxBodyBytes) throw new __velarServeRangeError("Request body maxBytes must be an integer from 1 through 16777216");
|
|
2670
|
+
const data = await rawBody();
|
|
2671
|
+
if (data.byteLength > maxBytes) throw new RequestBodyTooLargeError(maxBytes);
|
|
2672
|
+
return data;
|
|
2673
|
+
};
|
|
2674
|
+
const body = async (maxBytes = __velarServeMaxBodyBytes) => {
|
|
2675
|
+
const data = await bytes(maxBytes);
|
|
2676
|
+
try { return __velarServeCall(__velarServeTextDecode, __velarServeUtf8Decoder, [data]); }
|
|
2677
|
+
catch { throw new __velarServeTypeError("Request body must be valid UTF-8 text"); }
|
|
2678
|
+
};
|
|
2679
|
+
const json = async (maxBytes = __velarServeMaxBodyBytes) => __velarJsonParse(await body(maxBytes), "ServeRequest JSON text");
|
|
2680
|
+
return {
|
|
2681
|
+
request: __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{method, path, query: query.values, queryAll: query.all, headers: __velarServeNativeHeaders(request), cancellation, text: body, bytes, json, parse: async (Type, maxBytes = __velarServeMaxBodyBytes) => { Type = __velarRequireRuntimeType(Type, "ServeRequest.parse"); return Type.parse(await json(maxBytes)); }}]),
|
|
2682
|
+
cancellation,
|
|
2683
|
+
cleanup() { if (reservedBodyBytes > 0) { __velarServeReleaseOutbound(reservedBodyBytes); reservedBodyBytes = 0; } return null; },
|
|
2684
|
+
};
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
function __velarServeNativeSetHeaders(response, headers, cookies = []) {
|
|
2688
|
+
const allCookies = [];
|
|
2689
|
+
for (const [name, value] of headers) {
|
|
2690
|
+
if (__velarServeCall(__velarServeStringToLowerCase, name, []) === "set-cookie") allCookies[allCookies.length] = value;
|
|
2691
|
+
else response.setHeader(name, value);
|
|
2692
|
+
}
|
|
2693
|
+
for (let index = 0; index < cookies.length; index += 1) allCookies[allCookies.length] = cookies[index];
|
|
2694
|
+
if (allCookies.length > 0) response.setHeader("Set-Cookie", allCookies);
|
|
2695
|
+
}
|
|
2696
|
+
async function __velarServeNativeFile(value, operations) {
|
|
2697
|
+
const root = await operations.realpath(operations.resolve(value.root));
|
|
2698
|
+
const load = async path => {
|
|
2699
|
+
const target = await operations.realpath(operations.resolve(root, path.startsWith("/") ? "." + path : path));
|
|
2700
|
+
const relative = operations.relative(root, target);
|
|
2701
|
+
if (relative.startsWith("..") || operations.isAbsolute(relative)) throw new __velarServeTypeError("fileResponse path escapes its root");
|
|
2702
|
+
const info = await operations.stat(target);
|
|
2703
|
+
if (!info.isFile() || info.size > 64 * 1024 * 1024) throw new __velarServeRangeError("fileResponse file exceeds 64 MiB");
|
|
2704
|
+
return {target, info};
|
|
2705
|
+
};
|
|
2706
|
+
try { return await load(value.path); } catch (error) { if (value.fallback === null) throw error; return load(value.fallback); }
|
|
2707
|
+
}
|
|
2708
|
+
function __velarServeNativeContentType(path, operations) {
|
|
2709
|
+
if (typeof operations.extname !== "function") return "application/octet-stream";
|
|
2710
|
+
const extension = operations.extname(path).toLowerCase();
|
|
2711
|
+
return ({".css":"text/css; charset=utf-8",".gif":"image/gif",".html":"text/html; charset=utf-8",".ico":"image/x-icon",".jpeg":"image/jpeg",".jpg":"image/jpeg",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".map":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".png":"image/png",".svg":"image/svg+xml",".txt":"text/plain; charset=utf-8",".wasm":"application/wasm",".webp":"image/webp",".woff":"font/woff",".woff2":"font/woff2"})[extension] ?? "application/octet-stream";
|
|
2712
|
+
}
|
|
2713
|
+
async function __velarServeNativeSendFile(request, response, value, operations) {
|
|
2714
|
+
const file = await __velarServeNativeFile(value, operations);
|
|
2715
|
+
const modified = __velarServeCall(__velarServeMathFloor, __velarServeMath, [file.info.mtimeMs]);
|
|
2716
|
+
const etag = 'W/"' + __velarServeCall(__velarServeNumberToString, file.info.size, [16]) + "-" + __velarServeCall(__velarServeNumberToString, modified, [16]) + '"';
|
|
2717
|
+
response.setHeader("content-type", __velarServeNativeContentType(file.target, operations));
|
|
2718
|
+
response.setHeader("accept-ranges", "bytes");
|
|
2719
|
+
response.setHeader("etag", etag);
|
|
2720
|
+
response.setHeader("last-modified", file.info.mtime.toUTCString());
|
|
2721
|
+
const noneMatch = request.headers["if-none-match"];
|
|
2722
|
+
const modifiedSince = request.headers["if-modified-since"];
|
|
2723
|
+
if (typeof noneMatch === "string" && noneMatch.split(",").some(item => item.trim() === "*" || item.trim() === etag)
|
|
2724
|
+
|| typeof noneMatch !== "string" && typeof modifiedSince === "string" && __velarServeCall(__velarServeNumberIsFinite, __velarServeNumber, [__velarServeCall(__velarServeDateParse, __velarServeDate, [modifiedSince])])
|
|
2725
|
+
&& __velarServeCall(__velarServeMathFloor, __velarServeMath, [file.info.mtimeMs / 1000]) * 1000 <= __velarServeCall(__velarServeDateParse, __velarServeDate, [modifiedSince])) {
|
|
2726
|
+
response.statusCode = 304;
|
|
2727
|
+
return __velarServeNativeEnd(response);
|
|
2728
|
+
}
|
|
2729
|
+
let start = 0;
|
|
2730
|
+
let end = file.info.size - 1;
|
|
2731
|
+
let range = request.headers.range;
|
|
2732
|
+
const ifRange = request.headers["if-range"];
|
|
2733
|
+
if (typeof range === "string" && typeof ifRange === "string" && ifRange !== etag) {
|
|
2734
|
+
const time = __velarServeCall(__velarServeDateParse, __velarServeDate, [ifRange]);
|
|
2735
|
+
if (!__velarServeCall(__velarServeNumberIsFinite, __velarServeNumber, [time]) || __velarServeCall(__velarServeMathFloor, __velarServeMath, [file.info.mtimeMs / 1000]) * 1000 > time) range = undefined;
|
|
2736
|
+
}
|
|
2737
|
+
if (typeof range === "string") {
|
|
2738
|
+
const match = /^bytes=(\d*)-(\d*)$/u.exec(range.trim());
|
|
2739
|
+
if (!match || match[1] === "" && match[2] === "" || file.info.size === 0) {
|
|
2740
|
+
response.statusCode = 416; response.setHeader("content-range", "bytes */" + file.info.size); return __velarServeNativeEnd(response, request.method === "HEAD" ? undefined : "Range not satisfiable");
|
|
2741
|
+
}
|
|
2742
|
+
if (match[1] === "") { const suffix = __velarServeCall(__velarServeNumber, undefined, [match[2]]); if (!__velarServeCall(__velarServeNumberIsSafeInteger, __velarServeNumber, [suffix]) || suffix < 1) { response.statusCode = 416; response.setHeader("content-range", "bytes */" + file.info.size); return __velarServeNativeEnd(response); } start = __velarServeCall(__velarServeMathMax, __velarServeMath, [0, file.info.size - suffix]); }
|
|
2743
|
+
else { start = __velarServeCall(__velarServeNumber, undefined, [match[1]]); end = match[2] === "" ? end : __velarServeCall(__velarServeNumber, undefined, [match[2]]); }
|
|
2744
|
+
if (!__velarServeCall(__velarServeNumberIsSafeInteger, __velarServeNumber, [start]) || !__velarServeCall(__velarServeNumberIsSafeInteger, __velarServeNumber, [end]) || start < 0 || start >= file.info.size || end < start) { response.statusCode = 416; response.setHeader("content-range", "bytes */" + file.info.size); return __velarServeNativeEnd(response); }
|
|
2745
|
+
if (end >= file.info.size) end = file.info.size - 1;
|
|
2746
|
+
response.statusCode = 206;
|
|
2747
|
+
response.setHeader("content-range", "bytes " + start + "-" + end + "/" + file.info.size);
|
|
2748
|
+
} else response.statusCode = 200;
|
|
2749
|
+
response.setHeader("content-length", __velarServeCall(__velarServeMathMax, __velarServeMath, [0, end - start + 1]));
|
|
2750
|
+
if (request.method === "HEAD") return __velarServeNativeEnd(response);
|
|
2751
|
+
if (typeof operations.createReadStream !== "function") {
|
|
2752
|
+
const data = await operations.readFile(file.target);
|
|
2753
|
+
if (!(data instanceof __velarServeUint8Array) || data.byteLength > 64 * 1024 * 1024) throw new __velarServeRangeError("fileResponse file exceeds 64 MiB");
|
|
2754
|
+
return __velarServeWithOutbound(end - start + 1, () => __velarServeNativeEnd(response, data.subarray(start, end + 1)));
|
|
2755
|
+
}
|
|
2756
|
+
const source = operations.createReadStream(file.target, {start, end, highWaterMark: 64 * 1024});
|
|
2757
|
+
try { for await (const chunk of source) await __velarServeWithOutbound(chunk.byteLength, () => __velarServeNativeWrite(response, chunk)); }
|
|
2758
|
+
finally { source.destroy(); }
|
|
2759
|
+
return __velarServeNativeEnd(response);
|
|
2760
|
+
}
|
|
2761
|
+
async function __velarServeNativeEnd(response, value) { await new __velarServePromise((resolve, reject) => { response.once("error", reject); response.end(value, () => { response.off("error", reject); resolve(null); }); }); }
|
|
2762
|
+
async function __velarServeNativeWrite(response, value) { if (response.write(value)) return; await new __velarServePromise((resolve, reject) => { const failed = error => { response.off("drain", ready); reject(error); }; const ready = () => { response.off("error", failed); resolve(null); }; response.once("error", failed); response.once("drain", ready); }); }
|
|
2763
|
+
async function __velarServeNativeBody(response, value, checked, suppressBody, operations) {
|
|
2764
|
+
if (suppressBody) return __velarServeNativeEnd(response);
|
|
2765
|
+
const bytes = typeof value === "string" ? __velarUtf8ByteLength(value) : value.byteLength;
|
|
2766
|
+
if (checked.compression == null || typeof operations.compress !== "function") return __velarServeWithOutbound(bytes, () => __velarServeNativeEnd(response, value));
|
|
2767
|
+
const compressed = await __velarServeWithOutbound(bytes * 2, () => operations.compress(checked.compression, value));
|
|
2768
|
+
if (!(compressed instanceof __velarServeUint8Array) || compressed.byteLength > __velarServeMaxBodyBytes) throw new __velarServeRangeError("Compressed ServeResponse exceeds 16 MiB");
|
|
2769
|
+
response.setHeader("content-encoding", checked.compression);
|
|
2770
|
+
if (!response.hasHeader("vary")) response.setHeader("vary", "Accept-Encoding");
|
|
2771
|
+
return __velarServeWithOutbound(compressed.byteLength, () => __velarServeNativeEnd(response, compressed));
|
|
2772
|
+
}
|
|
2773
|
+
async function __velarServeHandleNative(handler, request, response, operations) {
|
|
2774
|
+
let cleanup = null;
|
|
2775
|
+
let backgroundTasks = null;
|
|
2776
|
+
let incoming = null;
|
|
2777
|
+
let disconnected = null;
|
|
2778
|
+
try {
|
|
2779
|
+
incoming = __velarServeNativeRequest(request);
|
|
2780
|
+
disconnected = () => { if (!response.writableFinished) __velarServeCancellation.__velarCancel(incoming.cancellation, "client_disconnect"); };
|
|
2781
|
+
request.once("aborted", disconnected);
|
|
2782
|
+
response.once("close", disconnected);
|
|
2783
|
+
let value = await handler(incoming.request);
|
|
2784
|
+
const managed = value && typeof value === "object" ? __velarServeOwnDescriptor(value, __velarServeManagedResponseMarker) : null;
|
|
2785
|
+
if (managed?.enumerable === true && "value" in managed && managed.value === true) { cleanup = value.cleanup; value = value.response; }
|
|
2786
|
+
if (__velarServeIsFileResponse(value)) { __velarServeNativeSetHeaders(response, __velarServeResponseHeaders(value.headers)); await __velarServeNativeSendFile(request, response, value, operations); return null; }
|
|
2787
|
+
const checked = __velarServeResponse(value); backgroundTasks = checked.background ?? null; response.statusCode = checked.status; __velarServeNativeSetHeaders(response, __velarServeResponseHeaders(checked.headers), __velarServeCookies(checked));
|
|
2788
|
+
const suppressBody = request.method === "HEAD" || checked.status >= 100 && checked.status < 200 || checked.status === 204 || checked.status === 304;
|
|
2789
|
+
if (__velarServeOwnDescriptor(checked, "json")) { if (request.method === "HEAD" || !suppressBody) response.setHeader("content-type", "application/json; charset=utf-8"); await __velarServeNativeBody(response, __velarServeCall(__velarServeWeakMapGet, __velarServeSerializedJson, [checked]), checked, suppressBody, operations); return null; }
|
|
2790
|
+
if (__velarServeOwnDescriptor(checked, "text")) { if (request.method === "HEAD" || !suppressBody) response.setHeader("content-type", checked.contentType ?? "text/plain; charset=utf-8"); await __velarServeNativeBody(response, checked.text, checked, suppressBody, operations); return null; }
|
|
2791
|
+
let writing = false;
|
|
2792
|
+
const write = async chunk => { if (writing) throw new __velarServeError("ServeResponse allows only one active stream write"); writing = true; try { if (typeof chunk !== "string" || __velarUtf8ByteLength(chunk) > 1024 * 1024) throw new __velarServeTypeError("ServeResponse.stream chunks must be text of at most 1 MiB"); if (!suppressBody) await __velarServeWithOutbound(__velarUtf8ByteLength(chunk), () => __velarServeNativeWrite(response, chunk)); return null; } finally { writing = false; } };
|
|
2793
|
+
const result = await checked.stream(write); if (result !== null) throw new __velarServeTypeError("ServeResponse.stream producer must resolve to null"); if (writing) throw new __velarServeError("ServeResponse stream producer returned before its write completed"); await __velarServeNativeEnd(response); return null;
|
|
2794
|
+
} catch (error) { __velarServeReportFailure(error); if (!response.headersSent) { response.statusCode = 500; response.setHeader("content-type", "text/plain; charset=utf-8"); response.end("Internal server error"); } else response.destroy(); return null; }
|
|
2795
|
+
finally {
|
|
2796
|
+
if (disconnected !== null) { request.off("aborted", disconnected); response.off("close", disconnected); }
|
|
2797
|
+
await __velarServeRunBackground(backgroundTasks);
|
|
2798
|
+
if (typeof cleanup === "function") { try { await cleanup(); } catch (error) { __velarServeReportFailure(error); } }
|
|
2799
|
+
if (incoming !== null) incoming.cleanup();
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
function __velarServeTestOverrides(value) {
|
|
2804
|
+
if (value == null) return new __velarServeMap();
|
|
2805
|
+
let size;
|
|
2806
|
+
let iterator;
|
|
2807
|
+
try { size = __velarServeCall(__velarServeMapSize, value, []); iterator = __velarServeCall(__velarServeMapEntries, value, []); }
|
|
2808
|
+
catch { throw new __velarServeTypeError("server-test overrides must be a Map<Provider, value>"); }
|
|
2809
|
+
if (!__velarServeIsSafeInteger(size) || size < 0 || size > 128) throw new __velarServeRangeError("server-test overrides cannot contain more than 128 providers");
|
|
2810
|
+
const output = new __velarServeMap();
|
|
2811
|
+
while (true) {
|
|
2812
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, iterator, []);
|
|
2813
|
+
if (step.done) break;
|
|
2814
|
+
if (!__velarServeIsArray(step.value) || step.value.length !== 2 || !__velarServeIsProvider(step.value[0])) throw new __velarServeTypeError("server-test override keys must be Providers");
|
|
2815
|
+
__velarServeCall(__velarServeMapSet, output, [step.value[0], step.value[1]]);
|
|
2816
|
+
}
|
|
2817
|
+
return output;
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
function __velarServeTestHeaders(value) {
|
|
2821
|
+
if (value == null) return new __velarServeMap();
|
|
2822
|
+
const pairs = __velarServeMapSnapshot(value, "server-test headers");
|
|
2823
|
+
const output = new __velarServeMap();
|
|
2824
|
+
for (let index = 0; index < pairs.length; index += 1) __velarServeCall(__velarServeMapSet, output, [__velarServeCall(__velarServeStringToLowerCase, pairs[index][0], []), pairs[index][1]]);
|
|
2825
|
+
return output;
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
function __velarServeTestUploadEntries(value) {
|
|
2829
|
+
if (value == null) return [];
|
|
2830
|
+
let size;
|
|
2831
|
+
let iterator;
|
|
2832
|
+
try { size = __velarServeCall(__velarServeMapSize, value, []); iterator = __velarServeCall(__velarServeMapEntries, value, []); }
|
|
2833
|
+
catch { throw new __velarServeTypeError("server-test files must be a Map<string, upload>"); }
|
|
2834
|
+
if (!__velarServeIsSafeInteger(size) || size < 0 || size > 128) throw new __velarServeRangeError("server-test files cannot contain more than 128 uploads");
|
|
2835
|
+
const output = [];
|
|
2836
|
+
while (true) {
|
|
2837
|
+
const step = __velarServeCall(__velarServeMapIteratorNext, iterator, []);
|
|
2838
|
+
if (step.done) break;
|
|
2839
|
+
if (!__velarServeIsArray(step.value) || step.value.length !== 2) throw new __velarServeTypeError("server-test files must be a Map<string, upload>");
|
|
2840
|
+
const field = step.value[0];
|
|
2841
|
+
const file = __velarServeRecord(step.value[1], __velarServeTestUploadFields, "server-test upload");
|
|
2842
|
+
if (typeof field !== "string" || field.length === 0 || field.length > 256 || /[\0\r\n"]/u.test(field)
|
|
2843
|
+
|| typeof file.filename !== "string" || file.filename.length === 0 || file.filename.length > 1024 || /[\0\r\n"]/u.test(file.filename)
|
|
2844
|
+
|| file.contentType !== undefined && (typeof file.contentType !== "string" || file.contentType.length === 0 || file.contentType.length > 1024 || /[\0\r\n]/u.test(file.contentType))) {
|
|
2845
|
+
throw new __velarServeTypeError("server-test upload names and content types must be bounded HTTP text");
|
|
2846
|
+
}
|
|
2847
|
+
const data = typeof file.data === "string"
|
|
2848
|
+
? __velarServeCall(__velarServeTextEncode, __velarServeUtf8Encoder, [file.data])
|
|
2849
|
+
: __velarServeBytesType.parse(file.data);
|
|
2850
|
+
output[output.length] = {field, filename: file.filename, contentType: file.contentType ?? "application/octet-stream", data};
|
|
2851
|
+
}
|
|
2852
|
+
if (output.length !== size) throw new __velarServeTypeError("server-test files changed while they were being read");
|
|
2853
|
+
return output;
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
function __velarServeBytesContain(source, pattern) {
|
|
2857
|
+
if (pattern.byteLength === 0 || pattern.byteLength > source.byteLength) return false;
|
|
2858
|
+
for (let offset = 0; offset <= source.byteLength - pattern.byteLength; offset += 1) {
|
|
2859
|
+
let equal = true;
|
|
2860
|
+
for (let index = 0; index < pattern.byteLength; index += 1) if (source[offset + index] !== pattern[index]) { equal = false; break; }
|
|
2861
|
+
if (equal) return true;
|
|
2862
|
+
}
|
|
2863
|
+
return false;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
function __velarServeTestMultipart(form, files) {
|
|
2867
|
+
const fields = form == null ? [] : __velarServeMapSnapshot(form, "server-test form", 256);
|
|
2868
|
+
for (let index = 0; index < fields.length; index += 1) if (fields[index][0].length === 0 || /[\0\r\n"]/u.test(fields[index][0])) throw new __velarServeTypeError("server-test form field names must be bounded HTTP text");
|
|
2869
|
+
const uploads = __velarServeTestUploadEntries(files);
|
|
2870
|
+
const values = [];
|
|
2871
|
+
for (let index = 0; index < fields.length; index += 1) values[values.length] = __velarServeCall(__velarServeTextEncode, __velarServeUtf8Encoder, [fields[index][1]]);
|
|
2872
|
+
for (let index = 0; index < uploads.length; index += 1) values[values.length] = uploads[index].data;
|
|
2873
|
+
let boundary = null;
|
|
2874
|
+
for (let attempt = 0; attempt < 32; attempt += 1) {
|
|
2875
|
+
if (!__velarServeIsSafeInteger(__velarServeNextTestBoundary)) __velarServeNextTestBoundary = 1;
|
|
2876
|
+
const candidate = "velar-test-" + __velarServeCall(__velarServeDateNow, __velarServeDate, []) + "-" + __velarServeNextTestBoundary++;
|
|
2877
|
+
const encoded = __velarServeCall(__velarServeTextEncode, __velarServeUtf8Encoder, [candidate]);
|
|
2878
|
+
let collision = false;
|
|
2879
|
+
for (let index = 0; index < values.length; index += 1) if (__velarServeBytesContain(values[index], encoded)) { collision = true; break; }
|
|
2880
|
+
if (!collision) { boundary = candidate; break; }
|
|
2881
|
+
}
|
|
2882
|
+
if (boundary === null) throw new __velarServeRangeError("server-test could not choose a collision-free multipart boundary");
|
|
2883
|
+
const parts = [];
|
|
2884
|
+
let total = 0;
|
|
2885
|
+
const appendText = text => { const data = __velarServeCall(__velarServeTextEncode, __velarServeUtf8Encoder, [text]); total += data.byteLength; parts[parts.length] = data; };
|
|
2886
|
+
const appendData = data => { total += data.byteLength; parts[parts.length] = data; };
|
|
2887
|
+
for (let index = 0; index < fields.length; index += 1) {
|
|
2888
|
+
appendText("--" + boundary + "\r\nContent-Disposition: form-data; name=\"" + fields[index][0] + "\"\r\n\r\n");
|
|
2889
|
+
appendData(values[index]);
|
|
2890
|
+
appendText("\r\n");
|
|
2891
|
+
}
|
|
2892
|
+
for (let index = 0; index < uploads.length; index += 1) {
|
|
2893
|
+
const upload = uploads[index];
|
|
2894
|
+
appendText("--" + boundary + "\r\nContent-Disposition: form-data; name=\"" + upload.field + "\"; filename=\"" + upload.filename + "\"\r\nContent-Type: " + upload.contentType + "\r\n\r\n");
|
|
2895
|
+
appendData(upload.data);
|
|
2896
|
+
appendText("\r\n");
|
|
2897
|
+
}
|
|
2898
|
+
appendText("--" + boundary + "--\r\n");
|
|
2899
|
+
if (total > __velarServeMaxBodyBytes) throw new __velarServeRangeError("server-test multipart body exceeds 16 MiB");
|
|
2900
|
+
const data = new __velarServeUint8Array(total);
|
|
2901
|
+
let offset = 0;
|
|
2902
|
+
for (let index = 0; index < parts.length; index += 1) { __velarServeCall(__velarServeUint8Set, data, [parts[index], offset]); offset += parts[index].byteLength; }
|
|
2903
|
+
return {data, contentType: "multipart/form-data; boundary=" + boundary};
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
function __velarServeTestRequest(method, target, options, cookies) {
|
|
2907
|
+
if (typeof method !== "string" || !__velarServeCall(__velarServeRegExpTest, __velarServeMethodPattern, [method])) throw new __velarServeTypeError("server-test method is invalid");
|
|
2908
|
+
if (typeof target !== "string" || target.length === 0 || __velarUtf8ByteLength(target) > 64 * 1024 || !__velarServeCall(__velarServeStringStartsWith, target, ["/"])) throw new __velarServeTypeError("server-test target must be a bounded absolute URL path");
|
|
2909
|
+
options = options == null ? {} : __velarServePlainRecord(options, "server-test request options");
|
|
2910
|
+
const keys = __velarServeCall(__velarServeOwnKeys, __velarServeReflect, [options]);
|
|
2911
|
+
for (let index = 0; index < keys.length; index += 1) if (!__velarServeCall(__velarServeArrayIncludes, ["headers", "json", "text", "form", "files"], [keys[index]])) throw new __velarServeTypeError("server-test request options have an unknown field");
|
|
2912
|
+
const structuredBody = __velarServeOwnDescriptor(options, "form") || __velarServeOwnDescriptor(options, "files");
|
|
2913
|
+
if ((__velarServeOwnDescriptor(options, "json") ? 1 : 0) + (__velarServeOwnDescriptor(options, "text") ? 1 : 0) + (structuredBody ? 1 : 0) > 1) throw new __velarServeTypeError("server-test request accepts one body source: json, text, or form/files");
|
|
2914
|
+
const targetParts = __velarServeTargetParts(target, "server-test target");
|
|
2915
|
+
const path = targetParts.path;
|
|
2916
|
+
const query = __velarServePairsMaps(targetParts.query, "server-test query");
|
|
2917
|
+
const headers = __velarServeTestHeaders(options.headers);
|
|
2918
|
+
if (!__velarServeCall(__velarServeMapHas, headers, ["cookie"]) && __velarServeCall(__velarServeMapSize, cookies, []) > 0) {
|
|
2919
|
+
const values = [];
|
|
2920
|
+
const iterator = __velarServeCall(__velarServeMapEntries, cookies, []);
|
|
2921
|
+
while (true) { const step = __velarServeCall(__velarServeMapIteratorNext, iterator, []); if (step.done) break; values[values.length] = step.value[0] + "=" + step.value[1]; }
|
|
2922
|
+
__velarServeCall(__velarServeMapSet, headers, ["cookie", __velarServeCall(__velarServeArrayJoin, values, ["; "])]);
|
|
2923
|
+
}
|
|
2924
|
+
let bodyText = "";
|
|
2925
|
+
let bodyData = __velarServeCall(__velarServeTextEncode, __velarServeUtf8Encoder, [bodyText]);
|
|
2926
|
+
if (__velarServeOwnDescriptor(options, "json")) {
|
|
2927
|
+
bodyText = __velarJsonStringify(options.json);
|
|
2928
|
+
if (!__velarServeCall(__velarServeMapHas, headers, ["content-type"])) __velarServeCall(__velarServeMapSet, headers, ["content-type", "application/json"]);
|
|
2929
|
+
} else if (__velarServeOwnDescriptor(options, "text")) {
|
|
2930
|
+
if (typeof options.text !== "string") throw new __velarServeTypeError("server-test text body must be string");
|
|
2931
|
+
bodyText = options.text;
|
|
2932
|
+
} else if (structuredBody) {
|
|
2933
|
+
const multipart = __velarServeTestMultipart(options.form, options.files);
|
|
2934
|
+
bodyData = multipart.data;
|
|
2935
|
+
__velarServeCall(__velarServeMapSet, headers, ["content-type", multipart.contentType]);
|
|
2936
|
+
}
|
|
2937
|
+
if (__velarUtf8ByteLength(bodyText) > __velarServeMaxBodyBytes) throw new __velarServeRangeError("server-test request body exceeds 16 MiB");
|
|
2938
|
+
if (!structuredBody) bodyData = __velarServeCall(__velarServeTextEncode, __velarServeUtf8Encoder, [bodyText]);
|
|
2939
|
+
const bytes = async maxBytes => {
|
|
2940
|
+
const maximum = maxBytes ?? __velarServeMaxBodyBytes;
|
|
2941
|
+
if (!__velarServeIsSafeInteger(maximum) || maximum < 1 || maximum > __velarServeMaxBodyBytes || bodyData.byteLength > maximum) throw new RequestBodyTooLargeError(maximum);
|
|
2942
|
+
return bodyData;
|
|
2943
|
+
};
|
|
2944
|
+
const textBody = async maxBytes => { const data = await bytes(maxBytes); if (!structuredBody) return bodyText; try { return __velarServeCall(__velarServeTextDecode, __velarServeUtf8Decoder, [data]); } catch { throw new __velarServeTypeError("server-test request body is not UTF-8 text"); } };
|
|
2945
|
+
const jsonBody = async maxBytes => __velarJsonParse(await textBody(maxBytes), "server-test JSON text");
|
|
2946
|
+
const cancellation = __velarServeCancellation.__velarCreate();
|
|
2947
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
2948
|
+
method, path, query: query.values, queryAll: query.all, headers, cancellation, text: textBody, bytes, json: jsonBody,
|
|
2949
|
+
parse: async (Type, maxBytes = __velarServeMaxBodyBytes) => { Type = __velarRequireRuntimeType(Type, "server-test request parse"); return Type.parse(await jsonBody(maxBytes)); },
|
|
2950
|
+
}]);
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
async function __velarServeTestResponse(value, cookies) {
|
|
2954
|
+
let cleanup = null;
|
|
2955
|
+
const managed = value && typeof value === "object" ? __velarServeOwnDescriptor(value, __velarServeManagedResponseMarker) : null;
|
|
2956
|
+
if (managed?.enumerable === true && "value" in managed && managed.value === true) { cleanup = value.cleanup; value = value.response; }
|
|
2957
|
+
let backgroundTasks = null;
|
|
2958
|
+
try {
|
|
2959
|
+
if (__velarServeIsFileResponse(value)) {
|
|
2960
|
+
const loaded = __velarServeRecord(await __velarNodeHostInvoke("serve.readFile", [value.root, value.path, value.fallback]), __velarServeTestFileFields, "server-test file result");
|
|
2961
|
+
if (!__velarServeBytesType.is(loaded.data) || loaded.data.byteLength > __velarServeMaxBodyBytes || typeof loaded.contentType !== "string" || loaded.contentType.length === 0 || loaded.contentType.length > 1024 || /[\0\r\n]/u.test(loaded.contentType)) {
|
|
2962
|
+
throw new __velarServeTypeError("Node host returned an invalid server-test file result");
|
|
2963
|
+
}
|
|
2964
|
+
const headers = __velarServeHeaders(value.headers, "content-type", loaded.contentType);
|
|
2965
|
+
let textValue = null;
|
|
2966
|
+
const readText = async () => {
|
|
2967
|
+
if (textValue === null) {
|
|
2968
|
+
try { textValue = __velarServeCall(__velarServeTextDecode, __velarServeUtf8Decoder, [loaded.data]); }
|
|
2969
|
+
catch { throw new __velarServeTypeError("server-test file response is not UTF-8 text"); }
|
|
2970
|
+
}
|
|
2971
|
+
return textValue;
|
|
2972
|
+
};
|
|
2973
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{status: 200, headers, text: readText, json: async () => __velarJsonParse(await readText(), "server-test file response JSON")}]);
|
|
2974
|
+
}
|
|
2975
|
+
const response = __velarServeResponse(value);
|
|
2976
|
+
backgroundTasks = response.background ?? null;
|
|
2977
|
+
const headers = __velarServeHeaders(response.headers);
|
|
2978
|
+
const checkedCookies = __velarServeCookies(response);
|
|
2979
|
+
const responseCookies = [];
|
|
2980
|
+
for (let index = 0; index < checkedCookies.length; index += 1) responseCookies[index] = checkedCookies[index];
|
|
2981
|
+
if (__velarServeCall(__velarServeMapHas, headers, ["set-cookie"])) responseCookies[responseCookies.length] = __velarServeCall(__velarServeMapGet, headers, ["set-cookie"]);
|
|
2982
|
+
for (let cookieIndex = 0; cookieIndex < responseCookies.length; cookieIndex += 1) {
|
|
2983
|
+
const cookie = responseCookies[cookieIndex];
|
|
2984
|
+
const first = __velarServeCall(__velarServeStringSplit, cookie, [";"])[0];
|
|
2985
|
+
const separator = __velarServeCall(__velarServeStringIndexOf, first, ["="]);
|
|
2986
|
+
if (separator > 0) {
|
|
2987
|
+
const name = __velarServeCall(__velarServeStringSlice, first, [0, separator]);
|
|
2988
|
+
const content = __velarServeCall(__velarServeStringSlice, first, [separator + 1]);
|
|
2989
|
+
if (__velarServeCall(__velarServeStringIncludes, __velarServeCall(__velarServeStringToLowerCase, cookie, []), ["max-age=0"])) __velarServeCall(__velarServeMapDelete, cookies, [name]);
|
|
2990
|
+
else __velarServeCall(__velarServeMapSet, cookies, [name, content]);
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
let jsonValue = __velarServeMissing;
|
|
2994
|
+
let textValue = "";
|
|
2995
|
+
if (__velarServeOwnDescriptor(response, "json")) { jsonValue = response.json; textValue = __velarJsonStringify(response.json); }
|
|
2996
|
+
else if (__velarServeOwnDescriptor(response, "text")) textValue = response.text;
|
|
2997
|
+
else {
|
|
2998
|
+
const chunks = [];
|
|
2999
|
+
let size = 0;
|
|
3000
|
+
const result = await response.stream(async chunk => { if (typeof chunk !== "string") throw new __velarServeTypeError("server-test stream chunks must be strings"); size += __velarUtf8ByteLength(chunk); if (size > __velarServeMaxBodyBytes) throw new __velarServeRangeError("server-test response exceeds 16 MiB"); chunks[chunks.length] = chunk; return null; });
|
|
3001
|
+
if (result !== null) throw new __velarServeTypeError("ServeResponse.stream producer must resolve to null");
|
|
3002
|
+
textValue = __velarServeCall(__velarServeArrayJoin, chunks, [""]);
|
|
3003
|
+
}
|
|
3004
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
3005
|
+
status: response.status,
|
|
3006
|
+
headers,
|
|
3007
|
+
text: async () => textValue,
|
|
3008
|
+
json: async () => jsonValue === __velarServeMissing ? __velarJsonParse(textValue, "server-test response JSON") : jsonValue,
|
|
3009
|
+
}]);
|
|
3010
|
+
} finally {
|
|
3011
|
+
try {
|
|
3012
|
+
await __velarServeRunBackground(backgroundTasks);
|
|
3013
|
+
} finally {
|
|
3014
|
+
if (typeof cleanup === "function") await cleanup();
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
async function __velarServeTestClient(app, overrides = null) {
|
|
3020
|
+
if (!__velarServeIsApp(app)) throw new __velarServeTypeError("server-test client requires a ServeApp");
|
|
3021
|
+
const appState = __velarServeAppState(__velarServeTestOverrides(overrides));
|
|
3022
|
+
const cookies = new __velarServeMap();
|
|
3023
|
+
let closing = null;
|
|
3024
|
+
let closed = null;
|
|
3025
|
+
try { await __velarServeRunStartup(app, appState); await __velarServeInitializeEagerProviders(app, __velarServeMaxBodyBytes, appState); }
|
|
3026
|
+
catch (error) { try { await __velarServeCleanupAppState(appState); } catch {} try { await __velarServeRunShutdown(app, appState); } catch {} throw error; }
|
|
3027
|
+
const request = async (method, target, options = null) => {
|
|
3028
|
+
if (closed !== null || closing !== null) throw new __velarServeError("server-test client is closed");
|
|
3029
|
+
const incoming = __velarServeTestRequest(method, target, options, cookies);
|
|
3030
|
+
return await __velarServeTestResponse(await __velarServeHandleApp(app, incoming, __velarServeMaxBodyBytes, appState), cookies);
|
|
3031
|
+
};
|
|
3032
|
+
const close = async (grace = __velarServeDefaultShutdownGrace) => {
|
|
3033
|
+
if (closed !== null) return closed;
|
|
3034
|
+
if (closing !== null) return closing;
|
|
3035
|
+
const pending = (async () => {
|
|
3036
|
+
try { await __velarServeDrainAppState(appState, grace); }
|
|
3037
|
+
catch (error) {
|
|
3038
|
+
closed = __velarServeFinishAppAfterDrain(app, appState);
|
|
3039
|
+
__velarServeCall(__velarServePromiseThen, closed, [() => null, failure => __velarServeReportFailure(failure)]);
|
|
3040
|
+
throw error;
|
|
3041
|
+
}
|
|
3042
|
+
closed = __velarServeFinishApp(app, appState);
|
|
3043
|
+
return await closed;
|
|
3044
|
+
})();
|
|
3045
|
+
closing = pending;
|
|
3046
|
+
try { return await pending; }
|
|
3047
|
+
finally { if (closing === pending) closing = null; }
|
|
3048
|
+
};
|
|
3049
|
+
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{
|
|
3050
|
+
request,
|
|
3051
|
+
get: (target, options = null) => request("GET", target, options),
|
|
3052
|
+
post: (target, options = null) => request("POST", target, options),
|
|
3053
|
+
put: (target, options = null) => request("PUT", target, options),
|
|
3054
|
+
patch: (target, options = null) => request("PATCH", target, options),
|
|
3055
|
+
delete: (target, options = null) => request("DELETE", target, options),
|
|
3056
|
+
close,
|
|
3057
|
+
}]);
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
async function __velarServeDispatch(event) {
|
|
3061
|
+
let value;
|
|
3062
|
+
try { value = __velarServeRequest(event); }
|
|
3063
|
+
catch (error) {
|
|
3064
|
+
__velarServeReportFailure(error);
|
|
3065
|
+
const request = event && typeof event === "object" ? __velarServeOwnDescriptor(event, "request") : null;
|
|
3066
|
+
if (request && "value" in request && __velarServeIsSafeInteger(request.value) && request.value > 0) {
|
|
3067
|
+
try { await __velarNodeHostInvoke("serve.fail", [request.value]); }
|
|
3068
|
+
catch {}
|
|
3069
|
+
}
|
|
3070
|
+
return;
|
|
3071
|
+
}
|
|
3072
|
+
const handler = __velarServeCall(__velarServeMapGet, __velarServeHandlers, [value.token]);
|
|
3073
|
+
if (typeof handler !== "function") { __velarServeReportFailure(new __velarServeError("Node host requested an unknown server token")); await __velarNodeHostInvoke("serve.fail", [value.handle]); return; }
|
|
3074
|
+
try { await __velarServeWriteResponse(value.handle, await handler(value.request)); }
|
|
3075
|
+
catch (error) {
|
|
3076
|
+
__velarServeReportFailure(error);
|
|
3077
|
+
try { await __velarNodeHostInvoke("serve.fail", [value.handle]); }
|
|
3078
|
+
catch {}
|
|
3079
|
+
} finally { __velarServeCall(__velarServeMapDelete, __velarServeHostCancellations, [value.handle]); }
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
__velarNodeHostOn("serve.request", event => { __velarServeDispatch(event); });
|
|
3083
|
+
__velarNodeHostOn("serve.cancel", event => {
|
|
3084
|
+
try {
|
|
3085
|
+
event = __velarServePlainRecord(event, "Node serve cancellation event");
|
|
3086
|
+
const token = __velarServeDataField(event, "token", "Node serve cancellation event");
|
|
3087
|
+
const request = __velarServeDataField(event, "request", "Node serve cancellation event");
|
|
3088
|
+
const reason = __velarServeDataField(event, "reason", "Node serve cancellation event");
|
|
3089
|
+
if (!__velarServeIsSafeInteger(token) || token < 1 || !__velarServeIsSafeInteger(request) || request < 1 || typeof reason !== "string" || reason.length > 1024) throw new __velarServeTypeError("Node serve cancellation event is invalid");
|
|
3090
|
+
const owned = __velarServeCall(__velarServeMapGet, __velarServeHostCancellations, [request]);
|
|
3091
|
+
if (owned !== undefined && owned.token === token) __velarServeCancellation.__velarCancel(owned.cancellation, reason);
|
|
3092
|
+
} catch (error) { __velarServeReportFailure(error); }
|
|
3093
|
+
});
|
|
3094
|
+
__velarNodeHostOn("serve.error", event => {
|
|
3095
|
+
try {
|
|
3096
|
+
event = __velarServePlainRecord(event, "Node serve error event");
|
|
3097
|
+
const message = __velarServeDataField(event, "message", "Node serve error event");
|
|
3098
|
+
if (typeof message !== "string" || message.length === 0 || message.length > 65536) throw new __velarServeTypeError("Node serve error event is invalid");
|
|
3099
|
+
__velarServeReportFailure(new __velarServeError(message));
|
|
3100
|
+
} catch (error) { __velarServeReportFailure(error); }
|
|
3101
|
+
});
|
|
3102
|
+
|
|
3103
|
+
export async function serve(app, port, host = "127.0.0.1", maxBodyBytes = __velarServeMaxBodyBytes) {
|
|
3104
|
+
if (!__velarServeIsSafeInteger(maxBodyBytes) || maxBodyBytes < 1 || maxBodyBytes > __velarServeMaxBodyBytes) {
|
|
3105
|
+
throw new __velarServeRangeError("serve maxBodyBytes must be an integer from 1 through 16777216");
|
|
3106
|
+
}
|
|
3107
|
+
const appState = __velarServeAppState();
|
|
3108
|
+
if (!__velarServeIsApp(app) && typeof app !== "function") throw new __velarServeTypeError("serve requires a ServeApp or async request handler");
|
|
3109
|
+
const handler = __velarServeIsApp(app)
|
|
3110
|
+
? request => __velarServeHandleApp(app, request, maxBodyBytes, appState)
|
|
3111
|
+
: request => __velarServeHandleFunction(app, request, appState);
|
|
3112
|
+
if (!__velarServeIsSafeInteger(port) || port < 0 || port > 65535) throw new __velarServeRangeError("serve port must be an integer from 0 through 65535");
|
|
3113
|
+
if (typeof host !== "string" || host.length === 0 || host.length > 255 || __velarServeCall(__velarServeStringIncludes, host, ["\0"])) {
|
|
3114
|
+
throw new __velarServeTypeError("serve host must be bounded text");
|
|
3115
|
+
}
|
|
3116
|
+
if (__velarServeCall(__velarServeMapSize, __velarServeHandlers, []) >= 128) throw new __velarServeRangeError("serve cannot own more than 128 servers");
|
|
3117
|
+
if (!__velarServeIsSafeInteger(__velarServeNextToken)) __velarServeNextToken = 1;
|
|
3118
|
+
let attempts = 0;
|
|
3119
|
+
while (__velarServeCall(__velarServeMapHas, __velarServeHandlers, [__velarServeNextToken])) {
|
|
3120
|
+
__velarServeNextToken += 1;
|
|
3121
|
+
if (!__velarServeIsSafeInteger(__velarServeNextToken)) __velarServeNextToken = 1;
|
|
3122
|
+
attempts += 1;
|
|
3123
|
+
if (attempts > 128) throw new __velarServeRangeError("serve cannot own more than 128 servers");
|
|
439
3124
|
}
|
|
440
3125
|
const token = __velarServeNextToken++;
|
|
441
3126
|
__velarServeCall(__velarServeMapSet, __velarServeHandlers, [token, handler]);
|
|
3127
|
+
if (__velarServeIsApp(app)) {
|
|
3128
|
+
try {
|
|
3129
|
+
await __velarServeRunStartup(app, appState);
|
|
3130
|
+
await __velarServeInitializeEagerProviders(app, maxBodyBytes, appState);
|
|
3131
|
+
} catch (error) {
|
|
3132
|
+
__velarServeCall(__velarServeMapDelete, __velarServeHandlers, [token]);
|
|
3133
|
+
try { await __velarServeCleanupAppState(appState); } catch (cleanupError) { __velarServeReportFailure(cleanupError); }
|
|
3134
|
+
try { await __velarServeRunShutdown(app, appState); } catch (shutdownError) { __velarServeReportFailure(shutdownError); }
|
|
3135
|
+
throw error;
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
442
3138
|
let started;
|
|
443
3139
|
try { started = __velarServeRecord(await __velarNodeHostInvoke("serve.start", [token, port, host]), __velarServeStartFields, "Node serve start result"); }
|
|
444
|
-
catch (error) {
|
|
3140
|
+
catch (error) {
|
|
3141
|
+
__velarServeCall(__velarServeMapDelete, __velarServeHandlers, [token]);
|
|
3142
|
+
try { await __velarServeCleanupAppState(appState); } catch (cleanupError) { __velarServeReportFailure(cleanupError); }
|
|
3143
|
+
if (__velarServeIsApp(app)) try { await __velarServeRunShutdown(app, appState); } catch (shutdownError) { __velarServeReportFailure(shutdownError); }
|
|
3144
|
+
throw error;
|
|
3145
|
+
}
|
|
445
3146
|
if (!__velarServeIsSafeInteger(started.handle) || started.handle < 1 || !__velarServeIsSafeInteger(started.port) || started.port < 0 || started.port > 65535) {
|
|
446
3147
|
__velarServeCall(__velarServeMapDelete, __velarServeHandlers, [token]);
|
|
3148
|
+
try {
|
|
3149
|
+
if (__velarServeIsSafeInteger(started.handle) && started.handle > 0) await __velarNodeHostInvoke("serve.stop", [started.handle]);
|
|
3150
|
+
} catch (stopError) { __velarServeReportFailure(stopError); }
|
|
3151
|
+
try { await __velarServeCleanupAppState(appState); } catch (cleanupError) { __velarServeReportFailure(cleanupError); }
|
|
3152
|
+
if (__velarServeIsApp(app)) try { await __velarServeRunShutdown(app, appState); } catch (shutdownError) { __velarServeReportFailure(shutdownError); }
|
|
447
3153
|
throw new __velarServeTypeError("Node host returned an invalid server");
|
|
448
3154
|
}
|
|
449
3155
|
let stopped = null;
|
|
450
|
-
const stop = async () => {
|
|
3156
|
+
const stop = async (grace = __velarServeDefaultShutdownGrace) => {
|
|
3157
|
+
if (!__velarServeIsSafeInteger(grace) || grace < 1 || grace > 120_000) throw new __velarServeRangeError("Server.stop grace must be 1 through 120000 milliseconds");
|
|
451
3158
|
if (stopped !== null) return stopped;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
3159
|
+
let transportStopped = false;
|
|
3160
|
+
const pending = (async () => {
|
|
3161
|
+
const transport = __velarNodeHostInvoke("serve.stop", [started.handle, grace]);
|
|
3162
|
+
const drain = __velarServeDrainAppState(appState, grace);
|
|
3163
|
+
let result;
|
|
3164
|
+
try { result = await transport; }
|
|
3165
|
+
catch (error) {
|
|
3166
|
+
__velarServeCall(__velarServePromiseThen, drain, [() => null, failure => __velarServeReportFailure(failure)]);
|
|
3167
|
+
throw error;
|
|
3168
|
+
}
|
|
3169
|
+
transportStopped = true;
|
|
3170
|
+
const protocolFailure = result === null ? null : new __velarServeTypeError("Node host returned an invalid server stop completion");
|
|
455
3171
|
__velarServeCall(__velarServeMapDelete, __velarServeHandlers, [token]);
|
|
3172
|
+
try { await drain; }
|
|
3173
|
+
catch (error) {
|
|
3174
|
+
const finalization = __velarServeFinishAppAfterDrain(app, appState);
|
|
3175
|
+
stopped = finalization;
|
|
3176
|
+
__velarServeCall(__velarServePromiseThen, finalization, [() => null, failure => __velarServeReportFailure(failure)]);
|
|
3177
|
+
throw error;
|
|
3178
|
+
}
|
|
3179
|
+
const finalization = __velarServeFinishApp(app, appState);
|
|
3180
|
+
await finalization;
|
|
3181
|
+
if (protocolFailure !== null) throw protocolFailure;
|
|
456
3182
|
return null;
|
|
457
3183
|
})();
|
|
458
|
-
|
|
3184
|
+
stopped = pending;
|
|
3185
|
+
try { return await pending; }
|
|
3186
|
+
catch (error) { if (!transportStopped && stopped === pending) stopped = null; throw error; }
|
|
459
3187
|
};
|
|
460
3188
|
return __velarServeCall(__velarServeObjectFreeze, __velarServeObject, [{port: started.port, stop}]);
|
|
461
3189
|
}
|