jamdesk 1.1.147 → 1.1.148

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jamdesk",
3
- "version": "1.1.147",
3
+ "version": "1.1.148",
4
4
  "description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
5
5
  "keywords": [
6
6
  "jamdesk",
@@ -0,0 +1,87 @@
1
+ // builder/build-service/components/ConsentGate.tsx
2
+ 'use client';
3
+
4
+ import { useEffect, useRef } from 'react';
5
+ import type { GatedScript } from '../lib/consent-gating';
6
+
7
+ const POLL_MS = 1000;
8
+
9
+ function hasConsent(key: string, value: string): boolean {
10
+ try {
11
+ return window.localStorage.getItem(key) === value;
12
+ } catch {
13
+ // Storage unavailable (private mode / blocked iframe) → treat as no consent
14
+ return false;
15
+ }
16
+ }
17
+
18
+ function injectScripts(scripts: GatedScript[]): void {
19
+ for (const s of scripts) {
20
+ const el = document.createElement('script');
21
+ el.setAttribute('data-jd-consent-script', s.id);
22
+ if (s.kind === 'src' && s.src) {
23
+ el.src = s.src;
24
+ for (const [name, val] of Object.entries(s.attrs ?? {})) el.setAttribute(name, val);
25
+ } else if (s.kind === 'inline' && s.code) {
26
+ el.textContent = s.code;
27
+ } else {
28
+ continue;
29
+ }
30
+ (s.appendTo === 'head' ? document.head : document.body).appendChild(el);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Consent gate for analytics/tracking scripts (integrations.cookies).
36
+ * Renders nothing. Injects the given scripts once localStorage[consentKey]
37
+ * equals consentValue — immediately on mount if consent pre-exists, else via
38
+ * a storage listener (cross-tab) + poll (same-tab CMP writes don't fire
39
+ * storage events). Scripts are injected at most once per page load; consent
40
+ * revocation takes effect on the next page load.
41
+ */
42
+ export function ConsentGate({
43
+ consentKey,
44
+ consentValue,
45
+ scripts,
46
+ }: {
47
+ consentKey: string;
48
+ consentValue: string;
49
+ scripts: GatedScript[];
50
+ }) {
51
+ // scripts is a fresh array identity each render (built server-side, serialized
52
+ // across the RSC boundary). Hold it in a ref so the effect doesn't list it as
53
+ // a dependency — otherwise a parent re-render before consent arrives would
54
+ // tear down and restart the polling window. Content is stable per page.
55
+ const scriptsRef = useRef(scripts);
56
+ scriptsRef.current = scripts;
57
+
58
+ useEffect(() => {
59
+ if ((window as any).__jdConsentInjected) return;
60
+ let intervalId: ReturnType<typeof setInterval> | undefined;
61
+
62
+ const stop = () => {
63
+ if (intervalId !== undefined) clearInterval(intervalId);
64
+ window.removeEventListener('storage', check);
65
+ };
66
+ const check = () => {
67
+ if ((window as any).__jdConsentInjected) { stop(); return; }
68
+ if (!hasConsent(consentKey, consentValue)) return;
69
+ (window as any).__jdConsentInjected = true;
70
+ stop();
71
+ injectScripts(scriptsRef.current);
72
+ };
73
+
74
+ check();
75
+ if (!(window as any).__jdConsentInjected) {
76
+ // Diagnostics: wrong key/value otherwise fails silently forever.
77
+ console.debug(
78
+ `[Jamdesk] Consent gating active — analytics withheld until localStorage["${consentKey}"] equals the configured value.`,
79
+ );
80
+ window.addEventListener('storage', check);
81
+ intervalId = setInterval(check, POLL_MS);
82
+ }
83
+ return stop;
84
+ }, [consentKey, consentValue]);
85
+
86
+ return null;
87
+ }
@@ -0,0 +1,119 @@
1
+ // builder/build-service/lib/consent-gating.ts
2
+ // Consent gating for analytics/tracking scripts (integrations.cookies) and
3
+ // CMP loader validation (integrations.osano / integrations.termly).
4
+ // Pure module — no DOM, no React — so the server layout and tests share it.
5
+ import type { IntegrationsConfig } from './docs-types';
6
+
7
+ export interface GatedScript {
8
+ /** Stable id — used as DOM marker (data-jd-consent-script) and React key */
9
+ id: string;
10
+ kind: 'inline' | 'src';
11
+ /** Inline JS source (kind: 'inline') */
12
+ code?: string;
13
+ /** External src (kind: 'src') */
14
+ src?: string;
15
+ /** Extra attributes set via setAttribute (kind: 'src'); '' = boolean attr */
16
+ attrs?: Record<string, string>;
17
+ appendTo: 'head' | 'body';
18
+ }
19
+
20
+ // Tracking-ID guards: these values get interpolated into inline JS / URLs, so
21
+ // they must be strictly validated (same posture as the existing Crisp guard).
22
+ // /i is intentional: tolerate lowercase-typed IDs (alphanumerics + hyphen
23
+ // can't break out of the inline JS string regardless of case).
24
+ const GA4_ID_RE = /^G-[A-Z0-9]+$/i;
25
+ const GTM_ID_RE = /^GTM-[A-Z0-9]+$/i;
26
+ const CRISP_ID_RE = /^[a-f0-9-]{36}$/; // mirrors layout-helpers.tsx guard
27
+
28
+ // CMP loader URL pins. Defense-in-depth: schema validates these too, but the
29
+ // renderer must not trust config blobs from R2. Path charset includes '.' so
30
+ // every schema-valid Osano URL also passes here (schema only pins prefix/suffix).
31
+ const OSANO_SRC_RE = /^https:\/\/cmp\.osano\.com\/[A-Za-z0-9._/-]+\/osano\.js$/;
32
+ const TERMLY_SRC_RE =
33
+ /^https:\/\/app\.termly\.io\/resource-blocker\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(\?autoBlock=(on|off))?$/;
34
+
35
+ export function isConsentGatingEnabled(integrations: IntegrationsConfig | undefined): boolean {
36
+ const c = integrations?.cookies;
37
+ return typeof c?.key === 'string' && c.key.length > 0 &&
38
+ typeof c?.value === 'string' && c.value.length > 0;
39
+ }
40
+
41
+ /** Validated CMP script srcs to render ungated in <head> (Osano first). */
42
+ export function getCmpScriptSrcs(integrations: IntegrationsConfig | undefined): string[] {
43
+ const srcs: string[] = [];
44
+ const osano = integrations?.osano?.scriptSource;
45
+ if (osano && OSANO_SRC_RE.test(osano)) srcs.push(osano);
46
+ const termly = integrations?.termly?.scriptSource;
47
+ if (termly && TERMLY_SRC_RE.test(termly)) srcs.push(termly);
48
+ return srcs;
49
+ }
50
+
51
+ /**
52
+ * Build serializable descriptors for every analytics/tracking script.
53
+ * Emitted unconditionally — the caller is responsible for the prod gate:
54
+ * layout-helpers renders <ConsentGate> (and thus calls this) only when
55
+ * NODE_ENV === 'production', matching the dev-suppression the direct render
56
+ * paths already apply to Plausible/Crisp/customJs/first-party analytics.
57
+ */
58
+ export function buildGatedScripts({
59
+ integrations,
60
+ customJs,
61
+ analyticsScript,
62
+ }: {
63
+ integrations: IntegrationsConfig | undefined;
64
+ customJs: string | null;
65
+ analyticsScript: string | null;
66
+ }): GatedScript[] {
67
+ const scripts: GatedScript[] = [];
68
+ const i = integrations ?? {};
69
+
70
+ if (i.plausible?.scriptUrl) {
71
+ // Proxy mode — mirrors PlausibleScript's scriptUrl branch in layout-helpers
72
+ scripts.push({ id: 'plausible-proxy', kind: 'src', src: i.plausible.scriptUrl, attrs: { async: '' }, appendTo: 'head' });
73
+ scripts.push({
74
+ id: 'plausible-proxy-init', kind: 'inline', appendTo: 'head',
75
+ code: 'window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};plausible.init()',
76
+ });
77
+ } else if (i.plausible?.domain) {
78
+ const baseServer = (i.plausible.server || 'https://plausible.io').replace(/\/+$/, '');
79
+ const attrs: Record<string, string> = { defer: '', 'data-domain': i.plausible.domain };
80
+ if (i.plausible.server) attrs['data-api'] = `${baseServer}/api/event`;
81
+ scripts.push({ id: 'plausible', kind: 'src', src: `${baseServer}/js/script.js`, attrs, appendTo: 'head' });
82
+ }
83
+
84
+ // GTM/GA4 here are raw inline equivalents of the standard snippets. The
85
+ // ungated direct renders use @next/third-parties (ConditionalGTM/ConditionalGA)
86
+ // instead; these descriptors deliberately bypass it because they're injected
87
+ // client-side at consent time, not at SSR. Functionally equivalent for tracking,
88
+ // but they carry NO Next.js CSP nonce and GTM omits the <noscript> iframe
89
+ // fallback (irrelevant — no-JS clients can't trigger the consent gate anyway).
90
+ if (i.gtm?.tagId && GTM_ID_RE.test(i.gtm.tagId)) {
91
+ scripts.push({
92
+ id: 'gtm', kind: 'inline', appendTo: 'head',
93
+ code: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','${i.gtm.tagId}');`,
94
+ });
95
+ }
96
+
97
+ if (i.ga4?.measurementId && GA4_ID_RE.test(i.ga4.measurementId)) {
98
+ scripts.push({
99
+ id: 'ga4-loader', kind: 'src', appendTo: 'head', attrs: { async: '' },
100
+ src: `https://www.googletagmanager.com/gtag/js?id=${i.ga4.measurementId}`,
101
+ });
102
+ scripts.push({
103
+ id: 'ga4-init', kind: 'inline', appendTo: 'head',
104
+ code: `window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','${i.ga4.measurementId}');`,
105
+ });
106
+ }
107
+
108
+ if (i.crisp?.websiteId && CRISP_ID_RE.test(i.crisp.websiteId)) {
109
+ scripts.push({
110
+ id: 'crisp', kind: 'inline', appendTo: 'body',
111
+ code: `window.$crisp=[];window.CRISP_WEBSITE_ID="${i.crisp.websiteId}";(function(){var d=document;var s=d.createElement("script");s.src="https://client.crisp.chat/l.js";s.async=1;d.getElementsByTagName("head")[0].appendChild(s)})();`,
112
+ });
113
+ }
114
+
115
+ if (customJs) scripts.push({ id: 'custom-js', kind: 'inline', code: customJs, appendTo: 'body' });
116
+ if (analyticsScript) scripts.push({ id: 'jd-analytics', kind: 'inline', code: analyticsScript, appendTo: 'body' });
117
+
118
+ return scripts;
119
+ }
@@ -644,7 +644,10 @@ export interface NewsletterConfig {
644
644
  }
645
645
 
646
646
  /**
647
- * Analytics and integration configurations (stubs - TODO: implement)
647
+ * Analytics and integration configurations.
648
+ * Implemented: ga4, gtm, plausible, crisp, osano, termly, cookies (consent
649
+ * gating), analytics opt-out via AnalyticsConfig. Others remain config-only
650
+ * stubs accepted for Mintlify compatibility.
648
651
  */
649
652
  export interface IntegrationsConfig {
650
653
  ga4?: { measurementId: string };
@@ -664,6 +667,7 @@ export interface IntegrationsConfig {
664
667
  logrocket?: { appId: string };
665
668
  mixpanel?: { projectToken: string };
666
669
  osano?: { scriptSource: string };
670
+ termly?: { scriptSource: string };
667
671
  pirsch?: { id: string };
668
672
  posthog?: { apiKey: string; apiHost?: string };
669
673
  plausible?: { domain?: string; server?: string; scriptUrl?: string };
@@ -25,6 +25,8 @@ import type { BackgroundConfig, DocsConfig, FontConfig, LanguageCode } from '@/l
25
25
  import { LinkPrefixProvider } from '@/lib/link-prefix-context';
26
26
  import { ProjectSlugProvider } from '@/lib/project-slug-context';
27
27
  import { getAnalyticsScript } from '@/lib/analytics-script';
28
+ import { isConsentGatingEnabled, buildGatedScripts, getCmpScriptSrcs } from '@/lib/consent-gating';
29
+ import { ConsentGate } from '@/components/ConsentGate';
28
30
  import { AgentDirective } from './agent-directive';
29
31
  import { fetchCustomCss, fetchCustomJs } from '@/lib/r2-content';
30
32
  import { toHreflang } from '@/lib/language-utils';
@@ -429,6 +431,21 @@ export async function DocsChrome({
429
431
  ? getAnalyticsScript(resolvedProjectSlug)
430
432
  : null;
431
433
 
434
+ // integrations.cookies consent gating: when key+value are configured, all
435
+ // analytics/tracking scripts are withheld until the CMP writes
436
+ // localStorage[key] === value (ConsentGate injects them client-side).
437
+ const consentGating = isConsentGatingEnabled(config.integrations);
438
+ // CMP loaders (Osano/Termly) are NEVER gated — they ARE the consent banner.
439
+ const cmpScriptSrcs = process.env.NODE_ENV === 'production'
440
+ ? getCmpScriptSrcs(config.integrations)
441
+ : [];
442
+ // Preconnect origins are best-effort: getCmpScriptSrcs only returns
443
+ // regex-validated absolute https URLs (so new URL can't throw today), but
444
+ // guard anyway — a malformed preconnect must never 500 the whole render.
445
+ const cmpPreconnectOrigins = cmpScriptSrcs
446
+ .map((src) => { try { return new URL(src).origin; } catch { return null; } })
447
+ .filter((origin): origin is string => origin !== null);
448
+
432
449
  // Global banner: only emit the pre-paint no-flash guard for a dismissible
433
450
  // banner (a non-dismissible banner is never hidden, so the guard would risk
434
451
  // hiding a mandatory message via a stale dismissal hash). Trimmed content
@@ -457,6 +474,15 @@ export async function DocsChrome({
457
474
  return (
458
475
  <html lang={toHreflang(lang)} dir={dir} suppressHydrationWarning data-scroll-behavior="smooth" data-scroll-locked="true">
459
476
  <head>
477
+ {/* CMP scripts must be first + synchronous: auto-blocking CMPs
478
+ (Osano/Termly) need to intercept later scripts. Never gated.
479
+ preconnect first: the sync fetch is render-blocking. */}
480
+ {cmpPreconnectOrigins.map((origin) => (
481
+ <link key={`pc-${origin}`} rel="preconnect" href={origin} />
482
+ ))}
483
+ {cmpScriptSrcs.map((src) => (
484
+ <script key={src} src={src} />
485
+ ))}
460
486
  {/*
461
487
  SSR scroll lock — prevents Chrome's same-tab cross-origin "preserve
462
488
  scroll" heuristic from scrolling #content-scroll-container on first
@@ -674,7 +700,7 @@ export async function DocsChrome({
674
700
  {customCss}
675
701
  </style>
676
702
  )}
677
- {process.env.NODE_ENV === 'production' && (config.integrations?.plausible?.domain || config.integrations?.plausible?.scriptUrl) && (
703
+ {!consentGating && process.env.NODE_ENV === 'production' && (config.integrations?.plausible?.domain || config.integrations?.plausible?.scriptUrl) && (
678
704
  <PlausibleScript
679
705
  domain={config.integrations.plausible.domain}
680
706
  server={config.integrations.plausible.server}
@@ -684,7 +710,7 @@ export async function DocsChrome({
684
710
  </head>
685
711
  <body className={fontClassName} data-theme={themeName || 'jam'} data-decoration={decoration || undefined} suppressHydrationWarning>
686
712
  {!embed && <AgentDirective hostAtDocs={!!config.hostAtDocs} />}
687
- {config.integrations?.gtm?.tagId && (
713
+ {!consentGating && config.integrations?.gtm?.tagId && (
688
714
  <ConditionalGTM gtmId={config.integrations.gtm.tagId} />
689
715
  )}
690
716
  <ThemeProvider
@@ -712,7 +738,7 @@ export async function DocsChrome({
712
738
  client" warning in `jamdesk dev`. Same pattern as the
713
739
  project-subdomain hydration suppressor in <head>.
714
740
  */}
715
- {process.env.NODE_ENV === 'production' && config.integrations?.crisp?.websiteId &&
741
+ {!consentGating && process.env.NODE_ENV === 'production' && config.integrations?.crisp?.websiteId &&
716
742
  /^[a-f0-9-]{36}$/.test(config.integrations.crisp.websiteId) && (
717
743
  <script
718
744
  dangerouslySetInnerHTML={{
@@ -720,15 +746,26 @@ export async function DocsChrome({
720
746
  }}
721
747
  />
722
748
  )}
723
- {process.env.NODE_ENV === 'production' && customJs && (
749
+ {!consentGating && process.env.NODE_ENV === 'production' && customJs && (
724
750
  <script dangerouslySetInnerHTML={{ __html: customJs }} />
725
751
  )}
726
- {process.env.NODE_ENV === 'production' && analyticsScript && (
752
+ {!consentGating && process.env.NODE_ENV === 'production' && analyticsScript && (
727
753
  <script dangerouslySetInnerHTML={{ __html: analyticsScript }} />
728
754
  )}
729
- {config.integrations?.ga4?.measurementId && (
755
+ {!consentGating && config.integrations?.ga4?.measurementId && (
730
756
  <ConditionalGA gaId={config.integrations.ga4.measurementId} />
731
757
  )}
758
+ {consentGating && process.env.NODE_ENV === 'production' && (
759
+ <ConsentGate
760
+ consentKey={config.integrations!.cookies!.key!}
761
+ consentValue={config.integrations!.cookies!.value!}
762
+ scripts={buildGatedScripts({
763
+ integrations: config.integrations,
764
+ customJs,
765
+ analyticsScript,
766
+ })}
767
+ />
768
+ )}
732
769
  <JdReadySentinel />
733
770
  </body>
734
771
  </html>
@@ -36,6 +36,9 @@ export interface IntegrationsConfig {
36
36
  ga4?: { measurementId: string };
37
37
  gtm?: { tagId: string };
38
38
  plausible?: { domain?: string; server?: string; scriptUrl?: string };
39
+ osano?: { scriptSource: string };
40
+ termly?: { scriptSource: string };
41
+ cookies?: { key?: string; value?: string };
39
42
  [key: string]: unknown;
40
43
  }
41
44
 
@@ -1258,6 +1258,19 @@
1258
1258
  ],
1259
1259
  "additionalProperties": false
1260
1260
  },
1261
+ "termly": {
1262
+ "type": "object",
1263
+ "properties": {
1264
+ "scriptSource": {
1265
+ "type": "string",
1266
+ "pattern": "^https://app\\.termly\\.io/resource-blocker/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(\\?autoBlock=(on|off))?$"
1267
+ }
1268
+ },
1269
+ "required": [
1270
+ "scriptSource"
1271
+ ],
1272
+ "additionalProperties": false
1273
+ },
1261
1274
  "pirsch": {
1262
1275
  "type": "object",
1263
1276
  "properties": {
@@ -1345,12 +1358,18 @@
1345
1358
  "type": "object",
1346
1359
  "properties": {
1347
1360
  "key": {
1348
- "type": "string"
1361
+ "type": "string",
1362
+ "minLength": 1
1349
1363
  },
1350
1364
  "value": {
1351
- "type": "string"
1365
+ "type": "string",
1366
+ "minLength": 1
1352
1367
  }
1353
1368
  },
1369
+ "required": [
1370
+ "key",
1371
+ "value"
1372
+ ],
1354
1373
  "additionalProperties": false
1355
1374
  }
1356
1375
  },
@@ -373,6 +373,9 @@
373
373
  "cpu": [
374
374
  "arm"
375
375
  ],
376
+ "libc": [
377
+ "glibc"
378
+ ],
376
379
  "license": "LGPL-3.0-or-later",
377
380
  "optional": true,
378
381
  "os": [
@@ -389,6 +392,9 @@
389
392
  "cpu": [
390
393
  "arm64"
391
394
  ],
395
+ "libc": [
396
+ "glibc"
397
+ ],
392
398
  "license": "LGPL-3.0-or-later",
393
399
  "optional": true,
394
400
  "os": [
@@ -405,6 +411,9 @@
405
411
  "cpu": [
406
412
  "ppc64"
407
413
  ],
414
+ "libc": [
415
+ "glibc"
416
+ ],
408
417
  "license": "LGPL-3.0-or-later",
409
418
  "optional": true,
410
419
  "os": [
@@ -421,6 +430,9 @@
421
430
  "cpu": [
422
431
  "riscv64"
423
432
  ],
433
+ "libc": [
434
+ "glibc"
435
+ ],
424
436
  "license": "LGPL-3.0-or-later",
425
437
  "optional": true,
426
438
  "os": [
@@ -437,6 +449,9 @@
437
449
  "cpu": [
438
450
  "s390x"
439
451
  ],
452
+ "libc": [
453
+ "glibc"
454
+ ],
440
455
  "license": "LGPL-3.0-or-later",
441
456
  "optional": true,
442
457
  "os": [
@@ -453,6 +468,9 @@
453
468
  "cpu": [
454
469
  "x64"
455
470
  ],
471
+ "libc": [
472
+ "glibc"
473
+ ],
456
474
  "license": "LGPL-3.0-or-later",
457
475
  "optional": true,
458
476
  "os": [
@@ -469,6 +487,9 @@
469
487
  "cpu": [
470
488
  "arm64"
471
489
  ],
490
+ "libc": [
491
+ "musl"
492
+ ],
472
493
  "license": "LGPL-3.0-or-later",
473
494
  "optional": true,
474
495
  "os": [
@@ -485,6 +506,9 @@
485
506
  "cpu": [
486
507
  "x64"
487
508
  ],
509
+ "libc": [
510
+ "musl"
511
+ ],
488
512
  "license": "LGPL-3.0-or-later",
489
513
  "optional": true,
490
514
  "os": [
@@ -501,6 +525,9 @@
501
525
  "cpu": [
502
526
  "arm"
503
527
  ],
528
+ "libc": [
529
+ "glibc"
530
+ ],
504
531
  "license": "Apache-2.0",
505
532
  "optional": true,
506
533
  "os": [
@@ -523,6 +550,9 @@
523
550
  "cpu": [
524
551
  "arm64"
525
552
  ],
553
+ "libc": [
554
+ "glibc"
555
+ ],
526
556
  "license": "Apache-2.0",
527
557
  "optional": true,
528
558
  "os": [
@@ -545,6 +575,9 @@
545
575
  "cpu": [
546
576
  "ppc64"
547
577
  ],
578
+ "libc": [
579
+ "glibc"
580
+ ],
548
581
  "license": "Apache-2.0",
549
582
  "optional": true,
550
583
  "os": [
@@ -567,6 +600,9 @@
567
600
  "cpu": [
568
601
  "riscv64"
569
602
  ],
603
+ "libc": [
604
+ "glibc"
605
+ ],
570
606
  "license": "Apache-2.0",
571
607
  "optional": true,
572
608
  "os": [
@@ -589,6 +625,9 @@
589
625
  "cpu": [
590
626
  "s390x"
591
627
  ],
628
+ "libc": [
629
+ "glibc"
630
+ ],
592
631
  "license": "Apache-2.0",
593
632
  "optional": true,
594
633
  "os": [
@@ -611,6 +650,9 @@
611
650
  "cpu": [
612
651
  "x64"
613
652
  ],
653
+ "libc": [
654
+ "glibc"
655
+ ],
614
656
  "license": "Apache-2.0",
615
657
  "optional": true,
616
658
  "os": [
@@ -633,6 +675,9 @@
633
675
  "cpu": [
634
676
  "arm64"
635
677
  ],
678
+ "libc": [
679
+ "musl"
680
+ ],
636
681
  "license": "Apache-2.0",
637
682
  "optional": true,
638
683
  "os": [
@@ -655,6 +700,9 @@
655
700
  "cpu": [
656
701
  "x64"
657
702
  ],
703
+ "libc": [
704
+ "musl"
705
+ ],
658
706
  "license": "Apache-2.0",
659
707
  "optional": true,
660
708
  "os": [
@@ -929,6 +977,9 @@
929
977
  "cpu": [
930
978
  "arm64"
931
979
  ],
980
+ "libc": [
981
+ "glibc"
982
+ ],
932
983
  "license": "MIT",
933
984
  "optional": true,
934
985
  "os": [
@@ -945,6 +996,9 @@
945
996
  "cpu": [
946
997
  "arm64"
947
998
  ],
999
+ "libc": [
1000
+ "musl"
1001
+ ],
948
1002
  "license": "MIT",
949
1003
  "optional": true,
950
1004
  "os": [
@@ -961,6 +1015,9 @@
961
1015
  "cpu": [
962
1016
  "x64"
963
1017
  ],
1018
+ "libc": [
1019
+ "glibc"
1020
+ ],
964
1021
  "license": "MIT",
965
1022
  "optional": true,
966
1023
  "os": [
@@ -977,6 +1034,9 @@
977
1034
  "cpu": [
978
1035
  "x64"
979
1036
  ],
1037
+ "libc": [
1038
+ "musl"
1039
+ ],
980
1040
  "license": "MIT",
981
1041
  "optional": true,
982
1042
  "os": [
@@ -1316,6 +1376,9 @@
1316
1376
  "cpu": [
1317
1377
  "arm64"
1318
1378
  ],
1379
+ "libc": [
1380
+ "glibc"
1381
+ ],
1319
1382
  "license": "MIT",
1320
1383
  "optional": true,
1321
1384
  "os": [
@@ -1332,6 +1395,9 @@
1332
1395
  "cpu": [
1333
1396
  "arm64"
1334
1397
  ],
1398
+ "libc": [
1399
+ "musl"
1400
+ ],
1335
1401
  "license": "MIT",
1336
1402
  "optional": true,
1337
1403
  "os": [
@@ -1348,6 +1414,9 @@
1348
1414
  "cpu": [
1349
1415
  "x64"
1350
1416
  ],
1417
+ "libc": [
1418
+ "glibc"
1419
+ ],
1351
1420
  "license": "MIT",
1352
1421
  "optional": true,
1353
1422
  "os": [
@@ -1364,6 +1433,9 @@
1364
1433
  "cpu": [
1365
1434
  "x64"
1366
1435
  ],
1436
+ "libc": [
1437
+ "musl"
1438
+ ],
1367
1439
  "license": "MIT",
1368
1440
  "optional": true,
1369
1441
  "os": [
@@ -2867,9 +2939,9 @@
2867
2939
  "license": "MIT"
2868
2940
  },
2869
2941
  "node_modules/electron-to-chromium": {
2870
- "version": "1.5.373",
2871
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.373.tgz",
2872
- "integrity": "sha512-G2Hym8JIf/QreuseqkDibgH8Ci8KfJzqGDKdakbhSx9UltwRBH2cBLAWU/lBX0sCdv0TlhyxQyDCnSfxgMWsjA==",
2942
+ "version": "1.5.374",
2943
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.374.tgz",
2944
+ "integrity": "sha512-HCF5i7izveksHSGqa7mhDh6tr3Uz9Dar2RAjwuh69bw3QGPVObjQIgLwQWeO/Rxp9/r0KdboKy9RbpQDl97fjg==",
2873
2945
  "license": "ISC"
2874
2946
  },
2875
2947
  "node_modules/enhanced-resolve": {
@@ -3865,6 +3937,9 @@
3865
3937
  "cpu": [
3866
3938
  "arm64"
3867
3939
  ],
3940
+ "libc": [
3941
+ "glibc"
3942
+ ],
3868
3943
  "license": "MPL-2.0",
3869
3944
  "optional": true,
3870
3945
  "os": [
@@ -3885,6 +3960,9 @@
3885
3960
  "cpu": [
3886
3961
  "arm64"
3887
3962
  ],
3963
+ "libc": [
3964
+ "musl"
3965
+ ],
3888
3966
  "license": "MPL-2.0",
3889
3967
  "optional": true,
3890
3968
  "os": [
@@ -3905,6 +3983,9 @@
3905
3983
  "cpu": [
3906
3984
  "x64"
3907
3985
  ],
3986
+ "libc": [
3987
+ "glibc"
3988
+ ],
3908
3989
  "license": "MPL-2.0",
3909
3990
  "optional": true,
3910
3991
  "os": [
@@ -3925,6 +4006,9 @@
3925
4006
  "cpu": [
3926
4007
  "x64"
3927
4008
  ],
4009
+ "libc": [
4010
+ "musl"
4011
+ ],
3928
4012
  "license": "MPL-2.0",
3929
4013
  "optional": true,
3930
4014
  "os": [