@blamejs/core 0.14.17 → 0.14.19
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 +4 -0
- package/README.md +3 -3
- package/lib/agent-orchestrator.js +10 -4
- package/lib/ai-prompt.js +1 -1
- package/lib/app-shutdown.js +28 -0
- package/lib/archive-read.js +215 -16
- package/lib/archive.js +206 -52
- package/lib/auth/oauth.js +58 -0
- package/lib/auth/oid4vci.js +84 -27
- package/lib/breach-deadline.js +166 -1
- package/lib/cloud-events.js +3 -1
- package/lib/codepoint-class.js +21 -0
- package/lib/db-schema.js +120 -3
- package/lib/db.js +10 -3
- package/lib/error-page.js +88 -8
- package/lib/external-db.js +164 -13
- package/lib/guard-email.js +36 -3
- package/lib/mail-auth.js +554 -55
- package/lib/mail-send-deliver.js +15 -5
- package/lib/mail-sieve.js +2 -1
- package/lib/middleware/ai-act-disclosure.js +88 -19
- package/lib/middleware/asyncapi-serve.js +56 -4
- package/lib/middleware/attach-user.js +45 -10
- package/lib/middleware/body-parser.js +70 -14
- package/lib/middleware/csp-report.js +30 -2
- package/lib/middleware/deny-response.js +22 -7
- package/lib/middleware/openapi-serve.js +56 -4
- package/lib/middleware/scim-server.js +301 -14
- package/lib/openapi-paths-builder.js +105 -29
- package/lib/openapi.js +225 -100
- package/lib/queue-local.js +148 -38
- package/lib/queue.js +41 -11
- package/lib/render.js +21 -3
- package/lib/safe-buffer.js +55 -0
- package/lib/static.js +46 -17
- package/lib/uri-template.js +3 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/static.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* quotas (cluster-shared via b.cache), Range support (RFC 7233 single-range),
|
|
7
7
|
* the full conditional-request set (If-None-Match / If-Match /
|
|
8
8
|
* If-Modified-Since / If-Unmodified-Since), MIME allowlist with magic-byte
|
|
9
|
-
* verification (composes b.fileType), per-request operator
|
|
9
|
+
* verification (composes b.fileType), per-request operator hooks (onServe
|
|
10
|
+
* on the success path, onError on every refusal path), idle-stream
|
|
10
11
|
* timeout, cancellation propagation, force-revoke, and compliance-retention
|
|
11
12
|
* gating.
|
|
12
13
|
*
|
|
@@ -16,6 +17,12 @@
|
|
|
16
17
|
* var mw = serve.middleware; // (req, res, next)
|
|
17
18
|
* await b.staticServe.integrity(absFilePath); // SRI helper (SHA-384)
|
|
18
19
|
*
|
|
20
|
+
* // onError mirrors onServe for the refusal paths (403 / 404 / 415 /
|
|
21
|
+
* // 412 / 416 / 429 / 451 / 500): it receives
|
|
22
|
+
* // { req, res, urlPath, absPath, status, code, actor } AFTER the
|
|
23
|
+
* // refusal response is written. Observability-only — a throw is
|
|
24
|
+
* // swallowed so a broken hook can't tear down the socket.
|
|
25
|
+
*
|
|
19
26
|
* Backwards compatible: every existing opt (root, mountPath,
|
|
20
27
|
* hashedPathPattern, indexFile, defaultMaxAge, contentTypes) keeps its
|
|
21
28
|
* original meaning. New opts (permissions, cache, audit, observability,
|
|
@@ -450,6 +457,7 @@ function _validateCreateOpts(opts) {
|
|
|
450
457
|
validateOpts.auditShape(opts.audit, "staticServe.create", StaticServeError);
|
|
451
458
|
validateOpts.observabilityShape(opts.observability, "staticServe.create", StaticServeError);
|
|
452
459
|
validateOpts.optionalFunction(opts.onServe, "staticServe.create: onServe", StaticServeError);
|
|
460
|
+
validateOpts.optionalFunction(opts.onError, "staticServe.create: onError", StaticServeError);
|
|
453
461
|
// contentSafety — extension-keyed gate map. Default behaviour: when
|
|
454
462
|
// undefined, the framework wires b.guardAll.byExtension({ profile:
|
|
455
463
|
// "strict" }) automatically so every shipped guard is ON by default.
|
|
@@ -604,7 +612,7 @@ function create(opts) {
|
|
|
604
612
|
"root", "mountPath", "hashedPathPattern",
|
|
605
613
|
"indexFile", "defaultMaxAge", "contentTypes",
|
|
606
614
|
"permissions", "cache", "fileType", "retention", "revokeStore",
|
|
607
|
-
"allowedFileTypes", "audit", "observability", "onServe",
|
|
615
|
+
"allowedFileTypes", "audit", "observability", "onServe", "onError",
|
|
608
616
|
"acceptRanges", "auditSuccess", "auditFailures",
|
|
609
617
|
"maxBytesPerActorPerWindowMs", "maxBytesAllActorsPerWindowMs",
|
|
610
618
|
"bandwidthWindowMs", "maxConcurrentDownloadsPerActor", "maxIdleMs",
|
|
@@ -657,6 +665,7 @@ function create(opts) {
|
|
|
657
665
|
contentSafety = opts.contentSafety;
|
|
658
666
|
}
|
|
659
667
|
var onServe = opts.onServe || null;
|
|
668
|
+
var onError = opts.onError || null;
|
|
660
669
|
var audit = opts.audit || null;
|
|
661
670
|
var auditSuccess = cfg.auditSuccess;
|
|
662
671
|
var auditFailures = cfg.auditFailures;
|
|
@@ -756,6 +765,26 @@ function create(opts) {
|
|
|
756
765
|
var actorCtx = requestHelpers.extractActorContext(req);
|
|
757
766
|
var actorKey = _actorKeyFromContext(actorCtx);
|
|
758
767
|
|
|
768
|
+
// Request-scoped error writer. Wraps the module-level _writeError so
|
|
769
|
+
// every refusal path (403 / 404 / 415 / 412 / 416 / 429 / 451 / 500)
|
|
770
|
+
// also invokes the operator's onError hook — the success-path mirror
|
|
771
|
+
// of onServe. The signature matches _writeError exactly; the `code`
|
|
772
|
+
// argument routes through to the hook so operators can branch on the
|
|
773
|
+
// refusal reason. The hook is observability-only: it runs AFTER the
|
|
774
|
+
// response is written and a throw is swallowed so a broken sink can't
|
|
775
|
+
// turn a 4xx into a torn-down socket.
|
|
776
|
+
function writeErr(r, status, code, message, headers) {
|
|
777
|
+
_writeError(r, status, code, message, headers);
|
|
778
|
+
if (onError) {
|
|
779
|
+
try {
|
|
780
|
+
onError({
|
|
781
|
+
req: req, res: r, urlPath: urlPath, absPath: absPath,
|
|
782
|
+
status: status, code: code, actor: actorCtx,
|
|
783
|
+
});
|
|
784
|
+
} catch (_he) { /* hook best-effort */ }
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
759
788
|
// Permission gate (403)
|
|
760
789
|
var perm = await _checkPermission(req);
|
|
761
790
|
if (!perm.ok) {
|
|
@@ -766,7 +795,7 @@ function create(opts) {
|
|
|
766
795
|
outcome: "failure", reason: "permission_denied", resource: urlPath,
|
|
767
796
|
}, actorCtx));
|
|
768
797
|
}
|
|
769
|
-
return
|
|
798
|
+
return writeErr(res, HTTP.FORBIDDEN, "permission_denied",
|
|
770
799
|
"Forbidden");
|
|
771
800
|
}
|
|
772
801
|
|
|
@@ -788,7 +817,7 @@ function create(opts) {
|
|
|
788
817
|
outcome: "failure", reason: "revoked", resource: urlPath,
|
|
789
818
|
}, actorCtx));
|
|
790
819
|
}
|
|
791
|
-
return
|
|
820
|
+
return writeErr(res, HTTP.NOT_FOUND, "not_found", "Not Found");
|
|
792
821
|
}
|
|
793
822
|
|
|
794
823
|
// Compliance retention (451)
|
|
@@ -800,7 +829,7 @@ function create(opts) {
|
|
|
800
829
|
outcome: "failure", reason: "retention_blocked", resource: urlPath,
|
|
801
830
|
}, actorCtx));
|
|
802
831
|
}
|
|
803
|
-
return
|
|
832
|
+
return writeErr(res, HTTP.UNAVAILABLE_FOR_LEGAL_REASONS,
|
|
804
833
|
"retention_blocked", "Unavailable For Legal Reasons");
|
|
805
834
|
}
|
|
806
835
|
|
|
@@ -820,7 +849,7 @@ function create(opts) {
|
|
|
820
849
|
detectedMime: mimeCheck.detected || null,
|
|
821
850
|
}, actorCtx));
|
|
822
851
|
}
|
|
823
|
-
return
|
|
852
|
+
return writeErr(res, HTTP.UNSUPPORTED_MEDIA_TYPE,
|
|
824
853
|
"mime_rejected", "Unsupported Media Type");
|
|
825
854
|
}
|
|
826
855
|
}
|
|
@@ -861,7 +890,7 @@ function create(opts) {
|
|
|
861
890
|
catch (_e) {
|
|
862
891
|
stats.failures += 1;
|
|
863
892
|
if (gateHandle) { try { await gateHandle.close(); } catch (_ce) { /* close best-effort */ } }
|
|
864
|
-
return
|
|
893
|
+
return writeErr(res, HTTP.INTERNAL_SERVER_ERROR,
|
|
865
894
|
"read_failed", "Internal Server Error");
|
|
866
895
|
}
|
|
867
896
|
try { await gateHandle.close(); } catch (_ce) { /* close best-effort */ }
|
|
@@ -885,7 +914,7 @@ function create(opts) {
|
|
|
885
914
|
error: gateErr && gateErr.message,
|
|
886
915
|
}, actorCtx));
|
|
887
916
|
}
|
|
888
|
-
return
|
|
917
|
+
return writeErr(res, HTTP.INTERNAL_SERVER_ERROR,
|
|
889
918
|
"content_safety_threw", "Internal Server Error");
|
|
890
919
|
}
|
|
891
920
|
if (!gateDecision.ok || gateDecision.action === "refuse") {
|
|
@@ -898,7 +927,7 @@ function create(opts) {
|
|
|
898
927
|
issues: gateContract.summarizeIssues(gateDecision.issues),
|
|
899
928
|
}, actorCtx));
|
|
900
929
|
}
|
|
901
|
-
return
|
|
930
|
+
return writeErr(res, HTTP.UNSUPPORTED_MEDIA_TYPE,
|
|
902
931
|
"content_safety_refused", "Unsupported Media Type");
|
|
903
932
|
}
|
|
904
933
|
if (gateDecision.action === "sanitize" && gateDecision.sanitized) {
|
|
@@ -929,7 +958,7 @@ function create(opts) {
|
|
|
929
958
|
if (ifMatch && ifMatch !== "*" && ifMatch !== meta.etag) {
|
|
930
959
|
stats.failures += 1;
|
|
931
960
|
_emitObs("staticServe.precondition_failed", 1, { route: urlPath, header: "if-match" });
|
|
932
|
-
return
|
|
961
|
+
return writeErr(res, HTTP.PRECONDITION_FAILED || 412,
|
|
933
962
|
"precondition_failed", "Precondition Failed");
|
|
934
963
|
}
|
|
935
964
|
|
|
@@ -956,7 +985,7 @@ function create(opts) {
|
|
|
956
985
|
if (isFinite(ius) && Math.floor(meta.mtimeMs / C.TIME.seconds(1)) > Math.floor(ius / C.TIME.seconds(1))) {
|
|
957
986
|
stats.failures += 1;
|
|
958
987
|
_emitObs("staticServe.precondition_failed", 1, { route: urlPath, header: "if-unmodified-since" });
|
|
959
|
-
return
|
|
988
|
+
return writeErr(res, HTTP.PRECONDITION_FAILED,
|
|
960
989
|
"precondition_failed", "Precondition Failed");
|
|
961
990
|
}
|
|
962
991
|
}
|
|
@@ -970,19 +999,19 @@ function create(opts) {
|
|
|
970
999
|
if (range && (range.malformed || range.multi)) {
|
|
971
1000
|
stats.failures += 1;
|
|
972
1001
|
_emitObs("staticServe.range_invalid", 1, { route: urlPath });
|
|
973
|
-
return
|
|
1002
|
+
return writeErr(res, HTTP.RANGE_NOT_SATISFIABLE, "range_not_satisfiable",
|
|
974
1003
|
"Range Not Satisfiable", { "Content-Range": "bytes */" + meta.size });
|
|
975
1004
|
}
|
|
976
1005
|
if (range && range.unsatisfiable) {
|
|
977
1006
|
stats.failures += 1;
|
|
978
1007
|
_emitObs("staticServe.range_invalid", 1, { route: urlPath });
|
|
979
|
-
return
|
|
1008
|
+
return writeErr(res, HTTP.RANGE_NOT_SATISFIABLE, "range_not_satisfiable",
|
|
980
1009
|
"Range Not Satisfiable", { "Content-Range": "bytes */" + meta.size });
|
|
981
1010
|
}
|
|
982
1011
|
if (range && cfg.maxRangeBytes !== Infinity && range.length > cfg.maxRangeBytes) {
|
|
983
1012
|
stats.failures += 1;
|
|
984
1013
|
_emitObs("staticServe.range_too_large", 1, { route: urlPath });
|
|
985
|
-
return
|
|
1014
|
+
return writeErr(res, HTTP.RANGE_NOT_SATISFIABLE, "range_too_large",
|
|
986
1015
|
"Range Not Satisfiable", { "Content-Range": "bytes */" + meta.size });
|
|
987
1016
|
}
|
|
988
1017
|
if (range) {
|
|
@@ -1005,7 +1034,7 @@ function create(opts) {
|
|
|
1005
1034
|
current: concCheck.current, cap: concCheck.cap,
|
|
1006
1035
|
}, actorCtx));
|
|
1007
1036
|
}
|
|
1008
|
-
return
|
|
1037
|
+
return writeErr(res, HTTP.TOO_MANY_REQUESTS,
|
|
1009
1038
|
"concurrency_cap", "Too Many Requests",
|
|
1010
1039
|
{ "Retry-After": "5" });
|
|
1011
1040
|
}
|
|
@@ -1021,7 +1050,7 @@ function create(opts) {
|
|
|
1021
1050
|
scope: bwCheck.scope, used: bwCheck.used, cap: bwCheck.cap,
|
|
1022
1051
|
}, actorCtx));
|
|
1023
1052
|
}
|
|
1024
|
-
return
|
|
1053
|
+
return writeErr(res, HTTP.TOO_MANY_REQUESTS,
|
|
1025
1054
|
"bandwidth_quota", "Too Many Requests",
|
|
1026
1055
|
{ "Retry-After": String(bwCheck.retryAfter) });
|
|
1027
1056
|
}
|
|
@@ -1077,7 +1106,7 @@ function create(opts) {
|
|
|
1077
1106
|
error: e && e.message,
|
|
1078
1107
|
}, actorCtx));
|
|
1079
1108
|
}
|
|
1080
|
-
return
|
|
1109
|
+
return writeErr(res, HTTP.INTERNAL_SERVER_ERROR, "onServe_threw",
|
|
1081
1110
|
"Internal Server Error");
|
|
1082
1111
|
}
|
|
1083
1112
|
}
|
package/lib/uri-template.js
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
var { defineClass } = require("./framework-error");
|
|
32
|
+
var codepointClass = require("./codepoint-class");
|
|
32
33
|
|
|
33
34
|
var UriTemplateError = defineClass("UriTemplateError", { alwaysPermanent: true });
|
|
34
35
|
|
|
@@ -59,7 +60,8 @@ function _pctEncode(str, allowReserved) {
|
|
|
59
60
|
var ch = str.charAt(i);
|
|
60
61
|
// Preserve existing percent-encoded triplets when the reserved set is
|
|
61
62
|
// allowed (operators "+" and "#").
|
|
62
|
-
|
|
63
|
+
// allow:regex-no-length-cap — the substr is a fixed 2-char window
|
|
64
|
+
if (allowReserved && ch === "%" && codepointClass.HEX_PAIR_RE.test(str.substr(i + 1, 2))) {
|
|
63
65
|
out += str.substr(i, 3); i += 2; continue;
|
|
64
66
|
}
|
|
65
67
|
if (UNRESERVED.test(ch) || (allowReserved && RESERVED.test(ch))) { out += ch; continue; }
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:c7c9f488-0e96-4295-af9d-359b64bbfce6",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-06-
|
|
8
|
+
"timestamp": "2026-06-02T19:28:34.781Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.14.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.14.19",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.14.
|
|
25
|
+
"version": "0.14.19",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.14.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.14.19",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.14.
|
|
57
|
+
"ref": "@blamejs/core@0.14.19",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|