@botim/mp-debug-sdk 0.6.2 → 0.7.1

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/dist/index.cjs CHANGED
@@ -22,6 +22,214 @@ var BotimConsentError = class extends Error {
22
22
  }
23
23
  };
24
24
 
25
+ // src/consent-ui.ts
26
+ var STORAGE_PREFIX = "__botim_debug_consent_";
27
+ var CSS_PREFIX = "__botim-debug-consent";
28
+ var STYLE_ELEMENT_ID = `${CSS_PREFIX}-styles`;
29
+ var DEFAULT_COPY = {
30
+ title: "Enable BOTIM debug logging?",
31
+ body: [
32
+ "This shares your screen content, console logs, and network calls with the BOTIM team to help debug this mini-program.",
33
+ "No data leaves your device unless you agree. You can revoke this anytime by clearing site data or via developer tools."
34
+ ],
35
+ acceptLabel: "Allow",
36
+ declineLabel: "Skip"
37
+ };
38
+ function readConsentDecision(mpId) {
39
+ if (typeof localStorage === "undefined") return null;
40
+ try {
41
+ const raw = localStorage.getItem(STORAGE_PREFIX + mpId);
42
+ if (raw === "granted" || raw === "denied") return raw;
43
+ return null;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ function writeConsentDecision(mpId, decision) {
49
+ if (typeof localStorage === "undefined") return;
50
+ try {
51
+ localStorage.setItem(STORAGE_PREFIX + mpId, decision);
52
+ } catch {
53
+ }
54
+ }
55
+ function clearConsentDecision(mpId) {
56
+ if (typeof localStorage === "undefined") return;
57
+ try {
58
+ localStorage.removeItem(STORAGE_PREFIX + mpId);
59
+ } catch {
60
+ }
61
+ }
62
+ var inflightPrompt = null;
63
+ async function promptForConsent(mpId, copy = {}) {
64
+ if (inflightPrompt) return inflightPrompt;
65
+ if (typeof document === "undefined" || typeof window === "undefined") {
66
+ return "denied";
67
+ }
68
+ const merged = {
69
+ title: copy.title ?? DEFAULT_COPY.title,
70
+ body: copy.body ?? DEFAULT_COPY.body,
71
+ acceptLabel: copy.acceptLabel ?? DEFAULT_COPY.acceptLabel,
72
+ declineLabel: copy.declineLabel ?? DEFAULT_COPY.declineLabel
73
+ };
74
+ inflightPrompt = new Promise((resolve) => {
75
+ injectStylesheet();
76
+ const overlay = buildOverlay(merged, (decision) => {
77
+ writeConsentDecision(mpId, decision);
78
+ try {
79
+ overlay.remove();
80
+ } catch {
81
+ }
82
+ inflightPrompt = null;
83
+ resolve(decision);
84
+ });
85
+ document.body.appendChild(overlay);
86
+ });
87
+ return inflightPrompt;
88
+ }
89
+ function injectStylesheet() {
90
+ if (document.getElementById(STYLE_ELEMENT_ID)) return;
91
+ const style = document.createElement("style");
92
+ style.id = STYLE_ELEMENT_ID;
93
+ style.textContent = `
94
+ .${CSS_PREFIX}-overlay {
95
+ position: fixed !important;
96
+ inset: 0 !important;
97
+ z-index: 2147483647 !important;
98
+ display: flex !important;
99
+ align-items: flex-end !important;
100
+ justify-content: center !important;
101
+ background: rgba(0, 0, 0, 0.45) !important;
102
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
103
+ Roboto, "Helvetica Neue", sans-serif !important;
104
+ animation: ${CSS_PREFIX}-fade-in 160ms ease-out !important;
105
+ box-sizing: border-box !important;
106
+ padding: 16px !important;
107
+ }
108
+ @media (min-width: 480px) {
109
+ .${CSS_PREFIX}-overlay {
110
+ align-items: center !important;
111
+ }
112
+ }
113
+ @keyframes ${CSS_PREFIX}-fade-in {
114
+ from { opacity: 0; }
115
+ to { opacity: 1; }
116
+ }
117
+ .${CSS_PREFIX}-card {
118
+ background: #ffffff !important;
119
+ color: #0e1116 !important;
120
+ border-radius: 16px !important;
121
+ max-width: 420px !important;
122
+ width: 100% !important;
123
+ padding: 20px 20px 16px !important;
124
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.18) !important;
125
+ box-sizing: border-box !important;
126
+ }
127
+ .${CSS_PREFIX}-title {
128
+ font-size: 17px !important;
129
+ font-weight: 600 !important;
130
+ margin: 0 0 12px !important;
131
+ line-height: 1.3 !important;
132
+ }
133
+ .${CSS_PREFIX}-body {
134
+ font-size: 14px !important;
135
+ line-height: 1.5 !important;
136
+ color: #4a5160 !important;
137
+ margin: 0 0 8px !important;
138
+ }
139
+ .${CSS_PREFIX}-actions {
140
+ display: flex !important;
141
+ justify-content: flex-end !important;
142
+ gap: 8px !important;
143
+ margin-top: 16px !important;
144
+ }
145
+ .${CSS_PREFIX}-btn {
146
+ font-family: inherit !important;
147
+ font-size: 14px !important;
148
+ font-weight: 500 !important;
149
+ padding: 9px 16px !important;
150
+ border-radius: 10px !important;
151
+ border: none !important;
152
+ cursor: pointer !important;
153
+ line-height: 1 !important;
154
+ min-height: 36px !important;
155
+ box-sizing: border-box !important;
156
+ }
157
+ .${CSS_PREFIX}-btn-decline {
158
+ background: transparent !important;
159
+ color: #4a5160 !important;
160
+ }
161
+ .${CSS_PREFIX}-btn-decline:hover {
162
+ background: #f3f4f6 !important;
163
+ }
164
+ .${CSS_PREFIX}-btn-accept {
165
+ background: #0066ff !important;
166
+ color: #ffffff !important;
167
+ }
168
+ .${CSS_PREFIX}-btn-accept:hover {
169
+ background: #0055d4 !important;
170
+ }
171
+ @media (prefers-color-scheme: dark) {
172
+ .${CSS_PREFIX}-card {
173
+ background: #1c1f24 !important;
174
+ color: #e8eaed !important;
175
+ }
176
+ .${CSS_PREFIX}-body {
177
+ color: #b9bec7 !important;
178
+ }
179
+ .${CSS_PREFIX}-btn-decline {
180
+ color: #b9bec7 !important;
181
+ }
182
+ .${CSS_PREFIX}-btn-decline:hover {
183
+ background: #2a2e35 !important;
184
+ }
185
+ }
186
+ `;
187
+ document.head.appendChild(style);
188
+ }
189
+ function buildOverlay(copy, onDecision) {
190
+ const overlay = document.createElement("div");
191
+ overlay.className = `${CSS_PREFIX}-overlay`;
192
+ overlay.setAttribute("role", "dialog");
193
+ overlay.setAttribute("aria-modal", "true");
194
+ overlay.setAttribute("aria-labelledby", `${CSS_PREFIX}-title`);
195
+ const card = document.createElement("div");
196
+ card.className = `${CSS_PREFIX}-card`;
197
+ const title = document.createElement("h2");
198
+ title.id = `${CSS_PREFIX}-title`;
199
+ title.className = `${CSS_PREFIX}-title`;
200
+ title.textContent = copy.title;
201
+ card.appendChild(title);
202
+ for (const para of copy.body) {
203
+ const p = document.createElement("p");
204
+ p.className = `${CSS_PREFIX}-body`;
205
+ p.textContent = para;
206
+ card.appendChild(p);
207
+ }
208
+ const actions = document.createElement("div");
209
+ actions.className = `${CSS_PREFIX}-actions`;
210
+ const declineBtn = document.createElement("button");
211
+ declineBtn.type = "button";
212
+ declineBtn.className = `${CSS_PREFIX}-btn ${CSS_PREFIX}-btn-decline`;
213
+ declineBtn.textContent = copy.declineLabel;
214
+ declineBtn.addEventListener("click", () => onDecision("denied"));
215
+ const acceptBtn = document.createElement("button");
216
+ acceptBtn.type = "button";
217
+ acceptBtn.className = `${CSS_PREFIX}-btn ${CSS_PREFIX}-btn-accept`;
218
+ acceptBtn.textContent = copy.acceptLabel;
219
+ acceptBtn.addEventListener("click", () => onDecision("granted"));
220
+ actions.appendChild(declineBtn);
221
+ actions.appendChild(acceptBtn);
222
+ card.appendChild(actions);
223
+ overlay.appendChild(card);
224
+ requestAnimationFrame(() => {
225
+ try {
226
+ acceptBtn.focus();
227
+ } catch {
228
+ }
229
+ });
230
+ return overlay;
231
+ }
232
+
25
233
  // src/buffer.ts
26
234
  var RingBuffer = class {
27
235
  constructor(opts) {
@@ -1408,6 +1616,26 @@ function assertConfig(config) {
1408
1616
  async function enableRemoteDebug(options) {
1409
1617
  if (options.enabled === false) return NOOP_HANDLE;
1410
1618
  assertConfig(options.config);
1619
+ const consentInput = options.consent ?? {};
1620
+ const hasExplicitConsent = consentInput.userOptIn === true || typeof consentInput.hostToken === "string" && consentInput.hostToken.length > 0;
1621
+ const promptDisabled = consentInput.promptUser === false;
1622
+ const canPrompt = typeof document !== "undefined" && typeof window !== "undefined";
1623
+ const shouldPrompt = !hasExplicitConsent && !promptDisabled && canPrompt;
1624
+ if (shouldPrompt) {
1625
+ const cached = readConsentDecision(options.config.miniProgramId);
1626
+ let decision = cached;
1627
+ if (decision === null) {
1628
+ decision = await promptForConsent(
1629
+ options.config.miniProgramId,
1630
+ consentInput.promptCopy
1631
+ );
1632
+ }
1633
+ if (decision === "denied") return NOOP_HANDLE;
1634
+ options = {
1635
+ ...options,
1636
+ consent: { ...consentInput, userOptIn: true }
1637
+ };
1638
+ }
1411
1639
  assertConsent(options.config, options.consent);
1412
1640
  const onError = options.onError ?? (() => {
1413
1641
  });
@@ -1610,7 +1838,11 @@ exports.DEFAULT_MAX_BATCH_SIZE = DEFAULT_MAX_BATCH_SIZE;
1610
1838
  exports.DEFAULT_MAX_BODY_BYTES = DEFAULT_MAX_BODY_BYTES;
1611
1839
  exports.DEFAULT_REDACT_HEADERS = DEFAULT_REDACT_HEADERS;
1612
1840
  exports.SCHEMA_VERSION = SCHEMA_VERSION;
1841
+ exports.clearConsentDecision = clearConsentDecision;
1613
1842
  exports.enableRemoteDebug = enableRemoteDebug;
1614
1843
  exports.getBOT = getBOT;
1844
+ exports.promptForConsent = promptForConsent;
1845
+ exports.readConsentDecision = readConsentDecision;
1846
+ exports.writeConsentDecision = writeConsentDecision;
1615
1847
  //# sourceMappingURL=index.cjs.map
1616
1848
  //# sourceMappingURL=index.cjs.map