@blamejs/core 0.7.4 → 0.7.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +423 -395
- package/README.md +150 -149
- package/bin/blamejs.js +0 -0
- package/index.js +308 -284
- package/lib/api-key.js +660 -663
- package/lib/api-snapshot.js +338 -338
- package/lib/app-shutdown.js +385 -385
- package/lib/app.js +365 -365
- package/lib/archive.js +250 -250
- package/lib/atomic-file.js +544 -544
- package/lib/audit-chain.js +177 -177
- package/lib/audit-sign.js +344 -344
- package/lib/audit-tools.js +677 -677
- package/lib/audit.js +766 -766
- package/lib/auth/jwt.js +311 -311
- package/lib/auth/lockout.js +436 -436
- package/lib/auth/oauth.js +721 -721
- package/lib/auth/passkey.js +181 -181
- package/lib/auth/password.js +594 -594
- package/lib/backup/bundle.js +217 -217
- package/lib/backup/crypto.js +176 -176
- package/lib/backup/index.js +515 -515
- package/lib/backup/manifest.js +282 -282
- package/lib/break-glass.js +1338 -1338
- package/lib/bundler.js +441 -441
- package/lib/cache-redis.js +256 -256
- package/lib/cache.js +1206 -1206
- package/lib/canonical-json.js +115 -115
- package/lib/chain-writer.js +234 -234
- package/lib/cli-helpers.js +206 -206
- package/lib/cli.js +2334 -2334
- package/lib/cluster-provider-db.js +317 -317
- package/lib/cluster-storage.js +226 -226
- package/lib/cluster.js +703 -703
- package/lib/codepoint-class.js +196 -0
- package/lib/config-drift.js +301 -301
- package/lib/consent.js +222 -222
- package/lib/constants.js +191 -191
- package/lib/cookies.js +315 -315
- package/lib/credential-hash.js +322 -322
- package/lib/crypto.js +266 -266
- package/lib/csv.js +275 -286
- package/lib/db-declare-row-policy.js +267 -267
- package/lib/db-declare-view.js +420 -421
- package/lib/db-query.js +406 -406
- package/lib/db-schema.js +319 -319
- package/lib/db.js +1288 -1288
- package/lib/deprecate.js +222 -222
- package/lib/dev.js +335 -335
- package/lib/dual-control.js +473 -473
- package/lib/error-page.js +420 -420
- package/lib/external-db-migrate.js +441 -441
- package/lib/external-db.js +1061 -1061
- package/lib/file-type.js +273 -273
- package/lib/file-upload.js +213 -10
- package/lib/forms.js +422 -422
- package/lib/framework-error.js +293 -215
- package/lib/framework-schema.js +717 -717
- package/lib/gate-contract.js +971 -0
- package/lib/guard-all.js +405 -0
- package/lib/guard-archive.js +739 -0
- package/lib/guard-csv.js +816 -0
- package/lib/guard-email.js +744 -0
- package/lib/guard-filename.js +724 -0
- package/lib/guard-html.js +976 -0
- package/lib/guard-json.js +729 -0
- package/lib/guard-markdown.js +586 -0
- package/lib/guard-svg.js +976 -0
- package/lib/guard-xml.js +405 -0
- package/lib/guard-yaml.js +529 -0
- package/lib/handlers.js +350 -350
- package/lib/http-client-cookie-jar.js +508 -508
- package/lib/http-client.js +1195 -1195
- package/lib/i18n.js +878 -878
- package/lib/jobs.js +185 -185
- package/lib/log-stream-cloudwatch.js +369 -369
- package/lib/log-stream-local.js +146 -146
- package/lib/log-stream-otlp-grpc.js +410 -410
- package/lib/log-stream-otlp.js +286 -286
- package/lib/log-stream-syslog.js +302 -302
- package/lib/log-stream-webhook.js +199 -199
- package/lib/log-stream.js +330 -330
- package/lib/log.js +500 -500
- package/lib/mail-bounce.js +528 -528
- package/lib/mail-dkim.js +369 -362
- package/lib/mail.js +981 -962
- package/lib/metrics.js +683 -683
- package/lib/middleware/api-encrypt.js +936 -936
- package/lib/middleware/attach-user.js +157 -157
- package/lib/middleware/body-parser.js +1170 -1091
- package/lib/middleware/bot-guard.js +178 -178
- package/lib/middleware/compression.js +452 -452
- package/lib/middleware/cors.js +314 -314
- package/lib/middleware/csp-nonce.js +348 -348
- package/lib/middleware/csrf-protect.js +316 -316
- package/lib/middleware/db-role-for.js +264 -264
- package/lib/middleware/health.js +392 -392
- package/lib/middleware/index.js +79 -79
- package/lib/middleware/rate-limit.js +358 -358
- package/lib/middleware/request-id.js +61 -61
- package/lib/middleware/request-log.js +168 -168
- package/lib/middleware/require-auth.js +104 -104
- package/lib/middleware/security-headers.js +116 -116
- package/lib/middleware/sse.js +166 -166
- package/lib/migrations.js +383 -383
- package/lib/mtls-ca.js +518 -518
- package/lib/mtls-engine-default.js +481 -481
- package/lib/network-dns.js +632 -632
- package/lib/network-heartbeat.js +290 -290
- package/lib/network-nts.js +574 -574
- package/lib/network-proxy.js +265 -265
- package/lib/network-tls.js +328 -328
- package/lib/network.js +233 -233
- package/lib/notify.js +612 -612
- package/lib/ntp-check.js +229 -229
- package/lib/numeric-bounds.js +111 -91
- package/lib/object-store/azure-blob-bucket-ops.js +349 -349
- package/lib/object-store/azure-blob.js +488 -488
- package/lib/object-store/gcs-bucket-ops.js +351 -351
- package/lib/object-store/gcs.js +519 -519
- package/lib/object-store/http-put.js +153 -153
- package/lib/object-store/index.js +197 -197
- package/lib/object-store/sigv4-bucket-ops.js +1092 -1092
- package/lib/object-store/sigv4.js +903 -903
- package/lib/observability.js +151 -151
- package/lib/otel-export.js +269 -269
- package/lib/pagination.js +464 -464
- package/lib/parsers/index.js +80 -80
- package/lib/parsers/safe-env.js +642 -642
- package/lib/parsers/safe-ini.js +292 -292
- package/lib/parsers/safe-toml.js +784 -784
- package/lib/parsers/safe-xml.js +390 -390
- package/lib/parsers/safe-yaml.js +1015 -1015
- package/lib/permissions.js +708 -708
- package/lib/pqc-agent.js +87 -87
- package/lib/pqc-gate.js +279 -279
- package/lib/protobuf-encoder.js +190 -190
- package/lib/protocol-dispatcher.js +161 -161
- package/lib/pubsub-redis.js +167 -167
- package/lib/pubsub.js +429 -429
- package/lib/queue-local.js +476 -476
- package/lib/queue-redis.js +745 -745
- package/lib/queue-sqs.js +319 -319
- package/lib/queue.js +695 -695
- package/lib/redis-client.js +519 -519
- package/lib/request-helpers.js +340 -340
- package/lib/restore-bundle.js +237 -237
- package/lib/restore-rollback.js +259 -259
- package/lib/restore.js +409 -409
- package/lib/retry.js +376 -376
- package/lib/router.js +748 -748
- package/lib/safe-async.js +735 -735
- package/lib/safe-buffer.js +237 -237
- package/lib/safe-json.js +541 -541
- package/lib/safe-schema.js +1266 -1266
- package/lib/safe-url.js +159 -159
- package/lib/scheduler.js +706 -706
- package/lib/security-assert.js +373 -373
- package/lib/seeders.js +618 -618
- package/lib/session.js +478 -478
- package/lib/slug.js +269 -269
- package/lib/ssrf-guard.js +401 -401
- package/lib/static.js +184 -4
- package/lib/storage.js +471 -471
- package/lib/subject.js +281 -281
- package/lib/template.js +791 -791
- package/lib/testing.js +798 -798
- package/lib/time.js +310 -310
- package/lib/totp.js +302 -302
- package/lib/tracing.js +494 -494
- package/lib/uuid.js +132 -132
- package/lib/validate-opts.js +340 -319
- package/lib/vault/index.js +308 -308
- package/lib/vault/rotate.js +784 -784
- package/lib/vault/wrap.js +296 -296
- package/lib/vendor/noble-ciphers.cjs +9 -9
- package/lib/webhook.js +595 -595
- package/lib/websocket.js +1048 -1048
- package/package.json +77 -77
- package/sbom.cyclonedx.json +7 -7
package/lib/static.js
CHANGED
|
@@ -36,6 +36,7 @@ var fsp = require("node:fs/promises");
|
|
|
36
36
|
var nodeCrypto = require("node:crypto");
|
|
37
37
|
var path = require("node:path");
|
|
38
38
|
var C = require("./constants");
|
|
39
|
+
var gateContract = require("./gate-contract");
|
|
39
40
|
var lazyRequire = require("./lazy-require");
|
|
40
41
|
var numericBounds = require("./numeric-bounds");
|
|
41
42
|
var requestHelpers = require("./request-helpers");
|
|
@@ -47,6 +48,11 @@ var { StaticServeError } = require("./framework-error");
|
|
|
47
48
|
// observability is ready.
|
|
48
49
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
49
50
|
|
|
51
|
+
// guard-* family is wired on by default; lazy-loaded to avoid eager
|
|
52
|
+
// import cycles. Operators opt out via contentSafety: null (audited).
|
|
53
|
+
var guardAll = lazyRequire(function () { return require("./guard-all"); });
|
|
54
|
+
var guardFilename = lazyRequire(function () { return require("./guard-filename"); });
|
|
55
|
+
|
|
50
56
|
var _err = StaticServeError.factory;
|
|
51
57
|
|
|
52
58
|
var HTTP = requestHelpers.HTTP_STATUS;
|
|
@@ -159,6 +165,39 @@ function _resolveSafe(root, requestedPath) {
|
|
|
159
165
|
var rootResolved = path.resolve(root);
|
|
160
166
|
if (resolved !== rootResolved &&
|
|
161
167
|
!resolved.startsWith(rootResolved + path.sep)) return null;
|
|
168
|
+
|
|
169
|
+
// Symlink-escape defense — the lexical resolve above only sees the
|
|
170
|
+
// requested path tokens; a symlink anywhere along `resolved` can
|
|
171
|
+
// still point outside `rootResolved` on disk. realpath every node
|
|
172
|
+
// (only when it exists; missing files are routed through the
|
|
173
|
+
// standard 404 path by the caller).
|
|
174
|
+
try {
|
|
175
|
+
var real = fs.realpathSync(resolved);
|
|
176
|
+
var rootReal = fs.realpathSync(rootResolved);
|
|
177
|
+
if (real !== rootReal && !real.startsWith(rootReal + path.sep)) return null;
|
|
178
|
+
resolved = real;
|
|
179
|
+
} catch (_e) {
|
|
180
|
+
// Path doesn't exist (or is denied) — fall through with the lexical
|
|
181
|
+
// resolution so the caller's stat() returns the natural ENOENT and
|
|
182
|
+
// 404s. realpath failures from non-existence are NOT a smuggling
|
|
183
|
+
// signal.
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Filename safety — the basename gates against path-traversal /
|
|
187
|
+
// null-byte / NTFS ADS / UNC / RTLO bidi / overlong UTF-8 / Windows
|
|
188
|
+
// reserved device names. Uses balanced profile + explicit
|
|
189
|
+
// shellExecExtPolicy: "allow" because static-serve serves operator-
|
|
190
|
+
// deposited disk content: shell-exec extensions (.exe / .bin / .so /
|
|
191
|
+
// legitimate `<name>.<hash>.js` bundler output) are valid here. The
|
|
192
|
+
// other balanced checks still reject the traversal + smuggling
|
|
193
|
+
// surface the user surfaced.
|
|
194
|
+
var fname = path.basename(resolved);
|
|
195
|
+
var rv = guardFilename().validate(fname, {
|
|
196
|
+
profile: "balanced",
|
|
197
|
+
shellExecExtPolicy: "allow",
|
|
198
|
+
});
|
|
199
|
+
if (!rv.ok) return null;
|
|
200
|
+
|
|
162
201
|
return resolved;
|
|
163
202
|
}
|
|
164
203
|
|
|
@@ -236,10 +275,8 @@ function _validateCreateOpts(opts) {
|
|
|
236
275
|
"staticServe.create: indexFile", StaticServeError, "BAD_OPT");
|
|
237
276
|
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.defaultMaxAge,
|
|
238
277
|
"staticServe.create: defaultMaxAge", StaticServeError, "BAD_OPT");
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
throw _err("BAD_OPT", "staticServe.create: contentTypes must be a plain object");
|
|
242
|
-
}
|
|
278
|
+
validateOpts.optionalPlainObject(opts.contentTypes,
|
|
279
|
+
"staticServe.create: contentTypes", StaticServeError, "BAD_OPT");
|
|
243
280
|
validateOpts.optionalObjectWithMethod(opts.permissions, "check",
|
|
244
281
|
"staticServe.create: permissions", StaticServeError, "BAD_OPT",
|
|
245
282
|
"must be a b.permissions instance (check fn)");
|
|
@@ -266,6 +303,29 @@ function _validateCreateOpts(opts) {
|
|
|
266
303
|
validateOpts.auditShape(opts.audit, "staticServe.create", StaticServeError);
|
|
267
304
|
validateOpts.observabilityShape(opts.observability, "staticServe.create", StaticServeError);
|
|
268
305
|
validateOpts.optionalFunction(opts.onServe, "staticServe.create: onServe", StaticServeError);
|
|
306
|
+
// contentSafety — extension-keyed gate map. Default behaviour: when
|
|
307
|
+
// undefined, the framework wires b.guardAll.byExtension({ profile:
|
|
308
|
+
// "strict" }) automatically so every shipped guard is ON by default.
|
|
309
|
+
// Explicit opt-out: contentSafety: null (audited at create() time so
|
|
310
|
+
// a security review can reconstruct which deploys disabled the
|
|
311
|
+
// default-on protection).
|
|
312
|
+
// Example: contentSafety: { ".csv": b.guardCsv.gate({ profile: "strict" }) }
|
|
313
|
+
if (opts.contentSafety !== undefined && opts.contentSafety !== null) {
|
|
314
|
+
validateOpts.optionalPlainObject(opts.contentSafety,
|
|
315
|
+
"staticServe.create: contentSafety", StaticServeError, "BAD_OPT",
|
|
316
|
+
"must be a plain { ext: gate } object, null to opt out, or " +
|
|
317
|
+
"undefined for the default-on b.guardAll wiring");
|
|
318
|
+
var safetyKeys = Object.keys(opts.contentSafety);
|
|
319
|
+
for (var sk = 0; sk < safetyKeys.length; sk++) {
|
|
320
|
+
var ext = safetyKeys[sk];
|
|
321
|
+
var g = opts.contentSafety[ext];
|
|
322
|
+
if (!g || typeof g.check !== "function") {
|
|
323
|
+
throw _err("BAD_OPT",
|
|
324
|
+
"staticServe.create: contentSafety[" + JSON.stringify(ext) +
|
|
325
|
+
"] must be a gate (b.guardCsv.gate / b.guardHtml.gate / etc.)");
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
269
329
|
validateOpts.optionalBoolean(opts.acceptRanges, "staticServe.create: acceptRanges", StaticServeError);
|
|
270
330
|
validateOpts.optionalBoolean(opts.auditSuccess, "staticServe.create: auditSuccess", StaticServeError);
|
|
271
331
|
validateOpts.optionalBoolean(opts.auditFailures, "staticServe.create: auditFailures", StaticServeError);
|
|
@@ -393,6 +453,7 @@ function create(opts) {
|
|
|
393
453
|
"acceptRanges", "auditSuccess", "auditFailures",
|
|
394
454
|
"maxBytesPerActorPerWindowMs", "maxBytesAllActorsPerWindowMs",
|
|
395
455
|
"bandwidthWindowMs", "maxConcurrentDownloadsPerActor", "maxIdleMs",
|
|
456
|
+
"contentSafety", "contentSafetyDisabledReason",
|
|
396
457
|
], "staticServe.create");
|
|
397
458
|
_validateCreateOpts(opts);
|
|
398
459
|
var cfg = validateOpts.applyDefaults(opts, DEFAULTS);
|
|
@@ -408,6 +469,37 @@ function create(opts) {
|
|
|
408
469
|
var retention = opts.retention || null;
|
|
409
470
|
var revokeStore = opts.revokeStore || null;
|
|
410
471
|
var allowedFileTypes = Array.isArray(opts.allowedFileTypes) ? opts.allowedFileTypes.slice() : [];
|
|
472
|
+
// contentSafety: undefined → wire b.guardAll.byExtension({ profile: "strict" })
|
|
473
|
+
// contentSafety: null → explicit opt-out, audit row emitted
|
|
474
|
+
// contentSafety: { ... } → use operator-supplied map
|
|
475
|
+
var contentSafety;
|
|
476
|
+
if (opts.contentSafety === undefined) {
|
|
477
|
+
// Strict profile is the security-correct default. Operators who
|
|
478
|
+
// serve a broader content vocabulary opt up explicitly via
|
|
479
|
+
// contentSafety: b.guardAll.byExtension({ profile: "balanced" |
|
|
480
|
+
// "permissive" }).
|
|
481
|
+
contentSafety = guardAll().byExtension({
|
|
482
|
+
profile: "strict",
|
|
483
|
+
audit: opts.audit,
|
|
484
|
+
observability: opts.observability,
|
|
485
|
+
});
|
|
486
|
+
} else if (opts.contentSafety === null) {
|
|
487
|
+
if (opts.audit && typeof opts.audit.safeEmit === "function") {
|
|
488
|
+
try {
|
|
489
|
+
opts.audit.safeEmit({
|
|
490
|
+
action: "staticServe.contentSafety.disabled",
|
|
491
|
+
actor: {},
|
|
492
|
+
outcome: "success",
|
|
493
|
+
metadata: {
|
|
494
|
+
reason: opts.contentSafetyDisabledReason || "operator-explicit-opt-out",
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
} catch (_e) { /* audit best-effort */ }
|
|
498
|
+
}
|
|
499
|
+
contentSafety = null;
|
|
500
|
+
} else {
|
|
501
|
+
contentSafety = opts.contentSafety;
|
|
502
|
+
}
|
|
411
503
|
var onServe = opts.onServe || null;
|
|
412
504
|
var audit = opts.audit || null;
|
|
413
505
|
var auditSuccess = cfg.auditSuccess;
|
|
@@ -573,6 +665,68 @@ function create(opts) {
|
|
|
573
665
|
}
|
|
574
666
|
}
|
|
575
667
|
|
|
668
|
+
// Content-safety gate — operator-supplied per-extension gate
|
|
669
|
+
// (b.guardCsv.gate / b.guardHtml.gate / etc.). Reads the file once
|
|
670
|
+
// up to maxRuntimeMs and routes the bytes through the gate's
|
|
671
|
+
// check() before serving. The gate's decision is honored:
|
|
672
|
+
// - serve → continue with the original bytes
|
|
673
|
+
// - sanitize → continue with decision.sanitized
|
|
674
|
+
// - refuse → 415 / opaque to clients
|
|
675
|
+
// - audit-only / warn → continue (gate emits to audit)
|
|
676
|
+
var gateBytesOverride = null;
|
|
677
|
+
if (contentSafety) {
|
|
678
|
+
var ext = path.extname(absPath).toLowerCase();
|
|
679
|
+
var safetyGate = contentSafety[ext];
|
|
680
|
+
if (safetyGate && typeof safetyGate.check === "function") {
|
|
681
|
+
var gateBuf;
|
|
682
|
+
try { gateBuf = await fsp.readFile(absPath); }
|
|
683
|
+
catch (_e) {
|
|
684
|
+
stats.failures += 1;
|
|
685
|
+
return _writeError(res, HTTP.INTERNAL_SERVER_ERROR,
|
|
686
|
+
"read_failed", "Internal Server Error");
|
|
687
|
+
}
|
|
688
|
+
var gateDecision;
|
|
689
|
+
try {
|
|
690
|
+
gateDecision = await safetyGate.check({
|
|
691
|
+
bytes: gateBuf,
|
|
692
|
+
contentType: _contentTypeFor(absPath, contentTypes),
|
|
693
|
+
filename: path.basename(absPath),
|
|
694
|
+
actor: actorCtx,
|
|
695
|
+
route: urlPath,
|
|
696
|
+
direction: "outbound",
|
|
697
|
+
req: req,
|
|
698
|
+
});
|
|
699
|
+
} catch (gateErr) {
|
|
700
|
+
stats.failures += 1;
|
|
701
|
+
_emitObs("staticServe.content_safety_threw", 1, { route: urlPath });
|
|
702
|
+
if (auditFailures) {
|
|
703
|
+
emitAudit("staticServe.serve.failure", Object.assign({
|
|
704
|
+
outcome: "failure", reason: "content_safety_threw", resource: urlPath,
|
|
705
|
+
error: gateErr && gateErr.message,
|
|
706
|
+
}, actorCtx));
|
|
707
|
+
}
|
|
708
|
+
return _writeError(res, HTTP.INTERNAL_SERVER_ERROR,
|
|
709
|
+
"content_safety_threw", "Internal Server Error");
|
|
710
|
+
}
|
|
711
|
+
if (!gateDecision.ok || gateDecision.action === "refuse") {
|
|
712
|
+
stats.failures += 1;
|
|
713
|
+
_emitObs("staticServe.content_safety_refused", 1, { route: urlPath });
|
|
714
|
+
if (auditFailures) {
|
|
715
|
+
emitAudit("staticServe.serve.failure", Object.assign({
|
|
716
|
+
outcome: "failure", reason: "content_safety_refused",
|
|
717
|
+
resource: urlPath, ext: ext,
|
|
718
|
+
issues: gateContract.summarizeIssues(gateDecision.issues),
|
|
719
|
+
}, actorCtx));
|
|
720
|
+
}
|
|
721
|
+
return _writeError(res, HTTP.UNSUPPORTED_MEDIA_TYPE,
|
|
722
|
+
"content_safety_refused", "Unsupported Media Type");
|
|
723
|
+
}
|
|
724
|
+
if (gateDecision.action === "sanitize" && gateDecision.sanitized) {
|
|
725
|
+
gateBytesOverride = gateDecision.sanitized;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
576
730
|
var cacheControl = _cacheControlFor(urlPath);
|
|
577
731
|
|
|
578
732
|
var headersIn = req.headers || {};
|
|
@@ -734,6 +888,32 @@ function create(opts) {
|
|
|
734
888
|
return;
|
|
735
889
|
}
|
|
736
890
|
|
|
891
|
+
// Sanitized override path — content-safety gate replaced the
|
|
892
|
+
// bytes; emit them directly without re-reading the file. Bypasses
|
|
893
|
+
// range / idle-timer machinery (the override is already in memory
|
|
894
|
+
// and Range over a sanitized variant doesn't have a useful
|
|
895
|
+
// contract — sanitization changes byte offsets).
|
|
896
|
+
if (gateBytesOverride) {
|
|
897
|
+
var overrideHeaders = Object.assign({}, headers, {
|
|
898
|
+
"Content-Length": gateBytesOverride.length,
|
|
899
|
+
});
|
|
900
|
+
delete overrideHeaders["Content-Range"];
|
|
901
|
+
res.writeHead(HTTP.OK, overrideHeaders);
|
|
902
|
+
res.end(gateBytesOverride);
|
|
903
|
+
stats.requestsServed += 1;
|
|
904
|
+
stats.bytesServed += gateBytesOverride.length;
|
|
905
|
+
_emitObs("staticServe.requests_served", 1, { route: urlPath, method: "GET", sanitized: true });
|
|
906
|
+
_emitObs("staticServe.bytes_served", gateBytesOverride.length, { route: urlPath, sanitized: true });
|
|
907
|
+
if (auditSuccess) {
|
|
908
|
+
emitAudit("staticServe.serve.success", Object.assign({
|
|
909
|
+
outcome: "success", resource: urlPath, method: "GET",
|
|
910
|
+
size: gateBytesOverride.length, contentType: overrideHeaders["Content-Type"],
|
|
911
|
+
sanitized: true,
|
|
912
|
+
}, actorCtx));
|
|
913
|
+
}
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
|
|
737
917
|
res.writeHead(status, headers);
|
|
738
918
|
|
|
739
919
|
// Acquire concurrency slot (released on stream end / error / abort).
|