@mushi-mushi/core 1.22.5 → 1.23.0
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/README.md +56 -1
- package/dist/index.cjs +216 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -2
- package/dist/index.d.ts +52 -2
- package/dist/index.js +214 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Core types, API client, and shared utilities for every Mushi SDK package.
|
|
|
23
23
|
|
|
24
24
|
| Type | Purpose |
|
|
25
25
|
|----------------------------|-----------------------------------------------------------------------------------------------|
|
|
26
|
-
| `MushiPreset` | `'production-calm' \| 'beta-loud' \| 'internal-debug' \| 'manual-only'`
|
|
26
|
+
| `MushiPreset` | Posture bundles: `'production-calm' \| 'beta-loud' \| 'internal-debug' \| 'manual-only'` plus the tiers `'minimal' \| 'standard' \| 'full'`. See [Config presets](#config-presets--precedence). |
|
|
27
27
|
| `MushiWidgetAnchor` | Raw-CSS positioning (`top` / `right` / `bottom` / `left`) for the widget launcher. |
|
|
28
28
|
| `MushiPrivacyConfig` | `maskSelectors`, `blockSelectors`, `allowUserRemoveScreenshot` for screenshot redaction. |
|
|
29
29
|
| `MushiUrlMatcher` | `string \| RegExp` element used by `capture.ignoreUrls` and `apiCascade.ignoreUrls`. |
|
|
@@ -46,6 +46,61 @@ Constants: `MUSHI_INTERNAL_HEADER` (`'X-Mushi-Internal'`),
|
|
|
46
46
|
`MUSHI_INTERNAL_INIT_MARKER`, and the `MushiInternalRequestKind` literal union
|
|
47
47
|
are re-exported so framework adapters can build their own self-noise filters.
|
|
48
48
|
|
|
49
|
+
## Config presets & precedence
|
|
50
|
+
|
|
51
|
+
Pass `preset` to pick a posture bundle instead of hand-assembling
|
|
52
|
+
`widget` / `capture` / `proactive` flags. `expandPreset(config)` expands the
|
|
53
|
+
preset into those nested objects; **your explicit config always wins** on every
|
|
54
|
+
key (the merge is per-sub-object, so overriding one `capture` flag keeps the
|
|
55
|
+
rest of the preset's defaults).
|
|
56
|
+
|
|
57
|
+
| Field | `minimal` | `standard` | `full` |
|
|
58
|
+
|----------------------|----------------|--------------------|------------------|
|
|
59
|
+
| Widget | on (`auto`) | on (SDK default) | on (`auto`) |
|
|
60
|
+
| `capture.console` | ✅ | default | ✅ |
|
|
61
|
+
| `capture.network` | ❌ | default | ✅ |
|
|
62
|
+
| `capture.performance`| ❌ | default | ✅ |
|
|
63
|
+
| `capture.screenshot` | `on-report` | default | `auto` |
|
|
64
|
+
| `capture.replay` | `off` | default | `lite` |
|
|
65
|
+
| Proactive triggers | none | default | all on |
|
|
66
|
+
|
|
67
|
+
- **`minimal`** — widget + console capture only, screenshot on report; no
|
|
68
|
+
network/performance/replay, no proactive nudges. Leanest footprint.
|
|
69
|
+
- **`standard`** — today's SDK defaults. Expansion is a **no-op** (the config
|
|
70
|
+
is returned untouched); the preset exists so the intent is explicit and
|
|
71
|
+
documented.
|
|
72
|
+
- **`full`** — every capture channel on (including a self-contained `lite`
|
|
73
|
+
session replay) and all proactive triggers. Loudest / most data.
|
|
74
|
+
|
|
75
|
+
The four legacy postures — `production-calm`, `beta-loud`, `internal-debug`,
|
|
76
|
+
`manual-only` — are still supported and expand the same way.
|
|
77
|
+
|
|
78
|
+
### Precedence
|
|
79
|
+
|
|
80
|
+
Highest wins. `resolveEnvConfig()` only ever supplies
|
|
81
|
+
`projectId` / `apiKey` / `apiEndpoint`, so it never competes with a preset for
|
|
82
|
+
the nested option objects.
|
|
83
|
+
|
|
84
|
+
| Priority | Source | Example |
|
|
85
|
+
|----------|-------------------------------------|-------------------------------------------|
|
|
86
|
+
| 1 (wins) | Explicit config | `capture: { network: true }` |
|
|
87
|
+
| 2 | `preset` | `preset: 'minimal'` → `network: false` |
|
|
88
|
+
| 3 | Environment variables | `NEXT_PUBLIC_MUSHI_*` / `VITE_MUSHI_*` |
|
|
89
|
+
| 4 | SDK defaults | built-in fallbacks |
|
|
90
|
+
|
|
91
|
+
### Validation
|
|
92
|
+
|
|
93
|
+
`validateConfig(config)` runs at init and **fails loud but never throws**: it
|
|
94
|
+
`console.error`s on unknown top-level keys (usually a typo) and on an invalid
|
|
95
|
+
`preset` value, then continues. Set `MUSHI_SILENT=1` to suppress the warnings.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { expandPreset, validateConfig } from '@mushi-mushi/core';
|
|
99
|
+
|
|
100
|
+
validateConfig(config); // warns on typos / bad preset (never throws)
|
|
101
|
+
const resolved = expandPreset(config); // preset → nested objects, explicit wins
|
|
102
|
+
```
|
|
103
|
+
|
|
49
104
|
## Usage
|
|
50
105
|
|
|
51
106
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -601,6 +601,219 @@ function resolveEnvConfig() {
|
|
|
601
601
|
if (apiEndpoint) result.apiEndpoint = apiEndpoint;
|
|
602
602
|
return result;
|
|
603
603
|
}
|
|
604
|
+
var ENV_PREFIXES = ["VITE_", "NEXT_PUBLIC_", "NUXT_PUBLIC_", "EXPO_PUBLIC_", ""];
|
|
605
|
+
var FIELD_SUFFIX = {
|
|
606
|
+
projectId: "MUSHI_PROJECT_ID",
|
|
607
|
+
apiKey: "MUSHI_API_KEY"
|
|
608
|
+
};
|
|
609
|
+
function diagnoseEnvConfig() {
|
|
610
|
+
const resolved = resolveEnvConfig();
|
|
611
|
+
const missing = [];
|
|
612
|
+
if (!resolved.projectId) missing.push("projectId");
|
|
613
|
+
if (!resolved.apiKey) missing.push("apiKey");
|
|
614
|
+
if (missing.length === 0) {
|
|
615
|
+
return { resolved, missing, nearMisses: [], message: null };
|
|
616
|
+
}
|
|
617
|
+
const importMetaEnv = (() => {
|
|
618
|
+
try {
|
|
619
|
+
const env = undefined;
|
|
620
|
+
return typeof env === "object" && env !== null ? env : null;
|
|
621
|
+
} catch {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
})();
|
|
625
|
+
const processEnv = (() => {
|
|
626
|
+
try {
|
|
627
|
+
return typeof process !== "undefined" && process.env ? process.env : null;
|
|
628
|
+
} catch {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
})();
|
|
632
|
+
const nearMisses = [];
|
|
633
|
+
for (const field of missing) {
|
|
634
|
+
for (const prefix of ENV_PREFIXES) {
|
|
635
|
+
const key = `${prefix}${FIELD_SUFFIX[field]}`;
|
|
636
|
+
if (importMetaEnv && importMetaEnv[key]) {
|
|
637
|
+
nearMisses.push({ field, key, via: "import.meta.env" });
|
|
638
|
+
} else if (processEnv && processEnv[key]) {
|
|
639
|
+
nearMisses.push({ field, key, via: "process.env" });
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const expectedPrefix = importMetaEnv ? "VITE_MUSHI_*" : "NEXT_PUBLIC_MUSHI_* (Next.js), EXPO_PUBLIC_MUSHI_* (Expo), or MUSHI_* (Node / React Native)";
|
|
644
|
+
const lines = [`[mushi] Missing ${missing.join(" and ")} \u2014 the SDK will not initialize.`];
|
|
645
|
+
for (const m of nearMisses) {
|
|
646
|
+
lines.push(
|
|
647
|
+
`[mushi] Found ${m.key} in ${m.via}, but this runtime did not resolve it \u2014 it is either the wrong prefix for your bundler or not exposed to the client bundle.`
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
lines.push(`[mushi] This runtime reads: ${expectedPrefix}. Set the matching variable(s) and restart your dev server.`);
|
|
651
|
+
if (nearMisses.length === 0) {
|
|
652
|
+
lines.push(
|
|
653
|
+
`[mushi] If you already set the variable, check the prefix: a mis-prefixed var is stripped from client bundles and resolves to undefined with no error.`
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
return { resolved, missing, nearMisses, message: lines.join("\n") };
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// src/presets.ts
|
|
660
|
+
var KNOWN_PRESETS = [
|
|
661
|
+
"production-calm",
|
|
662
|
+
"beta-loud",
|
|
663
|
+
"internal-debug",
|
|
664
|
+
"manual-only",
|
|
665
|
+
"minimal",
|
|
666
|
+
"standard",
|
|
667
|
+
"full"
|
|
668
|
+
];
|
|
669
|
+
var KNOWN_CONFIG_KEYS = [
|
|
670
|
+
"projectId",
|
|
671
|
+
"apiKey",
|
|
672
|
+
"apiEndpoint",
|
|
673
|
+
"timeout",
|
|
674
|
+
"maxRetries",
|
|
675
|
+
"circuitBreaker",
|
|
676
|
+
"preset",
|
|
677
|
+
"runtimeConfig",
|
|
678
|
+
"sentry",
|
|
679
|
+
"widget",
|
|
680
|
+
"capture",
|
|
681
|
+
"privacy",
|
|
682
|
+
"proactive",
|
|
683
|
+
"preFilter",
|
|
684
|
+
"integrations",
|
|
685
|
+
"offline",
|
|
686
|
+
"rewards",
|
|
687
|
+
"assistant",
|
|
688
|
+
"debug",
|
|
689
|
+
"enabled",
|
|
690
|
+
"appVersion",
|
|
691
|
+
"beforeSendFeedback",
|
|
692
|
+
"onCrashedLastRun"
|
|
693
|
+
];
|
|
694
|
+
function presetDefaults(preset) {
|
|
695
|
+
switch (preset) {
|
|
696
|
+
case "manual-only":
|
|
697
|
+
return {
|
|
698
|
+
widget: { trigger: "manual", outdatedBanner: "console-only" },
|
|
699
|
+
capture: { console: true, network: true, performance: false, screenshot: "on-report", elementSelector: false },
|
|
700
|
+
proactive: { rageClick: false, longTask: false, apiCascade: false, errorBoundary: false }
|
|
701
|
+
};
|
|
702
|
+
case "beta-loud":
|
|
703
|
+
return {
|
|
704
|
+
widget: { trigger: "auto", outdatedBanner: "banner" },
|
|
705
|
+
capture: { console: true, network: true, performance: true, screenshot: "auto", elementSelector: true },
|
|
706
|
+
proactive: { rageClick: true, longTask: true, apiCascade: true, errorBoundary: true }
|
|
707
|
+
};
|
|
708
|
+
case "internal-debug":
|
|
709
|
+
return {
|
|
710
|
+
widget: { trigger: "auto", outdatedBanner: "banner", brandFooter: true },
|
|
711
|
+
capture: { console: true, network: true, performance: true, screenshot: "auto", elementSelector: true },
|
|
712
|
+
proactive: {
|
|
713
|
+
rageClick: true,
|
|
714
|
+
longTask: true,
|
|
715
|
+
apiCascade: true,
|
|
716
|
+
errorBoundary: true,
|
|
717
|
+
cooldown: { maxProactivePerSession: 10, dismissCooldownHours: 0, suppressAfterDismissals: 99 }
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
case "production-calm":
|
|
721
|
+
return {
|
|
722
|
+
widget: { trigger: "auto", outdatedBanner: "console-only" },
|
|
723
|
+
capture: { console: true, network: true, performance: false, screenshot: "on-report", elementSelector: false },
|
|
724
|
+
proactive: { rageClick: false, longTask: false, apiCascade: false, errorBoundary: false }
|
|
725
|
+
};
|
|
726
|
+
// Lean posture: widget on, console capture only, screenshot on-report,
|
|
727
|
+
// no network/performance/replay, no proactive nudges.
|
|
728
|
+
case "minimal":
|
|
729
|
+
return {
|
|
730
|
+
widget: { trigger: "auto" },
|
|
731
|
+
capture: {
|
|
732
|
+
console: true,
|
|
733
|
+
network: false,
|
|
734
|
+
performance: false,
|
|
735
|
+
screenshot: "on-report",
|
|
736
|
+
replay: "off",
|
|
737
|
+
elementSelector: false
|
|
738
|
+
},
|
|
739
|
+
proactive: { rageClick: false, longTask: false, apiCascade: false, errorBoundary: false }
|
|
740
|
+
};
|
|
741
|
+
// Everything-on posture: full capture (incl. self-contained `lite` replay)
|
|
742
|
+
// and every proactive trigger.
|
|
743
|
+
case "full":
|
|
744
|
+
return {
|
|
745
|
+
widget: { trigger: "auto" },
|
|
746
|
+
capture: {
|
|
747
|
+
console: true,
|
|
748
|
+
network: true,
|
|
749
|
+
performance: true,
|
|
750
|
+
screenshot: "auto",
|
|
751
|
+
replay: "lite",
|
|
752
|
+
elementSelector: true
|
|
753
|
+
},
|
|
754
|
+
proactive: { rageClick: true, longTask: true, apiCascade: true, errorBoundary: true }
|
|
755
|
+
};
|
|
756
|
+
// 'standard' and anything unrecognised → no expansion.
|
|
757
|
+
default:
|
|
758
|
+
return void 0;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function expandPreset(config) {
|
|
762
|
+
const defaults = presetDefaults(config.preset);
|
|
763
|
+
if (!defaults) return config;
|
|
764
|
+
return {
|
|
765
|
+
...config,
|
|
766
|
+
widget: {
|
|
767
|
+
...defaults.widget,
|
|
768
|
+
...config.widget
|
|
769
|
+
},
|
|
770
|
+
capture: {
|
|
771
|
+
...defaults.capture,
|
|
772
|
+
...config.capture
|
|
773
|
+
},
|
|
774
|
+
proactive: {
|
|
775
|
+
...defaults.proactive,
|
|
776
|
+
...config.proactive,
|
|
777
|
+
cooldown: {
|
|
778
|
+
...defaults.proactive?.cooldown,
|
|
779
|
+
...config.proactive?.cooldown
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function isSilenced() {
|
|
785
|
+
const truthy = (v) => v === "1" || v === "true";
|
|
786
|
+
try {
|
|
787
|
+
if (typeof process !== "undefined" && process.env && truthy(process.env.MUSHI_SILENT)) return true;
|
|
788
|
+
} catch {
|
|
789
|
+
}
|
|
790
|
+
try {
|
|
791
|
+
const env = undefined;
|
|
792
|
+
if (env && typeof env === "object" && (truthy(env.MUSHI_SILENT) || truthy(env.VITE_MUSHI_SILENT))) return true;
|
|
793
|
+
} catch {
|
|
794
|
+
}
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
function validateConfig(config) {
|
|
798
|
+
try {
|
|
799
|
+
if (isSilenced()) return;
|
|
800
|
+
if (!config || typeof config !== "object") return;
|
|
801
|
+
const unknownKeys = Object.keys(config).filter((key) => !KNOWN_CONFIG_KEYS.includes(key));
|
|
802
|
+
if (unknownKeys.length > 0) {
|
|
803
|
+
console.error(
|
|
804
|
+
`[mushi] Unknown config key${unknownKeys.length > 1 ? "s" : ""}: ${unknownKeys.map((k) => `\`${k}\``).join(", ")} \u2014 check for typos (see MushiConfig). Ignored.`
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
if (config.preset !== void 0 && !KNOWN_PRESETS.includes(config.preset)) {
|
|
808
|
+
console.error(
|
|
809
|
+
`[mushi] Unknown preset \`${String(config.preset)}\` \u2014 expected one of ${KNOWN_PRESETS.map(
|
|
810
|
+
(p) => `'${p}'`
|
|
811
|
+
).join(", ")}. Preset ignored.`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
} catch {
|
|
815
|
+
}
|
|
816
|
+
}
|
|
604
817
|
|
|
605
818
|
// src/pre-filter.ts
|
|
606
819
|
var DEFAULT_MIN_LENGTH = 10;
|
|
@@ -2206,7 +2419,9 @@ exports.createOfflineQueue = createOfflineQueue;
|
|
|
2206
2419
|
exports.createPiiScrubber = createPiiScrubber;
|
|
2207
2420
|
exports.createPreFilter = createPreFilter;
|
|
2208
2421
|
exports.createRateLimiter = createRateLimiter;
|
|
2422
|
+
exports.diagnoseEnvConfig = diagnoseEnvConfig;
|
|
2209
2423
|
exports.estimateJsonBytes = estimateJsonBytes;
|
|
2424
|
+
exports.expandPreset = expandPreset;
|
|
2210
2425
|
exports.faviconUrlCandidates = faviconUrlCandidates;
|
|
2211
2426
|
exports.formatBytes = formatBytes;
|
|
2212
2427
|
exports.getDeviceFingerprintHash = getDeviceFingerprintHash;
|
|
@@ -2235,5 +2450,6 @@ exports.resolveWidgetAccent = resolveWidgetAccent;
|
|
|
2235
2450
|
exports.safeWidgetHex = safeWidgetHex;
|
|
2236
2451
|
exports.scrubPii = scrubPii;
|
|
2237
2452
|
exports.sha256Hex = sha256Hex;
|
|
2453
|
+
exports.validateConfig = validateConfig;
|
|
2238
2454
|
//# sourceMappingURL=index.cjs.map
|
|
2239
2455
|
//# sourceMappingURL=index.cjs.map
|