@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.
@@ -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\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
@@ -222,6 +222,50 @@ interface ConsentInput {
222
222
  hostToken?: string;
223
223
  /** Explicit user opt-in (e.g., set after a privacy dialog). */
224
224
  userOptIn?: boolean;
225
+ /**
226
+ * Controls whether the SDK shows its built-in consent modal.
227
+ *
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.
249
+ */
250
+ promptUser?: boolean;
251
+ /**
252
+ * Optional copy override for the consent modal. Keys map to the
253
+ * default English strings; provide any subset to localize.
254
+ */
255
+ promptCopy?: ConsentPromptCopy;
256
+ }
257
+ interface ConsentPromptCopy {
258
+ /** Short headline. Default: "Enable BOTIM debug logging?" */
259
+ title?: string;
260
+ /**
261
+ * Body paragraphs (rendered as separate <p> elements). Default
262
+ * explains what data is collected and that the user can revoke.
263
+ */
264
+ body?: string[];
265
+ /** Default: "Allow" */
266
+ acceptLabel?: string;
267
+ /** Default: "Skip" */
268
+ declineLabel?: string;
225
269
  }
226
270
  interface AttachRequest {
227
271
  miniProgramId: string;
@@ -262,4 +306,4 @@ interface CommandContext {
262
306
  signal: AbortSignal;
263
307
  }
264
308
 
265
- export { type AttachRequest, type AttachResponse, type BotimConfig, type BotimEnv, type BridgePayload, type CommandAckPayload, type CommandContext, type CommandHandler, type CommandPollResponse, type CommandRejectedPayload, type CommandRequest, type ConsentInput, type ConsolePayload, type DebugEvent, type DebugEventBase, type DeviceInfo, type ErrorPayload, type ErrorSource, type EventLevel, type EventMeta, type EventType, type LifecycleEvent, type LifecyclePayload, type NetworkPayload, type NetworkPhase, type PerfPayload, SCHEMA_VERSION, type SerializedValue, type StreamFrame, type UploadAck, type UploadBatch };
309
+ export { type AttachRequest, type AttachResponse, type BotimConfig, type BotimEnv, type BridgePayload, type CommandAckPayload, type CommandContext, type CommandHandler, type CommandPollResponse, type CommandRejectedPayload, type CommandRequest, type ConsentInput, type ConsentPromptCopy, type ConsolePayload, type DebugEvent, type DebugEventBase, type DeviceInfo, type ErrorPayload, type ErrorSource, type EventLevel, type EventMeta, type EventType, type LifecycleEvent, type LifecyclePayload, type NetworkPayload, type NetworkPhase, type PerfPayload, SCHEMA_VERSION, type SerializedValue, type StreamFrame, type UploadAck, type UploadBatch };
package/dist/types.d.ts CHANGED
@@ -222,6 +222,50 @@ interface ConsentInput {
222
222
  hostToken?: string;
223
223
  /** Explicit user opt-in (e.g., set after a privacy dialog). */
224
224
  userOptIn?: boolean;
225
+ /**
226
+ * Controls whether the SDK shows its built-in consent modal.
227
+ *
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.
249
+ */
250
+ promptUser?: boolean;
251
+ /**
252
+ * Optional copy override for the consent modal. Keys map to the
253
+ * default English strings; provide any subset to localize.
254
+ */
255
+ promptCopy?: ConsentPromptCopy;
256
+ }
257
+ interface ConsentPromptCopy {
258
+ /** Short headline. Default: "Enable BOTIM debug logging?" */
259
+ title?: string;
260
+ /**
261
+ * Body paragraphs (rendered as separate <p> elements). Default
262
+ * explains what data is collected and that the user can revoke.
263
+ */
264
+ body?: string[];
265
+ /** Default: "Allow" */
266
+ acceptLabel?: string;
267
+ /** Default: "Skip" */
268
+ declineLabel?: string;
225
269
  }
226
270
  interface AttachRequest {
227
271
  miniProgramId: string;
@@ -262,4 +306,4 @@ interface CommandContext {
262
306
  signal: AbortSignal;
263
307
  }
264
308
 
265
- export { type AttachRequest, type AttachResponse, type BotimConfig, type BotimEnv, type BridgePayload, type CommandAckPayload, type CommandContext, type CommandHandler, type CommandPollResponse, type CommandRejectedPayload, type CommandRequest, type ConsentInput, type ConsolePayload, type DebugEvent, type DebugEventBase, type DeviceInfo, type ErrorPayload, type ErrorSource, type EventLevel, type EventMeta, type EventType, type LifecycleEvent, type LifecyclePayload, type NetworkPayload, type NetworkPhase, type PerfPayload, SCHEMA_VERSION, type SerializedValue, type StreamFrame, type UploadAck, type UploadBatch };
309
+ export { type AttachRequest, type AttachResponse, type BotimConfig, type BotimEnv, type BridgePayload, type CommandAckPayload, type CommandContext, type CommandHandler, type CommandPollResponse, type CommandRejectedPayload, type CommandRequest, type ConsentInput, type ConsentPromptCopy, type ConsolePayload, type DebugEvent, type DebugEventBase, type DeviceInfo, type ErrorPayload, type ErrorSource, type EventLevel, type EventMeta, type EventType, type LifecycleEvent, type LifecyclePayload, type NetworkPayload, type NetworkPhase, type PerfPayload, SCHEMA_VERSION, type SerializedValue, type StreamFrame, type UploadAck, type UploadBatch };
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\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.6.2",
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",