@botim/mp-debug-sdk 0.8.0 → 1.0.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.
package/dist/index.cjs CHANGED
@@ -264,7 +264,7 @@ var RingBuffer = class {
264
264
  // src/transport.ts
265
265
  var DEFAULT_MAX_RETRIES = 3;
266
266
  var DEFAULT_POLL_TIMEOUT_MS = 25e3;
267
- var DEFAULT_MIN_BACKOFF_MS = 500;
267
+ var DEFAULT_MIN_BACKOFF_MS = 2e3;
268
268
  var DEFAULT_MAX_BACKOFF_MS = 3e4;
269
269
  var Transport = class {
270
270
  constructor(opts) {
@@ -1572,7 +1572,7 @@ var DEFAULT_BODY_PATTERNS = [
1572
1572
  /[A-Za-z0-9_-]{40,}/g
1573
1573
  ];
1574
1574
  var DEFAULT_MAX_BODY_BYTES = 4096;
1575
- var DEFAULT_FLUSH_INTERVAL_MS = 8e3;
1575
+ var DEFAULT_FLUSH_INTERVAL_MS = 4e3;
1576
1576
  var DEFAULT_BUFFER_SIZE = 1e3;
1577
1577
  var DEFAULT_MAX_BATCH_SIZE = 50;
1578
1578
  var NOOP_HANDLE = {
@@ -1587,6 +1587,10 @@ var NOOP_HANDLE = {
1587
1587
  return false;
1588
1588
  }
1589
1589
  };
1590
+ var defaultBotimConfig = null;
1591
+ function setDefaultBotimConfig(config) {
1592
+ defaultBotimConfig = config;
1593
+ }
1590
1594
  function assertConsent(config, consent) {
1591
1595
  if (config.env !== "prod") return;
1592
1596
  const hasHostToken = typeof consent?.hostToken === "string" && consent.hostToken.length > 0;
@@ -1599,7 +1603,7 @@ function assertConsent(config, consent) {
1599
1603
  function assertConfig(config) {
1600
1604
  if (!config || typeof config !== "object") {
1601
1605
  throw new BotimConfigError(
1602
- 'options.config is required (import botimConfig from "virtual:botim/config")',
1606
+ 'config is required \u2014 pass options.config, or register a default via "@botim/mp-debug-sdk/auto"',
1603
1607
  { code: "config-missing" }
1604
1608
  );
1605
1609
  }
@@ -1617,18 +1621,31 @@ function assertConfig(config) {
1617
1621
  }
1618
1622
  async function enableRemoteDebug(options) {
1619
1623
  if (options.enabled === false) return NOOP_HANDLE;
1620
- assertConfig(options.config);
1624
+ if (!options.endpoint || typeof options.endpoint !== "string") {
1625
+ throw new BotimConfigError(
1626
+ 'relay server URL is required \u2014 pass options.endpoint (e.g. "https://relay.example.com")',
1627
+ { code: "endpoint-missing" }
1628
+ );
1629
+ }
1630
+ const resolvedConfig = options.config ?? defaultBotimConfig;
1631
+ if (!resolvedConfig) {
1632
+ throw new BotimConfigError(
1633
+ 'no config provided and no default registered \u2014 import { enableRemoteDebug } from "@botim/mp-debug-sdk/auto" (Vite), or pass options.config',
1634
+ { code: "config-missing" }
1635
+ );
1636
+ }
1637
+ assertConfig(resolvedConfig);
1621
1638
  const consentInput = options.consent ?? {};
1622
1639
  const hasExplicitConsent = consentInput.userOptIn === true || typeof consentInput.hostToken === "string" && consentInput.hostToken.length > 0;
1623
1640
  const promptDisabled = consentInput.promptUser === false;
1624
1641
  const canPrompt = typeof document !== "undefined" && typeof window !== "undefined";
1625
1642
  const shouldPrompt = !hasExplicitConsent && !promptDisabled && canPrompt;
1626
1643
  if (shouldPrompt) {
1627
- const cached = readConsentDecision(options.config.miniProgramId);
1644
+ const cached = readConsentDecision(resolvedConfig.miniProgramId);
1628
1645
  let decision = cached;
1629
1646
  if (decision === null) {
1630
1647
  decision = await promptForConsent(
1631
- options.config.miniProgramId,
1648
+ resolvedConfig.miniProgramId,
1632
1649
  consentInput.promptCopy
1633
1650
  );
1634
1651
  }
@@ -1638,16 +1655,16 @@ async function enableRemoteDebug(options) {
1638
1655
  consent: { ...consentInput, userOptIn: true }
1639
1656
  };
1640
1657
  }
1641
- assertConsent(options.config, options.consent);
1658
+ assertConsent(resolvedConfig, options.consent);
1642
1659
  const onError = options.onError ?? (() => {
1643
1660
  });
1644
1661
  const app = options.app ?? {
1645
- name: options.config.appName ?? options.config.miniProgramId,
1646
- version: options.config.appVersion ?? "0.0.0"
1662
+ name: resolvedConfig.appName ?? resolvedConfig.miniProgramId,
1663
+ version: resolvedConfig.appVersion ?? "0.0.0"
1647
1664
  };
1648
1665
  const deviceInfo = detectDeviceInfo(app, options.device);
1649
1666
  const consent = options.consent ?? {};
1650
- const session = await attachDevice(options.endpoint, options.config, deviceInfo, consent, options.token);
1667
+ const session = await attachDevice(options.endpoint, resolvedConfig, deviceInfo, consent, options.token);
1651
1668
  const buffer = new RingBuffer({
1652
1669
  capacity: options.bufferSize ?? DEFAULT_BUFFER_SIZE
1653
1670
  });
@@ -1845,6 +1862,7 @@ exports.enableRemoteDebug = enableRemoteDebug;
1845
1862
  exports.getBOT = getBOT;
1846
1863
  exports.promptForConsent = promptForConsent;
1847
1864
  exports.readConsentDecision = readConsentDecision;
1865
+ exports.setDefaultBotimConfig = setDefaultBotimConfig;
1848
1866
  exports.writeConsentDecision = writeConsentDecision;
1849
1867
  //# sourceMappingURL=index.cjs.map
1850
1868
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.cts CHANGED
@@ -243,11 +243,12 @@ interface RemoteDebugOptions {
243
243
  */
244
244
  app?: AppInfo;
245
245
  /**
246
- * Build-time-resolved mini-program identity. In typical usage this is
247
- * `botimConfig` imported from `virtual:botim/config` (provided by the
248
- * `@botim/debug-sdk/vite` plugin). REQUIRED.
246
+ * Build-time-resolved mini-program identity. Optional: when omitted, the SDK
247
+ * uses the config registered via `setDefaultBotimConfig` which the
248
+ * `@botim/mp-debug-sdk/auto` entry sets from `virtual:botim/config`. Pass it
249
+ * explicitly to override.
249
250
  */
250
- config: BotimConfig;
251
+ config?: BotimConfig;
251
252
  /**
252
253
  * Consent for telemetry. Required in `prod`; implicit in non-prod builds.
253
254
  */
@@ -287,21 +288,18 @@ declare const DEFAULT_MAX_BODY_BYTES = 4096;
287
288
  /**
288
289
  * How often the transport drains the buffer to the relay.
289
290
  *
290
- * 8 seconds is deliberately slow: this is a debug telemetry channel, not a
291
- * realtime one. With a 200ms cadence, a busy session producing ~10 events/sec
292
- * would make ~5 ingest POSTs/second, hammering the device's network stack
293
- * and the relay's write path for no human-visible benefit (admins poll at
294
- * 2s anyway, so end-to-end perceived latency is dominated by their poll).
295
- *
296
- * The timer fully drains the buffer per tick (multiple POSTs back-to-back if
297
- * needed, each capped at maxBatchSize), so the only cost of the longer
298
- * interval is up to 8s of potential delay before an event reaches the relay.
299
- *
300
- * Hosts can opt for tighter latency by setting `flushIntervalMs` explicitly.
291
+ * 4 seconds balances latency against chattiness — this is a debug telemetry
292
+ * channel, not a realtime one. A much tighter cadence would make a busy session
293
+ * (~10 events/sec) fire many ingest POSTs/sec for little human-visible gain
294
+ * (admins poll at ~2s). The timer fully drains the buffer per tick (back-to-back
295
+ * POSTs, each capped at maxBatchSize), so the only cost is up to ~4s of delay
296
+ * before an event reaches the relay. Hosts can set `flushIntervalMs` to tune it.
301
297
  */
302
- declare const DEFAULT_FLUSH_INTERVAL_MS = 8000;
298
+ declare const DEFAULT_FLUSH_INTERVAL_MS = 4000;
303
299
  declare const DEFAULT_BUFFER_SIZE = 1000;
304
300
  declare const DEFAULT_MAX_BATCH_SIZE = 50;
301
+ /** Register (or, with null, clear) the default BotimConfig used when `enableRemoteDebug` gets no `config`. */
302
+ declare function setDefaultBotimConfig(config: BotimConfig | null): void;
305
303
  declare function enableRemoteDebug(options: RemoteDebugOptions): Promise<RemoteDebugHandle>;
306
304
 
307
- export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, type ConsentDecision, ConsentInput, ConsentPromptCopy, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, type ExecOptions, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, clearConsentDecision, enableRemoteDebug, getBOT, promptForConsent, readConsentDecision, writeConsentDecision };
305
+ export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, type ConsentDecision, ConsentInput, ConsentPromptCopy, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, type ExecOptions, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, clearConsentDecision, enableRemoteDebug, getBOT, promptForConsent, readConsentDecision, setDefaultBotimConfig, writeConsentDecision };
package/dist/index.d.ts CHANGED
@@ -243,11 +243,12 @@ interface RemoteDebugOptions {
243
243
  */
244
244
  app?: AppInfo;
245
245
  /**
246
- * Build-time-resolved mini-program identity. In typical usage this is
247
- * `botimConfig` imported from `virtual:botim/config` (provided by the
248
- * `@botim/debug-sdk/vite` plugin). REQUIRED.
246
+ * Build-time-resolved mini-program identity. Optional: when omitted, the SDK
247
+ * uses the config registered via `setDefaultBotimConfig` which the
248
+ * `@botim/mp-debug-sdk/auto` entry sets from `virtual:botim/config`. Pass it
249
+ * explicitly to override.
249
250
  */
250
- config: BotimConfig;
251
+ config?: BotimConfig;
251
252
  /**
252
253
  * Consent for telemetry. Required in `prod`; implicit in non-prod builds.
253
254
  */
@@ -287,21 +288,18 @@ declare const DEFAULT_MAX_BODY_BYTES = 4096;
287
288
  /**
288
289
  * How often the transport drains the buffer to the relay.
289
290
  *
290
- * 8 seconds is deliberately slow: this is a debug telemetry channel, not a
291
- * realtime one. With a 200ms cadence, a busy session producing ~10 events/sec
292
- * would make ~5 ingest POSTs/second, hammering the device's network stack
293
- * and the relay's write path for no human-visible benefit (admins poll at
294
- * 2s anyway, so end-to-end perceived latency is dominated by their poll).
295
- *
296
- * The timer fully drains the buffer per tick (multiple POSTs back-to-back if
297
- * needed, each capped at maxBatchSize), so the only cost of the longer
298
- * interval is up to 8s of potential delay before an event reaches the relay.
299
- *
300
- * Hosts can opt for tighter latency by setting `flushIntervalMs` explicitly.
291
+ * 4 seconds balances latency against chattiness — this is a debug telemetry
292
+ * channel, not a realtime one. A much tighter cadence would make a busy session
293
+ * (~10 events/sec) fire many ingest POSTs/sec for little human-visible gain
294
+ * (admins poll at ~2s). The timer fully drains the buffer per tick (back-to-back
295
+ * POSTs, each capped at maxBatchSize), so the only cost is up to ~4s of delay
296
+ * before an event reaches the relay. Hosts can set `flushIntervalMs` to tune it.
301
297
  */
302
- declare const DEFAULT_FLUSH_INTERVAL_MS = 8000;
298
+ declare const DEFAULT_FLUSH_INTERVAL_MS = 4000;
303
299
  declare const DEFAULT_BUFFER_SIZE = 1000;
304
300
  declare const DEFAULT_MAX_BATCH_SIZE = 50;
301
+ /** Register (or, with null, clear) the default BotimConfig used when `enableRemoteDebug` gets no `config`. */
302
+ declare function setDefaultBotimConfig(config: BotimConfig | null): void;
305
303
  declare function enableRemoteDebug(options: RemoteDebugOptions): Promise<RemoteDebugHandle>;
306
304
 
307
- export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, type ConsentDecision, ConsentInput, ConsentPromptCopy, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, type ExecOptions, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, clearConsentDecision, enableRemoteDebug, getBOT, promptForConsent, readConsentDecision, writeConsentDecision };
305
+ export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, type ConsentDecision, ConsentInput, ConsentPromptCopy, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, type ExecOptions, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, clearConsentDecision, enableRemoteDebug, getBOT, promptForConsent, readConsentDecision, setDefaultBotimConfig, writeConsentDecision };
package/dist/index.js CHANGED
@@ -262,7 +262,7 @@ var RingBuffer = class {
262
262
  // src/transport.ts
263
263
  var DEFAULT_MAX_RETRIES = 3;
264
264
  var DEFAULT_POLL_TIMEOUT_MS = 25e3;
265
- var DEFAULT_MIN_BACKOFF_MS = 500;
265
+ var DEFAULT_MIN_BACKOFF_MS = 2e3;
266
266
  var DEFAULT_MAX_BACKOFF_MS = 3e4;
267
267
  var Transport = class {
268
268
  constructor(opts) {
@@ -1570,7 +1570,7 @@ var DEFAULT_BODY_PATTERNS = [
1570
1570
  /[A-Za-z0-9_-]{40,}/g
1571
1571
  ];
1572
1572
  var DEFAULT_MAX_BODY_BYTES = 4096;
1573
- var DEFAULT_FLUSH_INTERVAL_MS = 8e3;
1573
+ var DEFAULT_FLUSH_INTERVAL_MS = 4e3;
1574
1574
  var DEFAULT_BUFFER_SIZE = 1e3;
1575
1575
  var DEFAULT_MAX_BATCH_SIZE = 50;
1576
1576
  var NOOP_HANDLE = {
@@ -1585,6 +1585,10 @@ var NOOP_HANDLE = {
1585
1585
  return false;
1586
1586
  }
1587
1587
  };
1588
+ var defaultBotimConfig = null;
1589
+ function setDefaultBotimConfig(config) {
1590
+ defaultBotimConfig = config;
1591
+ }
1588
1592
  function assertConsent(config, consent) {
1589
1593
  if (config.env !== "prod") return;
1590
1594
  const hasHostToken = typeof consent?.hostToken === "string" && consent.hostToken.length > 0;
@@ -1597,7 +1601,7 @@ function assertConsent(config, consent) {
1597
1601
  function assertConfig(config) {
1598
1602
  if (!config || typeof config !== "object") {
1599
1603
  throw new BotimConfigError(
1600
- 'options.config is required (import botimConfig from "virtual:botim/config")',
1604
+ 'config is required \u2014 pass options.config, or register a default via "@botim/mp-debug-sdk/auto"',
1601
1605
  { code: "config-missing" }
1602
1606
  );
1603
1607
  }
@@ -1615,18 +1619,31 @@ function assertConfig(config) {
1615
1619
  }
1616
1620
  async function enableRemoteDebug(options) {
1617
1621
  if (options.enabled === false) return NOOP_HANDLE;
1618
- assertConfig(options.config);
1622
+ if (!options.endpoint || typeof options.endpoint !== "string") {
1623
+ throw new BotimConfigError(
1624
+ 'relay server URL is required \u2014 pass options.endpoint (e.g. "https://relay.example.com")',
1625
+ { code: "endpoint-missing" }
1626
+ );
1627
+ }
1628
+ const resolvedConfig = options.config ?? defaultBotimConfig;
1629
+ if (!resolvedConfig) {
1630
+ throw new BotimConfigError(
1631
+ 'no config provided and no default registered \u2014 import { enableRemoteDebug } from "@botim/mp-debug-sdk/auto" (Vite), or pass options.config',
1632
+ { code: "config-missing" }
1633
+ );
1634
+ }
1635
+ assertConfig(resolvedConfig);
1619
1636
  const consentInput = options.consent ?? {};
1620
1637
  const hasExplicitConsent = consentInput.userOptIn === true || typeof consentInput.hostToken === "string" && consentInput.hostToken.length > 0;
1621
1638
  const promptDisabled = consentInput.promptUser === false;
1622
1639
  const canPrompt = typeof document !== "undefined" && typeof window !== "undefined";
1623
1640
  const shouldPrompt = !hasExplicitConsent && !promptDisabled && canPrompt;
1624
1641
  if (shouldPrompt) {
1625
- const cached = readConsentDecision(options.config.miniProgramId);
1642
+ const cached = readConsentDecision(resolvedConfig.miniProgramId);
1626
1643
  let decision = cached;
1627
1644
  if (decision === null) {
1628
1645
  decision = await promptForConsent(
1629
- options.config.miniProgramId,
1646
+ resolvedConfig.miniProgramId,
1630
1647
  consentInput.promptCopy
1631
1648
  );
1632
1649
  }
@@ -1636,16 +1653,16 @@ async function enableRemoteDebug(options) {
1636
1653
  consent: { ...consentInput, userOptIn: true }
1637
1654
  };
1638
1655
  }
1639
- assertConsent(options.config, options.consent);
1656
+ assertConsent(resolvedConfig, options.consent);
1640
1657
  const onError = options.onError ?? (() => {
1641
1658
  });
1642
1659
  const app = options.app ?? {
1643
- name: options.config.appName ?? options.config.miniProgramId,
1644
- version: options.config.appVersion ?? "0.0.0"
1660
+ name: resolvedConfig.appName ?? resolvedConfig.miniProgramId,
1661
+ version: resolvedConfig.appVersion ?? "0.0.0"
1645
1662
  };
1646
1663
  const deviceInfo = detectDeviceInfo(app, options.device);
1647
1664
  const consent = options.consent ?? {};
1648
- const session = await attachDevice(options.endpoint, options.config, deviceInfo, consent, options.token);
1665
+ const session = await attachDevice(options.endpoint, resolvedConfig, deviceInfo, consent, options.token);
1649
1666
  const buffer = new RingBuffer({
1650
1667
  capacity: options.bufferSize ?? DEFAULT_BUFFER_SIZE
1651
1668
  });
@@ -1829,6 +1846,6 @@ async function enableRemoteDebug(options) {
1829
1846
  };
1830
1847
  }
1831
1848
 
1832
- export { BotimConfigError, BotimConsentError, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, SCHEMA_VERSION, clearConsentDecision, enableRemoteDebug, getBOT, promptForConsent, readConsentDecision, writeConsentDecision };
1849
+ export { BotimConfigError, BotimConsentError, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, SCHEMA_VERSION, clearConsentDecision, enableRemoteDebug, getBOT, promptForConsent, readConsentDecision, setDefaultBotimConfig, writeConsentDecision };
1833
1850
  //# sourceMappingURL=index.js.map
1834
1851
  //# sourceMappingURL=index.js.map
@@ -44,7 +44,7 @@ var BotimConfigError = class extends Error {
44
44
 
45
45
  // src/vite/resolve-config.ts
46
46
  var VALID_ENVS = ["dev", "uat", "beta", "prod"];
47
- var ID_PATTERN = /^[a-z][a-z0-9_-]{1,63}$/i;
47
+ var ID_PATTERN = /^[a-z][a-z0-9._-]{1,127}$/i;
48
48
  function defaultMapMode(mode) {
49
49
  if (mode === "development") return "dev";
50
50
  if (mode === "production") return "prod";
@@ -66,30 +66,44 @@ function resolveBotimConfig(mode, root, opts = {}) {
66
66
  }
67
67
  const env = mapped;
68
68
  const projectRoot = (0, import_node_path.isAbsolute)(root) ? root : (0, import_node_path.resolve)(process.cwd(), root);
69
- const fileName = resolveFileName(opts.configFileName, env, mode);
70
- const filePath = (0, import_node_path.isAbsolute)(fileName) ? fileName : (0, import_node_path.resolve)(projectRoot, fileName);
69
+ const explicit = opts.configFileName !== void 0;
70
+ const candidates = explicit ? [
71
+ {
72
+ env,
73
+ path: (() => {
74
+ const fileName = resolveFileName(opts.configFileName, env, mode);
75
+ return (0, import_node_path.isAbsolute)(fileName) ? fileName : (0, import_node_path.resolve)(projectRoot, fileName);
76
+ })()
77
+ }
78
+ ] : buildCandidatePaths(projectRoot, env);
71
79
  const inline = opts.inlineConfig;
72
- const inlineHasIdentity = typeof inline?.mp_id === "string" && inline.mp_id.length > 0 ? true : typeof inline?.miniProgramId === "string" && inline.miniProgramId.length > 0;
80
+ const inlineHasIdentity = typeof inline?.app_id === "string" && inline.app_id.length > 0 || typeof inline?.miniProgramId === "string" && inline.miniProgramId.length > 0;
81
+ const source = candidates.find((c) => (0, import_node_fs.existsSync)(c.path));
73
82
  let raw;
74
83
  let parsedFromFile = {};
75
- if ((0, import_node_fs.existsSync)(filePath)) {
76
- raw = (0, import_node_fs.readFileSync)(filePath, "utf8");
84
+ if (source) {
85
+ raw = (0, import_node_fs.readFileSync)(source.path, "utf8");
77
86
  try {
78
87
  parsedFromFile = JSON.parse(raw);
79
88
  } catch (err) {
80
89
  const detail = err instanceof Error ? err.message : String(err);
81
- throw new BotimConfigError(`failed to parse JSON in ${filePath}: ${detail}`, {
90
+ throw new BotimConfigError(`failed to parse JSON in ${source.path}: ${detail}`, {
82
91
  code: "config-invalid-json",
83
- path: filePath
92
+ path: source.path
84
93
  });
85
94
  }
86
- } else if (!inlineHasIdentity) {
95
+ if (source.env !== env) {
96
+ console.warn(
97
+ `[@botim/debug-sdk] no config for env "${env}"; borrowed app_id from ${(0, import_node_path.basename)(source.path)} (env stays ${env})`
98
+ );
99
+ }
100
+ } else if (explicit && !inlineHasIdentity) {
101
+ const wanted = candidates[0].path;
87
102
  throw new BotimConfigError(
88
- `expected config file not found: ${filePath}
89
- Either create ${fileName} at the project root with at least { "mp_id": "..." },
90
- pass an explicit { configFileName } / { config: { mp_id: "..." } } to botimDebug(),
91
- or supply inlineConfig with mp_id when calling resolveBotimConfig() directly.`,
92
- { code: "config-missing", path: filePath }
103
+ `expected config file not found: ${wanted}
104
+ Create it with at least { "app_id": "..." }, pass { configFileName },
105
+ or supply inlineConfig with app_id.`,
106
+ { code: "config-missing", path: wanted }
93
107
  );
94
108
  }
95
109
  const parsed = {
@@ -103,22 +117,28 @@ function resolveBotimConfig(mode, root, opts = {}) {
103
117
  parsed.package_url = { ...parsedFromFile.package_url, ...inline?.package_url };
104
118
  }
105
119
  if (parsed.deleted === 1) {
120
+ const label = source?.path ?? "inlineConfig";
106
121
  throw new BotimConfigError(
107
- `${filePath} reports the mini-program as deleted (deleted: 1). Cannot ship debug SDK against a retired mini-program.`,
108
- { code: "config-deleted", path: filePath }
122
+ `${label} reports the mini-program as deleted (deleted: 1). Cannot ship debug SDK against a retired mini-program.`,
123
+ { code: "config-deleted", path: source?.path }
109
124
  );
110
125
  }
111
- const idValue = parsed.mp_id ?? parsed.miniProgramId;
112
- if (typeof idValue !== "string" || idValue.length === 0) {
113
- throw new BotimConfigError(
114
- `${filePath} is missing required field "mp_id" (must be a non-empty string)`,
115
- { code: "config-missing-field", path: filePath }
116
- );
117
- }
118
- if (!ID_PATTERN.test(idValue)) {
119
- throw new BotimConfigError(
120
- `${filePath} field "mp_id" must match ${ID_PATTERN.toString()} (got: ${JSON.stringify(idValue)})`,
121
- { code: "config-invalid-id", path: filePath }
126
+ const idValue = parsed.app_id ?? parsed.miniProgramId;
127
+ let miniProgramId;
128
+ if (typeof idValue === "string" && idValue.length > 0) {
129
+ if (!ID_PATTERN.test(idValue)) {
130
+ const label = source?.path ?? "inlineConfig";
131
+ throw new BotimConfigError(
132
+ `${label} field "app_id" must match ${ID_PATTERN.toString()} (got: ${JSON.stringify(idValue)})`,
133
+ { code: "config-invalid-id", path: source?.path }
134
+ );
135
+ }
136
+ miniProgramId = idValue;
137
+ } else {
138
+ miniProgramId = synthesizeMiniProgramId(projectRoot, env);
139
+ const reason = source ? `config ${(0, import_node_path.basename)(source.path)} has no app_id` : "no botim_config.*.json found for any env";
140
+ console.warn(
141
+ `[@botim/debug-sdk] ${reason}; synthesized id ${miniProgramId} (env=${env})`
122
142
  );
123
143
  }
124
144
  const md5 = typeof parsed.app?.md5 === "string" ? parsed.app.md5 : typeof parsed.package_url?.md5 === "string" ? parsed.package_url.md5 : void 0;
@@ -129,13 +149,13 @@ function resolveBotimConfig(mode, root, opts = {}) {
129
149
  } else if (versionForSig && md5) {
130
150
  buildSignature = `v${versionForSig}+md5:${md5.slice(0, 12)}`;
131
151
  } else {
132
- const sigSource = raw ?? JSON.stringify({ mp_id: idValue, env, version: versionForSig, app: parsed.app });
152
+ const sigSource = raw ?? JSON.stringify({ app_id: miniProgramId, env, version: versionForSig, app: parsed.app });
133
153
  buildSignature = "sha256:" + (0, import_node_crypto.createHash)("sha256").update(sigSource).digest("hex").slice(0, 12);
134
154
  }
135
155
  const cfg = {
136
156
  // Spread first so the canonical fields below override anything inherited.
137
157
  ...parsed,
138
- miniProgramId: idValue,
158
+ miniProgramId,
139
159
  env,
140
160
  buildSignature,
141
161
  appName: typeof parsed.app_id === "string" ? parsed.app_id : parsed.appName,
@@ -168,6 +188,20 @@ function resolveFileName(override, env, mode) {
168
188
  { code: "config-invalid-filename" }
169
189
  );
170
190
  }
191
+ function synthesizeMiniProgramId(projectRoot, env) {
192
+ const dir = (0, import_node_path.basename)(projectRoot);
193
+ const hash = (0, import_node_crypto.createHash)("sha256").update(`${dir}:${env}`).digest("hex").slice(0, 8);
194
+ return `mp-${hash}`;
195
+ }
196
+ function buildCandidatePaths(projectRoot, selected) {
197
+ const order = [selected, ...VALID_ENVS.filter((e) => e !== selected)];
198
+ const out = [];
199
+ for (const e of order) {
200
+ out.push({ env: e, path: (0, import_node_path.resolve)(projectRoot, `botim_config.${e}.json`) });
201
+ out.push({ env: e, path: (0, import_node_path.resolve)(projectRoot, `botim.${e}.json`) });
202
+ }
203
+ return out;
204
+ }
171
205
 
172
206
  // src/vite/plugin.ts
173
207
  var VIRTUAL_ID = "virtual:botim/config";
@@ -25,14 +25,14 @@ interface BotimConfig {
25
25
  *
26
26
  * external schema → SDK-internal `BotimConfig`
27
27
  * ─────────────── ──────────────────────────
28
- * mp_id miniProgramId
29
- * app_id → appId (extra; preserved)
28
+ * app_id (canonical) | miniProgramId (legacy input alias) miniProgramId
30
29
  * version → appVersion
31
30
  * app.md5 | package_url.md5 → buildSignature (deterministic per release)
32
31
  * <env from filename> → env
33
32
  *
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.
33
+ * Prefers `botim_config.{env}.json`, falls back to legacy `botim.{env}.json`,
34
+ * and borrows app_id from a sibling env when the requested env has no file.
35
+ * Build-time only (Node fs/crypto) — never import from device code.
36
36
  */
37
37
 
38
38
  /**
@@ -86,7 +86,7 @@ interface ResolveOptions {
86
86
  * JSON (`{ mp_id: 'mbrx_p2p', version: process.env.VERSION }`).
87
87
  *
88
88
  * When no file exists at the resolved path, the resolver tolerates the
89
- * miss as long as `inlineConfig` carries a usable `mp_id` (or its legacy
89
+ * miss as long as `inlineConfig` carries a usable `app_id` (or its legacy
90
90
  * `miniProgramId` alias). This is the file-less workflow — handy for
91
91
  * monorepos / native hosts that synthesize identity from env vars.
92
92
  */
@@ -99,7 +99,8 @@ interface ResolveOptions {
99
99
  * - unknown env after `mapMode` is applied
100
100
  * - missing or unreadable file
101
101
  * - invalid JSON
102
- * - missing/invalid `mp_id` (or its legacy `miniProgramId` alias)
102
+ * - missing/invalid `app_id` (or its legacy `miniProgramId` alias); when
103
+ * absent on the default file path a stable id is synthesized instead of thrown
103
104
  * - the platform marking the mini-program as deleted (`deleted: 1`)
104
105
  */
105
106
  declare function resolveBotimConfig(mode: string, root: string, opts?: ResolveOptions): BotimConfig;
@@ -25,14 +25,14 @@ interface BotimConfig {
25
25
  *
26
26
  * external schema → SDK-internal `BotimConfig`
27
27
  * ─────────────── ──────────────────────────
28
- * mp_id miniProgramId
29
- * app_id → appId (extra; preserved)
28
+ * app_id (canonical) | miniProgramId (legacy input alias) miniProgramId
30
29
  * version → appVersion
31
30
  * app.md5 | package_url.md5 → buildSignature (deterministic per release)
32
31
  * <env from filename> → env
33
32
  *
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.
33
+ * Prefers `botim_config.{env}.json`, falls back to legacy `botim.{env}.json`,
34
+ * and borrows app_id from a sibling env when the requested env has no file.
35
+ * Build-time only (Node fs/crypto) — never import from device code.
36
36
  */
37
37
 
38
38
  /**
@@ -86,7 +86,7 @@ interface ResolveOptions {
86
86
  * JSON (`{ mp_id: 'mbrx_p2p', version: process.env.VERSION }`).
87
87
  *
88
88
  * When no file exists at the resolved path, the resolver tolerates the
89
- * miss as long as `inlineConfig` carries a usable `mp_id` (or its legacy
89
+ * miss as long as `inlineConfig` carries a usable `app_id` (or its legacy
90
90
  * `miniProgramId` alias). This is the file-less workflow — handy for
91
91
  * monorepos / native hosts that synthesize identity from env vars.
92
92
  */
@@ -99,7 +99,8 @@ interface ResolveOptions {
99
99
  * - unknown env after `mapMode` is applied
100
100
  * - missing or unreadable file
101
101
  * - invalid JSON
102
- * - missing/invalid `mp_id` (or its legacy `miniProgramId` alias)
102
+ * - missing/invalid `app_id` (or its legacy `miniProgramId` alias); when
103
+ * absent on the default file path a stable id is synthesized instead of thrown
103
104
  * - the platform marking the mini-program as deleted (`deleted: 1`)
104
105
  */
105
106
  declare function resolveBotimConfig(mode: string, root: string, opts?: ResolveOptions): BotimConfig;