@blamejs/blamejs-shop 0.3.56 → 0.3.58

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.
Files changed (84) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/SECURITY.md +11 -0
  4. package/lib/asset-manifest.json +3 -3
  5. package/lib/checkout.js +89 -10
  6. package/lib/security-middleware.js +17 -1
  7. package/lib/storefront.js +156 -51
  8. package/lib/vendor/MANIFEST.json +84 -72
  9. package/lib/vendor/blamejs/CHANGELOG.md +6 -0
  10. package/lib/vendor/blamejs/README.md +3 -3
  11. package/lib/vendor/blamejs/SECURITY.md +5 -0
  12. package/lib/vendor/blamejs/api-snapshot.json +15 -3
  13. package/lib/vendor/blamejs/lib/agent-orchestrator.js +10 -4
  14. package/lib/vendor/blamejs/lib/ai-prompt.js +1 -1
  15. package/lib/vendor/blamejs/lib/app-shutdown.js +28 -0
  16. package/lib/vendor/blamejs/lib/archive-read.js +215 -16
  17. package/lib/vendor/blamejs/lib/archive.js +206 -52
  18. package/lib/vendor/blamejs/lib/auth/oauth.js +58 -0
  19. package/lib/vendor/blamejs/lib/auth/oid4vci.js +84 -27
  20. package/lib/vendor/blamejs/lib/breach-deadline.js +166 -1
  21. package/lib/vendor/blamejs/lib/cloud-events.js +3 -1
  22. package/lib/vendor/blamejs/lib/codepoint-class.js +21 -0
  23. package/lib/vendor/blamejs/lib/db-schema.js +120 -3
  24. package/lib/vendor/blamejs/lib/db.js +10 -3
  25. package/lib/vendor/blamejs/lib/error-page.js +93 -9
  26. package/lib/vendor/blamejs/lib/external-db.js +164 -13
  27. package/lib/vendor/blamejs/lib/guard-email.js +36 -3
  28. package/lib/vendor/blamejs/lib/http-client.js +37 -7
  29. package/lib/vendor/blamejs/lib/mail-auth.js +554 -55
  30. package/lib/vendor/blamejs/lib/mail-send-deliver.js +15 -5
  31. package/lib/vendor/blamejs/lib/mail-sieve.js +2 -1
  32. package/lib/vendor/blamejs/lib/middleware/ai-act-disclosure.js +88 -19
  33. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +58 -11
  34. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +56 -4
  35. package/lib/vendor/blamejs/lib/middleware/attach-user.js +45 -10
  36. package/lib/vendor/blamejs/lib/middleware/body-parser.js +70 -14
  37. package/lib/vendor/blamejs/lib/middleware/csp-report.js +30 -2
  38. package/lib/vendor/blamejs/lib/middleware/deny-response.js +29 -9
  39. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +56 -4
  40. package/lib/vendor/blamejs/lib/middleware/scim-server.js +301 -14
  41. package/lib/vendor/blamejs/lib/openapi-paths-builder.js +105 -29
  42. package/lib/vendor/blamejs/lib/openapi.js +225 -100
  43. package/lib/vendor/blamejs/lib/problem-details.js +15 -3
  44. package/lib/vendor/blamejs/lib/queue-local.js +148 -38
  45. package/lib/vendor/blamejs/lib/queue.js +41 -11
  46. package/lib/vendor/blamejs/lib/render.js +21 -3
  47. package/lib/vendor/blamejs/lib/router.js +13 -6
  48. package/lib/vendor/blamejs/lib/safe-buffer.js +55 -0
  49. package/lib/vendor/blamejs/lib/sse.js +7 -5
  50. package/lib/vendor/blamejs/lib/static.js +46 -17
  51. package/lib/vendor/blamejs/lib/uri-template.js +3 -1
  52. package/lib/vendor/blamejs/package.json +1 -1
  53. package/lib/vendor/blamejs/release-notes/v0.14.17.json +57 -0
  54. package/lib/vendor/blamejs/release-notes/v0.14.18.json +127 -0
  55. package/lib/vendor/blamejs/release-notes/v0.14.19.json +61 -0
  56. package/lib/vendor/blamejs/test/00-primitives.js +151 -0
  57. package/lib/vendor/blamejs/test/layer-0-primitives/agent-orchestrator.test.js +18 -0
  58. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +86 -0
  59. package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +58 -0
  60. package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +201 -0
  61. package/lib/vendor/blamejs/test/layer-0-primitives/archive.test.js +179 -0
  62. package/lib/vendor/blamejs/test/layer-0-primitives/asyncapi.test.js +19 -0
  63. package/lib/vendor/blamejs/test/layer-0-primitives/attach-user-bearer-scheme.test.js +154 -0
  64. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-chunked-malformed.test.js +10 -8
  65. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-smuggling.test.js +99 -20
  66. package/lib/vendor/blamejs/test/layer-0-primitives/breach-deadline.test.js +85 -0
  67. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +50 -1
  68. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +63 -0
  69. package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +107 -2
  70. package/lib/vendor/blamejs/test/layer-0-primitives/db-schema-drift.test.js +145 -0
  71. package/lib/vendor/blamejs/test/layer-0-primitives/deny-response.test.js +32 -0
  72. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +119 -0
  73. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +121 -1
  74. package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +14 -0
  75. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +53 -0
  76. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +179 -5
  77. package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +45 -0
  78. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +80 -0
  79. package/lib/vendor/blamejs/test/layer-0-primitives/openapi.test.js +177 -0
  80. package/lib/vendor/blamejs/test/layer-0-primitives/queue-byo-db.test.js +312 -0
  81. package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +165 -2
  82. package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +33 -1
  83. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +59 -0
  84. package/package.json +1 -1
