@botim/mp-debug-sdk 0.4.1 → 0.6.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 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}\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\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
@@ -72,6 +72,8 @@ interface NetworkPayload {
72
72
  method: string;
73
73
  url: string;
74
74
  status?: number;
75
+ /** HTTP status text (e.g., "OK", "Not Found"). Lets the admin show "200 OK". */
76
+ statusText?: string;
75
77
  durationMs?: number;
76
78
  reqHeaders?: Record<string, string>;
77
79
  reqBody?: string;
@@ -81,6 +83,16 @@ interface NetworkPayload {
81
83
  resBodyTruncated?: boolean;
82
84
  errorMessage?: string;
83
85
  errorName?: string;
86
+ /** Stack trace for the failing call. Captured at the failure site so
87
+ * operators can locate the originating code path. For fetch this is
88
+ * `err.stack` from the rejected promise; for XHR (which doesn't carry
89
+ * an Error object on `error`/`timeout`/`abort`) we synthesise a stack
90
+ * from the call site of `xhr.send()`. */
91
+ errorStack?: string;
92
+ /** Flattened cause chain (`err.cause.cause...`) — undici-style fetch
93
+ * failures frequently wrap the useful reason here (e.g. `ECONNREFUSED`
94
+ * inside a generic `TypeError: fetch failed`). One line per cause. */
95
+ errorCause?: string;
84
96
  }
85
97
  type ErrorSource = 'window.error' | 'unhandledrejection' | 'manual';
86
98
  interface ErrorPayload {
package/dist/types.d.ts CHANGED
@@ -72,6 +72,8 @@ interface NetworkPayload {
72
72
  method: string;
73
73
  url: string;
74
74
  status?: number;
75
+ /** HTTP status text (e.g., "OK", "Not Found"). Lets the admin show "200 OK". */
76
+ statusText?: string;
75
77
  durationMs?: number;
76
78
  reqHeaders?: Record<string, string>;
77
79
  reqBody?: string;
@@ -81,6 +83,16 @@ interface NetworkPayload {
81
83
  resBodyTruncated?: boolean;
82
84
  errorMessage?: string;
83
85
  errorName?: string;
86
+ /** Stack trace for the failing call. Captured at the failure site so
87
+ * operators can locate the originating code path. For fetch this is
88
+ * `err.stack` from the rejected promise; for XHR (which doesn't carry
89
+ * an Error object on `error`/`timeout`/`abort`) we synthesise a stack
90
+ * from the call site of `xhr.send()`. */
91
+ errorStack?: string;
92
+ /** Flattened cause chain (`err.cause.cause...`) — undici-style fetch
93
+ * failures frequently wrap the useful reason here (e.g. `ECONNREFUSED`
94
+ * inside a generic `TypeError: fetch failed`). One line per cause. */
95
+ errorCause?: string;
84
96
  }
85
97
  type ErrorSource = 'window.error' | 'unhandledrejection' | 'manual';
86
98
  interface ErrorPayload {
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 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}\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\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.4.1",
3
+ "version": "0.6.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",
@@ -34,7 +34,9 @@
34
34
  "build:copy-shim": "node -e \"require('node:fs').mkdirSync('dist/vite',{recursive:true});require('node:fs').copyFileSync('src/vite/virtual-shim.d.ts','dist/vite/virtual-shim.d.ts')\"",
35
35
  "dev": "tsup --watch",
36
36
  "type-check": "tsc --noEmit",
37
- "prepublishOnly": "npm run type-check && npm run build"
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "prepublishOnly": "npm run type-check && npm test && npm run build"
38
40
  },
39
41
  "keywords": [
40
42
  "botim",
@@ -65,9 +67,11 @@
65
67
  "sideEffects": false,
66
68
  "devDependencies": {
67
69
  "@types/node": "24.9.2",
70
+ "happy-dom": "^15.11.7",
68
71
  "tsup": "^8.5.0",
69
72
  "typescript": "^5.9.3",
70
- "vite": "^5.0.0"
73
+ "vite": "^5.0.0",
74
+ "vitest": "^2.1.9"
71
75
  },
72
76
  "peerDependencies": {
73
77
  "vite": ">=5.0.0"