@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/tracing.js
CHANGED
|
@@ -1,84 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @module b.tracing
|
|
4
|
+
* @nav Observability
|
|
5
|
+
* @title Tracing
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* @intro
|
|
8
|
+
* Distributed-tracing seam — W3C trace-context propagation,
|
|
9
|
+
* OpenTelemetry-shaped span lifecycle, sampling routed through OTel
|
|
10
|
+
* when installed.
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* The framework keeps zero npm runtime deps, so the OTel SDK isn't
|
|
13
|
+
* bundled. `b.tracing.create()` detects `@opentelemetry/api` at
|
|
14
|
+
* first use: when it's installed, every span call flows into the
|
|
15
|
+
* operator's tracer (Jaeger / Zipkin / OTLP / console — whatever
|
|
16
|
+
* exporter they wired) and OTel's sampler decides per-span
|
|
17
|
+
* `sampled` flag from the configured `TraceIdRatioBased` /
|
|
18
|
+
* `ParentBased` rules. When OTel is absent every call falls through
|
|
19
|
+
* a pass-through tracer that still executes the wrapped function,
|
|
20
|
+
* propagates return values and exceptions, and emits no span data.
|
|
17
21
|
*
|
|
18
|
-
*
|
|
22
|
+
* `contextHeaders()` and `extractContext()` always parse and emit
|
|
23
|
+
* the W3C `traceparent` format
|
|
24
|
+
* (`00-<32-hex traceId>-<16-hex spanId>-<2-hex flags>`) regardless
|
|
25
|
+
* of whether OTel is loaded — so operators get trace-ID per request
|
|
26
|
+
* as a free correlation baseline even without a tracer SDK. Span
|
|
27
|
+
* shape mirrors OTel: `setAttribute` / `addEvent` /
|
|
28
|
+
* `recordException` / `setStatus` / `end` / `updateName`.
|
|
19
29
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
30
|
+
* `b.tracing.tap("audit.record", attributes, fn)` mirrors
|
|
31
|
+
* `b.metrics.tap` for tracing — wraps `fn` in a span if a registry
|
|
32
|
+
* is active, passes through otherwise. `requestMiddleware()` opens
|
|
33
|
+
* one span per inbound request, extracts any incoming
|
|
34
|
+
* `traceparent`, and promotes `http.route` to the matched route
|
|
35
|
+
* template at response time.
|
|
24
36
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* span.setAttribute("user_id", "abc");
|
|
28
|
-
* span.addEvent("cache-miss");
|
|
29
|
-
* return await doWork();
|
|
30
|
-
* }, { kind: "internal", attributes: { route: "/users" } });
|
|
31
|
-
*
|
|
32
|
-
* // Sync variant.
|
|
33
|
-
* var x = t.spanSync("compute", function (span) { ... return v; });
|
|
34
|
-
*
|
|
35
|
-
* // Read / write the current active span.
|
|
36
|
-
* t.currentSpan(); // null when no active span
|
|
37
|
-
* t.setAttributes({ user_id: "abc" }); // sets on current
|
|
38
|
-
* t.recordException(err); // records on current
|
|
39
|
-
*
|
|
40
|
-
* // HTTP propagation. contextHeaders() returns headers to add to
|
|
41
|
-
* // outbound requests (W3C `traceparent`); extractContext(headers)
|
|
42
|
-
* // pulls a parent context from inbound headers.
|
|
43
|
-
* var headers = t.contextHeaders();
|
|
44
|
-
* var parentCtx = t.extractContext(req.headers);
|
|
45
|
-
*
|
|
46
|
-
* // Auto-span request middleware — wraps each handler in a span
|
|
47
|
-
* // named after method + route pattern.
|
|
48
|
-
* router.use(t.requestMiddleware());
|
|
49
|
-
*
|
|
50
|
-
* // Framework-internal hot-path tap — wraps fn in a span named
|
|
51
|
-
* // `name` if a registry is active; pass-through otherwise. Like
|
|
52
|
-
* // metrics.tap() but for tracing instead of counting.
|
|
53
|
-
* b.tracing.tap("audit.record", attributes, fn);
|
|
54
|
-
*
|
|
55
|
-
* Even WITHOUT @opentelemetry/api installed:
|
|
56
|
-
* - contextHeaders() / extractContext() still parse and emit the
|
|
57
|
-
* W3C traceparent format. So a framework process without OTel
|
|
58
|
-
* can still propagate trace IDs through logs and HTTP for
|
|
59
|
-
* correlation, even without span telemetry. Operators get the
|
|
60
|
-
* "trace ID per request" plumbing as a free baseline.
|
|
61
|
-
* - currentSpan() returns a minimal pass-through "span" object so
|
|
62
|
-
* operator code can call setAttribute / addEvent / recordException
|
|
63
|
-
* unconditionally — they're no-ops without OTel.
|
|
64
|
-
*
|
|
65
|
-
* Why no @otel runtime dep:
|
|
66
|
-
* - The framework keeps zero npm runtime deps. Apps that want tracing
|
|
67
|
-
* install OTel themselves; apps that don't pay nothing.
|
|
68
|
-
* - The OTel API is unstable enough that pinning a vendored version
|
|
69
|
-
* would create more churn than it saves.
|
|
70
|
-
*
|
|
71
|
-
* Out of scope (with structural reasons):
|
|
72
|
-
* - Vendoring the SDK: see above.
|
|
73
|
-
* - Sampling decisions: OTel handles this when wired; without OTel
|
|
74
|
-
* there's nothing to sample.
|
|
75
|
-
* - Exporter integration: belongs to the OTel SDK, not the framework.
|
|
76
|
-
* - Custom propagators: framework ships W3C traceparent only. OTel
|
|
77
|
-
* adds others (b3, jaeger) when wired by the operator.
|
|
78
|
-
* - Async-context propagation across setTimeout / setImmediate:
|
|
79
|
-
* OTel's NodeSDK auto-instrumentation handles this when installed;
|
|
80
|
-
* without OTel, framework code uses fn-passing rather than
|
|
81
|
-
* async-context, which is fine for the surfaces we instrument.
|
|
37
|
+
* @card
|
|
38
|
+
* Distributed-tracing seam — W3C trace-context propagation, OpenTelemetry-shaped span lifecycle, sampling routed through OTel when installed.
|
|
82
39
|
*/
|
|
83
40
|
|
|
84
41
|
var C = require("./constants");
|
|
@@ -205,6 +162,42 @@ function _passthroughTracer() {
|
|
|
205
162
|
|
|
206
163
|
// ---- Registry factory ----
|
|
207
164
|
|
|
165
|
+
/**
|
|
166
|
+
* @primitive b.tracing.create
|
|
167
|
+
* @signature b.tracing.create(opts)
|
|
168
|
+
* @since 0.4.0
|
|
169
|
+
* @status stable
|
|
170
|
+
* @related b.tracing.tap, b.metrics.create, b.observability.tap
|
|
171
|
+
*
|
|
172
|
+
* Build a tracing registry. The returned registry exposes `span`,
|
|
173
|
+
* `spanSync`, `currentSpan`, `setAttributes`, `recordException`,
|
|
174
|
+
* `contextHeaders` / `extractContext` for W3C traceparent
|
|
175
|
+
* propagation, `requestMiddleware()` for per-request auto-spans, and
|
|
176
|
+
* `tap()` for framework hot-path wrapping. Detects
|
|
177
|
+
* `@opentelemetry/api` once at first use; without OTel installed the
|
|
178
|
+
* registry runs in pass-through mode but still propagates trace IDs
|
|
179
|
+
* over HTTP.
|
|
180
|
+
*
|
|
181
|
+
* @opts
|
|
182
|
+
* instrumentationName: string, // OTel tracer name; default "blamejs"
|
|
183
|
+
* instrumentationVersion: string, // OTel tracer version; default "0.0.0"
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* var t = b.tracing.create({
|
|
187
|
+
* instrumentationName: "myapp",
|
|
188
|
+
* instrumentationVersion: "1.2.3",
|
|
189
|
+
* });
|
|
190
|
+
*
|
|
191
|
+
* var users = await t.span("load-users", async function (span) {
|
|
192
|
+
* span.setAttribute("user_id", "abc");
|
|
193
|
+
* span.addEvent("cache-miss");
|
|
194
|
+
* return await db.query("SELECT id, email FROM users");
|
|
195
|
+
* }, { kind: "internal", attributes: { route: "/users" } });
|
|
196
|
+
*
|
|
197
|
+
* // Outbound — propagate the active trace.
|
|
198
|
+
* var headers = t.contextHeaders();
|
|
199
|
+
* // → { traceparent: "00-<32 hex>-<16 hex>-01" } when a span is active
|
|
200
|
+
*/
|
|
208
201
|
function create(opts) {
|
|
209
202
|
opts = opts || {};
|
|
210
203
|
validateOpts(opts, [
|
|
@@ -461,6 +454,29 @@ function create(opts) {
|
|
|
461
454
|
|
|
462
455
|
var _globalRegistry = null;
|
|
463
456
|
|
|
457
|
+
/**
|
|
458
|
+
* @primitive b.tracing.tap
|
|
459
|
+
* @signature b.tracing.tap(name, attributes, fn)
|
|
460
|
+
* @since 0.4.0
|
|
461
|
+
* @related b.tracing.create, b.metrics.tap, b.observability.tap
|
|
462
|
+
*
|
|
463
|
+
* Framework hot-path tracing tap. Modules call
|
|
464
|
+
* `tap("audit.record", { action: "login" }, fn)` without importing a
|
|
465
|
+
* registry. Until `b.tracing.create()` runs the call passes `fn(null)`
|
|
466
|
+
* through directly (zero overhead, no span); afterwards the active
|
|
467
|
+
* registry wraps `fn` in a span named `name` with the supplied
|
|
468
|
+
* attributes. The two-arg form `tap(name, fn)` is permitted when no
|
|
469
|
+
* attributes are needed.
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* // Module-level — passthrough until a registry exists.
|
|
473
|
+
* var rows = b.tracing.tap("db.query", { table: "users" }, function () {
|
|
474
|
+
* return db.queryAll("SELECT id FROM users");
|
|
475
|
+
* });
|
|
476
|
+
*
|
|
477
|
+
* // Two-arg — no attributes:
|
|
478
|
+
* b.tracing.tap("queue.enqueue", function () { return enqueueJob(); });
|
|
479
|
+
*/
|
|
464
480
|
function tap(name, attributes, fn) {
|
|
465
481
|
if (typeof attributes === "function") {
|
|
466
482
|
fn = attributes; attributes = null;
|
package/lib/uuid.js
CHANGED
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @module b.uuid
|
|
4
|
+
* @featured true
|
|
5
|
+
* @nav Tools
|
|
6
|
+
* @title UUID
|
|
4
7
|
*
|
|
5
|
-
*
|
|
8
|
+
* @intro
|
|
9
|
+
* RFC 4122 v4 (random) + RFC 9562 v7 (time-ordered).
|
|
6
10
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
11
|
+
* v4 is fully random — the standard portable choice when ordering
|
|
12
|
+
* doesn't matter. v7 prefixes a 48-bit Unix-millisecond timestamp,
|
|
13
|
+
* then 74 random bits — IDs sort by creation time even
|
|
14
|
+
* lexicographically, ideal as a database PK because B-tree inserts
|
|
15
|
+
* stay near the right edge.
|
|
9
16
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* because B-tree inserts stay near the right edge
|
|
14
|
-
* — no random scattering across the index.
|
|
17
|
+
* All entropy comes from `b.crypto.generateBytes`, which routes
|
|
18
|
+
* through Node's `crypto.randomBytes` — same source as
|
|
19
|
+
* `crypto.randomUUID()`.
|
|
15
20
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
21
|
+
* Why ship v7 ourselves? Native `crypto.randomUUID()` only emits
|
|
22
|
+
* v4. v7 is the modern recommendation for any UUID landing in a
|
|
23
|
+
* sortable column (job queues, audit chain extensions, anything
|
|
24
|
+
* where insertion order matters for index locality).
|
|
19
25
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* All entropy comes from `b.crypto.generateBytes`, which routes through
|
|
24
|
-
* `node:crypto.randomBytes` — same source as `crypto.randomUUID()`.
|
|
25
|
-
*
|
|
26
|
-
* Why ship v7 ourselves? Native `crypto.randomUUID()` only emits v4.
|
|
27
|
-
* v7 is the modern recommendation for any UUID landing in a sortable
|
|
28
|
-
* column (jobs queue, audit chain extensions, anything where insertion
|
|
29
|
-
* order matters for index locality).
|
|
26
|
+
* @card
|
|
27
|
+
* RFC 4122 v4 (random) + RFC 9562 v7 (time-ordered).
|
|
30
28
|
*/
|
|
31
29
|
var C = require("./constants");
|
|
32
30
|
var { generateBytes } = require("./crypto");
|
|
@@ -67,6 +65,20 @@ function _bytesToString(bytes) {
|
|
|
67
65
|
hex.slice(HEX_CLOCK_SEQ_END, HEX_NODE_END);
|
|
68
66
|
}
|
|
69
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @primitive b.uuid.v4
|
|
70
|
+
* @signature b.uuid.v4()
|
|
71
|
+
* @since 0.1.0
|
|
72
|
+
* @related b.uuid.v7, b.uuid.parse
|
|
73
|
+
*
|
|
74
|
+
* Fully random 128-bit UUID. Standard, portable; the default choice
|
|
75
|
+
* when ordering doesn't matter. Returns the canonical 8-4-4-4-12
|
|
76
|
+
* hex form.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* var id = b.uuid.v4();
|
|
80
|
+
* // → "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
|
81
|
+
*/
|
|
70
82
|
function v4() {
|
|
71
83
|
var b = generateBytes(UUID_BYTE_LEN);
|
|
72
84
|
// version = 4 (0100): high nibble of byte 6
|
|
@@ -76,6 +88,34 @@ function v4() {
|
|
|
76
88
|
return _bytesToString(b);
|
|
77
89
|
}
|
|
78
90
|
|
|
91
|
+
/**
|
|
92
|
+
* @primitive b.uuid.v7
|
|
93
|
+
* @signature b.uuid.v7(opts?)
|
|
94
|
+
* @since 0.4.0
|
|
95
|
+
* @related b.uuid.v4, b.uuid.parse
|
|
96
|
+
*
|
|
97
|
+
* RFC 9562 §5.7 time-ordered UUID. The first 48 bits encode a Unix
|
|
98
|
+
* millisecond timestamp (big-endian); the next 4 bits are version (7);
|
|
99
|
+
* the remaining 74 bits are random. IDs generated within the same
|
|
100
|
+
* millisecond sort by their random suffix; across milliseconds they
|
|
101
|
+
* sort by time. B-tree index locality is dramatically better than v4
|
|
102
|
+
* for INSERT-heavy tables.
|
|
103
|
+
*
|
|
104
|
+
* @opts
|
|
105
|
+
* now: number, // override the timestamp (testing / fixtures)
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* var id = b.uuid.v7();
|
|
109
|
+
* // → "01941bf3-9c4a-7d8e-9c11-3a4b5c6d7e8f"
|
|
110
|
+
*
|
|
111
|
+
* // Deterministic fixture: same ms produces the same time prefix.
|
|
112
|
+
* var fixed = b.uuid.v7({ now: Date.UTC(2026, 0, 1) });
|
|
113
|
+
*
|
|
114
|
+
* // v7 sorts by time even as plain strings:
|
|
115
|
+
* var earlier = b.uuid.v7({ now: 1700000000000 });
|
|
116
|
+
* var later = b.uuid.v7({ now: 1700000001000 });
|
|
117
|
+
* earlier < later; // → true
|
|
118
|
+
*/
|
|
79
119
|
function v7(opts) {
|
|
80
120
|
// RFC 9562 §5.7 layout:
|
|
81
121
|
// bytes 0-5 : 48-bit big-endian Unix timestamp in milliseconds
|
|
@@ -101,6 +141,27 @@ function v7(opts) {
|
|
|
101
141
|
return _bytesToString(b);
|
|
102
142
|
}
|
|
103
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @primitive b.uuid.parse
|
|
146
|
+
* @signature b.uuid.parse(str)
|
|
147
|
+
* @since 0.1.0
|
|
148
|
+
* @related b.uuid.isValid
|
|
149
|
+
*
|
|
150
|
+
* Strict parse: validates canonical form AND version (1-7) AND
|
|
151
|
+
* RFC 4122 variant. Returns `{ ok: true, version, bytes }` on success;
|
|
152
|
+
* `{ ok: false, reason }` on failure. Never throws — operators who
|
|
153
|
+
* want a thrown error layer one on top.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* var parsed = b.uuid.parse("f47ac10b-58cc-4372-a567-0e02b2c3d479");
|
|
157
|
+
* if (parsed.ok) {
|
|
158
|
+
* console.log(parsed.version); // → 4
|
|
159
|
+
* console.log(parsed.bytes); // → <Buffer f4 7a c1 0b ...>
|
|
160
|
+
* }
|
|
161
|
+
*
|
|
162
|
+
* b.uuid.parse("not-a-uuid").ok; // → false
|
|
163
|
+
* b.uuid.parse("not-a-uuid").reason; // → "malformed"
|
|
164
|
+
*/
|
|
104
165
|
function parse(str) {
|
|
105
166
|
if (typeof str !== "string") return { ok: false, reason: "not-a-string" };
|
|
106
167
|
// Length cap before regex — RFC 4122 canonical form is exactly 36
|
|
@@ -118,6 +179,20 @@ function parse(str) {
|
|
|
118
179
|
return { ok: true, version: version, bytes: bytes };
|
|
119
180
|
}
|
|
120
181
|
|
|
182
|
+
/**
|
|
183
|
+
* @primitive b.uuid.isValid
|
|
184
|
+
* @signature b.uuid.isValid(str)
|
|
185
|
+
* @since 0.1.0
|
|
186
|
+
* @related b.uuid.parse
|
|
187
|
+
*
|
|
188
|
+
* Loose shape-only check — returns `true` for any 8-4-4-4-12 hex
|
|
189
|
+
* string regardless of version or variant bits. Cheap. Use `parse()`
|
|
190
|
+
* when version/variant matter (most operator code does).
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* b.uuid.isValid("f47ac10b-58cc-4372-a567-0e02b2c3d479"); // → true
|
|
194
|
+
* b.uuid.isValid("not-a-uuid"); // → false
|
|
195
|
+
*/
|
|
121
196
|
function isValid(str) {
|
|
122
197
|
if (typeof str !== "string") return false;
|
|
123
198
|
if (str.length > UUID_STR_MAX_LEN) return false;
|