@@ -22,8 +22,14 @@ function _mkRes() {
22
22
  async function _drive(mw, req) {
23
23
  var res = _mkRes();
24
24
  var nextCalled = false;
25
+ var ended = false;
26
+ var origEnd = res.end;
27
+ res.end = function (b) { ended = true; return origEnd.call(res, b); };
25
28
  mw(req, res, function () { nextCalled = true; });
26
- await helpers.passiveObserve(50, "scim-server: middleware short-circuit window");
29
+ await helpers.waitUntil(function () { return ended || nextCalled; }, {
30
+ timeoutMs: 5000,
31
+ label: "scim-server: middleware resolves (response written or next() called)",
32
+ });
27
33
  return { res: res, nextCalled: nextCalled };
28
34
  }
29
35
 
@@ -82,11 +88,168 @@ async function run() {
82
88
  var scimMod = require("../../lib/middleware/scim-server");
83
89
  check("ALLOWED_FILTER_OPS exported", Array.isArray(scimMod.ALLOWED_FILTER_OPS));
84
90
  check("SCIM_CORE_SCHEMA_USER exported", scimMod.SCIM_CORE_SCHEMA_USER === "urn:ietf:params:scim:schemas:core:2.0:User");
91
+ check("BulkRequest URN exported", scimMod.SCIM_MESSAGE_BULK_REQUEST === "urn:ietf:params:scim:api:messages:2.0:BulkRequest");
92
+ check("BulkResponse URN exported", scimMod.SCIM_MESSAGE_BULK_RESPONSE === "urn:ietf:params:scim:api:messages:2.0:BulkResponse");
93
+
94
+ await _runBulkTests();
95
+ }
96
+
97
+ // RFC 7644 §3.7 — /Bulk operations.
98
+ function _mkUsers() {
99
+ return {
100
+ _records: new Map(),
101
+ _seq: 0,
102
+ async create(rec) {
103
+ var id = "u-" + (++this._seq);
104
+ var out = Object.assign({ id: id, meta: { resourceType: "User" } }, rec);
105
+ this._records.set(id, out);
106
+ return out;
107
+ },
108
+ async read(id) { return this._records.get(id) || null; },
109
+ async update(id, rec) { var r = Object.assign({}, rec, { id: id }); this._records.set(id, r); return r; },
110
+ async patch(id, ops) { var r = this._records.get(id); ops.forEach(function (o) { if (o.op === "replace") r[o.path] = o.value; }); return r; },
111
+ async remove(id) { this._records.delete(id); },
112
+ async list() { return { totalResults: this._records.size, Resources: Array.from(this._records.values()) }; },
113
+ };
114
+ }
115
+
116
+ function _mkGroups() {
117
+ return {
118
+ _records: new Map(),
119
+ _seq: 0,
120
+ _lastCreate: null,
121
+ async create(rec) {
122
+ var id = "g-" + (++this._seq);
123
+ var out = Object.assign({ id: id, meta: { resourceType: "Group" } }, rec);
124
+ this._records.set(id, out);
125
+ this._lastCreate = out;
126
+ return out;
127
+ },
128
+ async read(id) { return this._records.get(id) || null; },
129
+ async update(id, rec) { var r = Object.assign({}, rec, { id: id }); this._records.set(id, r); return r; },
130
+ async patch(id, ops) { var r = this._records.get(id); return r; },
131
+ async remove(id) { this._records.delete(id); },
132
+ async list() { return { totalResults: this._records.size, Resources: Array.from(this._records.values()) }; },
133
+ };
134
+ }
135
+
136
+ async function _bulkBody(mw, ops, extra) {
137
+ var body = Object.assign({
138
+ schemas: ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],
139
+ Operations: ops,
140
+ }, extra || {});
141
+ return _drive(mw, { method: "POST", url: "/scim/v2/Bulk", headers: {}, body: body });
142
+ }
143
+
144
+ async function _runBulkTests() {
145
+ // Bulk disabled by default — SPC stays unsupported, /Bulk returns 501.
146
+ var off = b.middleware.scimServer({ basePath: "/scim/v2", users: _mkUsers() });
147
+ var offSpc = await _drive(off, { method: "GET", url: "/scim/v2/ServiceProviderConfig", headers: {} });
148
+ check("bulk default: SPC supported=false", JSON.parse(offSpc.res._body()).bulk.supported === false);
149
+ var offBulk = await _bulkBody(off, []);
150
+ check("bulk default: /Bulk → 501", offBulk.res._sc() === 501);
151
+
152
+ // Enabled — SPC advertises supported=true + configured maxOperations.
153
+ var users = _mkUsers();
154
+ var groups = _mkGroups();
155
+ var mw = b.middleware.scimServer({
156
+ basePath: "/scim/v2",
157
+ users: users,
158
+ groups: groups,
159
+ bulk: { maxOperations: 3 },
160
+ });
161
+ var onSpc = JSON.parse((await _drive(mw, { method: "GET", url: "/scim/v2/ServiceProviderConfig", headers: {} })).res._body());
162
+ check("bulk enabled: SPC supported=true", onSpc.bulk.supported === true);
163
+ check("bulk enabled: SPC maxOperations", onSpc.bulk.maxOperations === 3);
164
+
165
+ // create + update + delete in one request → per-op statuses.
166
+ var seedUser = await users.create({ userName: "seed" }); // u-1
167
+ var batch = await _bulkBody(mw, [
168
+ { method: "POST", path: "/Users", bulkId: "newuser",
169
+ data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "alice" } },
170
+ { method: "PUT", path: "/Users/" + seedUser.id,
171
+ data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "seed-renamed" } },
172
+ { method: "DELETE", path: "/Users/" + seedUser.id },
173
+ ]);
174
+ var resp = JSON.parse(batch.res._body());
175
+ check("bulk: 200 envelope", batch.res._sc() === 200);
176
+ check("bulk: BulkResponse schema", resp.schemas.indexOf("urn:ietf:params:scim:api:messages:2.0:BulkResponse") !== -1);
177
+ check("bulk: 3 op results", resp.Operations.length === 3);
178
+ check("bulk: POST → 201", resp.Operations[0].status === "201");
179
+ check("bulk: POST echoes bulkId", resp.Operations[0].bulkId === "newuser");
180
+ check("bulk: POST location present", typeof resp.Operations[0].location === "string");
181
+ check("bulk: PUT → 200", resp.Operations[1].status === "200");
182
+ check("bulk: DELETE → 204", resp.Operations[2].status === "204");
183
+ check("bulk: DELETE has no response body", resp.Operations[2].response === undefined);
184
+
185
+ // A bulk POST whose resource body is missing its SCIM schema is rejected
186
+ // per-op (the same gate the singleton POST/PUT routes apply), not
187
+ // persisted through the adapter.
188
+ var badSchema = await _bulkBody(mw, [
189
+ { method: "POST", path: "/Users", data: { userName: "noschema" } },
190
+ ]);
191
+ var badSchemaResp = JSON.parse(badSchema.res._body());
192
+ check("bulk: POST with missing schema → per-op 400",
193
+ badSchemaResp.Operations.length === 1 && badSchemaResp.Operations[0].status === "400");
194
+
195
+ // bulkId cross-reference: create a user, then a group whose member
196
+ // references the just-created user by "bulkId:<id>" (RFC 7644 §3.7.2).
197
+ var users2 = _mkUsers();
198
+ var groups2 = _mkGroups();
199
+ var mw2 = b.middleware.scimServer({ basePath: "/scim/v2", users: users2, groups: groups2, bulk: { maxOperations: 10 } });
200
+ var ref = await _bulkBody(mw2, [
201
+ { method: "POST", path: "/Users", bulkId: "bob",
202
+ data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "bob" } },
203
+ { method: "POST", path: "/Groups", bulkId: "admins",
204
+ data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:Group"], displayName: "Admins",
205
+ members: [{ value: "bulkId:bob" }] } },
206
+ ]);
207
+ var refResp = JSON.parse(ref.res._body());
208
+ check("bulk ref: both succeed", refResp.Operations[0].status === "201" && refResp.Operations[1].status === "201");
209
+ var createdUserId = users2._records.size === 1 ? Array.from(users2._records.keys())[0] : null;
210
+ var groupMembers = groups2._lastCreate && groups2._lastCreate.members;
211
+ check("bulk ref: bulkId resolved to id", Array.isArray(groupMembers) && groupMembers[0].value === createdUserId);
212
+ check("bulk ref: ref not left as token", groupMembers[0].value.indexOf("bulkId:") === -1);
213
+
214
+ // over-maxOperations → 413, no operation dispatched.
215
+ var users3 = _mkUsers();
216
+ var mw3 = b.middleware.scimServer({ basePath: "/scim/v2", users: users3, bulk: { maxOperations: 1 } });
217
+ var tooMany = await _bulkBody(mw3, [
218
+ { method: "POST", path: "/Users", bulkId: "a", data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "a" } },
219
+ { method: "POST", path: "/Users", bulkId: "b", data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "b" } },
220
+ ]);
221
+ check("bulk: over-maxOperations → 413", tooMany.res._sc() === 413);
222
+ check("bulk: over-max scimType=tooMany", JSON.parse(tooMany.res._body()).scimType === "tooMany");
223
+ check("bulk: over-max dispatched nothing", users3._records.size === 0);
224
+
225
+ // failOnErrors short-circuits at the first error.
226
+ var users4 = _mkUsers();
227
+ var failing = {
228
+ create: async function () { var e = new Error("upstream conflict"); e.statusCode = 409; e.scimType = "uniqueness"; throw e; },
229
+ read: users4.read.bind(users4), update: users4.update.bind(users4),
230
+ patch: users4.patch.bind(users4), remove: users4.remove.bind(users4), list: users4.list.bind(users4),
231
+ };
232
+ var mw4 = b.middleware.scimServer({ basePath: "/scim/v2", users: failing, bulk: { maxOperations: 10 } });
233
+ var short = await _bulkBody(mw4, [
234
+ { method: "POST", path: "/Users", bulkId: "x", data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "x" } },
235
+ { method: "POST", path: "/Users", bulkId: "y", data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "y" } },
236
+ { method: "POST", path: "/Users", bulkId: "z", data: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "z" } },
237
+ ], { failOnErrors: 1 });
238
+ var shortResp = JSON.parse(short.res._body());
239
+ check("bulk failOnErrors: stops after 1", shortResp.Operations.length === 1);
240
+ check("bulk failOnErrors: error carries status", shortResp.Operations[0].status === "409");
241
+ check("bulk failOnErrors: error has body", shortResp.Operations[0].response.schemas.indexOf("urn:ietf:params:scim:api:messages:2.0:Error") !== -1);
242
+
243
+ // config-time refusal: non-integer cap throws at create time.
244
+ var threwCap = false;
245
+ try { b.middleware.scimServer({ basePath: "/scim/v2", users: _mkUsers(), bulk: { maxOperations: -5 } }); }
246
+ catch (e) { threwCap = /bad-bulk-max-operations/.test(e.code || ""); }
247
+ check("bulk: bad maxOperations refused", threwCap);
85
248
  }
