@blamejs/core 0.7.83 → 0.7.85

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 CHANGED
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.7.x
10
10
 
11
+ - **0.7.85** (2026-05-06) — `b.auth.statusList` — OAuth Token Status List (draft-ietf-oauth-status-list-20). The canonical credential-revocation mechanism for SD-JWT VC and OpenID for Verifiable Credentials. An issuer publishes a JWT-wrapped bitstring at a URL; relying parties fetch + check the bit at index N to determine if the credential whose `status_list` claim points at that URL+index is valid / invalid / suspended / application-specific. **`b.auth.statusList.create({ size, bits?, fill? })`** allocates a bit-packed buffer (`bits` ∈ {1, 2, 4, 8} per draft §6.1.1, default 1). The returned object exposes `.set(idx, status)` / `.get(idx)` / `.snapshot()` / `.toJwt({ issuer, subject, privateKey, algorithm, expiresInSec?, ... })`. **`b.auth.statusList.fromJwt(token, { publicKey | keyResolver, algorithms?, expectedIssuer?, ... })`** verifies the JWT through `b.auth.jwt.verify` and returns `{ list, claims }` — the list exposes `.get(idx)` to check status of an individual credential without decompressing into a separate Buffer. The bitstring is zlib-deflated (RFC 1951 raw deflate per draft §6.1.4) before base64url encoding so a million-entry list collapses to ~125 KB on the wire when most bits are zero. Caps the compressed payload at 1 MiB; operators publishing larger lists shard. Status constants exported as `b.auth.statusList.STATUS_{VALID,INVALID,SUSPENDED,APPLICATION_SPECIFIC}`. Foundational for the v0.7.58 EU AI Act + eIDAS 2.0 wallet slice.
12
+
13
+ - **0.7.84** (2026-05-06) — `b.crypto.sri(content, { algorithm? })` — Subresource Integrity hash builder per W3C SRI 1.0. Operators emit `<script integrity="sha384-...">` and `<link integrity="sha384-...">` to defend against CDN compromise + ISP MITM injection — the browser refuses to load a resource whose actual hash diverges from the integrity attribute. Default algorithm is `sha384` (W3C §3.2 — collision margin without sha512's 64-byte overhead); `sha256` and `sha512` also accepted, anything else refused. Accepts `Buffer` / `Uint8Array` / `string` / array of those — array inputs emit multiple space-separated integrity tokens per W3C §3.3 multi-integrity (browser picks the strongest it recognizes). Returns the standard `sha###-<base64>` format ready to paste into the `integrity=""` attribute.
14
+
11
15
  - **0.7.83** (2026-05-06) — `b.auth.oauth.endSessionUrl()` (OpenID Connect RP-Initiated Logout) + `b.auth.oauth.pushAuthorizationRequest()` (RFC 9126 PAR). **`endSessionUrl({ idTokenHint?, postLogoutRedirectUri?, state?, logoutHint?, uiLocales?, clientId?, extraParams? })`** builds the URL the operator's `/logout` route redirects the user-agent to so the IdP terminates the session and bounces back to the operator's app. The IdP's `end_session_endpoint` is read from the OIDC discovery document or operator-supplied at `create({ endSessionEndpoint })`. **`pushAuthorizationRequest({ state?, nonce?, prompt?, loginHint?, maxAge?, extraParams? })`** POSTs the authorization-request parameters directly to the IdP's PAR endpoint (mTLS or client-secret authenticated) and returns `{ url, state, nonce, verifier, challenge, requestUri, expiresIn }` — the browser-side redirect URL is `<authorizationEndpoint>?client_id=...&request_uri=...`. Defends against authorization-request parameter tampering by an MITM at the user-agent + against URL-length overflow on long authorization requests (request_uri reference is short). The PAR endpoint is read from the discovery doc's `pushed_authorization_request_endpoint` or operator-supplied at `create({ pushedAuthorizationRequestEndpoint })`. Both helpers throw `auth-oauth/no-end-session-endpoint` / `auth-oauth/no-par-endpoint` when neither the discovery doc nor the operator opts supply the endpoint.
12
16
 
13
17
  - **0.7.82** (2026-05-06) — `b.mail.bimi` — BIMI record builder + verifier (RFC 9091). BIMI publishes a sender's brand logo URL in DNS so receiving MTAs can render it next to the message in supported clients (Gmail, Yahoo, Apple Mail). **`b.mail.bimi.recordShape({ logoUrl, vmcUrl?, selector? })`** produces the canonical `v=BIMI1; l=https://...; a=https://...` TXT-record string per RFC 9091 §4. Both `l=` (SVG logo URL) and `a=` (Verified Mark Certificate URL per RFC 9091 §6) are HTTPS-required (refuses `http://`). All field values are CR/LF/NUL/semicolon-screened so a hostile URL can't inject a record-separator into the published TXT. **`b.mail.bimi.fetchPolicy(domain, { selector?, dnsLookup? })`** queries `<selector>._bimi.<domain>` (default selector `"default"`) and returns the structured record `{ v, l, a }` or `null` when no policy is published / record is malformed. **`b.mail.bimi.parseRecord(text)`** parses any operator-supplied TXT body — semicolon-separated `key=value` pairs per RFC 9091 §4. The framework does NOT validate the SVG / VMC contents against the RFC §5/§6 profiles — operators feed those to their own asset pipeline; the fetch primitive is a thin DNS lookup that returns the structured record so an operator dashboard or SMTP send-time preflight can verify the publication. BIMI is layered on a passing DMARC posture (the receiver requires DMARC quarantine/reject); operators with the existing `b.mail.dmarc` posture set up benefit from BIMI rendering immediately on receivers that honor it.
package/index.js CHANGED
@@ -139,6 +139,7 @@ var auth = {
139
139
  lockout: require("./lib/auth/lockout"),
140
140
  dpop: require("./lib/auth/dpop"),
141
141
  aal: require("./lib/auth/aal"),
142
+ statusList: require("./lib/auth/status-list"),
142
143
  };
143
144
  var template = require("./lib/template");
144
145
  var render = require("./lib/render");
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ /**
3
+ * OAuth Token Status List (draft-ietf-oauth-status-list-20).
4
+ *
5
+ * An issuer publishes a JWT-wrapped bitstring at a URL; relying
6
+ * parties fetch + check the bit at index N to determine if the
7
+ * credential whose `status_list` claim points at that URL + index
8
+ * has been revoked / suspended / is still valid. The format is the
9
+ * canonical replacement for the older "status list" mechanisms in
10
+ * SD-JWT VC and OpenID for Verifiable Credentials.
11
+ *
12
+ * Status values per draft §4.2:
13
+ * 0 = VALID
14
+ * 1 = INVALID
15
+ * 2 = SUSPENDED
16
+ * 3 = APPLICATION_SPECIFIC
17
+ * ... (1-bit / 2-bit / 4-bit / 8-bit `bits` size — operator picks)
18
+ *
19
+ * var list = b.auth.statusList.create({ size: 1024, bits: 1 });
20
+ * list.set(42, 1); // mark idx 42 INVALID
21
+ * var jwt = await list.toJwt({
22
+ * issuer: "https://issuer.example.com",
23
+ * subject: "https://issuer.example.com/status/list/1",
24
+ * privateKey: env("STATUS_LIST_PRIVATE_KEY_PEM"),
25
+ * algorithm: "ML-DSA-87", // matches b.auth.jwt's PQC default
26
+ * });
27
+ *
28
+ * // Receive side:
29
+ * var rv = await b.auth.statusList.fromJwt(jwt, { publicKey: pem });
30
+ * rv.list.get(42) // → 1 (INVALID)
31
+ *
32
+ * The JWT payload shape per draft §6.1:
33
+ * {
34
+ * iss: "<issuer>",
35
+ * sub: "<this-list-uri>",
36
+ * iat: <issued-at>,
37
+ * exp: <optional-expires>,
38
+ * ttl: <optional-cache-ttl>,
39
+ * status_list: { bits: 1|2|4|8, lst: "<base64url(zlib(bitstring))>" },
40
+ * }
41
+ *
42
+ * The bitstring is zlib-deflated (RFC 1951 raw deflate per draft
43
+ * §6.1.4) before base64url encoding so a million-entry list collapses
44
+ * to a few KB on the wire when most bits are zero.
45
+ */
46
+
47
+ var nodeCrypto = require("crypto");
48
+ var zlib = require("node:zlib");
49
+ var safeJson = require("../safe-json");
50
+ var validateOpts = require("../validate-opts");
51
+ var C = require("../constants");
52
+ var jwt = require("./jwt");
53
+ var { defineClass } = require("../framework-error");
54
+
55
+ var StatusListError = defineClass("StatusListError", { alwaysPermanent: true });
56
+
57
+ var SUPPORTED_BIT_SIZES = { 1: 1, 2: 1, 4: 1, 8: 1 }; // allow:raw-byte-literal — bit-size enum (1/2/4/8 bits per status), not bytes
58
+ var STATUS_VALID = 0;
59
+ var STATUS_INVALID = 1;
60
+ var STATUS_SUSPENDED = 2;
61
+ var STATUS_APPLICATION_SPECIFIC = 3;
62
+
63
+ // Cap the on-the-wire compressed payload at 1 MiB (a million 1-bit
64
+ // entries compress to ~125 KB when most are zero; 8 MiB on the wire
65
+ // is more than the spec's expected use). Operators publishing larger
66
+ // status lists should shard.
67
+ var MAX_LIST_BYTES = C.BYTES.mib(1);
68
+
69
+ function _b64url(buf) {
70
+ return buf.toString("base64").replace(/=+$/g, "").replace(/\+/g, "-").replace(/\//g, "_");
71
+ }
72
+
73
+ function _fromB64url(s) {
74
+ var padded = s.replace(/-/g, "+").replace(/_/g, "/");
75
+ while (padded.length % 4) padded += "="; // allow:raw-byte-literal — base64 quartet padding
76
+ return Buffer.from(padded, "base64");
77
+ }
78
+
79
+ function _validateBits(bits) {
80
+ if (!SUPPORTED_BIT_SIZES[bits]) {
81
+ throw new StatusListError("status-list/bad-bits",
82
+ "statusList: bits must be 1, 2, 4, or 8 (draft §6.1.1) — got " + bits);
83
+ }
84
+ }
85
+
86
+ function _validateStatus(status, bits) {
87
+ if (typeof status !== "number" || !isFinite(status) || status < 0 || (status >> 0) !== status) {
88
+ throw new StatusListError("status-list/bad-status",
89
+ "statusList: status must be a non-negative integer — got " + status);
90
+ }
91
+ var max = (1 << bits) - 1;
92
+ if (status > max) {
93
+ throw new StatusListError("status-list/bad-status",
94
+ "statusList: status " + status + " exceeds bits=" + bits + " ceiling " + max);
95
+ }
96
+ }
97
+
98
+ function create(opts) {
99
+ validateOpts.requireObject(opts, "statusList.create", StatusListError);
100
+ validateOpts(opts, ["size", "bits", "fill"], "statusList.create");
101
+ var size = opts.size;
102
+ if (typeof size !== "number" || !isFinite(size) || size <= 0 || (size >> 0) !== size) {
103
+ throw new StatusListError("status-list/bad-size",
104
+ "statusList.create: size must be a positive integer — got " + size);
105
+ }
106
+ var bits = opts.bits === undefined ? 1 : opts.bits;
107
+ _validateBits(bits);
108
+ // Allocate the bit-packed buffer up front. byteCount = ceil(size*bits/8).
109
+ var bitBytes = Math.ceil((size * bits) / 8); // allow:raw-byte-literal — bits-per-byte conversion
110
+ var bytes = Buffer.alloc(bitBytes);
111
+ if (opts.fill !== undefined && opts.fill !== 0) {
112
+ _validateStatus(opts.fill, bits);
113
+ for (var i = 0; i < size; i += 1) _setAt(bytes, bits, i, opts.fill);
114
+ }
115
+
116
+ function set(idx, status) {
117
+ if (typeof idx !== "number" || idx < 0 || idx >= size || (idx >> 0) !== idx) {
118
+ throw new StatusListError("status-list/bad-index",
119
+ "statusList.set: idx out of range — got " + idx + ", size=" + size);
120
+ }
121
+ _validateStatus(status, bits);
122
+ _setAt(bytes, bits, idx, status);
123
+ }
124
+
125
+ function get(idx) {
126
+ if (typeof idx !== "number" || idx < 0 || idx >= size || (idx >> 0) !== idx) {
127
+ throw new StatusListError("status-list/bad-index",
128
+ "statusList.get: idx out of range — got " + idx + ", size=" + size);
129
+ }
130
+ return _getAt(bytes, bits, idx);
131
+ }
132
+
133
+ function snapshot() {
134
+ return { size: size, bits: bits, bytes: Buffer.from(bytes) };
135
+ }
136
+
137
+ // ---- JWT issuance ----
138
+ // Returns the canonical JWT payload + signed compact form per
139
+ // draft §6.1. The signing key is operator-supplied; the framework
140
+ // wraps b.auth.jwt.sign with the status-list-specific claim shape.
141
+ async function toJwt(jwtOpts) {
142
+ validateOpts.requireObject(jwtOpts, "statusList.toJwt", StatusListError);
143
+ validateOpts(jwtOpts, [
144
+ "issuer", "subject", "privateKey", "algorithm",
145
+ "expiresInSec", "notBeforeSec", "now", "ttl",
146
+ ], "statusList.toJwt");
147
+ validateOpts.requireNonEmptyString(jwtOpts.issuer,
148
+ "statusList.toJwt: issuer", StatusListError, "status-list/bad-issuer");
149
+ validateOpts.requireNonEmptyString(jwtOpts.subject,
150
+ "statusList.toJwt: subject", StatusListError, "status-list/bad-subject");
151
+ var deflated = zlib.deflateRawSync(bytes);
152
+ if (deflated.length > MAX_LIST_BYTES) {
153
+ throw new StatusListError("status-list/too-large",
154
+ "statusList.toJwt: compressed list exceeds " + MAX_LIST_BYTES + " bytes — shard the list");
155
+ }
156
+ var lst = _b64url(deflated);
157
+ var claims = {
158
+ iss: jwtOpts.issuer,
159
+ sub: jwtOpts.subject,
160
+ status_list: { bits: bits, lst: lst },
161
+ };
162
+ if (typeof jwtOpts.ttl === "number") claims.ttl = jwtOpts.ttl;
163
+ return await jwt.sign(claims, {
164
+ privateKey: jwtOpts.privateKey,
165
+ algorithm: jwtOpts.algorithm,
166
+ typ: "statuslist+jwt",
167
+ expiresInSec: jwtOpts.expiresInSec,
168
+ notBeforeSec: jwtOpts.notBeforeSec,
169
+ now: jwtOpts.now,
170
+ });
171
+ }
172
+
173
+ return {
174
+ set: set,
175
+ get: get,
176
+ size: size,
177
+ bits: bits,
178
+ snapshot: snapshot,
179
+ toJwt: toJwt,
180
+ };
181
+ }
182
+
183
+ // ---- bit-packed helpers ----
184
+
185
+ function _setAt(bytes, bits, idx, status) {
186
+ if (bits === 8) { bytes[idx] = status & 0xff; return; } // allow:raw-byte-literal — byte mask
187
+ var bitOffset = idx * bits;
188
+ var byteIdx = Math.floor(bitOffset / 8); // allow:raw-byte-literal — bits-per-byte
189
+ var bitInByte = bitOffset % 8; // allow:raw-byte-literal — bits-per-byte
190
+ var mask = ((1 << bits) - 1) << bitInByte;
191
+ bytes[byteIdx] = (bytes[byteIdx] & ~mask) | ((status << bitInByte) & mask);
192
+ }
193
+
194
+ function _getAt(bytes, bits, idx) {
195
+ if (bits === 8) return bytes[idx]; // allow:raw-byte-literal — 8-bit fast path
196
+ var bitOffset = idx * bits;
197
+ var byteIdx = Math.floor(bitOffset / 8); // allow:raw-byte-literal — bits-per-byte
198
+ var bitInByte = bitOffset % 8; // allow:raw-byte-literal — bits-per-byte
199
+ var mask = (1 << bits) - 1;
200
+ return (bytes[byteIdx] >> bitInByte) & mask;
201
+ }
202
+
203
+ // ---- JWT verification ----
204
+
205
+ async function fromJwt(token, opts) {
206
+ validateOpts.requireObject(opts, "statusList.fromJwt", StatusListError);
207
+ if (typeof token !== "string" || token.length === 0) {
208
+ throw new StatusListError("status-list/bad-token",
209
+ "statusList.fromJwt: token must be a non-empty string");
210
+ }
211
+ // Verify the JWT signature using the framework's b.auth.jwt verifier.
212
+ // Allow operator-supplied algorithms (defaults to PQC list).
213
+ var claims = await jwt.verify(token, {
214
+ publicKey: opts.publicKey,
215
+ keyResolver: opts.keyResolver,
216
+ algorithms: opts.algorithms,
217
+ issuer: opts.expectedIssuer,
218
+ audience: opts.expectedAudience,
219
+ clockToleranceSec: opts.clockToleranceSec,
220
+ now: opts.now,
221
+ });
222
+ var sl = claims.status_list;
223
+ if (!sl || typeof sl !== "object" || typeof sl.lst !== "string") {
224
+ throw new StatusListError("status-list/bad-claims",
225
+ "statusList.fromJwt: payload missing status_list.lst (draft §6.1)");
226
+ }
227
+ var bits = sl.bits === undefined ? 1 : sl.bits;
228
+ _validateBits(bits);
229
+ var deflated;
230
+ try { deflated = _fromB64url(sl.lst); }
231
+ catch (e) {
232
+ throw new StatusListError("status-list/bad-base64",
233
+ "statusList.fromJwt: lst is not valid base64url: " + ((e && e.message) || String(e)));
234
+ }
235
+ if (deflated.length > MAX_LIST_BYTES) {
236
+ throw new StatusListError("status-list/too-large",
237
+ "statusList.fromJwt: compressed list exceeds " + MAX_LIST_BYTES + " bytes");
238
+ }
239
+ var inflated;
240
+ try { inflated = zlib.inflateRawSync(deflated, { maxOutputLength: MAX_LIST_BYTES * 8 }); } // allow:raw-byte-literal — 8x compression-ratio cap
241
+ catch (e) {
242
+ throw new StatusListError("status-list/inflate-failed",
243
+ "statusList.fromJwt: zlib inflate failed: " + ((e && e.message) || String(e)));
244
+ }
245
+ // Reconstruct the list object pointing at the inflated bytes.
246
+ var size = (inflated.length * 8) / bits; // allow:raw-byte-literal — bits-per-byte
247
+ return {
248
+ list: {
249
+ size: size,
250
+ bits: bits,
251
+ get: function (idx) { return _getAt(inflated, bits, idx); },
252
+ snapshot: function () { return { size: size, bits: bits, bytes: Buffer.from(inflated) }; },
253
+ },
254
+ claims: claims,
255
+ };
256
+ }
257
+
258
+ // Provide structured-error helpers so a tree-shake-friendly consumer
259
+ // can write switch(status) { case b.auth.statusList.STATUS_VALID: ... }.
260
+ void safeJson; // imported for symmetry; reserved for future helpers
261
+ void nodeCrypto;
262
+
263
+ module.exports = {
264
+ create: create,
265
+ fromJwt: fromJwt,
266
+ STATUS_VALID: STATUS_VALID,
267
+ STATUS_INVALID: STATUS_INVALID,
268
+ STATUS_SUSPENDED: STATUS_SUSPENDED,
269
+ STATUS_APPLICATION_SPECIFIC: STATUS_APPLICATION_SPECIFIC,
270
+ StatusListError: StatusListError,
271
+ };
package/lib/crypto.js CHANGED
@@ -95,6 +95,46 @@ function kdf(input, outputLength) { return hash(input, "shake256", outputLength)
95
95
  function generateBytes(byteLength) { return Buffer.from(random(byteLength)); }
96
96
  function generateToken(byteLength) { return random(byteLength || 32).toString("hex"); }
97
97
 
98
+ // ---- Subresource Integrity (W3C SRI 1.0) ----
99
+ //
100
+ // b.crypto.sri(content, { algorithm? }) — returns a `sha###-base64`
101
+ // integrity attribute string operators paste into <script integrity="...">
102
+ // or <link integrity="..."> tags. Defends against CDN compromise + ISP
103
+ // MITM injection — the browser refuses to load the resource when its
104
+ // hash diverges from the integrity attribute.
105
+ //
106
+ // W3C SRI 1.0 §3.2 lists sha256 / sha384 / sha512 as the supported
107
+ // digest algorithms; sha384 is the recommended default (collision
108
+ // margin without sha512's 64-byte overhead).
109
+ //
110
+ // b.crypto.sri(scriptBuffer, { algorithm: "sha384" })
111
+ // → "sha384-AbCdEf...="
112
+ //
113
+ // b.crypto.sri(["a", "b"], { algorithm: "sha384" }) // array → multi-hash
114
+ // → "sha384-X1... sha384-X2..." (per W3C §3.3 multi-integrity)
115
+ var SRI_ALGORITHMS = { "sha256": "sha256", "sha384": "sha384", "sha512": "sha512" };
116
+
117
+ function sri(content, opts) {
118
+ opts = opts || {};
119
+ var algorithm = (opts.algorithm || "sha384").toLowerCase();
120
+ if (!SRI_ALGORITHMS[algorithm]) {
121
+ throw new Error("crypto.sri: unsupported algorithm '" + algorithm +
122
+ "' (W3C SRI 1.0 §3.2 supports sha256/sha384/sha512)");
123
+ }
124
+ // Array input — emit multiple integrity tokens space-separated per
125
+ // W3C §3.3 (browser picks the strongest one it recognizes).
126
+ if (Array.isArray(content)) {
127
+ return content.map(function (c) { return sri(c, opts); }).join(" ");
128
+ }
129
+ var buf;
130
+ if (Buffer.isBuffer(content)) buf = content;
131
+ else if (typeof content === "string") buf = Buffer.from(content, "utf8");
132
+ else if (content instanceof Uint8Array) buf = Buffer.from(content);
133
+ else throw new Error("crypto.sri: content must be a Buffer, Uint8Array, string, or array of those");
134
+ var digest = nodeCrypto.createHash(algorithm).update(buf).digest("base64");
135
+ return algorithm + "-" + digest;
136
+ }
137
+
98
138
  // ---- Key generation ----
99
139
  function generateEncryptionKeyPair() {
100
140
  var mlkem = generateKeyPair("ml-kem-1024");
@@ -454,6 +494,7 @@ var SUPPORTED_KEM_ALGORITHMS = Object.freeze([
454
494
  ]);
455
495
 
456
496
  module.exports = {
497
+ sri: sri,
457
498
  // Hashing
458
499
  sha3Hash: sha3Hash,
459
500
  hmacSha3: hmacSha3,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.7.83",
3
+ "version": "0.7.85",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -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:2bb408b6-50d7-4fb3-acf9-332f7472bd70",
5
+ "serialNumber": "urn:uuid:c3167b3e-7af4-4368-b131-46f9d9c44ccc",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-06T06:03:44.811Z",
8
+ "timestamp": "2026-05-06T06:22:16.402Z",
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.7.83",
22
+ "bom-ref": "@blamejs/core@0.7.85",
23
23
  "type": "library",
24
24
  "name": "blamejs",
25
- "version": "0.7.83",
25
+ "version": "0.7.85",
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.7.83",
29
+ "purl": "pkg:npm/%40blamejs/core@0.7.85",
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.7.83",
57
+ "ref": "@blamejs/core@0.7.85",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]