@botim/mp-debug-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,127 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ type BotimEnv = 'dev' | 'uat' | 'beta' | 'prod';
4
+ /**
5
+ * The contents of `botim.{env}.json`, resolved at build time and injected
6
+ * into the bundle via the virtual module `virtual:botim/config`.
7
+ */
8
+ interface BotimConfig {
9
+ miniProgramId: string;
10
+ env: BotimEnv;
11
+ /** Short hash of the resolved config or the build, set by the plugin. */
12
+ buildSignature: string;
13
+ appName?: string;
14
+ appVersion?: string;
15
+ /** Forward-compatible bag for fields the relay may add later. */
16
+ [extra: string]: unknown;
17
+ }
18
+
19
+ /**
20
+ * Build-time resolver for `botim.{env}.json`.
21
+ *
22
+ * The real BOTIM config schema is large and platform-managed (logos, package
23
+ * URLs, framework metadata, grayscale rollout, etc). The SDK only needs a few
24
+ * canonical fields, so this resolver acts as a *normalizer*:
25
+ *
26
+ * external schema → SDK-internal `BotimConfig`
27
+ * ─────────────── ──────────────────────────
28
+ * mp_id → miniProgramId
29
+ * app_id → appId (extra; preserved)
30
+ * version → appVersion
31
+ * app.md5 | package_url.md5 → buildSignature (deterministic per release)
32
+ * <env from filename> → env
33
+ *
34
+ * Reads `botim.{env}.json` from the consumer's project root. Build-time only:
35
+ * uses Node `fs`/`crypto`. Must NEVER be imported from device-targeted code.
36
+ */
37
+
38
+ interface ResolveOptions {
39
+ /** Map a Vite mode (e.g. "development", "staging") to a BotimEnv. */
40
+ mapMode?: (mode: string) => BotimEnv | string;
41
+ }
42
+ /**
43
+ * Resolve `botim.{env}.json` for the given Vite `mode` from `root`.
44
+ *
45
+ * Throws `BotimConfigError` synchronously on:
46
+ * - unknown env after `mapMode` is applied
47
+ * - missing or unreadable file
48
+ * - invalid JSON
49
+ * - missing/invalid `mp_id` (or its legacy `miniProgramId` alias)
50
+ * - the platform marking the mini-program as deleted (`deleted: 1`)
51
+ */
52
+ declare function resolveBotimConfig(mode: string, root: string, opts?: ResolveOptions): BotimConfig;
53
+
54
+ /**
55
+ * Errors that escape the SDK into host code.
56
+ *
57
+ * The SDK's no-throw invariant has exactly two carve-outs:
58
+ * - `BotimConfigError` — thrown synchronously by `enableRemoteDebug` when the
59
+ * resolved `BotimConfig` is missing or invalid. Prevents any I/O.
60
+ * - `BotimConsentError` — thrown synchronously by `enableRemoteDebug` when
61
+ * consent has not been granted in a `prod` build. Prevents any I/O.
62
+ *
63
+ * Both are intentionally synchronous and pre-installation: at the moment they
64
+ * fire, no globals have been wrapped, no listeners installed, no network sent.
65
+ */
66
+ declare class BotimConfigError extends Error {
67
+ readonly name = "BotimConfigError";
68
+ readonly code: string;
69
+ readonly path?: string;
70
+ constructor(message: string, opts?: {
71
+ code: string;
72
+ path?: string;
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Vite plugin for `@botim/debug-sdk`.
78
+ *
79
+ * - Reads `botim.{mode}.json` from the consumer's project root at build time.
80
+ * - Validates required fields; fails the build loudly if anything is wrong.
81
+ * - Exposes the resolved config via the virtual module `virtual:botim/config`
82
+ * as `export const botimConfig: Readonly<BotimConfig>`.
83
+ *
84
+ * The plugin's only Vite-specific surface is the `Plugin` type; we keep the
85
+ * import as `type-only` so `vite` remains an *optional* peer dependency. A
86
+ * consumer that never installs Vite will never load this file (it's only
87
+ * reachable via the `./vite` subpath export).
88
+ */
89
+
90
+ interface BotimDebugPluginOptions {
91
+ /**
92
+ * Override Vite's `mode`. Useful when the build is driven outside Vite's
93
+ * usual `--mode` flag, or in tests. If omitted, the plugin uses the mode
94
+ * Vite resolves from the CLI / config.
95
+ */
96
+ mode?: string;
97
+ /**
98
+ * Map Vite's mode to a BotimEnv. Defaults: `development` → `dev`,
99
+ * `production` → `prod`, others pass through verbatim.
100
+ */
101
+ mapMode?: (mode: string) => BotimEnv | string;
102
+ /**
103
+ * Project root override. Defaults to Vite's resolved `config.root`.
104
+ */
105
+ root?: string;
106
+ /**
107
+ * Relay endpoint to bake into `virtual:botim/config` as `relayUrl`. The
108
+ * SDK reads this at runtime so the host app can call:
109
+ *
110
+ * enableRemoteDebug({ endpoint: botimConfig.relayUrl, … })
111
+ *
112
+ * Without this option, hosts must either set up a Vite proxy
113
+ * (`server.proxy: { '/v1': 'http://localhost:8090' }`) and pass
114
+ * `endpoint: location.origin`, OR pass an explicit URL at the call
115
+ * site. Passing it through the plugin centralises the wiring and makes
116
+ * mode-specific URLs trivial: read process.env.RELAY_URL in
117
+ * vite.config and forward it here.
118
+ *
119
+ * The relay must allow the page's origin via CORS (CORS_ORIGINS env on
120
+ * @botim/debug-relay defaults to "*"). Trailing slash is stripped at
121
+ * resolve time so `/v1/attach` joins cleanly.
122
+ */
123
+ relayUrl?: string;
124
+ }
125
+ declare function botimDebug(options?: BotimDebugPluginOptions): Plugin;
126
+
127
+ export { type BotimConfig, BotimConfigError, type BotimDebugPluginOptions, type BotimEnv, botimDebug, resolveBotimConfig };
@@ -0,0 +1,149 @@
1
+ // src/vite/resolve-config.ts
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { resolve, isAbsolute } from "path";
4
+ import { createHash } from "crypto";
5
+
6
+ // src/errors.ts
7
+ var BRAND = "@botim/debug-sdk";
8
+ var BotimConfigError = class extends Error {
9
+ constructor(message, opts = { code: "invalid-config" }) {
10
+ super(`[${BRAND}] ${message}`);
11
+ this.name = "BotimConfigError";
12
+ this.code = opts.code;
13
+ this.path = opts.path;
14
+ }
15
+ };
16
+
17
+ // src/vite/resolve-config.ts
18
+ var VALID_ENVS = ["dev", "uat", "beta", "prod"];
19
+ var ID_PATTERN = /^[a-z][a-z0-9_-]{1,63}$/i;
20
+ function defaultMapMode(mode) {
21
+ if (mode === "development") return "dev";
22
+ if (mode === "production") return "prod";
23
+ return mode;
24
+ }
25
+ function isBotimEnv(s) {
26
+ return VALID_ENVS.includes(s);
27
+ }
28
+ function resolveBotimConfig(mode, root, opts = {}) {
29
+ if (!mode) {
30
+ throw new BotimConfigError("mode is required", { code: "no-mode" });
31
+ }
32
+ const mapped = (opts.mapMode ?? defaultMapMode)(mode);
33
+ if (typeof mapped !== "string" || !isBotimEnv(mapped)) {
34
+ throw new BotimConfigError(
35
+ `mode "${mode}" mapped to "${mapped}" which is not a valid BotimEnv (expected one of: ${VALID_ENVS.join(", ")})`,
36
+ { code: "invalid-env" }
37
+ );
38
+ }
39
+ const env = mapped;
40
+ const projectRoot = isAbsolute(root) ? root : resolve(process.cwd(), root);
41
+ const filePath = resolve(projectRoot, `botim.${env}.json`);
42
+ if (!existsSync(filePath)) {
43
+ throw new BotimConfigError(
44
+ `expected config file not found: ${filePath}
45
+ Create botim.${env}.json at the project root with at least { "mp_id": "..." }.`,
46
+ { code: "config-missing", path: filePath }
47
+ );
48
+ }
49
+ const raw = readFileSync(filePath, "utf8");
50
+ let parsed;
51
+ try {
52
+ parsed = JSON.parse(raw);
53
+ } catch (err) {
54
+ const detail = err instanceof Error ? err.message : String(err);
55
+ throw new BotimConfigError(`failed to parse JSON in ${filePath}: ${detail}`, {
56
+ code: "config-invalid-json",
57
+ path: filePath
58
+ });
59
+ }
60
+ if (parsed.deleted === 1) {
61
+ throw new BotimConfigError(
62
+ `${filePath} reports the mini-program as deleted (deleted: 1). Cannot ship debug SDK against a retired mini-program.`,
63
+ { code: "config-deleted", path: filePath }
64
+ );
65
+ }
66
+ const idValue = parsed.mp_id ?? parsed.miniProgramId;
67
+ if (typeof idValue !== "string" || idValue.length === 0) {
68
+ throw new BotimConfigError(
69
+ `${filePath} is missing required field "mp_id" (must be a non-empty string)`,
70
+ { code: "config-missing-field", path: filePath }
71
+ );
72
+ }
73
+ if (!ID_PATTERN.test(idValue)) {
74
+ throw new BotimConfigError(
75
+ `${filePath} field "mp_id" must match ${ID_PATTERN.toString()} (got: ${JSON.stringify(idValue)})`,
76
+ { code: "config-invalid-id", path: filePath }
77
+ );
78
+ }
79
+ const md5 = typeof parsed.app?.md5 === "string" ? parsed.app.md5 : typeof parsed.package_url?.md5 === "string" ? parsed.package_url.md5 : void 0;
80
+ const versionForSig = typeof parsed.version === "string" && parsed.version || typeof parsed.app?.version === "string" && parsed.app.version || void 0;
81
+ let buildSignature;
82
+ if (typeof parsed.buildSignature === "string" && parsed.buildSignature.length > 0) {
83
+ buildSignature = parsed.buildSignature;
84
+ } else if (versionForSig && md5) {
85
+ buildSignature = `v${versionForSig}+md5:${md5.slice(0, 12)}`;
86
+ } else {
87
+ buildSignature = "sha256:" + createHash("sha256").update(raw).digest("hex").slice(0, 12);
88
+ }
89
+ const cfg = {
90
+ // Spread first so the canonical fields below override anything inherited.
91
+ ...parsed,
92
+ miniProgramId: idValue,
93
+ env,
94
+ buildSignature,
95
+ appName: typeof parsed.app_id === "string" ? parsed.app_id : parsed.appName,
96
+ appVersion: versionForSig ?? (typeof parsed.appVersion === "string" ? parsed.appVersion : void 0)
97
+ };
98
+ return cfg;
99
+ }
100
+
101
+ // src/vite/plugin.ts
102
+ var VIRTUAL_ID = "virtual:botim/config";
103
+ var RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ID;
104
+ function botimDebug(options = {}) {
105
+ let resolved = null;
106
+ let resolvedFromMode = null;
107
+ let resolvedError = null;
108
+ return {
109
+ name: "@botim/debug-sdk:vite",
110
+ enforce: "pre",
111
+ configResolved(viteConfig) {
112
+ const mode = options.mode ?? viteConfig.mode;
113
+ const root = options.root ?? viteConfig.root;
114
+ try {
115
+ resolved = resolveBotimConfig(mode, root, { mapMode: options.mapMode });
116
+ if (options.relayUrl) {
117
+ resolved.relayUrl = options.relayUrl.replace(/\/+$/, "");
118
+ }
119
+ resolvedFromMode = mode;
120
+ } catch (err) {
121
+ resolved = null;
122
+ resolvedError = err instanceof Error ? err : new Error(String(err));
123
+ resolvedFromMode = mode;
124
+ }
125
+ },
126
+ resolveId(id) {
127
+ if (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID;
128
+ return null;
129
+ },
130
+ load(id) {
131
+ if (id !== RESOLVED_VIRTUAL_ID) return null;
132
+ if (!resolved) {
133
+ const message = resolvedError?.message ?? `[@botim/debug-sdk] virtual:botim/config requested but configResolved did not run (mode=${resolvedFromMode ?? "unknown"})`;
134
+ this.error(message);
135
+ }
136
+ const body = `// AUTO-GENERATED by @botim/debug-sdk:vite \u2014 do not edit.
137
+ export const botimConfig = Object.freeze(${JSON.stringify(resolved)});
138
+ export default botimConfig;
139
+ `;
140
+ return { code: body, map: null };
141
+ }
142
+ };
143
+ }
144
+ export {
145
+ BotimConfigError,
146
+ botimDebug,
147
+ resolveBotimConfig
148
+ };
149
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vite/resolve-config.ts","../../src/errors.ts","../../src/vite/plugin.ts"],"sourcesContent":["/**\n * Build-time resolver for `botim.{env}.json`.\n *\n * The real BOTIM config schema is large and platform-managed (logos, package\n * URLs, framework metadata, grayscale rollout, etc). The SDK only needs a few\n * canonical fields, so this resolver acts as a *normalizer*:\n *\n * external schema → SDK-internal `BotimConfig`\n * ─────────────── ──────────────────────────\n * mp_id → miniProgramId\n * app_id → appId (extra; preserved)\n * version → appVersion\n * app.md5 | package_url.md5 → buildSignature (deterministic per release)\n * <env from filename> → env\n *\n * Reads `botim.{env}.json` from the consumer's project root. Build-time only:\n * uses Node `fs`/`crypto`. Must NEVER be imported from device-targeted code.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve, isAbsolute } from 'node:path';\nimport { createHash } from 'node:crypto';\n\nimport { BotimConfigError } from '../errors.js';\nimport type { BotimConfig, BotimEnv } from '../types.js';\n\nconst VALID_ENVS: readonly BotimEnv[] = ['dev', 'uat', 'beta', 'prod'];\n/**\n * Mini-program ids in the real config are short kebab/snake-case slugs like\n * `mbrx_p2p` — case-insensitive, allows `_` and `-`, must start with a letter.\n */\nconst ID_PATTERN = /^[a-z][a-z0-9_-]{1,63}$/i;\n\nexport interface ResolveOptions {\n /** Map a Vite mode (e.g. \"development\", \"staging\") to a BotimEnv. */\n mapMode?: (mode: string) => BotimEnv | string;\n}\n\n/** Built-in mapping; consumers can override via `mapMode`. */\nfunction defaultMapMode(mode: string): string {\n if (mode === 'development') return 'dev';\n if (mode === 'production') return 'prod';\n return mode;\n}\n\nfunction isBotimEnv(s: string): s is BotimEnv {\n return (VALID_ENVS as readonly string[]).includes(s);\n}\n\ninterface RawBotimFile {\n /** Canonical mini-program id used by the BOTIM platform, e.g. \"mbrx_p2p\". */\n mp_id?: string;\n /** Reverse-DNS app id, e.g. \"me.botim.rd.p2p\". Different concept from mp_id. */\n app_id?: string;\n /** Release version of the mini-program package, e.g. \"0.0.127\". */\n version?: string;\n /** Optional override; otherwise we derive from version+md5. */\n buildSignature?: string;\n /** Per-package metadata; when present we use `app.md5` for the signature. */\n app?: { version?: string; md5?: string; updateUrl?: string };\n /** Older/alternate location for the same md5. */\n package_url?: { md5?: string; version?: string };\n /** Operational flags maintained by the platform. */\n disabled?: number;\n deleted?: number;\n mp_status?: number;\n /** Forward-compat for everything else (logos, framework, grayscale, ...). */\n [extra: string]: unknown;\n /** Legacy alias accepted for forward/back compat. */\n miniProgramId?: string;\n}\n\n/**\n * Resolve `botim.{env}.json` for the given Vite `mode` from `root`.\n *\n * Throws `BotimConfigError` synchronously on:\n * - unknown env after `mapMode` is applied\n * - missing or unreadable file\n * - invalid JSON\n * - missing/invalid `mp_id` (or its legacy `miniProgramId` alias)\n * - the platform marking the mini-program as deleted (`deleted: 1`)\n */\nexport function resolveBotimConfig(\n mode: string,\n root: string,\n opts: ResolveOptions = {},\n): BotimConfig {\n if (!mode) {\n throw new BotimConfigError('mode is required', { code: 'no-mode' });\n }\n\n const mapped = (opts.mapMode ?? defaultMapMode)(mode);\n if (typeof mapped !== 'string' || !isBotimEnv(mapped)) {\n throw new BotimConfigError(\n `mode \"${mode}\" mapped to \"${mapped}\" which is not a valid BotimEnv (expected one of: ${VALID_ENVS.join(', ')})`,\n { code: 'invalid-env' },\n );\n }\n const env: BotimEnv = mapped;\n\n const projectRoot = isAbsolute(root) ? root : resolve(process.cwd(), root);\n const filePath = resolve(projectRoot, `botim.${env}.json`);\n\n if (!existsSync(filePath)) {\n throw new BotimConfigError(\n `expected config file not found: ${filePath}\\n` +\n ` Create botim.${env}.json at the project root with at least { \"mp_id\": \"...\" }.`,\n { code: 'config-missing', path: filePath },\n );\n }\n\n const raw = readFileSync(filePath, 'utf8');\n let parsed: RawBotimFile;\n try {\n parsed = JSON.parse(raw) as RawBotimFile;\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new BotimConfigError(`failed to parse JSON in ${filePath}: ${detail}`, {\n code: 'config-invalid-json',\n path: filePath,\n });\n }\n\n // Hard-stop on a decommissioned mini-program — refusing the build is far\n // safer than shipping a debug SDK that targets a relay slot the platform\n // has retired.\n if (parsed.deleted === 1) {\n throw new BotimConfigError(\n `${filePath} reports the mini-program as deleted (deleted: 1). Cannot ship debug SDK against a retired mini-program.`,\n { code: 'config-deleted', path: filePath },\n );\n }\n\n // Read the canonical id; fall back to the SDK-internal alias for repos that\n // hand-write a config without the platform's `mp_id` field.\n const idValue = (parsed.mp_id ?? parsed.miniProgramId) as unknown;\n if (typeof idValue !== 'string' || idValue.length === 0) {\n throw new BotimConfigError(\n `${filePath} is missing required field \"mp_id\" (must be a non-empty string)`,\n { code: 'config-missing-field', path: filePath },\n );\n }\n if (!ID_PATTERN.test(idValue)) {\n throw new BotimConfigError(\n `${filePath} field \"mp_id\" must match ${ID_PATTERN.toString()} (got: ${JSON.stringify(idValue)})`,\n { code: 'config-invalid-id', path: filePath },\n );\n }\n\n // ── Derive a stable, release-correlated buildSignature ────────────────────\n // Preference order:\n // 1. explicit buildSignature in the file (consumers may pin one)\n // 2. version + package md5 (deterministic per release; survives unrelated\n // platform-side mutations like update_time / mp_status_time)\n // 3. sha256 of the file contents (last-resort fallback)\n const md5 =\n typeof parsed.app?.md5 === 'string'\n ? parsed.app.md5\n : typeof parsed.package_url?.md5 === 'string'\n ? parsed.package_url.md5\n : undefined;\n const versionForSig =\n (typeof parsed.version === 'string' && parsed.version) ||\n (typeof parsed.app?.version === 'string' && parsed.app.version) ||\n undefined;\n\n let buildSignature: string;\n if (typeof parsed.buildSignature === 'string' && parsed.buildSignature.length > 0) {\n buildSignature = parsed.buildSignature;\n } else if (versionForSig && md5) {\n buildSignature = `v${versionForSig}+md5:${md5.slice(0, 12)}`;\n } else {\n buildSignature = 'sha256:' + createHash('sha256').update(raw).digest('hex').slice(0, 12);\n }\n\n const cfg: BotimConfig = {\n // Spread first so the canonical fields below override anything inherited.\n ...parsed,\n miniProgramId: idValue,\n env,\n buildSignature,\n appName: typeof parsed.app_id === 'string' ? parsed.app_id : (parsed.appName as string | undefined),\n appVersion:\n versionForSig ?? (typeof parsed.appVersion === 'string' ? parsed.appVersion : undefined),\n };\n\n return cfg;\n}\n\nexport const __test__ = { defaultMapMode, ID_PATTERN, VALID_ENVS };\n","/**\n * Errors that escape the SDK into host code.\n *\n * The SDK's no-throw invariant has exactly two carve-outs:\n * - `BotimConfigError` — thrown synchronously by `enableRemoteDebug` when the\n * resolved `BotimConfig` is missing or invalid. Prevents any I/O.\n * - `BotimConsentError` — thrown synchronously by `enableRemoteDebug` when\n * consent has not been granted in a `prod` build. Prevents any I/O.\n *\n * Both are intentionally synchronous and pre-installation: at the moment they\n * fire, no globals have been wrapped, no listeners installed, no network sent.\n */\n\nconst BRAND = '@botim/debug-sdk' as const;\n\nexport class BotimConfigError extends Error {\n readonly name = 'BotimConfigError';\n readonly code: string;\n readonly path?: string;\n\n constructor(message: string, opts: { code: string; path?: string } = { code: 'invalid-config' }) {\n super(`[${BRAND}] ${message}`);\n this.code = opts.code;\n this.path = opts.path;\n }\n}\n\nexport class BotimConsentError extends Error {\n readonly name = 'BotimConsentError';\n constructor(message: string) {\n super(`[${BRAND}] ${message}`);\n }\n}\n","/**\n * Vite plugin for `@botim/debug-sdk`.\n *\n * - Reads `botim.{mode}.json` from the consumer's project root at build time.\n * - Validates required fields; fails the build loudly if anything is wrong.\n * - Exposes the resolved config via the virtual module `virtual:botim/config`\n * as `export const botimConfig: Readonly<BotimConfig>`.\n *\n * The plugin's only Vite-specific surface is the `Plugin` type; we keep the\n * import as `type-only` so `vite` remains an *optional* peer dependency. A\n * consumer that never installs Vite will never load this file (it's only\n * reachable via the `./vite` subpath export).\n */\n\nimport type { Plugin } from 'vite';\nimport { resolveBotimConfig } from './resolve-config.js';\nimport type { BotimConfig, BotimEnv } from '../types.js';\n\nconst VIRTUAL_ID = 'virtual:botim/config';\nconst RESOLVED_VIRTUAL_ID = '\\0' + VIRTUAL_ID;\n\nexport interface BotimDebugPluginOptions {\n /**\n * Override Vite's `mode`. Useful when the build is driven outside Vite's\n * usual `--mode` flag, or in tests. If omitted, the plugin uses the mode\n * Vite resolves from the CLI / config.\n */\n mode?: string;\n /**\n * Map Vite's mode to a BotimEnv. Defaults: `development` → `dev`,\n * `production` → `prod`, others pass through verbatim.\n */\n mapMode?: (mode: string) => BotimEnv | string;\n /**\n * Project root override. Defaults to Vite's resolved `config.root`.\n */\n root?: string;\n /**\n * Relay endpoint to bake into `virtual:botim/config` as `relayUrl`. The\n * SDK reads this at runtime so the host app can call:\n *\n * enableRemoteDebug({ endpoint: botimConfig.relayUrl, … })\n *\n * Without this option, hosts must either set up a Vite proxy\n * (`server.proxy: { '/v1': 'http://localhost:8090' }`) and pass\n * `endpoint: location.origin`, OR pass an explicit URL at the call\n * site. Passing it through the plugin centralises the wiring and makes\n * mode-specific URLs trivial: read process.env.RELAY_URL in\n * vite.config and forward it here.\n *\n * The relay must allow the page's origin via CORS (CORS_ORIGINS env on\n * @botim/debug-relay defaults to \"*\"). Trailing slash is stripped at\n * resolve time so `/v1/attach` joins cleanly.\n */\n relayUrl?: string;\n}\n\nexport function botimDebug(options: BotimDebugPluginOptions = {}): Plugin {\n let resolved: BotimConfig | null = null;\n let resolvedFromMode: string | null = null;\n let resolvedError: Error | null = null;\n\n return {\n name: '@botim/debug-sdk:vite',\n enforce: 'pre',\n\n configResolved(viteConfig) {\n const mode = options.mode ?? viteConfig.mode;\n const root = options.root ?? viteConfig.root;\n try {\n resolved = resolveBotimConfig(mode, root, { mapMode: options.mapMode });\n // Merge the host-provided relay URL into the resolved config bag.\n // Strip trailing slash so callers don't get `/v1//attach` if they\n // pass `https://relay.example.com/`.\n if (options.relayUrl) {\n (resolved as BotimConfig & { relayUrl: string }).relayUrl =\n options.relayUrl.replace(/\\/+$/, '');\n }\n resolvedFromMode = mode;\n } catch (err) {\n // Stash the error; surface it the moment a build module imports the\n // virtual module. We don't throw from `configResolved` directly because\n // some consumers run Vite for non-build tasks (e.g. `vite-node` for\n // tooling) where the virtual module is never imported and a hard fail\n // would be a regression.\n resolved = null;\n resolvedError = err instanceof Error ? err : new Error(String(err));\n resolvedFromMode = mode;\n }\n },\n\n resolveId(id) {\n if (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID;\n return null;\n },\n\n load(id) {\n if (id !== RESOLVED_VIRTUAL_ID) return null;\n\n if (!resolved) {\n const message =\n resolvedError?.message ??\n `[@botim/debug-sdk] virtual:botim/config requested but configResolved did not run (mode=${resolvedFromMode ?? 'unknown'})`;\n // `this.error` aborts the build with a properly attributed Vite error.\n this.error(message);\n }\n\n // Frozen so a runtime mutation can't accidentally taint the constant.\n const body =\n `// AUTO-GENERATED by @botim/debug-sdk:vite — do not edit.\\n` +\n `export const botimConfig = Object.freeze(${JSON.stringify(resolved)});\\n` +\n `export default botimConfig;\\n`;\n return { code: body, map: null };\n },\n };\n}\n\nexport { resolveBotimConfig } from './resolve-config.js';\nexport { BotimConfigError } from '../errors.js';\nexport type { BotimConfig, BotimEnv } from '../types.js';\n"],"mappings":"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","names":[]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Ambient module declaration for the virtual module emitted by
3
+ * `@botim/debug-sdk/vite`. Add this file to your `tsconfig.json#include`
4
+ * (or reference `@botim/debug-sdk/vite` from `compilerOptions.types`) and
5
+ * `import { botimConfig } from 'virtual:botim/config'` will type-check.
6
+ */
7
+
8
+ declare module 'virtual:botim/config' {
9
+ import type { BotimConfig } from '@botim/debug-sdk';
10
+ /**
11
+ * Resolved BotimConfig augmented with the optional `relayUrl` carried
12
+ * over from the Vite plugin's `relayUrl` option (when provided). Pass
13
+ * it as the SDK's `endpoint`:
14
+ *
15
+ * await enableRemoteDebug({
16
+ * endpoint: botimConfig.relayUrl ?? location.origin,
17
+ * config: botimConfig,
18
+ * consent: { userOptIn: true },
19
+ * });
20
+ */
21
+ export const botimConfig: Readonly<BotimConfig & { relayUrl?: string }>;
22
+ const _default: Readonly<BotimConfig & { relayUrl?: string }>;
23
+ export default _default;
24
+ }
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@botim/mp-debug-sdk",
3
+ "version": "0.1.0",
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
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./types": {
16
+ "types": "./dist/types.d.ts",
17
+ "import": "./dist/types.js",
18
+ "require": "./dist/types.cjs"
19
+ },
20
+ "./vite": {
21
+ "types": "./dist/vite/plugin.d.ts",
22
+ "import": "./dist/vite/plugin.js",
23
+ "require": "./dist/vite/plugin.cjs"
24
+ },
25
+ "./vite/virtual-shim": "./dist/vite/virtual-shim.d.ts"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "LICENSE",
30
+ "README.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsup && npm run build:copy-shim",
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
+ "dev": "tsup --watch",
36
+ "type-check": "tsc --noEmit",
37
+ "prepublishOnly": "npm run type-check && npm run build"
38
+ },
39
+ "keywords": [
40
+ "botim",
41
+ "debug",
42
+ "remote-debugging",
43
+ "mini-program",
44
+ "logging",
45
+ "observability",
46
+ "ai-agent",
47
+ "vite-plugin"
48
+ ],
49
+ "author": "BOTIM",
50
+ "license": "ISC",
51
+ "homepage": "https://github.com/botim/mp-debug-sdk#readme",
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "git+https://github.com/botim/mp-debug-sdk.git"
55
+ },
56
+ "bugs": {
57
+ "url": "https://github.com/botim/mp-debug-sdk/issues"
58
+ },
59
+ "engines": {
60
+ "node": ">=16.0.0"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public"
64
+ },
65
+ "sideEffects": false,
66
+ "devDependencies": {
67
+ "@types/node": "24.9.2",
68
+ "tsup": "^8.5.0",
69
+ "typescript": "^5.9.3",
70
+ "vite": "^5.0.0"
71
+ },
72
+ "peerDependencies": {
73
+ "vite": ">=5.0.0"
74
+ },
75
+ "peerDependenciesMeta": {
76
+ "vite": {
77
+ "optional": true
78
+ }
79
+ },
80
+ "dependencies": {}
81
+ }