86
249
 
87
250
  module.exports = { run: run };
88
251
 
89
252
  if (require.main === module) {
90
253
  run().then(function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
91
- function (e) { console.error("FAIL:", e.stack || e); process.exit(1); });
254
+ function (e) { process.exitCode = 1; throw e; });
92
255
  }
@@ -7,7 +7,9 @@ var helpers = require("../helpers");
7
7
  var b = helpers.b;
8
8
  var check = helpers.check;
9
9
 
10
- var sseModule = require("../../lib/middleware/sse");
10
+ var sseModule = require("../../lib/middleware/sse");
11
+ var EventEmitter = require("events");
12
+ var auditModule = require("../../lib/audit");
11
13
 
12
14
  // Mock ServerResponse — captures writeHead + write + end.
13
15
  function _mockRes() {
@@ -148,6 +150,36 @@ async function run() {
148
150
  label: "sse: onAbort fires on res 'close'",
149
151
  });
150
152
  check("sse: onAbort fires on res close", aborted === true);
153
+
154
+ // ---- a transport-fault close audits as a failure, not a clean close ----
155
+ // A write-to-dead-pipe / stream error closes the channel like an operator
156
+ // close does, but it is NOT clean — the audit event must say so + carry the
157
+ // reason, so the evidence stream distinguishes the two.
158
+ var capturedAudit = [];
159
+ var origSafeEmit = auditModule.safeEmit;
160
+ auditModule.safeEmit = function (e) { capturedAudit.push(e); };
161
+ try {
162
+ var faultRes = new EventEmitter(); // needs .on so sse attaches its fault handlers
163
+ faultRes.writeHead = function () {};
164
+ faultRes.setHeader = function () {};
165
+ faultRes.getHeader = function () { return undefined; };
166
+ faultRes.write = function () { return true; };
167
+ faultRes.end = function () {};
168
+ var faultReq = { method: "GET", url: "/events", headers: {}, on: function () {}, once: function () {} };
169
+ // b.sse.create(req, res, opts) is the low-level channel; audit defaults on.
170
+ b.sse.create(faultReq, faultRes, { heartbeatMs: 0 });
171
+ faultRes.emit("error", new Error("pipe broke"));
172
+ await helpers.waitUntil(function () {
173
+ return capturedAudit.some(function (e) { return e.action === "sse.channel_closed"; });
174
+ }, { label: "sse: fault-close audit emitted" });
175
+ var faultEv = capturedAudit.find(function (e) { return e.action === "sse.channel_closed"; });
176
+ check("sse fault-close: outcome is failure (not success)",
177
+ faultEv && faultEv.outcome === "failure");
178
+ check("sse fault-close: reason recorded in metadata",
179
+ faultEv && faultEv.metadata && faultEv.metadata.reason === "stream-error");
180
+ } finally {
181
+ auditModule.safeEmit = origSafeEmit;
182
+ }
151
183
  }
