@botim/mp-debug-sdk 0.7.0 → 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 +8 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +22 -8
- package/dist/types.d.ts +22 -8
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/types.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAAO,IAAM,cAAA,GAAiB","file":"types.cjs","sourcesContent":["export const SCHEMA_VERSION = 2 as const;\n\nexport type EventLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\nexport type EventType =\n | 'console'\n | 'network'\n | 'error'\n | 'lifecycle'\n | 'bridge'\n | 'perf'\n | 'command-ack'\n | 'command-rejected';\n\nexport interface DebugEventBase {\n v: typeof SCHEMA_VERSION;\n sid: string;\n seq: number;\n ts: number;\n type: EventType;\n level: EventLevel;\n meta?: EventMeta;\n}\n\nexport interface EventMeta {\n route?: string;\n build?: string;\n appVersion?: string;\n deviceId?: string;\n userIdHash?: string;\n /** Set on command-ack / command-rejected events to correlate with the originating CommandRequest. */\n commandId?: string;\n /**\n * Set ONLY on synthesized rollup events emitted by the SDK's deduper.\n * Carries the count of suppressed identical events in the just-closed\n * window. Original (non-suppressed) events do NOT carry this field.\n */\n dedup?: {\n /** Stable signature used to bucket the suppressed events. */\n signature: string;\n /** Number of suppressed occurrences (excludes the leading event that was already emitted). */\n count: number;\n /** Timestamp of the first occurrence in the window. */\n firstTs: number;\n /** Timestamp of the last occurrence in the window. */\n lastTs: number;\n };\n}\n\nexport type SerializedValue =\n | { k: 'primitive'; v: string | number | boolean | null }\n | { k: 'undefined' }\n | { k: 'string'; v: string; truncated?: boolean }\n | { k: 'json'; v: string; truncated?: boolean }\n | { k: 'error'; name: string; message: string; stack?: string }\n | { k: 'circular' }\n | { k: 'function'; name?: string }\n | { k: 'unserializable'; reason: string };\n\nexport interface ConsolePayload {\n method: 'log' | 'info' | 'warn' | 'error' | 'debug';\n args: SerializedValue[];\n}\n\nexport type NetworkPhase = 'request' | 'response' | 'error';\n\nexport interface NetworkPayload {\n phase: NetworkPhase;\n reqId: string;\n method: string;\n url: string;\n status?: number;\n /** HTTP status text (e.g., \"OK\", \"Not Found\"). Lets the admin show \"200 OK\". */\n statusText?: string;\n durationMs?: number;\n reqHeaders?: Record<string, string>;\n reqBody?: string;\n reqBodyTruncated?: boolean;\n resHeaders?: Record<string, string>;\n resBody?: string;\n resBodyTruncated?: boolean;\n errorMessage?: string;\n errorName?: string;\n /** Stack trace for the failing call. Captured at the failure site so\n * operators can locate the originating code path. For fetch this is\n * `err.stack` from the rejected promise; for XHR (which doesn't carry\n * an Error object on `error`/`timeout`/`abort`) we synthesise a stack\n * from the call site of `xhr.send()`. */\n errorStack?: string;\n /** Flattened cause chain (`err.cause.cause...`) — undici-style fetch\n * failures frequently wrap the useful reason here (e.g. `ECONNREFUSED`\n * inside a generic `TypeError: fetch failed`). One line per cause. */\n errorCause?: string;\n}\n\nexport type ErrorSource = 'window.error' | 'unhandledrejection' | 'manual';\n\nexport interface ErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n source: ErrorSource;\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\nexport type LifecycleEvent =\n | 'launch'\n | 'pageShow'\n | 'pageHide'\n | 'route'\n | 'background'\n | 'foreground'\n | 'visibilityChange';\n\nexport interface LifecyclePayload {\n event: LifecycleEvent;\n data?: Record<string, unknown>;\n}\n\nexport interface BridgePayload {\n fn: string;\n argsPreview?: string;\n result?: 'ok' | 'error';\n durationMs?: number;\n errorMessage?: string;\n}\n\nexport interface PerfPayload {\n metric: string;\n value: number;\n unit?: string;\n}\n\n/** Payload for command-ack: the handler ran, here's its result. */\nexport interface CommandAckPayload {\n command: string;\n ok: boolean;\n /** Handler return value, JSON-serializable. */\n result?: unknown;\n durationMs?: number;\n}\n\n/** Payload for command-rejected: relay sent a command we won't run. */\nexport interface CommandRejectedPayload {\n command: string;\n reason:\n | 'unknown-command'\n | 'rate-limited'\n | 'invalid-args'\n | 'handler-threw'\n | 'overridden';\n details?: string;\n}\n\nexport type DebugEvent =\n | (DebugEventBase & { type: 'console'; payload: ConsolePayload })\n | (DebugEventBase & { type: 'network'; payload: NetworkPayload })\n | (DebugEventBase & { type: 'error'; payload: ErrorPayload })\n | (DebugEventBase & { type: 'lifecycle'; payload: LifecyclePayload })\n | (DebugEventBase & { type: 'bridge'; payload: BridgePayload })\n | (DebugEventBase & { type: 'perf'; payload: PerfPayload })\n | (DebugEventBase & { type: 'command-ack'; payload: CommandAckPayload })\n | (DebugEventBase & { type: 'command-rejected'; payload: CommandRejectedPayload });\n\nexport interface UploadBatch {\n sessionToken: string;\n events: DebugEvent[];\n}\n\nexport interface UploadAck {\n acceptedSeq: number;\n rejected?: Array<{ seq: number; reason: string }>;\n}\n\nexport type StreamFrame =\n | { kind: 'hello'; sid: string; devices: number }\n | { kind: 'event'; event: DebugEvent }\n | { kind: 'device-join'; deviceInfo: DeviceInfo }\n | { kind: 'device-leave'; deviceId: string }\n | { kind: 'gap'; deviceId: string; expectedSeq: number; gotSeq: number }\n | { kind: 'session-end'; reason: string };\n\nexport interface DeviceInfo {\n deviceId: string;\n platform: 'ios' | 'android' | 'web' | 'unknown';\n osVersion?: string;\n appName?: string;\n appVersion?: string;\n appBuild?: string;\n userAgent?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Build-time identity (resolved by the Vite plugin from `botim.{env}.json`)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type BotimEnv = 'dev' | 'uat' | 'beta' | 'prod';\n\n/**\n * The contents of `botim.{env}.json`, resolved at build time and injected\n * into the bundle via the virtual module `virtual:botim/config`.\n */\nexport interface BotimConfig {\n miniProgramId: string;\n env: BotimEnv;\n /** Short hash of the resolved config or the build, set by the plugin. */\n buildSignature: string;\n appName?: string;\n appVersion?: string;\n /** Forward-compatible bag for fields the relay may add later. */\n [extra: string]: unknown;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Consent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ConsentInput {\n /** JWT issued by the BOTIM host runtime — strongest form. */\n hostToken?: string;\n /** Explicit user opt-in (e.g., set after a privacy dialog). */\n userOptIn?: boolean;\n /**\n * If true, the SDK shows an in-app consent modal on first load and\n * persists the user's choice in `localStorage` (key\n * `__botim_debug_consent_<mpId>`). On subsequent loads the cached\n * decision is honored without re-prompting. The modal sets\n * `userOptIn: true` if the user accepts, or short-circuits attach if\n * they decline. Mutually orthogonal to `hostToken`/`userOptIn`: those\n * still take effect if explicitly set.\n *\n * No-op when `document` / `window` are unavailable (non-DOM runtimes).\n */\n promptUser?: boolean;\n /**\n * Optional copy override for the consent modal. Keys map to the\n * default English strings; provide any subset to localize.\n */\n promptCopy?: ConsentPromptCopy;\n}\n\nexport interface ConsentPromptCopy {\n /** Short headline. Default: \"Enable BOTIM debug logging?\" */\n title?: string;\n /**\n * Body paragraphs (rendered as separate <p> elements). Default\n * explains what data is collected and that the user can revoke.\n */\n body?: string[];\n /** Default: \"Allow\" */\n acceptLabel?: string;\n /** Default: \"Skip\" */\n declineLabel?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — attach\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AttachRequest {\n miniProgramId: string;\n env: BotimEnv;\n buildSignature: string;\n deviceInfo: DeviceInfo;\n consent: ConsentInput;\n schemaVersion: typeof SCHEMA_VERSION;\n}\n\nexport interface AttachResponse {\n sid: string;\n deviceToken: string;\n expiresAt: number;\n ingestUrl: string;\n /** Endpoint the device long-polls for AI-issued commands. */\n commandPollUrl: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — commands (relay → device → ack)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CommandRequest {\n /** Relay-issued unique id, echoed in the resulting ack/rejected event. */\n id: string;\n /** Registered command name (kebab-case). */\n name: string;\n /** Arbitrary JSON args, validated by the handler. */\n args?: Record<string, unknown>;\n /** Optional deadline; handlers SHOULD respect it. */\n deadlineTs?: number;\n}\n\n/** Long-poll response shape from `commandPollUrl`. */\nexport interface CommandPollResponse {\n commands: CommandRequest[];\n /** Suggested seconds until the next poll. */\n nextDelayMs?: number;\n}\n\nexport type CommandHandler = (\n args: Record<string, unknown>,\n ctx: CommandContext,\n) => unknown | Promise<unknown>;\n\nexport interface CommandContext {\n commandId: string;\n command: string;\n signal: AbortSignal;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";;;AAAO,IAAM,cAAA,GAAiB","file":"types.cjs","sourcesContent":["export const SCHEMA_VERSION = 2 as const;\n\nexport type EventLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\nexport type EventType =\n | 'console'\n | 'network'\n | 'error'\n | 'lifecycle'\n | 'bridge'\n | 'perf'\n | 'command-ack'\n | 'command-rejected';\n\nexport interface DebugEventBase {\n v: typeof SCHEMA_VERSION;\n sid: string;\n seq: number;\n ts: number;\n type: EventType;\n level: EventLevel;\n meta?: EventMeta;\n}\n\nexport interface EventMeta {\n route?: string;\n build?: string;\n appVersion?: string;\n deviceId?: string;\n userIdHash?: string;\n /** Set on command-ack / command-rejected events to correlate with the originating CommandRequest. */\n commandId?: string;\n /**\n * Set ONLY on synthesized rollup events emitted by the SDK's deduper.\n * Carries the count of suppressed identical events in the just-closed\n * window. Original (non-suppressed) events do NOT carry this field.\n */\n dedup?: {\n /** Stable signature used to bucket the suppressed events. */\n signature: string;\n /** Number of suppressed occurrences (excludes the leading event that was already emitted). */\n count: number;\n /** Timestamp of the first occurrence in the window. */\n firstTs: number;\n /** Timestamp of the last occurrence in the window. */\n lastTs: number;\n };\n}\n\nexport type SerializedValue =\n | { k: 'primitive'; v: string | number | boolean | null }\n | { k: 'undefined' }\n | { k: 'string'; v: string; truncated?: boolean }\n | { k: 'json'; v: string; truncated?: boolean }\n | { k: 'error'; name: string; message: string; stack?: string }\n | { k: 'circular' }\n | { k: 'function'; name?: string }\n | { k: 'unserializable'; reason: string };\n\nexport interface ConsolePayload {\n method: 'log' | 'info' | 'warn' | 'error' | 'debug';\n args: SerializedValue[];\n}\n\nexport type NetworkPhase = 'request' | 'response' | 'error';\n\nexport interface NetworkPayload {\n phase: NetworkPhase;\n reqId: string;\n method: string;\n url: string;\n status?: number;\n /** HTTP status text (e.g., \"OK\", \"Not Found\"). Lets the admin show \"200 OK\". */\n statusText?: string;\n durationMs?: number;\n reqHeaders?: Record<string, string>;\n reqBody?: string;\n reqBodyTruncated?: boolean;\n resHeaders?: Record<string, string>;\n resBody?: string;\n resBodyTruncated?: boolean;\n errorMessage?: string;\n errorName?: string;\n /** Stack trace for the failing call. Captured at the failure site so\n * operators can locate the originating code path. For fetch this is\n * `err.stack` from the rejected promise; for XHR (which doesn't carry\n * an Error object on `error`/`timeout`/`abort`) we synthesise a stack\n * from the call site of `xhr.send()`. */\n errorStack?: string;\n /** Flattened cause chain (`err.cause.cause...`) — undici-style fetch\n * failures frequently wrap the useful reason here (e.g. `ECONNREFUSED`\n * inside a generic `TypeError: fetch failed`). One line per cause. */\n errorCause?: string;\n}\n\nexport type ErrorSource = 'window.error' | 'unhandledrejection' | 'manual';\n\nexport interface ErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n source: ErrorSource;\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\nexport type LifecycleEvent =\n | 'launch'\n | 'pageShow'\n | 'pageHide'\n | 'route'\n | 'background'\n | 'foreground'\n | 'visibilityChange';\n\nexport interface LifecyclePayload {\n event: LifecycleEvent;\n data?: Record<string, unknown>;\n}\n\nexport interface BridgePayload {\n fn: string;\n argsPreview?: string;\n result?: 'ok' | 'error';\n durationMs?: number;\n errorMessage?: string;\n}\n\nexport interface PerfPayload {\n metric: string;\n value: number;\n unit?: string;\n}\n\n/** Payload for command-ack: the handler ran, here's its result. */\nexport interface CommandAckPayload {\n command: string;\n ok: boolean;\n /** Handler return value, JSON-serializable. */\n result?: unknown;\n durationMs?: number;\n}\n\n/** Payload for command-rejected: relay sent a command we won't run. */\nexport interface CommandRejectedPayload {\n command: string;\n reason:\n | 'unknown-command'\n | 'rate-limited'\n | 'invalid-args'\n | 'handler-threw'\n | 'overridden';\n details?: string;\n}\n\nexport type DebugEvent =\n | (DebugEventBase & { type: 'console'; payload: ConsolePayload })\n | (DebugEventBase & { type: 'network'; payload: NetworkPayload })\n | (DebugEventBase & { type: 'error'; payload: ErrorPayload })\n | (DebugEventBase & { type: 'lifecycle'; payload: LifecyclePayload })\n | (DebugEventBase & { type: 'bridge'; payload: BridgePayload })\n | (DebugEventBase & { type: 'perf'; payload: PerfPayload })\n | (DebugEventBase & { type: 'command-ack'; payload: CommandAckPayload })\n | (DebugEventBase & { type: 'command-rejected'; payload: CommandRejectedPayload });\n\nexport interface UploadBatch {\n sessionToken: string;\n events: DebugEvent[];\n}\n\nexport interface UploadAck {\n acceptedSeq: number;\n rejected?: Array<{ seq: number; reason: string }>;\n}\n\nexport type StreamFrame =\n | { kind: 'hello'; sid: string; devices: number }\n | { kind: 'event'; event: DebugEvent }\n | { kind: 'device-join'; deviceInfo: DeviceInfo }\n | { kind: 'device-leave'; deviceId: string }\n | { kind: 'gap'; deviceId: string; expectedSeq: number; gotSeq: number }\n | { kind: 'session-end'; reason: string };\n\nexport interface DeviceInfo {\n deviceId: string;\n platform: 'ios' | 'android' | 'web' | 'unknown';\n osVersion?: string;\n appName?: string;\n appVersion?: string;\n appBuild?: string;\n userAgent?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Build-time identity (resolved by the Vite plugin from `botim.{env}.json`)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type BotimEnv = 'dev' | 'uat' | 'beta' | 'prod';\n\n/**\n * The contents of `botim.{env}.json`, resolved at build time and injected\n * into the bundle via the virtual module `virtual:botim/config`.\n */\nexport interface BotimConfig {\n miniProgramId: string;\n env: BotimEnv;\n /** Short hash of the resolved config or the build, set by the plugin. */\n buildSignature: string;\n appName?: string;\n appVersion?: string;\n /** Forward-compatible bag for fields the relay may add later. */\n [extra: string]: unknown;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Consent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ConsentInput {\n /** JWT issued by the BOTIM host runtime — strongest form. */\n hostToken?: string;\n /** Explicit user opt-in (e.g., set after a privacy dialog). */\n userOptIn?: boolean;\n /**\n * Controls whether the SDK shows its built-in consent modal.\n *\n * Default behavior (as of 0.7.1) is to **show the modal automatically**\n * whenever:\n * • the host did NOT provide `userOptIn: true` or a `hostToken`, AND\n * • a DOM is available (`document` and `window` exist).\n *\n * The user's decision is persisted in `localStorage` under the key\n * `__botim_debug_consent_<mpId>` so subsequent loads attach silently\n * (or stay disabled, if declined) without re-prompting.\n *\n * Set to `false` to suppress the modal — useful for automated test\n * harnesses, hosts that wire their own consent UX upstream, or any\n * environment where a blocking dialog is undesirable. When suppressed,\n * `assertConsent` runs as before: it requires `userOptIn` or\n * `hostToken` in `prod`, and is a no-op in non-prod envs.\n *\n * Set to `true` for symmetry / explicitness — same effect as the\n * default when no other consent signal is present.\n *\n * Has no effect in non-DOM runtimes (native bridges, SSR) since the\n * modal can't be rendered there. Provide `userOptIn`/`hostToken` for\n * those.\n */\n promptUser?: boolean;\n /**\n * Optional copy override for the consent modal. Keys map to the\n * default English strings; provide any subset to localize.\n */\n promptCopy?: ConsentPromptCopy;\n}\n\nexport interface ConsentPromptCopy {\n /** Short headline. Default: \"Enable BOTIM debug logging?\" */\n title?: string;\n /**\n * Body paragraphs (rendered as separate <p> elements). Default\n * explains what data is collected and that the user can revoke.\n */\n body?: string[];\n /** Default: \"Allow\" */\n acceptLabel?: string;\n /** Default: \"Skip\" */\n declineLabel?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — attach\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AttachRequest {\n miniProgramId: string;\n env: BotimEnv;\n buildSignature: string;\n deviceInfo: DeviceInfo;\n consent: ConsentInput;\n schemaVersion: typeof SCHEMA_VERSION;\n}\n\nexport interface AttachResponse {\n sid: string;\n deviceToken: string;\n expiresAt: number;\n ingestUrl: string;\n /** Endpoint the device long-polls for AI-issued commands. */\n commandPollUrl: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — commands (relay → device → ack)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CommandRequest {\n /** Relay-issued unique id, echoed in the resulting ack/rejected event. */\n id: string;\n /** Registered command name (kebab-case). */\n name: string;\n /** Arbitrary JSON args, validated by the handler. */\n args?: Record<string, unknown>;\n /** Optional deadline; handlers SHOULD respect it. */\n deadlineTs?: number;\n}\n\n/** Long-poll response shape from `commandPollUrl`. */\nexport interface CommandPollResponse {\n commands: CommandRequest[];\n /** Suggested seconds until the next poll. */\n nextDelayMs?: number;\n}\n\nexport type CommandHandler = (\n args: Record<string, unknown>,\n ctx: CommandContext,\n) => unknown | Promise<unknown>;\n\nexport interface CommandContext {\n commandId: string;\n command: string;\n signal: AbortSignal;\n}\n"]}
|
package/dist/types.d.cts
CHANGED
|
@@ -223,15 +223,29 @@ interface ConsentInput {
|
|
|
223
223
|
/** Explicit user opt-in (e.g., set after a privacy dialog). */
|
|
224
224
|
userOptIn?: boolean;
|
|
225
225
|
/**
|
|
226
|
-
*
|
|
227
|
-
* persists the user's choice in `localStorage` (key
|
|
228
|
-
* `__botim_debug_consent_<mpId>`). On subsequent loads the cached
|
|
229
|
-
* decision is honored without re-prompting. The modal sets
|
|
230
|
-
* `userOptIn: true` if the user accepts, or short-circuits attach if
|
|
231
|
-
* they decline. Mutually orthogonal to `hostToken`/`userOptIn`: those
|
|
232
|
-
* still take effect if explicitly set.
|
|
226
|
+
* Controls whether the SDK shows its built-in consent modal.
|
|
233
227
|
*
|
|
234
|
-
*
|
|
228
|
+
* Default behavior (as of 0.7.1) is to **show the modal automatically**
|
|
229
|
+
* whenever:
|
|
230
|
+
* • the host did NOT provide `userOptIn: true` or a `hostToken`, AND
|
|
231
|
+
* • a DOM is available (`document` and `window` exist).
|
|
232
|
+
*
|
|
233
|
+
* The user's decision is persisted in `localStorage` under the key
|
|
234
|
+
* `__botim_debug_consent_<mpId>` so subsequent loads attach silently
|
|
235
|
+
* (or stay disabled, if declined) without re-prompting.
|
|
236
|
+
*
|
|
237
|
+
* Set to `false` to suppress the modal — useful for automated test
|
|
238
|
+
* harnesses, hosts that wire their own consent UX upstream, or any
|
|
239
|
+
* environment where a blocking dialog is undesirable. When suppressed,
|
|
240
|
+
* `assertConsent` runs as before: it requires `userOptIn` or
|
|
241
|
+
* `hostToken` in `prod`, and is a no-op in non-prod envs.
|
|
242
|
+
*
|
|
243
|
+
* Set to `true` for symmetry / explicitness — same effect as the
|
|
244
|
+
* default when no other consent signal is present.
|
|
245
|
+
*
|
|
246
|
+
* Has no effect in non-DOM runtimes (native bridges, SSR) since the
|
|
247
|
+
* modal can't be rendered there. Provide `userOptIn`/`hostToken` for
|
|
248
|
+
* those.
|
|
235
249
|
*/
|
|
236
250
|
promptUser?: boolean;
|
|
237
251
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -223,15 +223,29 @@ interface ConsentInput {
|
|
|
223
223
|
/** Explicit user opt-in (e.g., set after a privacy dialog). */
|
|
224
224
|
userOptIn?: boolean;
|
|
225
225
|
/**
|
|
226
|
-
*
|
|
227
|
-
* persists the user's choice in `localStorage` (key
|
|
228
|
-
* `__botim_debug_consent_<mpId>`). On subsequent loads the cached
|
|
229
|
-
* decision is honored without re-prompting. The modal sets
|
|
230
|
-
* `userOptIn: true` if the user accepts, or short-circuits attach if
|
|
231
|
-
* they decline. Mutually orthogonal to `hostToken`/`userOptIn`: those
|
|
232
|
-
* still take effect if explicitly set.
|
|
226
|
+
* Controls whether the SDK shows its built-in consent modal.
|
|
233
227
|
*
|
|
234
|
-
*
|
|
228
|
+
* Default behavior (as of 0.7.1) is to **show the modal automatically**
|
|
229
|
+
* whenever:
|
|
230
|
+
* • the host did NOT provide `userOptIn: true` or a `hostToken`, AND
|
|
231
|
+
* • a DOM is available (`document` and `window` exist).
|
|
232
|
+
*
|
|
233
|
+
* The user's decision is persisted in `localStorage` under the key
|
|
234
|
+
* `__botim_debug_consent_<mpId>` so subsequent loads attach silently
|
|
235
|
+
* (or stay disabled, if declined) without re-prompting.
|
|
236
|
+
*
|
|
237
|
+
* Set to `false` to suppress the modal — useful for automated test
|
|
238
|
+
* harnesses, hosts that wire their own consent UX upstream, or any
|
|
239
|
+
* environment where a blocking dialog is undesirable. When suppressed,
|
|
240
|
+
* `assertConsent` runs as before: it requires `userOptIn` or
|
|
241
|
+
* `hostToken` in `prod`, and is a no-op in non-prod envs.
|
|
242
|
+
*
|
|
243
|
+
* Set to `true` for symmetry / explicitness — same effect as the
|
|
244
|
+
* default when no other consent signal is present.
|
|
245
|
+
*
|
|
246
|
+
* Has no effect in non-DOM runtimes (native bridges, SSR) since the
|
|
247
|
+
* modal can't be rendered there. Provide `userOptIn`/`hostToken` for
|
|
248
|
+
* those.
|
|
235
249
|
*/
|
|
236
250
|
promptUser?: boolean;
|
|
237
251
|
/**
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAAO,IAAM,cAAA,GAAiB","file":"types.js","sourcesContent":["export const SCHEMA_VERSION = 2 as const;\n\nexport type EventLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\nexport type EventType =\n | 'console'\n | 'network'\n | 'error'\n | 'lifecycle'\n | 'bridge'\n | 'perf'\n | 'command-ack'\n | 'command-rejected';\n\nexport interface DebugEventBase {\n v: typeof SCHEMA_VERSION;\n sid: string;\n seq: number;\n ts: number;\n type: EventType;\n level: EventLevel;\n meta?: EventMeta;\n}\n\nexport interface EventMeta {\n route?: string;\n build?: string;\n appVersion?: string;\n deviceId?: string;\n userIdHash?: string;\n /** Set on command-ack / command-rejected events to correlate with the originating CommandRequest. */\n commandId?: string;\n /**\n * Set ONLY on synthesized rollup events emitted by the SDK's deduper.\n * Carries the count of suppressed identical events in the just-closed\n * window. Original (non-suppressed) events do NOT carry this field.\n */\n dedup?: {\n /** Stable signature used to bucket the suppressed events. */\n signature: string;\n /** Number of suppressed occurrences (excludes the leading event that was already emitted). */\n count: number;\n /** Timestamp of the first occurrence in the window. */\n firstTs: number;\n /** Timestamp of the last occurrence in the window. */\n lastTs: number;\n };\n}\n\nexport type SerializedValue =\n | { k: 'primitive'; v: string | number | boolean | null }\n | { k: 'undefined' }\n | { k: 'string'; v: string; truncated?: boolean }\n | { k: 'json'; v: string; truncated?: boolean }\n | { k: 'error'; name: string; message: string; stack?: string }\n | { k: 'circular' }\n | { k: 'function'; name?: string }\n | { k: 'unserializable'; reason: string };\n\nexport interface ConsolePayload {\n method: 'log' | 'info' | 'warn' | 'error' | 'debug';\n args: SerializedValue[];\n}\n\nexport type NetworkPhase = 'request' | 'response' | 'error';\n\nexport interface NetworkPayload {\n phase: NetworkPhase;\n reqId: string;\n method: string;\n url: string;\n status?: number;\n /** HTTP status text (e.g., \"OK\", \"Not Found\"). Lets the admin show \"200 OK\". */\n statusText?: string;\n durationMs?: number;\n reqHeaders?: Record<string, string>;\n reqBody?: string;\n reqBodyTruncated?: boolean;\n resHeaders?: Record<string, string>;\n resBody?: string;\n resBodyTruncated?: boolean;\n errorMessage?: string;\n errorName?: string;\n /** Stack trace for the failing call. Captured at the failure site so\n * operators can locate the originating code path. For fetch this is\n * `err.stack` from the rejected promise; for XHR (which doesn't carry\n * an Error object on `error`/`timeout`/`abort`) we synthesise a stack\n * from the call site of `xhr.send()`. */\n errorStack?: string;\n /** Flattened cause chain (`err.cause.cause...`) — undici-style fetch\n * failures frequently wrap the useful reason here (e.g. `ECONNREFUSED`\n * inside a generic `TypeError: fetch failed`). One line per cause. */\n errorCause?: string;\n}\n\nexport type ErrorSource = 'window.error' | 'unhandledrejection' | 'manual';\n\nexport interface ErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n source: ErrorSource;\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\nexport type LifecycleEvent =\n | 'launch'\n | 'pageShow'\n | 'pageHide'\n | 'route'\n | 'background'\n | 'foreground'\n | 'visibilityChange';\n\nexport interface LifecyclePayload {\n event: LifecycleEvent;\n data?: Record<string, unknown>;\n}\n\nexport interface BridgePayload {\n fn: string;\n argsPreview?: string;\n result?: 'ok' | 'error';\n durationMs?: number;\n errorMessage?: string;\n}\n\nexport interface PerfPayload {\n metric: string;\n value: number;\n unit?: string;\n}\n\n/** Payload for command-ack: the handler ran, here's its result. */\nexport interface CommandAckPayload {\n command: string;\n ok: boolean;\n /** Handler return value, JSON-serializable. */\n result?: unknown;\n durationMs?: number;\n}\n\n/** Payload for command-rejected: relay sent a command we won't run. */\nexport interface CommandRejectedPayload {\n command: string;\n reason:\n | 'unknown-command'\n | 'rate-limited'\n | 'invalid-args'\n | 'handler-threw'\n | 'overridden';\n details?: string;\n}\n\nexport type DebugEvent =\n | (DebugEventBase & { type: 'console'; payload: ConsolePayload })\n | (DebugEventBase & { type: 'network'; payload: NetworkPayload })\n | (DebugEventBase & { type: 'error'; payload: ErrorPayload })\n | (DebugEventBase & { type: 'lifecycle'; payload: LifecyclePayload })\n | (DebugEventBase & { type: 'bridge'; payload: BridgePayload })\n | (DebugEventBase & { type: 'perf'; payload: PerfPayload })\n | (DebugEventBase & { type: 'command-ack'; payload: CommandAckPayload })\n | (DebugEventBase & { type: 'command-rejected'; payload: CommandRejectedPayload });\n\nexport interface UploadBatch {\n sessionToken: string;\n events: DebugEvent[];\n}\n\nexport interface UploadAck {\n acceptedSeq: number;\n rejected?: Array<{ seq: number; reason: string }>;\n}\n\nexport type StreamFrame =\n | { kind: 'hello'; sid: string; devices: number }\n | { kind: 'event'; event: DebugEvent }\n | { kind: 'device-join'; deviceInfo: DeviceInfo }\n | { kind: 'device-leave'; deviceId: string }\n | { kind: 'gap'; deviceId: string; expectedSeq: number; gotSeq: number }\n | { kind: 'session-end'; reason: string };\n\nexport interface DeviceInfo {\n deviceId: string;\n platform: 'ios' | 'android' | 'web' | 'unknown';\n osVersion?: string;\n appName?: string;\n appVersion?: string;\n appBuild?: string;\n userAgent?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Build-time identity (resolved by the Vite plugin from `botim.{env}.json`)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type BotimEnv = 'dev' | 'uat' | 'beta' | 'prod';\n\n/**\n * The contents of `botim.{env}.json`, resolved at build time and injected\n * into the bundle via the virtual module `virtual:botim/config`.\n */\nexport interface BotimConfig {\n miniProgramId: string;\n env: BotimEnv;\n /** Short hash of the resolved config or the build, set by the plugin. */\n buildSignature: string;\n appName?: string;\n appVersion?: string;\n /** Forward-compatible bag for fields the relay may add later. */\n [extra: string]: unknown;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Consent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ConsentInput {\n /** JWT issued by the BOTIM host runtime — strongest form. */\n hostToken?: string;\n /** Explicit user opt-in (e.g., set after a privacy dialog). */\n userOptIn?: boolean;\n /**\n * If true, the SDK shows an in-app consent modal on first load and\n * persists the user's choice in `localStorage` (key\n * `__botim_debug_consent_<mpId>`). On subsequent loads the cached\n * decision is honored without re-prompting. The modal sets\n * `userOptIn: true` if the user accepts, or short-circuits attach if\n * they decline. Mutually orthogonal to `hostToken`/`userOptIn`: those\n * still take effect if explicitly set.\n *\n * No-op when `document` / `window` are unavailable (non-DOM runtimes).\n */\n promptUser?: boolean;\n /**\n * Optional copy override for the consent modal. Keys map to the\n * default English strings; provide any subset to localize.\n */\n promptCopy?: ConsentPromptCopy;\n}\n\nexport interface ConsentPromptCopy {\n /** Short headline. Default: \"Enable BOTIM debug logging?\" */\n title?: string;\n /**\n * Body paragraphs (rendered as separate <p> elements). Default\n * explains what data is collected and that the user can revoke.\n */\n body?: string[];\n /** Default: \"Allow\" */\n acceptLabel?: string;\n /** Default: \"Skip\" */\n declineLabel?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — attach\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AttachRequest {\n miniProgramId: string;\n env: BotimEnv;\n buildSignature: string;\n deviceInfo: DeviceInfo;\n consent: ConsentInput;\n schemaVersion: typeof SCHEMA_VERSION;\n}\n\nexport interface AttachResponse {\n sid: string;\n deviceToken: string;\n expiresAt: number;\n ingestUrl: string;\n /** Endpoint the device long-polls for AI-issued commands. */\n commandPollUrl: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — commands (relay → device → ack)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CommandRequest {\n /** Relay-issued unique id, echoed in the resulting ack/rejected event. */\n id: string;\n /** Registered command name (kebab-case). */\n name: string;\n /** Arbitrary JSON args, validated by the handler. */\n args?: Record<string, unknown>;\n /** Optional deadline; handlers SHOULD respect it. */\n deadlineTs?: number;\n}\n\n/** Long-poll response shape from `commandPollUrl`. */\nexport interface CommandPollResponse {\n commands: CommandRequest[];\n /** Suggested seconds until the next poll. */\n nextDelayMs?: number;\n}\n\nexport type CommandHandler = (\n args: Record<string, unknown>,\n ctx: CommandContext,\n) => unknown | Promise<unknown>;\n\nexport interface CommandContext {\n commandId: string;\n command: string;\n signal: AbortSignal;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AAAO,IAAM,cAAA,GAAiB","file":"types.js","sourcesContent":["export const SCHEMA_VERSION = 2 as const;\n\nexport type EventLevel = 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\nexport type EventType =\n | 'console'\n | 'network'\n | 'error'\n | 'lifecycle'\n | 'bridge'\n | 'perf'\n | 'command-ack'\n | 'command-rejected';\n\nexport interface DebugEventBase {\n v: typeof SCHEMA_VERSION;\n sid: string;\n seq: number;\n ts: number;\n type: EventType;\n level: EventLevel;\n meta?: EventMeta;\n}\n\nexport interface EventMeta {\n route?: string;\n build?: string;\n appVersion?: string;\n deviceId?: string;\n userIdHash?: string;\n /** Set on command-ack / command-rejected events to correlate with the originating CommandRequest. */\n commandId?: string;\n /**\n * Set ONLY on synthesized rollup events emitted by the SDK's deduper.\n * Carries the count of suppressed identical events in the just-closed\n * window. Original (non-suppressed) events do NOT carry this field.\n */\n dedup?: {\n /** Stable signature used to bucket the suppressed events. */\n signature: string;\n /** Number of suppressed occurrences (excludes the leading event that was already emitted). */\n count: number;\n /** Timestamp of the first occurrence in the window. */\n firstTs: number;\n /** Timestamp of the last occurrence in the window. */\n lastTs: number;\n };\n}\n\nexport type SerializedValue =\n | { k: 'primitive'; v: string | number | boolean | null }\n | { k: 'undefined' }\n | { k: 'string'; v: string; truncated?: boolean }\n | { k: 'json'; v: string; truncated?: boolean }\n | { k: 'error'; name: string; message: string; stack?: string }\n | { k: 'circular' }\n | { k: 'function'; name?: string }\n | { k: 'unserializable'; reason: string };\n\nexport interface ConsolePayload {\n method: 'log' | 'info' | 'warn' | 'error' | 'debug';\n args: SerializedValue[];\n}\n\nexport type NetworkPhase = 'request' | 'response' | 'error';\n\nexport interface NetworkPayload {\n phase: NetworkPhase;\n reqId: string;\n method: string;\n url: string;\n status?: number;\n /** HTTP status text (e.g., \"OK\", \"Not Found\"). Lets the admin show \"200 OK\". */\n statusText?: string;\n durationMs?: number;\n reqHeaders?: Record<string, string>;\n reqBody?: string;\n reqBodyTruncated?: boolean;\n resHeaders?: Record<string, string>;\n resBody?: string;\n resBodyTruncated?: boolean;\n errorMessage?: string;\n errorName?: string;\n /** Stack trace for the failing call. Captured at the failure site so\n * operators can locate the originating code path. For fetch this is\n * `err.stack` from the rejected promise; for XHR (which doesn't carry\n * an Error object on `error`/`timeout`/`abort`) we synthesise a stack\n * from the call site of `xhr.send()`. */\n errorStack?: string;\n /** Flattened cause chain (`err.cause.cause...`) — undici-style fetch\n * failures frequently wrap the useful reason here (e.g. `ECONNREFUSED`\n * inside a generic `TypeError: fetch failed`). One line per cause. */\n errorCause?: string;\n}\n\nexport type ErrorSource = 'window.error' | 'unhandledrejection' | 'manual';\n\nexport interface ErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n source: ErrorSource;\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\nexport type LifecycleEvent =\n | 'launch'\n | 'pageShow'\n | 'pageHide'\n | 'route'\n | 'background'\n | 'foreground'\n | 'visibilityChange';\n\nexport interface LifecyclePayload {\n event: LifecycleEvent;\n data?: Record<string, unknown>;\n}\n\nexport interface BridgePayload {\n fn: string;\n argsPreview?: string;\n result?: 'ok' | 'error';\n durationMs?: number;\n errorMessage?: string;\n}\n\nexport interface PerfPayload {\n metric: string;\n value: number;\n unit?: string;\n}\n\n/** Payload for command-ack: the handler ran, here's its result. */\nexport interface CommandAckPayload {\n command: string;\n ok: boolean;\n /** Handler return value, JSON-serializable. */\n result?: unknown;\n durationMs?: number;\n}\n\n/** Payload for command-rejected: relay sent a command we won't run. */\nexport interface CommandRejectedPayload {\n command: string;\n reason:\n | 'unknown-command'\n | 'rate-limited'\n | 'invalid-args'\n | 'handler-threw'\n | 'overridden';\n details?: string;\n}\n\nexport type DebugEvent =\n | (DebugEventBase & { type: 'console'; payload: ConsolePayload })\n | (DebugEventBase & { type: 'network'; payload: NetworkPayload })\n | (DebugEventBase & { type: 'error'; payload: ErrorPayload })\n | (DebugEventBase & { type: 'lifecycle'; payload: LifecyclePayload })\n | (DebugEventBase & { type: 'bridge'; payload: BridgePayload })\n | (DebugEventBase & { type: 'perf'; payload: PerfPayload })\n | (DebugEventBase & { type: 'command-ack'; payload: CommandAckPayload })\n | (DebugEventBase & { type: 'command-rejected'; payload: CommandRejectedPayload });\n\nexport interface UploadBatch {\n sessionToken: string;\n events: DebugEvent[];\n}\n\nexport interface UploadAck {\n acceptedSeq: number;\n rejected?: Array<{ seq: number; reason: string }>;\n}\n\nexport type StreamFrame =\n | { kind: 'hello'; sid: string; devices: number }\n | { kind: 'event'; event: DebugEvent }\n | { kind: 'device-join'; deviceInfo: DeviceInfo }\n | { kind: 'device-leave'; deviceId: string }\n | { kind: 'gap'; deviceId: string; expectedSeq: number; gotSeq: number }\n | { kind: 'session-end'; reason: string };\n\nexport interface DeviceInfo {\n deviceId: string;\n platform: 'ios' | 'android' | 'web' | 'unknown';\n osVersion?: string;\n appName?: string;\n appVersion?: string;\n appBuild?: string;\n userAgent?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Build-time identity (resolved by the Vite plugin from `botim.{env}.json`)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type BotimEnv = 'dev' | 'uat' | 'beta' | 'prod';\n\n/**\n * The contents of `botim.{env}.json`, resolved at build time and injected\n * into the bundle via the virtual module `virtual:botim/config`.\n */\nexport interface BotimConfig {\n miniProgramId: string;\n env: BotimEnv;\n /** Short hash of the resolved config or the build, set by the plugin. */\n buildSignature: string;\n appName?: string;\n appVersion?: string;\n /** Forward-compatible bag for fields the relay may add later. */\n [extra: string]: unknown;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Consent\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ConsentInput {\n /** JWT issued by the BOTIM host runtime — strongest form. */\n hostToken?: string;\n /** Explicit user opt-in (e.g., set after a privacy dialog). */\n userOptIn?: boolean;\n /**\n * Controls whether the SDK shows its built-in consent modal.\n *\n * Default behavior (as of 0.7.1) is to **show the modal automatically**\n * whenever:\n * • the host did NOT provide `userOptIn: true` or a `hostToken`, AND\n * • a DOM is available (`document` and `window` exist).\n *\n * The user's decision is persisted in `localStorage` under the key\n * `__botim_debug_consent_<mpId>` so subsequent loads attach silently\n * (or stay disabled, if declined) without re-prompting.\n *\n * Set to `false` to suppress the modal — useful for automated test\n * harnesses, hosts that wire their own consent UX upstream, or any\n * environment where a blocking dialog is undesirable. When suppressed,\n * `assertConsent` runs as before: it requires `userOptIn` or\n * `hostToken` in `prod`, and is a no-op in non-prod envs.\n *\n * Set to `true` for symmetry / explicitness — same effect as the\n * default when no other consent signal is present.\n *\n * Has no effect in non-DOM runtimes (native bridges, SSR) since the\n * modal can't be rendered there. Provide `userOptIn`/`hostToken` for\n * those.\n */\n promptUser?: boolean;\n /**\n * Optional copy override for the consent modal. Keys map to the\n * default English strings; provide any subset to localize.\n */\n promptCopy?: ConsentPromptCopy;\n}\n\nexport interface ConsentPromptCopy {\n /** Short headline. Default: \"Enable BOTIM debug logging?\" */\n title?: string;\n /**\n * Body paragraphs (rendered as separate <p> elements). Default\n * explains what data is collected and that the user can revoke.\n */\n body?: string[];\n /** Default: \"Allow\" */\n acceptLabel?: string;\n /** Default: \"Skip\" */\n declineLabel?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — attach\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AttachRequest {\n miniProgramId: string;\n env: BotimEnv;\n buildSignature: string;\n deviceInfo: DeviceInfo;\n consent: ConsentInput;\n schemaVersion: typeof SCHEMA_VERSION;\n}\n\nexport interface AttachResponse {\n sid: string;\n deviceToken: string;\n expiresAt: number;\n ingestUrl: string;\n /** Endpoint the device long-polls for AI-issued commands. */\n commandPollUrl: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Wire protocol — commands (relay → device → ack)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface CommandRequest {\n /** Relay-issued unique id, echoed in the resulting ack/rejected event. */\n id: string;\n /** Registered command name (kebab-case). */\n name: string;\n /** Arbitrary JSON args, validated by the handler. */\n args?: Record<string, unknown>;\n /** Optional deadline; handlers SHOULD respect it. */\n deadlineTs?: number;\n}\n\n/** Long-poll response shape from `commandPollUrl`. */\nexport interface CommandPollResponse {\n commands: CommandRequest[];\n /** Suggested seconds until the next poll. */\n nextDelayMs?: number;\n}\n\nexport type CommandHandler = (\n args: Record<string, unknown>,\n ctx: CommandContext,\n) => unknown | Promise<unknown>;\n\nexport interface CommandContext {\n commandId: string;\n command: string;\n signal: AbortSignal;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botim/mp-debug-sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Remote-debug SDK for BOTIM mini-programs — streams console, network, and error events to a BOTIM debug-relay for live inspection, with an AI-observable command channel.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|