@blamejs/core 0.8.42 → 0.8.49
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 +93 -0
- package/README.md +10 -10
- package/index.js +52 -0
- package/lib/a2a.js +159 -34
- package/lib/acme.js +762 -0
- package/lib/ai-pref.js +166 -43
- package/lib/api-key.js +108 -47
- package/lib/api-snapshot.js +157 -40
- package/lib/app-shutdown.js +113 -77
- package/lib/archive.js +337 -40
- package/lib/arg-parser.js +697 -0
- package/lib/asyncapi.js +99 -55
- package/lib/atomic-file.js +465 -104
- package/lib/audit-chain.js +123 -34
- package/lib/audit-daily-review.js +389 -0
- package/lib/audit-sign.js +302 -56
- package/lib/audit-tools.js +412 -63
- package/lib/audit.js +656 -35
- package/lib/auth/jwt-external.js +17 -0
- package/lib/auth/oauth.js +7 -0
- package/lib/auth-bot-challenge.js +505 -0
- package/lib/auth-header.js +92 -25
- package/lib/backup/bundle.js +26 -0
- package/lib/backup/index.js +512 -89
- package/lib/backup/manifest.js +168 -7
- package/lib/break-glass.js +415 -39
- package/lib/budr.js +103 -30
- package/lib/bundler.js +86 -66
- package/lib/cache.js +192 -72
- package/lib/chain-writer.js +65 -40
- package/lib/circuit-breaker.js +56 -33
- package/lib/cli-helpers.js +106 -75
- package/lib/cli.js +6 -30
- package/lib/cloud-events.js +99 -32
- package/lib/cluster-storage.js +162 -37
- package/lib/cluster.js +340 -49
- package/lib/codepoint-class.js +66 -0
- package/lib/compliance.js +424 -24
- package/lib/config-drift.js +111 -46
- package/lib/config.js +94 -40
- package/lib/consent.js +165 -18
- package/lib/constants.js +1 -0
- package/lib/content-credentials.js +153 -48
- package/lib/cookies.js +154 -62
- package/lib/credential-hash.js +133 -61
- package/lib/crypto-field.js +702 -18
- package/lib/crypto-hpke.js +256 -0
- package/lib/crypto.js +744 -22
- package/lib/csv.js +178 -35
- package/lib/daemon.js +456 -0
- package/lib/dark-patterns.js +186 -55
- package/lib/db-query.js +79 -2
- package/lib/db.js +1431 -60
- package/lib/ddl-change-control.js +523 -0
- package/lib/deprecate.js +195 -40
- package/lib/dev.js +82 -39
- package/lib/dora.js +67 -48
- package/lib/dr-runbook.js +368 -0
- package/lib/dsr.js +142 -11
- package/lib/dual-control.js +91 -56
- package/lib/events.js +120 -41
- package/lib/external-db-migrate.js +192 -2
- package/lib/external-db.js +795 -50
- package/lib/fapi2.js +122 -1
- package/lib/fda-21cfr11.js +395 -0
- package/lib/fdx.js +132 -2
- package/lib/file-type.js +87 -0
- package/lib/file-upload.js +93 -0
- package/lib/flag.js +82 -20
- package/lib/forms.js +132 -29
- package/lib/framework-error.js +169 -0
- package/lib/framework-schema.js +163 -35
- package/lib/gate-contract.js +849 -175
- package/lib/graphql-federation.js +68 -7
- package/lib/guard-all.js +172 -55
- package/lib/guard-archive.js +286 -124
- package/lib/guard-auth.js +194 -21
- package/lib/guard-cidr.js +190 -28
- package/lib/guard-csv.js +397 -51
- package/lib/guard-domain.js +213 -91
- package/lib/guard-email.js +236 -29
- package/lib/guard-filename.js +307 -75
- package/lib/guard-graphql.js +263 -30
- package/lib/guard-html.js +310 -116
- package/lib/guard-image.js +243 -30
- package/lib/guard-json.js +260 -54
- package/lib/guard-jsonpath.js +235 -23
- package/lib/guard-jwt.js +284 -30
- package/lib/guard-markdown.js +204 -22
- package/lib/guard-mime.js +190 -26
- package/lib/guard-oauth.js +277 -28
- package/lib/guard-pdf.js +251 -27
- package/lib/guard-regex.js +226 -18
- package/lib/guard-shell.js +229 -26
- package/lib/guard-svg.js +177 -10
- package/lib/guard-template.js +232 -21
- package/lib/guard-time.js +195 -29
- package/lib/guard-uuid.js +189 -30
- package/lib/guard-xml.js +259 -36
- package/lib/guard-yaml.js +241 -44
- package/lib/honeytoken.js +63 -27
- package/lib/html-balance.js +83 -0
- package/lib/http-client.js +486 -59
- package/lib/http-message-signature.js +582 -0
- package/lib/i18n.js +102 -49
- package/lib/iab-mspa.js +112 -32
- package/lib/iab-tcf.js +107 -2
- package/lib/inbox.js +90 -52
- package/lib/keychain.js +865 -0
- package/lib/legal-hold.js +374 -0
- package/lib/local-db-thin.js +320 -0
- package/lib/log-stream.js +281 -51
- package/lib/log.js +184 -86
- package/lib/mail-bounce.js +107 -62
- package/lib/mail.js +295 -58
- package/lib/mcp.js +108 -27
- package/lib/metrics.js +98 -89
- package/lib/middleware/age-gate.js +36 -0
- package/lib/middleware/ai-act-disclosure.js +37 -0
- package/lib/middleware/api-encrypt.js +45 -0
- package/lib/middleware/assetlinks.js +40 -0
- package/lib/middleware/asyncapi-serve.js +35 -0
- package/lib/middleware/attach-user.js +40 -0
- package/lib/middleware/bearer-auth.js +40 -0
- package/lib/middleware/body-parser.js +230 -0
- package/lib/middleware/bot-disclose.js +34 -0
- package/lib/middleware/bot-guard.js +39 -0
- package/lib/middleware/compression.js +37 -0
- package/lib/middleware/cookies.js +32 -0
- package/lib/middleware/cors.js +40 -0
- package/lib/middleware/csp-nonce.js +40 -0
- package/lib/middleware/csp-report.js +34 -0
- package/lib/middleware/csrf-protect.js +43 -0
- package/lib/middleware/daily-byte-quota.js +53 -85
- package/lib/middleware/db-role-for.js +40 -0
- package/lib/middleware/dpop.js +40 -0
- package/lib/middleware/error-handler.js +37 -14
- package/lib/middleware/fetch-metadata.js +39 -0
- package/lib/middleware/flag-context.js +34 -0
- package/lib/middleware/gpc.js +33 -0
- package/lib/middleware/headers.js +35 -0
- package/lib/middleware/health.js +46 -0
- package/lib/middleware/host-allowlist.js +30 -0
- package/lib/middleware/network-allowlist.js +38 -0
- package/lib/middleware/openapi-serve.js +34 -0
- package/lib/middleware/rate-limit.js +160 -18
- package/lib/middleware/request-id.js +36 -18
- package/lib/middleware/request-log.js +37 -0
- package/lib/middleware/require-aal.js +29 -0
- package/lib/middleware/require-auth.js +32 -0
- package/lib/middleware/require-bound-key.js +41 -0
- package/lib/middleware/require-content-type.js +32 -0
- package/lib/middleware/require-methods.js +27 -0
- package/lib/middleware/require-mtls.js +33 -0
- package/lib/middleware/require-step-up.js +37 -0
- package/lib/middleware/security-headers.js +44 -0
- package/lib/middleware/security-txt.js +38 -0
- package/lib/middleware/span-http-server.js +37 -0
- package/lib/middleware/sse.js +36 -0
- package/lib/middleware/trace-log-correlation.js +33 -0
- package/lib/middleware/trace-propagate.js +32 -0
- package/lib/middleware/tus-upload.js +90 -0
- package/lib/middleware/web-app-manifest.js +53 -0
- package/lib/mtls-ca.js +100 -70
- package/lib/network-byte-quota.js +308 -0
- package/lib/network-heartbeat.js +135 -0
- package/lib/network-tls.js +534 -4
- package/lib/network.js +103 -0
- package/lib/notify.js +114 -43
- package/lib/ntp-check.js +192 -51
- package/lib/observability.js +145 -47
- package/lib/openapi.js +90 -44
- package/lib/outbox.js +99 -1
- package/lib/pagination.js +168 -86
- package/lib/parsers/index.js +16 -5
- package/lib/permissions.js +93 -40
- package/lib/pqc-agent.js +84 -8
- package/lib/pqc-software.js +94 -60
- package/lib/process-spawn.js +95 -21
- package/lib/pubsub.js +96 -66
- package/lib/queue.js +375 -54
- package/lib/redact.js +793 -21
- package/lib/render.js +139 -47
- package/lib/request-helpers.js +485 -121
- package/lib/restore-bundle.js +142 -39
- package/lib/restore-rollback.js +136 -45
- package/lib/retention.js +178 -50
- package/lib/retry.js +116 -33
- package/lib/router.js +475 -23
- package/lib/safe-async.js +543 -94
- package/lib/safe-buffer.js +337 -41
- package/lib/safe-json.js +467 -62
- package/lib/safe-jsonpath.js +285 -0
- package/lib/safe-schema.js +631 -87
- package/lib/safe-sql.js +221 -59
- package/lib/safe-url.js +278 -46
- package/lib/sandbox-worker.js +135 -0
- package/lib/sandbox.js +358 -0
- package/lib/scheduler.js +135 -70
- package/lib/self-update.js +647 -0
- package/lib/session-device-binding.js +431 -0
- package/lib/session.js +259 -49
- package/lib/slug.js +138 -26
- package/lib/ssrf-guard.js +316 -56
- package/lib/storage.js +433 -70
- package/lib/subject.js +405 -23
- package/lib/template.js +148 -8
- package/lib/tenant-quota.js +545 -0
- package/lib/testing.js +440 -53
- package/lib/time.js +291 -23
- package/lib/tls-exporter.js +239 -0
- package/lib/tracing.js +90 -74
- package/lib/uuid.js +97 -22
- package/lib/vault/index.js +284 -22
- package/lib/vault/seal-pem-file.js +66 -0
- package/lib/watcher.js +368 -0
- package/lib/webhook.js +196 -63
- package/lib/websocket.js +393 -68
- package/lib/wiki-concepts.js +338 -0
- package/lib/worker-pool.js +464 -0
- package/package.json +3 -3
- package/sbom.cyclonedx.json +7 -7
package/lib/time.js
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* time
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* @module b.time
|
|
4
|
+
* @featured true
|
|
5
|
+
* @nav Tools
|
|
6
|
+
* @title Time
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* @intro
|
|
9
|
+
* Timezone-aware datetime helpers built on top of native
|
|
10
|
+
* `Intl.DateTimeFormat`. No TZ-database vendor; operators get the
|
|
11
|
+
* IANA names Node's ICU build supports (full set on every mainstream
|
|
12
|
+
* platform).
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
+
* The module covers four concerns: parsing ISO 8601 strings into
|
|
15
|
+
* `Date`, decomposing an instant into calendar parts in a named
|
|
16
|
+
* timezone, formatting an instant for human display, and DST-safe
|
|
17
|
+
* calendar arithmetic (addDays / addMonths / startOfDay / endOfDay /
|
|
18
|
+
* diffDays).
|
|
14
19
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* b.time.addMonths(d, n, { timezone }) → calendar-month add
|
|
19
|
-
* b.time.diffDays(a, b, { timezone }) → calendar days between
|
|
20
|
+
* Every operation accepts a `Date`, a millisecond-epoch number, or
|
|
21
|
+
* an ISO 8601 string interchangeably. The `timezone` opt defaults
|
|
22
|
+
* to `"UTC"` and the `locale` opt defaults to `"en-US"`.
|
|
20
23
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
24
|
+
* Calendar arithmetic anchors on parts in the requested timezone,
|
|
25
|
+
* not on UTC milliseconds — so `addDays(d, 1, { timezone:
|
|
26
|
+
* "America/New_York" })` always lands on the same wall-clock time
|
|
27
|
+
* the next civil day, even across the spring-forward / fall-back
|
|
28
|
+
* transitions.
|
|
23
29
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
30
|
+
* @card
|
|
31
|
+
* Timezone-aware datetime helpers built on top of native `Intl.DateTimeFormat`.
|
|
26
32
|
*/
|
|
27
33
|
var C = require("./constants");
|
|
28
34
|
var { defineClass } = require("./framework-error");
|
|
@@ -68,6 +74,35 @@ var WEEKDAY_TO_NUM = {
|
|
|
68
74
|
"Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6, "Sun": 7,
|
|
69
75
|
};
|
|
70
76
|
|
|
77
|
+
/**
|
|
78
|
+
* @primitive b.time.toParts
|
|
79
|
+
* @signature b.time.toParts(input, opts)
|
|
80
|
+
* @since 0.1.0
|
|
81
|
+
* @related b.time.format, b.time.parseISO
|
|
82
|
+
*
|
|
83
|
+
* Decompose an instant into calendar parts as observed in a named
|
|
84
|
+
* timezone. Returns `{ year, month, day, hour, minute, second,
|
|
85
|
+
* millisecond, weekday: 1..7, weekdayName: "Mon".."Sun", dayOfYear }`.
|
|
86
|
+
* Weekday numbering follows ISO 8601 (Monday = 1, Sunday = 7).
|
|
87
|
+
*
|
|
88
|
+
* Accepts a `Date`, ms-epoch number, or ISO 8601 string. `timezone`
|
|
89
|
+
* defaults to `"UTC"`.
|
|
90
|
+
*
|
|
91
|
+
* @opts
|
|
92
|
+
* timezone: string, // IANA name; defaults to "UTC"
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* var parts = b.time.toParts("2026-05-09T14:30:00Z", {
|
|
96
|
+
* timezone: "America/New_York",
|
|
97
|
+
* });
|
|
98
|
+
* parts.year; // → 2026
|
|
99
|
+
* parts.month; // → 5
|
|
100
|
+
* parts.day; // → 9
|
|
101
|
+
* parts.hour; // → 10
|
|
102
|
+
* parts.weekdayName; // → "Sat"
|
|
103
|
+
* parts.weekday; // → 6
|
|
104
|
+
* parts.dayOfYear; // → 129
|
|
105
|
+
*/
|
|
71
106
|
function toParts(input, opts) {
|
|
72
107
|
opts = opts || {};
|
|
73
108
|
var date = _toDate(input);
|
|
@@ -111,6 +146,51 @@ function toParts(input, opts) {
|
|
|
111
146
|
return out;
|
|
112
147
|
}
|
|
113
148
|
|
|
149
|
+
/**
|
|
150
|
+
* @primitive b.time.format
|
|
151
|
+
* @signature b.time.format(input, opts)
|
|
152
|
+
* @since 0.1.0
|
|
153
|
+
* @related b.time.toParts, b.time.parseISO
|
|
154
|
+
*
|
|
155
|
+
* Render an instant as an operator-readable string in a named
|
|
156
|
+
* timezone and locale. Accepts the same `Date | number | string`
|
|
157
|
+
* input as the rest of the module. When neither `dateStyle` /
|
|
158
|
+
* `timeStyle` nor any per-field opt is supplied, defaults to
|
|
159
|
+
* `dateStyle: "medium"` + `timeStyle: "short"`.
|
|
160
|
+
*
|
|
161
|
+
* Per-field opts (`year` / `month` / `day` / `hour` / `minute` /
|
|
162
|
+
* `second` / `weekday` / `era` / `hour12` / `fractionalSecondDigits` /
|
|
163
|
+
* `timeZoneName`) pass through to `Intl.DateTimeFormat` unchanged.
|
|
164
|
+
*
|
|
165
|
+
* @opts
|
|
166
|
+
* timezone: string, // IANA name; defaults to "UTC"
|
|
167
|
+
* locale: string, // BCP 47; defaults to "en-US"
|
|
168
|
+
* dateStyle: string, // "full" | "long" | "medium" | "short"
|
|
169
|
+
* timeStyle: string, // "full" | "long" | "medium" | "short"
|
|
170
|
+
* year: string, // "numeric" | "2-digit"
|
|
171
|
+
* month: string, // "numeric" | "2-digit" | "long" | "short" | "narrow"
|
|
172
|
+
* day: string,
|
|
173
|
+
* hour: string,
|
|
174
|
+
* minute: string,
|
|
175
|
+
* second: string,
|
|
176
|
+
* weekday: string,
|
|
177
|
+
* era: string,
|
|
178
|
+
* hour12: boolean,
|
|
179
|
+
* fractionalSecondDigits: number,
|
|
180
|
+
* timeZoneName: string, // "long" | "short" | "shortOffset" | etc.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* var when = "2026-05-09T14:30:00Z";
|
|
184
|
+
* b.time.format(when, { timezone: "America/New_York" });
|
|
185
|
+
* // → "May 9, 2026, 10:30 AM"
|
|
186
|
+
*
|
|
187
|
+
* b.time.format(when, {
|
|
188
|
+
* timezone: "Asia/Tokyo",
|
|
189
|
+
* dateStyle: "full",
|
|
190
|
+
* timeStyle: "long",
|
|
191
|
+
* });
|
|
192
|
+
* // → operator-readable Japanese-locale-style string
|
|
193
|
+
*/
|
|
114
194
|
function format(input, opts) {
|
|
115
195
|
opts = opts || {};
|
|
116
196
|
var date = _toDate(input);
|
|
@@ -136,6 +216,27 @@ function format(input, opts) {
|
|
|
136
216
|
return _dtf(fmtOpts).format(date);
|
|
137
217
|
}
|
|
138
218
|
|
|
219
|
+
/**
|
|
220
|
+
* @primitive b.time.tzOffsetMs
|
|
221
|
+
* @signature b.time.tzOffsetMs(input, timezone)
|
|
222
|
+
* @since 0.1.0
|
|
223
|
+
* @related b.time.toParts, b.time.startOfDay
|
|
224
|
+
*
|
|
225
|
+
* Compute the offset in milliseconds between the named timezone's
|
|
226
|
+
* local wall-clock and UTC at the given instant. Positive east of
|
|
227
|
+
* UTC, negative west. The value depends on the instant — DST
|
|
228
|
+
* transitions are honoured automatically.
|
|
229
|
+
*
|
|
230
|
+
* Throws `TimeError` when `timezone` is missing, non-string, or not
|
|
231
|
+
* an IANA name supported by Node's ICU build.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* var offset = b.time.tzOffsetMs("2026-05-09T12:00:00Z", "America/New_York");
|
|
235
|
+
* // → -14400000 (UTC-4 during DST; 4h * 60m * 60s * 1000ms)
|
|
236
|
+
*
|
|
237
|
+
* var winter = b.time.tzOffsetMs("2026-01-15T12:00:00Z", "America/New_York");
|
|
238
|
+
* // → -18000000 (UTC-5 in standard time)
|
|
239
|
+
*/
|
|
139
240
|
function tzOffsetMs(input, timezone) {
|
|
140
241
|
var date = _toDate(input);
|
|
141
242
|
if (!timezone || typeof timezone !== "string") {
|
|
@@ -179,6 +280,27 @@ function _fromPartsAtTz(p, timezone) {
|
|
|
179
280
|
return new Date(step1 - (offset2 - offset1));
|
|
180
281
|
}
|
|
181
282
|
|
|
283
|
+
/**
|
|
284
|
+
* @primitive b.time.startOfDay
|
|
285
|
+
* @signature b.time.startOfDay(input, opts)
|
|
286
|
+
* @since 0.1.0
|
|
287
|
+
* @related b.time.endOfDay, b.time.diffDays
|
|
288
|
+
*
|
|
289
|
+
* Return a `Date` pointing at midnight (00:00:00.000) of the input's
|
|
290
|
+
* civil day in the named timezone. DST-safe — the spring-forward day
|
|
291
|
+
* still resolves to the first valid wall-clock instant. Useful for
|
|
292
|
+
* day-bucketed audit queries and "is this still today?" comparisons.
|
|
293
|
+
*
|
|
294
|
+
* @opts
|
|
295
|
+
* timezone: string, // IANA name; defaults to "UTC"
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* var dayStart = b.time.startOfDay("2026-05-09T14:30:00Z", {
|
|
299
|
+
* timezone: "America/New_York",
|
|
300
|
+
* });
|
|
301
|
+
* dayStart.toISOString();
|
|
302
|
+
* // → "2026-05-09T04:00:00.000Z" (midnight NY = 04:00 UTC during DST)
|
|
303
|
+
*/
|
|
182
304
|
function startOfDay(input, opts) {
|
|
183
305
|
opts = opts || {};
|
|
184
306
|
var tz = opts.timezone || DEFAULT_TIMEZONE;
|
|
@@ -189,6 +311,27 @@ function startOfDay(input, opts) {
|
|
|
189
311
|
}, tz);
|
|
190
312
|
}
|
|
191
313
|
|
|
314
|
+
/**
|
|
315
|
+
* @primitive b.time.endOfDay
|
|
316
|
+
* @signature b.time.endOfDay(input, opts)
|
|
317
|
+
* @since 0.1.0
|
|
318
|
+
* @related b.time.startOfDay, b.time.diffDays
|
|
319
|
+
*
|
|
320
|
+
* Return a `Date` pointing at the last representable millisecond
|
|
321
|
+
* (23:59:59.999) of the input's civil day in the named timezone.
|
|
322
|
+
* DST-safe. Pair with `startOfDay` to bracket "all events on day X
|
|
323
|
+
* in timezone Y" range queries.
|
|
324
|
+
*
|
|
325
|
+
* @opts
|
|
326
|
+
* timezone: string, // IANA name; defaults to "UTC"
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* var dayEnd = b.time.endOfDay("2026-05-09T14:30:00Z", {
|
|
330
|
+
* timezone: "America/New_York",
|
|
331
|
+
* });
|
|
332
|
+
* dayEnd.toISOString();
|
|
333
|
+
* // → "2026-05-10T03:59:59.999Z" (23:59:59.999 NY = 03:59 next-day UTC)
|
|
334
|
+
*/
|
|
192
335
|
function endOfDay(input, opts) {
|
|
193
336
|
opts = opts || {};
|
|
194
337
|
var tz = opts.timezone || DEFAULT_TIMEZONE;
|
|
@@ -199,6 +342,34 @@ function endOfDay(input, opts) {
|
|
|
199
342
|
}, tz);
|
|
200
343
|
}
|
|
201
344
|
|
|
345
|
+
/**
|
|
346
|
+
* @primitive b.time.addDays
|
|
347
|
+
* @signature b.time.addDays(input, n, opts)
|
|
348
|
+
* @since 0.1.0
|
|
349
|
+
* @related b.time.addMonths, b.time.diffDays
|
|
350
|
+
*
|
|
351
|
+
* Add `n` calendar days to the input, anchored on the named
|
|
352
|
+
* timezone's wall clock. Negative `n` subtracts. Calendar-day
|
|
353
|
+
* arithmetic — the wall-clock hour / minute / second / millisecond
|
|
354
|
+
* stay the same across DST transitions, even though the resulting
|
|
355
|
+
* UTC offset between the two instants will differ by an hour around
|
|
356
|
+
* the transition.
|
|
357
|
+
*
|
|
358
|
+
* Throws `TimeError` when `n` is not a finite number.
|
|
359
|
+
*
|
|
360
|
+
* @opts
|
|
361
|
+
* timezone: string, // IANA name; defaults to "UTC"
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* var due = b.time.addDays("2026-05-09T14:30:00Z", 7, {
|
|
365
|
+
* timezone: "America/New_York",
|
|
366
|
+
* });
|
|
367
|
+
* due.toISOString();
|
|
368
|
+
* // → "2026-05-16T14:30:00.000Z"
|
|
369
|
+
*
|
|
370
|
+
* // Subtract: "yesterday at this time"
|
|
371
|
+
* var yesterday = b.time.addDays(Date.now(), -1, { timezone: "UTC" });
|
|
372
|
+
*/
|
|
202
373
|
function addDays(input, n, opts) {
|
|
203
374
|
opts = opts || {};
|
|
204
375
|
if (typeof n !== "number" || !isFinite(n)) {
|
|
@@ -216,6 +387,32 @@ function addDays(input, n, opts) {
|
|
|
216
387
|
}, tz);
|
|
217
388
|
}
|
|
218
389
|
|
|
390
|
+
/**
|
|
391
|
+
* @primitive b.time.addMonths
|
|
392
|
+
* @signature b.time.addMonths(input, n, opts)
|
|
393
|
+
* @since 0.1.0
|
|
394
|
+
* @related b.time.addDays, b.time.diffDays
|
|
395
|
+
*
|
|
396
|
+
* Add `n` calendar months to the input, anchored on the named
|
|
397
|
+
* timezone's wall clock. Negative `n` subtracts. End-of-month days
|
|
398
|
+
* clamp to the target month's last day — Jan 31 + 1 month is
|
|
399
|
+
* Feb 28/29, not "March 3". Wall-clock hour / minute / second /
|
|
400
|
+
* millisecond are preserved.
|
|
401
|
+
*
|
|
402
|
+
* Throws `TimeError` when `n` is not a finite number.
|
|
403
|
+
*
|
|
404
|
+
* @opts
|
|
405
|
+
* timezone: string, // IANA name; defaults to "UTC"
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* var renewal = b.time.addMonths("2026-01-31T09:00:00Z", 1, {
|
|
409
|
+
* timezone: "UTC",
|
|
410
|
+
* });
|
|
411
|
+
* renewal.toISOString();
|
|
412
|
+
* // → "2026-02-28T09:00:00.000Z" (clamped: Feb has no day 31)
|
|
413
|
+
*
|
|
414
|
+
* var nextQuarter = b.time.addMonths(Date.now(), 3, { timezone: "UTC" });
|
|
415
|
+
*/
|
|
219
416
|
function addMonths(input, n, opts) {
|
|
220
417
|
opts = opts || {};
|
|
221
418
|
if (typeof n !== "number" || !isFinite(n)) {
|
|
@@ -234,6 +431,32 @@ function addMonths(input, n, opts) {
|
|
|
234
431
|
}, tz);
|
|
235
432
|
}
|
|
236
433
|
|
|
434
|
+
/**
|
|
435
|
+
* @primitive b.time.diffDays
|
|
436
|
+
* @signature b.time.diffDays(a, b, opts)
|
|
437
|
+
* @since 0.1.0
|
|
438
|
+
* @related b.time.addDays, b.time.startOfDay
|
|
439
|
+
*
|
|
440
|
+
* Calendar days between two instants in the named timezone, computed
|
|
441
|
+
* as `startOfDay(b) - startOfDay(a)` rounded to whole days. Positive
|
|
442
|
+
* when `b` is after `a`; negative otherwise. Foundation for
|
|
443
|
+
* "X days ago" / "Y days until" relative formatting.
|
|
444
|
+
*
|
|
445
|
+
* @opts
|
|
446
|
+
* timezone: string, // IANA name; defaults to "UTC"
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* var posted = "2026-05-02T08:00:00Z";
|
|
450
|
+
* var now = "2026-05-09T14:30:00Z";
|
|
451
|
+
* var ago = b.time.diffDays(posted, now, { timezone: "UTC" });
|
|
452
|
+
* // → 7
|
|
453
|
+
*
|
|
454
|
+
* // "X days ago" relative formatting:
|
|
455
|
+
* var label = ago === 0 ? "today"
|
|
456
|
+
* : ago === 1 ? "yesterday"
|
|
457
|
+
* : ago + " days ago";
|
|
458
|
+
* // → "7 days ago"
|
|
459
|
+
*/
|
|
237
460
|
function diffDays(a, b, opts) {
|
|
238
461
|
opts = opts || {};
|
|
239
462
|
var tz = opts.timezone || DEFAULT_TIMEZONE;
|
|
@@ -244,6 +467,35 @@ function diffDays(a, b, opts) {
|
|
|
244
467
|
|
|
245
468
|
var ISO_RE = /^(\d{4})-(\d{2})-(\d{2})(?:[T\s](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
246
469
|
|
|
470
|
+
/**
|
|
471
|
+
* @primitive b.time.parseISO
|
|
472
|
+
* @signature b.time.parseISO(s)
|
|
473
|
+
* @since 0.1.0
|
|
474
|
+
* @related b.time.toIso8601NoMs, b.time.toParts
|
|
475
|
+
*
|
|
476
|
+
* Parse an ISO 8601 / RFC 3339 datetime string into a `Date`.
|
|
477
|
+
* Accepts `YYYY-MM-DD`, `YYYY-MM-DDTHH:MM`, `YYYY-MM-DDTHH:MM:SS`,
|
|
478
|
+
* optional `.sss` fractional seconds, and an optional trailing
|
|
479
|
+
* `Z` / `+HH:MM` / `-HH:MM` zone designator. A space separator
|
|
480
|
+
* between date and time is also accepted. Strings without a zone
|
|
481
|
+
* designator are interpreted as UTC.
|
|
482
|
+
*
|
|
483
|
+
* Throws `TimeError` for non-strings, malformed input, or
|
|
484
|
+
* out-of-range component values (month > 12, day > 31, hour > 23,
|
|
485
|
+
* etc.).
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* var d = b.time.parseISO("2026-05-09T14:30:00Z");
|
|
489
|
+
* d.toISOString(); // → "2026-05-09T14:30:00.000Z"
|
|
490
|
+
*
|
|
491
|
+
* // Offset zone:
|
|
492
|
+
* var withOffset = b.time.parseISO("2026-05-09T10:30:00-04:00");
|
|
493
|
+
* withOffset.toISOString(); // → "2026-05-09T14:30:00.000Z"
|
|
494
|
+
*
|
|
495
|
+
* // Date-only (interpreted as UTC midnight):
|
|
496
|
+
* var date = b.time.parseISO("2026-05-09");
|
|
497
|
+
* date.toISOString(); // → "2026-05-09T00:00:00.000Z"
|
|
498
|
+
*/
|
|
247
499
|
function parseISO(s) {
|
|
248
500
|
if (typeof s !== "string" || s.length === 0) {
|
|
249
501
|
throw new TimeError("time/bad-iso", "parseISO: input must be a non-empty string");
|
|
@@ -283,12 +535,28 @@ function parseISO(s) {
|
|
|
283
535
|
return new Date(utcMs);
|
|
284
536
|
}
|
|
285
537
|
|
|
286
|
-
// toIso8601NoMs — emit ISO 8601 with the trailing `.\d{3}Z`
|
|
287
|
-
// milliseconds dropped (`2026-05-03T12:34:56Z` instead of
|
|
288
|
-
// `…:56.789Z`). Used by SAS / SigV4 / log filename builders that need
|
|
289
|
-
// a one-second-resolution timestamp string. Single source of truth so
|
|
290
|
-
// the strip pattern lives in one place.
|
|
291
538
|
var ISO_MS_RE = /\.\d{3}Z$/;
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* @primitive b.time.toIso8601NoMs
|
|
542
|
+
* @signature b.time.toIso8601NoMs(input)
|
|
543
|
+
* @since 0.1.0
|
|
544
|
+
* @related b.time.parseISO
|
|
545
|
+
*
|
|
546
|
+
* Emit an ISO 8601 string with the trailing `.sssZ` milliseconds
|
|
547
|
+
* dropped — produces `2026-05-09T14:30:00Z` instead of
|
|
548
|
+
* `2026-05-09T14:30:00.000Z`. Used by SAS / SigV4 / log-filename
|
|
549
|
+
* builders that need a one-second-resolution timestamp string. The
|
|
550
|
+
* strip pattern lives in one place so every caller agrees on the
|
|
551
|
+
* shape.
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
* b.time.toIso8601NoMs("2026-05-09T14:30:00.789Z");
|
|
555
|
+
* // → "2026-05-09T14:30:00Z"
|
|
556
|
+
*
|
|
557
|
+
* b.time.toIso8601NoMs(new Date(Date.UTC(2026, 4, 9, 14, 30, 0)));
|
|
558
|
+
* // → "2026-05-09T14:30:00Z"
|
|
559
|
+
*/
|
|
292
560
|
function toIso8601NoMs(input) {
|
|
293
561
|
var d = _toDate(input);
|
|
294
562
|
return d.toISOString().replace(ISO_MS_RE, "Z");
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module b.tlsExporter
|
|
4
|
+
* @nav Crypto
|
|
5
|
+
* @title TLS Exporter
|
|
6
|
+
*
|
|
7
|
+
* @intro
|
|
8
|
+
* RFC 5705 / RFC 9266 TLS Exporter for binding application-layer
|
|
9
|
+
* keys and tokens to the live TLS session. The exporter is a
|
|
10
|
+
* deterministic byte-string derived from the TLS 1.3 master secret
|
|
11
|
+
* (RFC 8446 §7.5) — pulling 32 bytes under the
|
|
12
|
+
* `EXPORTER-Channel-Binding` label gives the RFC 9266
|
|
13
|
+
* "tls-exporter" channel-binding identifier.
|
|
14
|
+
*
|
|
15
|
+
* Operators bind bearer tokens, FAPI 2.0 access-token-bound proofs,
|
|
16
|
+
* DPoP `cnf.tbh` claims, mTLS-derived auth headers, and session
|
|
17
|
+
* cookies to the exporter so a captured token cannot be replayed
|
|
18
|
+
* across a different TLS session — even if a downstream proxy
|
|
19
|
+
* re-terminates TLS (RFC 8705). The matching node primitive is
|
|
20
|
+
* `tls.TLSSocket#exportKeyingMaterial(length, label[, context])`.
|
|
21
|
+
*
|
|
22
|
+
* Validation throws at the call site for sockets that aren't TLS
|
|
23
|
+
* (channel binding has no meaning over plaintext), sockets whose
|
|
24
|
+
* protocol is not TLS 1.3 (RFC 9266 §4 conformance), or
|
|
25
|
+
* out-of-range `length` values. Mismatched bindings on
|
|
26
|
+
* `verifyTokenBinding` return `false` rather than throwing —
|
|
27
|
+
* token-binding mismatch is a normal request-time outcome, not a
|
|
28
|
+
* config bug.
|
|
29
|
+
*
|
|
30
|
+
* @card
|
|
31
|
+
* RFC 5705 / RFC 9266 TLS Exporter for binding application-layer keys and tokens to the live TLS session.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
var crypto = require("./crypto");
|
|
35
|
+
var C = require("./constants");
|
|
36
|
+
var lazyRequire = require("./lazy-require");
|
|
37
|
+
var nb = require("./numeric-bounds");
|
|
38
|
+
var { TlsExporterError } = require("./framework-error");
|
|
39
|
+
|
|
40
|
+
var _err = TlsExporterError.factory;
|
|
41
|
+
|
|
42
|
+
var observability = lazyRequire(function () { return require("./observability"); });
|
|
43
|
+
|
|
44
|
+
var EXPORTER_LABEL = "EXPORTER-Channel-Binding";
|
|
45
|
+
var EXPORTER_LENGTH = C.BYTES.bytes(32);
|
|
46
|
+
|
|
47
|
+
// _resolveTlsSocket — accept either a TLSSocket directly OR an http2/
|
|
48
|
+
// http(s) request whose .socket property is the TLSSocket. Operators
|
|
49
|
+
// almost always pass req.socket; the helper normalizes to the
|
|
50
|
+
// underlying socket so the exportKeyingMaterial call lands on the
|
|
51
|
+
// right object.
|
|
52
|
+
function _resolveTlsSocket(socketOrReq) {
|
|
53
|
+
if (!socketOrReq) {
|
|
54
|
+
throw _err("BAD_INPUT", "tlsExporter: socket or request object required");
|
|
55
|
+
}
|
|
56
|
+
// Express/Node http req → req.socket is the TLSSocket
|
|
57
|
+
var sock = socketOrReq;
|
|
58
|
+
if (typeof socketOrReq.exportKeyingMaterial !== "function" &&
|
|
59
|
+
socketOrReq.socket &&
|
|
60
|
+
typeof socketOrReq.socket.exportKeyingMaterial === "function") {
|
|
61
|
+
sock = socketOrReq.socket;
|
|
62
|
+
}
|
|
63
|
+
if (typeof sock.exportKeyingMaterial !== "function") {
|
|
64
|
+
throw _err("NOT_TLS",
|
|
65
|
+
"tlsExporter: socket has no exportKeyingMaterial — channel binding requires TLS");
|
|
66
|
+
}
|
|
67
|
+
// Per RFC 9266 §4 the exporter is only defined for TLS 1.3. Older
|
|
68
|
+
// protocol versions on the same API would technically return bytes
|
|
69
|
+
// but the channel-binding semantics are NOT RFC 9266 conformant —
|
|
70
|
+
// refuse so an operator-built check doesn't silently fall back to a
|
|
71
|
+
// weaker binding.
|
|
72
|
+
if (typeof sock.getProtocol === "function") {
|
|
73
|
+
var proto = sock.getProtocol();
|
|
74
|
+
if (proto && proto !== "TLSv1.3") {
|
|
75
|
+
throw _err("NOT_TLS_1_3",
|
|
76
|
+
"tlsExporter: TLS protocol is " + proto + ", RFC 9266 requires TLS 1.3");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return sock;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @primitive b.tlsExporter.fromSocket
|
|
84
|
+
* @signature b.tlsExporter.fromSocket(socketOrReq, opts)
|
|
85
|
+
* @since 0.7.45
|
|
86
|
+
* @status stable
|
|
87
|
+
* @related b.tlsExporter.bindToken, b.tlsExporter.verifyTokenBinding
|
|
88
|
+
*
|
|
89
|
+
* Extracts a TLS exporter from `socketOrReq` (either a `TLSSocket`
|
|
90
|
+
* directly or an HTTP/HTTP/2 request whose `.socket` is the
|
|
91
|
+
* `TLSSocket`). Defaults match RFC 9266 §4 — 32-byte length, label
|
|
92
|
+
* `EXPORTER-Channel-Binding`, no context — yielding the canonical
|
|
93
|
+
* "tls-exporter" channel-binding identifier. Custom labels and
|
|
94
|
+
* lengths pass through for applications defining their own exporter-
|
|
95
|
+
* derived identifiers; `length` is bounded 16..255 bytes per the
|
|
96
|
+
* keying-material range Node enforces. Throws when the socket is not
|
|
97
|
+
* TLS 1.3 or when the export call fails.
|
|
98
|
+
*
|
|
99
|
+
* @opts
|
|
100
|
+
* {
|
|
101
|
+
* label?: string, // default "EXPORTER-Channel-Binding"
|
|
102
|
+
* length?: number, // default 32; bounded 16..255 bytes
|
|
103
|
+
* context?: Buffer // default null (RFC 8446 §7.5 "no context")
|
|
104
|
+
* }
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* var b = require("blamejs").create();
|
|
108
|
+
* var server = b.https.createServer({ key: KEY, cert: CERT }, function (req, res) {
|
|
109
|
+
* var exporter = b.tlsExporter.fromSocket(req, { length: 32 });
|
|
110
|
+
* res.end("exporter bytes: " + exporter.length);
|
|
111
|
+
* // → "exporter bytes: 32"
|
|
112
|
+
* });
|
|
113
|
+
* server.listen(0);
|
|
114
|
+
*/
|
|
115
|
+
function fromSocket(socketOrReq, opts) {
|
|
116
|
+
opts = opts || {};
|
|
117
|
+
var label = typeof opts.label === "string" && opts.label.length > 0
|
|
118
|
+
? opts.label : EXPORTER_LABEL;
|
|
119
|
+
// length is operator-tunable; validate-when-present via numeric-bounds
|
|
120
|
+
// so a non-finite / negative / NaN input surfaces with the same error
|
|
121
|
+
// shape every other framework primitive uses for numeric opts.
|
|
122
|
+
nb.requirePositiveFiniteIntIfPresent(opts.length,
|
|
123
|
+
"tlsExporter.fromSocket: length", TlsExporterError, "BAD_LENGTH");
|
|
124
|
+
var length = opts.length !== undefined ? opts.length : EXPORTER_LENGTH;
|
|
125
|
+
if (length < C.BYTES.bytes(16) || length > C.BYTES.bytes(255)) {
|
|
126
|
+
throw _err("BAD_LENGTH",
|
|
127
|
+
"tlsExporter.fromSocket: length must be 16..255 bytes (got " + length + ")");
|
|
128
|
+
}
|
|
129
|
+
var context = opts.context;
|
|
130
|
+
if (context !== undefined && context !== null && !Buffer.isBuffer(context)) {
|
|
131
|
+
throw _err("BAD_CONTEXT",
|
|
132
|
+
"tlsExporter.fromSocket: context must be Buffer or null");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
var sock = _resolveTlsSocket(socketOrReq);
|
|
136
|
+
var bytes;
|
|
137
|
+
try {
|
|
138
|
+
// Node's exportKeyingMaterial signature: (length, label, [context]).
|
|
139
|
+
// Passing context=null (the default) corresponds to the RFC 8446
|
|
140
|
+
// §7.5 "no context" case which RFC 9266 §4 mandates for channel
|
|
141
|
+
// binding.
|
|
142
|
+
bytes = context
|
|
143
|
+
? sock.exportKeyingMaterial(length, label, context)
|
|
144
|
+
: sock.exportKeyingMaterial(length, label);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
throw _err("EXPORT_FAILED",
|
|
147
|
+
"tlsExporter.fromSocket: exportKeyingMaterial threw: " + e.message);
|
|
148
|
+
}
|
|
149
|
+
if (!Buffer.isBuffer(bytes) || bytes.length !== length) {
|
|
150
|
+
throw _err("EXPORT_SHORT",
|
|
151
|
+
"tlsExporter.fromSocket: short exporter (got " + (bytes && bytes.length) + " bytes, want " + length + ")");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try { observability().safeEvent("tlsExporter.fromSocket", 1, { outcome: "success" }); }
|
|
155
|
+
catch (_e) { /* drop-silent */ }
|
|
156
|
+
return bytes;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @primitive b.tlsExporter.bindToken
|
|
161
|
+
* @signature b.tlsExporter.bindToken(socketOrReq, token)
|
|
162
|
+
* @since 0.7.45
|
|
163
|
+
* @status stable
|
|
164
|
+
* @related b.tlsExporter.fromSocket, b.tlsExporter.verifyTokenBinding
|
|
165
|
+
*
|
|
166
|
+
* Binds an opaque token (string or Buffer) to the current TLS session
|
|
167
|
+
* by hashing `SHA3-512(label || exporter || token)`, where `label` is
|
|
168
|
+
* `"blamejs/tls-exporter/bind/v1"`. The framework label keeps the
|
|
169
|
+
* resulting digest distinct from any other place the same exporter +
|
|
170
|
+
* token bytes might be hashed (audit-chain rows, derived-hash columns,
|
|
171
|
+
* etc.) so a binding cannot be reinterpreted across primitives.
|
|
172
|
+
* Operators store the returned hex digest alongside the token and
|
|
173
|
+
* compare via `verifyTokenBinding` on the next request.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* var b = require("blamejs").create();
|
|
177
|
+
* b.https.createServer({ key: KEY, cert: CERT }, function (req, res) {
|
|
178
|
+
* var binding = b.tlsExporter.bindToken(req, "session-token-abc123");
|
|
179
|
+
* binding.length;
|
|
180
|
+
* // → 128 (SHA3-512 hex digest, 64 bytes × 2 hex chars)
|
|
181
|
+
* res.end("ok");
|
|
182
|
+
* }).listen(0);
|
|
183
|
+
*/
|
|
184
|
+
function bindToken(socketOrReq, token) {
|
|
185
|
+
if (typeof token !== "string" && !Buffer.isBuffer(token)) {
|
|
186
|
+
throw _err("BAD_TOKEN",
|
|
187
|
+
"tlsExporter.bindToken: token must be a string or Buffer");
|
|
188
|
+
}
|
|
189
|
+
var exporter = fromSocket(socketOrReq);
|
|
190
|
+
var tokenBuf = Buffer.isBuffer(token) ? token : Buffer.from(token, "utf8");
|
|
191
|
+
// SHA3-512 of (label || exporter || token). The label binds the
|
|
192
|
+
// hash to "tls-exporter binding" so the same token + exporter pair
|
|
193
|
+
// does NOT produce the same hash if used in another framework
|
|
194
|
+
// primitive (e.g., the audit-chain row hash).
|
|
195
|
+
var labelBuf = Buffer.from("blamejs/tls-exporter/bind/v1", "utf8");
|
|
196
|
+
return crypto.sha3Hash(Buffer.concat([labelBuf, exporter, tokenBuf]));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @primitive b.tlsExporter.verifyTokenBinding
|
|
201
|
+
* @signature b.tlsExporter.verifyTokenBinding(socketOrReq, token, claimedBinding)
|
|
202
|
+
* @since 0.7.45
|
|
203
|
+
* @status stable
|
|
204
|
+
* @related b.tlsExporter.fromSocket, b.tlsExporter.bindToken
|
|
205
|
+
*
|
|
206
|
+
* Constant-time compare of a previously-issued `bindToken` digest
|
|
207
|
+
* against a fresh binding computed from the current TLS session.
|
|
208
|
+
* Returns `true` when the digests match (token belongs to this TLS
|
|
209
|
+
* session) and `false` on any mismatch — token-binding mismatch is a
|
|
210
|
+
* normal request-time outcome, so this primitive never throws on
|
|
211
|
+
* mismatch. Throws only when `socketOrReq` is not TLS 1.3 or when
|
|
212
|
+
* the input shape is wrong.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* var b = require("blamejs").create();
|
|
216
|
+
* b.https.createServer({ key: KEY, cert: CERT }, function (req, res) {
|
|
217
|
+
* var stored = b.tlsExporter.bindToken(req, "session-token-abc123");
|
|
218
|
+
* var ok = b.tlsExporter.verifyTokenBinding(req, "session-token-abc123", stored);
|
|
219
|
+
* ok;
|
|
220
|
+
* // → true
|
|
221
|
+
* res.end(ok ? "bound" : "mismatch");
|
|
222
|
+
* }).listen(0);
|
|
223
|
+
*/
|
|
224
|
+
function verifyTokenBinding(socketOrReq, token, claimedBinding) {
|
|
225
|
+
var actual = bindToken(socketOrReq, token);
|
|
226
|
+
if (typeof claimedBinding !== "string" || claimedBinding.length === 0) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
return crypto.timingSafeEqual(actual, claimedBinding);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = {
|
|
233
|
+
fromSocket: fromSocket,
|
|
234
|
+
bindToken: bindToken,
|
|
235
|
+
verifyTokenBinding: verifyTokenBinding,
|
|
236
|
+
EXPORTER_LABEL: EXPORTER_LABEL,
|
|
237
|
+
EXPORTER_LENGTH: EXPORTER_LENGTH,
|
|
238
|
+
TlsExporterError: TlsExporterError,
|
|
239
|
+
};
|