152
184
 
153
185
  module.exports = { run: run };
@@ -243,6 +243,62 @@ function testRejectsUnknownOpts() {
243
243
  fs.rmSync(dir, { recursive: true, force: true });
244
244
  }
245
245
 
246
+ // onError mirrors onServe on the refusal paths. A denying permissions
247
+ // gate forces a 403, which previously fired no operator callback.
248
+ async function testOnErrorFiresOnRefusal() {
249
+ var ctx = await _server();
250
+ _writeFile(ctx.dir, "secret.txt", "classified");
251
+ var seen = [];
252
+ var denyPerms = { check: async function () { return false; } };
253
+ var srv = await ctx.start({
254
+ contentSafety: null,
255
+ permissions: denyPerms,
256
+ onError: function (info) { seen.push(info); },
257
+ });
258
+ try {
259
+ var r = await _get(srv.port, "/secret.txt");
260
+ check("onError: refusal returns 403", r.statusCode === 403);
261
+ check("onError: hook fired once on the refusal", seen.length === 1);
262
+ check("onError: hook carries status + code + urlPath",
263
+ seen[0].status === 403 && seen[0].code === "permission_denied" &&
264
+ seen[0].urlPath === "/secret.txt");
265
+ } finally {
266
+ srv.close();
267
+ ctx.cleanup();
268
+ }
269
+ }
270
+
271
+ // onError is observability-only: a throwing hook must not corrupt the
272
+ // refusal response already on the wire.
273
+ async function testOnErrorThrowDoesNotCorruptResponse() {
274
+ var ctx = await _server();
275
+ _writeFile(ctx.dir, "secret.txt", "classified");
276
+ var denyPerms = { check: async function () { return false; } };
277
+ var srv = await ctx.start({
278
+ contentSafety: null,
279
+ permissions: denyPerms,
280
+ onError: function () { throw new Error("sink broke"); },
281
+ });
282
+ try {
283
+ var r = await _get(srv.port, "/secret.txt");
284
+ check("onError: throwing hook still yields the 403",
285
+ r.statusCode === 403 && /Forbidden/.test(r.body.toString("utf8")));
286
+ } finally {
287
+ srv.close();
288
+ ctx.cleanup();
289
+ }
290
+ }
291
+
292
+ function testOnErrorRejectsNonFunction() {
293
+ var threw = null;
294
+ var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-static-fa-"));
295
+ try {
296
+ b.staticServe.create({ root: dir, onError: "nope" });
297
+ } catch (e) { threw = e; }
298
+ check("onError non-function rejected at create()", threw !== null);
299
+ fs.rmSync(dir, { recursive: true, force: true });
300
+ }
301
+
246
302
  async function run() {
247
303
  await testForceAttachmentDefaultOff();
248
304
  await testForceAttachmentOnHtml();
@@ -254,6 +310,9 @@ async function run() {
254
310
  await testForceAttachmentPdfDefaultDownload();
255
311
  await testForceAttachmentPdfOptInInline();
256
312
  testRejectsUnknownOpts();
313
+ await testOnErrorFiresOnRefusal();
314
+ await testOnErrorThrowDoesNotCorruptResponse();
315
+ testOnErrorRejectsNonFunction();
257
316
  }
258
317
 
259
318
  module.exports = { run: run };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.3.56",
3
+ "version": "0.3.58",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {