@scalebun/web 1.0.0 → 1.2.2

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.
Files changed (291) hide show
  1. package/dist/bootstrap/SDKBootstrapper.d.ts +6 -6
  2. package/dist/bootstrap/SDKBootstrapper.js +8 -8
  3. package/dist/browser.d.ts +1 -1
  4. package/dist/browser.d.ts.map +1 -1
  5. package/dist/browser.js +8 -6
  6. package/dist/browser.js.map +1 -1
  7. package/dist/config/endpoints.d.ts +12 -0
  8. package/dist/config/endpoints.d.ts.map +1 -0
  9. package/dist/config/endpoints.js +12 -0
  10. package/dist/config/endpoints.js.map +1 -0
  11. package/dist/config/schema.d.ts.map +1 -1
  12. package/dist/config/schema.js +5 -3
  13. package/dist/config/schema.js.map +1 -1
  14. package/dist/core/breadcrumbs.js +1 -1
  15. package/dist/core/index.d.ts +13 -13
  16. package/dist/core/index.js +13 -13
  17. package/dist/core/observe.js +1 -1
  18. package/dist/core/pageContext.d.ts +1 -1
  19. package/dist/features/analytics/AnalyticsFeature.d.ts +2 -2
  20. package/dist/features/analytics/AnalyticsFeature.js +10 -10
  21. package/dist/features/analytics/autocapture.js +4 -4
  22. package/dist/features/analytics/sampler.js +1 -1
  23. package/dist/features/bugreport/BugReportFeature.d.ts +1 -1
  24. package/dist/features/bugreport/BugReportFeature.js +1 -1
  25. package/dist/features/bugreport/collect.js +1 -1
  26. package/dist/features/bugreport/widget.js +9 -9
  27. package/dist/features/coach/CoachmarkFeature.d.ts +1 -1
  28. package/dist/features/coach/CoachmarkFeature.js +5 -5
  29. package/dist/features/coach/anchor.d.ts +1 -1
  30. package/dist/features/coach/overlay.d.ts +1 -1
  31. package/dist/features/coach/overlay.js +21 -21
  32. package/dist/features/coach/position.d.ts +1 -1
  33. package/dist/features/coach/predicate.d.ts +1 -1
  34. package/dist/features/coach/tour.d.ts +4 -4
  35. package/dist/features/coach/tour.js +1 -1
  36. package/dist/features/coach/types.d.ts +1 -1
  37. package/dist/features/coach/wait.d.ts +2 -2
  38. package/dist/features/config/ConfigFeature.d.ts +1 -1
  39. package/dist/features/config/ConfigFeature.js +5 -5
  40. package/dist/features/config/configStore.d.ts +1 -1
  41. package/dist/features/config/targeting.d.ts +1 -1
  42. package/dist/features/console/ConsoleFeature.js +3 -3
  43. package/dist/features/console/normalize.js +2 -2
  44. package/dist/features/console/throttle.d.ts +1 -1
  45. package/dist/features/crash/CrashFeature.js +2 -2
  46. package/dist/features/engage/EngageFeature.d.ts +3 -3
  47. package/dist/features/engage/EngageFeature.js +5 -5
  48. package/dist/features/engage/attachments.d.ts +1 -1
  49. package/dist/features/engage/attachments.js +3 -3
  50. package/dist/features/engage/gameLogic.d.ts +1 -1
  51. package/dist/features/engage/gameResolver.d.ts +2 -2
  52. package/dist/features/engage/gameResolver.js +1 -1
  53. package/dist/features/engage/inappSlots.d.ts +1 -1
  54. package/dist/features/engage/inappSlots.js +1 -1
  55. package/dist/features/engage/normalize.d.ts +1 -1
  56. package/dist/features/engage/richInApp.d.ts +3 -3
  57. package/dist/features/engage/richInApp.js +6 -6
  58. package/dist/features/engage/runtime.d.ts +3 -3
  59. package/dist/features/engage/runtime.js +7 -7
  60. package/dist/features/engage/shakeDetector.js +1 -1
  61. package/dist/features/engage/throttle.d.ts +1 -1
  62. package/dist/features/engage/transport.d.ts +1 -1
  63. package/dist/features/engage/transport.js +1 -1
  64. package/dist/features/engage/triggerEngine.d.ts +1 -1
  65. package/dist/features/engage/widget/InAppWidget.d.ts +1 -1
  66. package/dist/features/engage/widget/InAppWidget.js +18 -18
  67. package/dist/features/engage/widget/SurveyWidget.d.ts +1 -1
  68. package/dist/features/engage/widget/SurveyWidget.js +66 -66
  69. package/dist/features/errors/ErrorsFeature.d.ts +1 -1
  70. package/dist/features/errors/ErrorsFeature.js +6 -6
  71. package/dist/features/errors/group/fingerprint.js +1 -1
  72. package/dist/features/errors/lazy.d.ts +3 -3
  73. package/dist/features/errors/lazy.js +3 -3
  74. package/dist/features/errors/normalize.d.ts +2 -2
  75. package/dist/features/errors/normalize.js +2 -2
  76. package/dist/features/errors/redact.js +1 -1
  77. package/dist/features/errors/sampling.js +1 -1
  78. package/dist/features/exit-intent/ExitIntentFeature.js +1 -1
  79. package/dist/features/exit-intent/engine.js +1 -1
  80. package/dist/features/forms/index.js +1 -1
  81. package/dist/features/inapp/InApp2Feature.js +1 -1
  82. package/dist/features/inapp/anchor/controller.d.ts +2 -2
  83. package/dist/features/inapp/anchor/controller.js +3 -3
  84. package/dist/features/inapp/api.d.ts +5 -5
  85. package/dist/features/inapp/api.js +3 -3
  86. package/dist/features/inapp/blocks/blocks.d.ts +1 -1
  87. package/dist/features/inapp/blocks/blocks.js +1 -1
  88. package/dist/features/inapp/blocks/feedback.d.ts +1 -1
  89. package/dist/features/inapp/blocks/heavy/carousel.d.ts +1 -1
  90. package/dist/features/inapp/blocks/heavy/chart.d.ts +1 -1
  91. package/dist/features/inapp/blocks/heavy/code.d.ts +1 -1
  92. package/dist/features/inapp/blocks/heavy/confetti.d.ts +1 -1
  93. package/dist/features/inapp/blocks/heavy/countdown.d.ts +1 -1
  94. package/dist/features/inapp/blocks/heavy/index.d.ts +1 -1
  95. package/dist/features/inapp/blocks/heavy/index.js +18 -18
  96. package/dist/features/inapp/blocks/heavy/markdown.d.ts +1 -1
  97. package/dist/features/inapp/blocks/heavy/productgrid.d.ts +1 -1
  98. package/dist/features/inapp/blocks/heavy/qr.d.ts +1 -1
  99. package/dist/features/inapp/blocks/heavy/scratch.d.ts +1 -1
  100. package/dist/features/inapp/blocks/heavy/scratch.js +1 -1
  101. package/dist/features/inapp/blocks/heavy/slot.d.ts +1 -1
  102. package/dist/features/inapp/blocks/heavy/slot.js +1 -1
  103. package/dist/features/inapp/blocks/heavy/spinwheel.d.ts +1 -1
  104. package/dist/features/inapp/blocks/heavy/spinwheel.js +1 -1
  105. package/dist/features/inapp/blocks/heavy/steps.d.ts +1 -1
  106. package/dist/features/inapp/blocks/heavy/streak.d.ts +1 -1
  107. package/dist/features/inapp/blocks/heavy/table.d.ts +1 -1
  108. package/dist/features/inapp/blocks/heavy/unavailable.d.ts +1 -1
  109. package/dist/features/inapp/blocks/heavy/video.d.ts +1 -1
  110. package/dist/features/inapp/blocks/styles.js +191 -191
  111. package/dist/features/inapp/center/bridge.d.ts +3 -3
  112. package/dist/features/inapp/center/bridge.js +1 -1
  113. package/dist/features/inapp/center/panel.d.ts +2 -2
  114. package/dist/features/inapp/center/panel.js +33 -33
  115. package/dist/features/inapp/center/store.d.ts +1 -1
  116. package/dist/features/inapp/center/sync.d.ts +1 -1
  117. package/dist/features/inapp/center/types.d.ts +1 -1
  118. package/dist/features/inapp/core/analytics.d.ts +3 -3
  119. package/dist/features/inapp/core/analytics.js +1 -1
  120. package/dist/features/inapp/core/compat.d.ts +2 -2
  121. package/dist/features/inapp/core/debugOverlay.d.ts +2 -2
  122. package/dist/features/inapp/core/debugOverlay.js +12 -12
  123. package/dist/features/inapp/core/engine.d.ts +3 -3
  124. package/dist/features/inapp/core/engine.js +10 -10
  125. package/dist/features/inapp/core/host.js +22 -22
  126. package/dist/features/inapp/core/metrics.d.ts +1 -1
  127. package/dist/features/inapp/core/migration.d.ts +2 -2
  128. package/dist/features/inapp/core/migration.js +2 -2
  129. package/dist/features/inapp/core/orchestrator.d.ts +1 -1
  130. package/dist/features/inapp/core/respect.d.ts +1 -1
  131. package/dist/features/inapp/core/targeting.d.ts +1 -1
  132. package/dist/features/inapp/core/targeting.js +1 -1
  133. package/dist/features/inapp/core/triggers.d.ts +1 -1
  134. package/dist/features/inapp/core/triggers.js +2 -2
  135. package/dist/features/inapp/inappContract.web.d.ts +1 -1
  136. package/dist/features/inapp/index.d.ts +15 -15
  137. package/dist/features/inapp/index.js +10 -10
  138. package/dist/features/inapp/registry.web.canonical.d.ts +1 -1
  139. package/dist/features/inapp/registry.web.catalog.d.ts +1 -1
  140. package/dist/features/inapp/surfaces/banner.d.ts +1 -1
  141. package/dist/features/inapp/surfaces/banner.js +1 -1
  142. package/dist/features/inapp/surfaces/checklist.d.ts +1 -1
  143. package/dist/features/inapp/surfaces/coachmark.d.ts +1 -1
  144. package/dist/features/inapp/surfaces/coachmark.js +2 -2
  145. package/dist/features/inapp/surfaces/hotspot.d.ts +1 -1
  146. package/dist/features/inapp/surfaces/hotspot.js +2 -2
  147. package/dist/features/inapp/surfaces/inline.d.ts +4 -4
  148. package/dist/features/inapp/surfaces/inline.js +11 -11
  149. package/dist/features/inapp/surfaces/launcher.d.ts +1 -1
  150. package/dist/features/inapp/surfaces/modal.d.ts +1 -1
  151. package/dist/features/inapp/surfaces/modal.js +2 -2
  152. package/dist/features/inapp/surfaces/mount.d.ts +4 -4
  153. package/dist/features/inapp/surfaces/mount.js +4 -4
  154. package/dist/features/inapp/surfaces/palette.d.ts +1 -1
  155. package/dist/features/inapp/surfaces/palette.js +1 -1
  156. package/dist/features/inapp/surfaces/panel.d.ts +1 -1
  157. package/dist/features/inapp/surfaces/popover.d.ts +1 -1
  158. package/dist/features/inapp/surfaces/popover.js +2 -2
  159. package/dist/features/inapp/surfaces/render.d.ts +4 -4
  160. package/dist/features/inapp/surfaces/render.d.ts.map +1 -1
  161. package/dist/features/inapp/surfaces/render.js +23 -19
  162. package/dist/features/inapp/surfaces/render.js.map +1 -1
  163. package/dist/features/inapp/surfaces/slideover.d.ts +1 -1
  164. package/dist/features/inapp/surfaces/slideover.js +1 -1
  165. package/dist/features/inapp/surfaces/styles.js +179 -179
  166. package/dist/features/inapp/surfaces/surface.d.ts +4 -4
  167. package/dist/features/inapp/surfaces/surface.js +2 -2
  168. package/dist/features/inapp/surfaces/takeover.d.ts +1 -1
  169. package/dist/features/inapp/surfaces/takeover.js +2 -2
  170. package/dist/features/inapp/surfaces/toast.d.ts +1 -1
  171. package/dist/features/inapp/surfaces/toast.js +1 -1
  172. package/dist/features/inapp/surfaces/toolbar.d.ts +1 -1
  173. package/dist/features/inapp/surfaces/toolbar.js +2 -2
  174. package/dist/features/inapp/surfaces/tour.d.ts +1 -1
  175. package/dist/features/inapp/surfaces/tour.js +5 -5
  176. package/dist/features/inbox/InboxFeature.d.ts +1 -1
  177. package/dist/features/inbox/InboxFeature.js +5 -5
  178. package/dist/features/inbox/runtime.d.ts +2 -2
  179. package/dist/features/inbox/runtime.js +3 -3
  180. package/dist/features/inbox/store.d.ts +1 -1
  181. package/dist/features/inbox/sync.d.ts +2 -2
  182. package/dist/features/inbox/sync.js +1 -1
  183. package/dist/features/inbox/widget/InboxWidget.d.ts +1 -1
  184. package/dist/features/inbox/widget/InboxWidget.js +85 -85
  185. package/dist/features/journey/InteractionTracker.js +1 -1
  186. package/dist/features/journey/JourneyFeature.js +3 -3
  187. package/dist/features/manifest.d.ts +1 -1
  188. package/dist/features/manifest.js +18 -18
  189. package/dist/features/network/NetworkFeature.d.ts +1 -1
  190. package/dist/features/network/NetworkFeature.js +10 -10
  191. package/dist/features/network/capture/body.d.ts +1 -1
  192. package/dist/features/network/capture/body.js +1 -1
  193. package/dist/features/network/classify.d.ts +1 -1
  194. package/dist/features/network/fetchInterceptor.d.ts +1 -1
  195. package/dist/features/network/fetchInterceptor.js +1 -1
  196. package/dist/features/network/intercept/beacon.d.ts +1 -1
  197. package/dist/features/network/intercept/sse.d.ts +1 -1
  198. package/dist/features/network/intercept/streams.d.ts +4 -4
  199. package/dist/features/network/intercept/streams.js +3 -3
  200. package/dist/features/network/intercept/websocket.d.ts +1 -1
  201. package/dist/features/network/lazy.d.ts +3 -3
  202. package/dist/features/network/lazy.js +2 -2
  203. package/dist/features/network/normalize.d.ts +1 -1
  204. package/dist/features/network/normalize.js +3 -3
  205. package/dist/features/network/parse/graphql.d.ts +1 -1
  206. package/dist/features/network/sampler.js +1 -1
  207. package/dist/features/network/timing.d.ts +1 -1
  208. package/dist/features/network/xhrInterceptor.d.ts +1 -1
  209. package/dist/features/network/xhrInterceptor.js +1 -1
  210. package/dist/features/performance/PerformanceFeature.d.ts +2 -2
  211. package/dist/features/performance/PerformanceFeature.js +11 -11
  212. package/dist/features/performance/longTasks.js +1 -1
  213. package/dist/features/performance/normalize.js +1 -1
  214. package/dist/features/performance/sampler.js +1 -1
  215. package/dist/features/performance/trace/index.d.ts +4 -4
  216. package/dist/features/performance/trace/index.js +3 -3
  217. package/dist/features/performance/trace/pageload.d.ts +1 -1
  218. package/dist/features/performance/trace/pageload.js +1 -1
  219. package/dist/features/performance/trace/transaction.d.ts +2 -2
  220. package/dist/features/performance/trace/transaction.js +2 -2
  221. package/dist/features/profiler/ProfilerFeature.js +1 -1
  222. package/dist/features/push/PushFeature.d.ts +4 -4
  223. package/dist/features/push/PushFeature.js +3 -3
  224. package/dist/features/push/softAsk.d.ts +1 -1
  225. package/dist/features/push/softAsk.js +26 -26
  226. package/dist/features/replay/ReplayFeature.d.ts +1 -1
  227. package/dist/features/replay/ReplayFeature.js +12 -12
  228. package/dist/features/replay/engine/canvas.d.ts +2 -2
  229. package/dist/features/replay/engine/crossOrigin.d.ts +1 -1
  230. package/dist/features/replay/engine/crossOrigin.js +3 -3
  231. package/dist/features/replay/engine/cssom.d.ts +2 -2
  232. package/dist/features/replay/engine/iframe.d.ts +2 -2
  233. package/dist/features/replay/engine/iframe.js +3 -3
  234. package/dist/features/replay/engine/interactions.d.ts +1 -1
  235. package/dist/features/replay/engine/interactions.js +1 -1
  236. package/dist/features/replay/engine/mutations.d.ts +3 -3
  237. package/dist/features/replay/engine/mutations.js +2 -2
  238. package/dist/features/replay/engine/player.d.ts +2 -2
  239. package/dist/features/replay/engine/player.js +1 -1
  240. package/dist/features/replay/engine/rebuild.d.ts +1 -1
  241. package/dist/features/replay/engine/replayWorker.js +16 -16
  242. package/dist/features/replay/engine/resources.d.ts +1 -1
  243. package/dist/features/replay/engine/resources.js +1 -1
  244. package/dist/features/replay/engine/snapshot.d.ts +2 -2
  245. package/dist/features/replay/engine/snapshot.js +1 -1
  246. package/dist/features/replay/engine/traverse.d.ts +1 -1
  247. package/dist/features/replay/lifecycle/recorder.d.ts +1 -1
  248. package/dist/features/replay/lifecycle/recorder.js +20 -20
  249. package/dist/features/replay/lifecycle/sampling.js +1 -1
  250. package/dist/features/replay/lifecycle/viewContext.js +1 -1
  251. package/dist/features/scroll/index.js +1 -1
  252. package/dist/features/session/sessionManager.d.ts +1 -1
  253. package/dist/features/session/sessionManager.js +2 -2
  254. package/dist/features/session/sessionStore.js +1 -1
  255. package/dist/features/watchdog/WatchdogFeature.d.ts +1 -1
  256. package/dist/features/watchdog/WatchdogFeature.js +1 -1
  257. package/dist/features/watchdog/collectors.d.ts +1 -1
  258. package/dist/features/watchdog/collectors.js +1 -1
  259. package/dist/features/watchdog/engine.js +8 -8
  260. package/dist/features/watchdog/incidents.d.ts +1 -1
  261. package/dist/features/watchdog/incidents.js +1 -1
  262. package/dist/features/watchdog/rollup.d.ts +1 -1
  263. package/dist/features/watchdog/rollup.js +1 -1
  264. package/dist/features/webVitals/WebVitalsFeature.js +4 -4
  265. package/dist/features/webVitals/observe.d.ts +1 -1
  266. package/dist/features/webVitals/observe.js +1 -1
  267. package/dist/features/webperf/WebPerfFeature.d.ts +1 -1
  268. package/dist/features/webperf/WebPerfFeature.js +3 -3
  269. package/dist/features/webperf/collectors.js +1 -1
  270. package/dist/inapp.d.ts +1 -1
  271. package/dist/inapp.js +1 -1
  272. package/dist/index.d.ts +3 -3
  273. package/dist/index.js +1 -1
  274. package/dist/pipeline/index.d.ts +1 -1
  275. package/dist/pipeline/index.js +1 -1
  276. package/dist/pipeline/pipeline.d.ts +3 -3
  277. package/dist/pipeline/pipeline.js +4 -4
  278. package/dist/public/ScaleBunFacade.d.ts +18 -18
  279. package/dist/public/ScaleBunFacade.js +11 -11
  280. package/dist/public/index.d.ts +4 -4
  281. package/dist/public/index.js +2 -2
  282. package/dist/scalebun-push-sw.js +236 -236
  283. package/dist/scalebun.min.js +17 -17
  284. package/dist/scalebun.min.js.map +4 -4
  285. package/dist/storage/index.d.ts +1 -1
  286. package/dist/storage/index.js +1 -1
  287. package/dist/transport/httpClient.d.ts +1 -1
  288. package/dist/transport/httpClient.js +3 -3
  289. package/dist/transport/index.d.ts +1 -1
  290. package/dist/transport/index.js +1 -1
  291. package/package.json +7 -7
@@ -8,7 +8,7 @@
8
8
  * Zero third-party deps (web SDK core rule). Tiny + core (not lazy) — flags must resolve early/sync.
9
9
  */
10
10
  import type { IFeature, FeatureContext } from '@scalebun/sdk-contracts';
11
- import { type VariationDetail } from './types';
11
+ import { type VariationDetail } from './types.js';
12
12
  export declare class ConfigFeature implements IFeature {
13
13
  readonly name = "config";
14
14
  private ctx;
@@ -1,8 +1,8 @@
1
- import { now, getEvalContext, patchEvalContext, getReplayCoord } from '../../core';
2
- import { bucketOf, pickVariant } from './bucketing';
3
- import { evaluateTargeting } from './targeting';
4
- import { loadConfig, saveConfig, loadOverrides, saveOverrides } from './configStore';
5
- import { EMPTY_CONFIG } from './types';
1
+ import { now, getEvalContext, patchEvalContext, getReplayCoord } from '../../core/index.js';
2
+ import { bucketOf, pickVariant } from './bucketing.js';
3
+ import { evaluateTargeting } from './targeting.js';
4
+ import { loadConfig, saveConfig, loadOverrides, saveOverrides } from './configStore.js';
5
+ import { EMPTY_CONFIG } from './types.js';
6
6
  const TTL_MS = 5 * 60 * 1000; // refresh on foreground past this age (mirror RN)
7
7
  /** A flag's bucketing salt. Rule rollouts always hashed on the flag KEY, which meant renaming a flag
8
8
  * silently re-bucketed its whole population. The server now sends an immutable `salt` (backfilled to
@@ -3,7 +3,7 @@
3
3
  * means flags are available SYNCHRONOUSLY on the next page load (before the network resolves) — no
4
4
  * flash of default values. All access is best-effort + guarded (SSR / disabled storage / quota).
5
5
  */
6
- import type { SdkConfigPayload } from './types';
6
+ import type { SdkConfigPayload } from './types.js';
7
7
  export declare function loadConfig(): SdkConfigPayload | null;
8
8
  export declare function saveConfig(payload: SdkConfigPayload): void;
9
9
  export declare function loadOverrides(): Record<string, unknown>;
@@ -3,7 +3,7 @@
3
3
  * return the first matching rule (top-down) or null. Conditions read context attributes (dot-path
4
4
  * supported) and apply an operator. Kept tiny — the whole engine is a handful of comparisons.
5
5
  */
6
- import type { TargetRule } from './types';
6
+ import type { TargetRule } from './types.js';
7
7
  type Ctx = Record<string, unknown>;
8
8
  /** First rule (top-down) whose conditions match the context, or null when none do. */
9
9
  export declare function evaluateTargeting(rules: TargetRule[] | undefined, ctx: Ctx): TargetRule | null;
@@ -8,9 +8,9 @@
8
8
  * serialize/emit path may log — a re-entrancy flag + a `[scalebun]` prefix skip prevent capturing
9
9
  * our own diagnostics (which would otherwise recurse / pollute the customer's stream).
10
10
  */
11
- import { WZB_LOG_PREFIX } from '../../core/logger';
12
- import { toLogItem, levelFor } from './normalize';
13
- import { LogThrottle } from './throttle';
11
+ import { WZB_LOG_PREFIX } from '../../core/logger.js';
12
+ import { toLogItem, levelFor } from './normalize.js';
13
+ import { LogThrottle } from './throttle.js';
14
14
  const METHODS = ['debug', 'info', 'log', 'warn', 'error'];
15
15
  const RING_MAX = 50;
16
16
  export class ConsoleFeature {
@@ -1,6 +1,6 @@
1
1
  /** Builds a contract `log` Item from a console call. Pure — shared by the wrapper + tests. */
2
- import { uuid, now, scrubText } from '../../core';
3
- import { safeSerialize, firstErrorStack } from './serialize';
2
+ import { uuid, now, scrubText } from '../../core/index.js';
3
+ import { safeSerialize, firstErrorStack } from './serialize.js';
4
4
  /**
5
5
  * Map a console method → contract level. The contract has no `log` level, so `console.log` collapses
6
6
  * to `info` (the conventional mapping); the literal method is preserved in `metadata.method`.
@@ -4,7 +4,7 @@
4
4
  * counts what it drops (no silent caps). The one difference: it's LEVEL-AWARE — `warn`/`error` are
5
5
  * the debugging-valuable lines and are ALWAYS kept; only chatty `info`/`debug` get sampled + capped.
6
6
  */
7
- import type { LogLevel } from './normalize';
7
+ import type { LogLevel } from './normalize.js';
8
8
  export interface LogThrottleOptions {
9
9
  /** Hard cap on sampled (info/debug) logs accepted per `capWindowMs`. */
10
10
  capCount?: number;
@@ -1,5 +1,5 @@
1
- import { uuid, now, scrubText } from '../../core';
2
- import { getTabId, readRecord, writeRecord, markEnded, touch, cleanupStale, } from './liveness';
1
+ import { uuid, now, scrubText } from '../../core/index.js';
2
+ import { getTabId, readRecord, writeRecord, markEnded, touch, cleanupStale, } from './liveness.js';
3
3
  const HEARTBEAT_MS = 15000;
4
4
  export class CrashFeature {
5
5
  constructor() {
@@ -10,7 +10,7 @@
10
10
  * honouring each campaign's REMAINING delay — so a "show on session #1 / app_open" campaign isn't lost.
11
11
  */
12
12
  import type { IFeature, FeatureContext } from '@scalebun/sdk-contracts';
13
- import type { LifecycleKind } from './triggerEngine';
13
+ import type { LifecycleKind } from './triggerEngine.js';
14
14
  export declare class EngageFeature implements IFeature {
15
15
  readonly name = "engage";
16
16
  private ctx;
@@ -32,9 +32,9 @@ export declare class EngageFeature implements IFeature {
32
32
  * emitRatingEvent/submit). Returns undefined only before setup / without a transport. */
33
33
  private ensureRuntime;
34
34
  /** Programmatic submit for a host's own survey UI → 'success'|'error' (transport-retried). */
35
- submit(surveyId: string, result: import('./types').SurveyResult): Promise<'success' | 'error'>;
35
+ submit(surveyId: string, result: import('./types.js').SurveyResult): Promise<'success' | 'error'>;
36
36
  /** Emit one rating-funnel event (works even without a served campaign). */
37
- emitRatingEvent(type: import('./types').RatingEventType, input?: import('./types').RatingEventInput): void;
37
+ emitRatingEvent(type: import('./types.js').RatingEventType, input?: import('./types.js').RatingEventInput): void;
38
38
  /** Dev/QA — clear on-device engage state (throttle/session/test-seen) so campaigns show again. */
39
39
  resetInAppState(): Promise<void>;
40
40
  onScreenView(screen: string): void;
@@ -1,5 +1,5 @@
1
- import { uuid, now, getReplayCoord } from '../../core';
2
- import { EngageTransport } from './transport';
1
+ import { uuid, now, getReplayCoord } from '../../core/index.js';
2
+ import { EngageTransport } from './transport.js';
3
3
  const BUF_CAP = 32; // bounded ring — a burst of pre-config events can't grow unbounded
4
4
  const BUF_TTL_MS = 30000; // a buffered trigger older than this is stale on replay → dropped
5
5
  const SESSIONS_KEY = 'scalebun_engage_sessions';
@@ -78,7 +78,7 @@ export class EngageFeature {
78
78
  this.closeBuffer(); // cold-start window is over; nothing wanted the pre-config events
79
79
  return;
80
80
  }
81
- const { normalizeConfig } = await import('./normalize');
81
+ const { normalizeConfig } = await import('./normalize.js');
82
82
  const cfg = normalizeConfig(raw);
83
83
  const rt = await this.ensureRuntime();
84
84
  if (!rt)
@@ -94,7 +94,7 @@ export class EngageFeature {
94
94
  return this.runtime;
95
95
  if (!this.transport)
96
96
  return undefined;
97
- const { EngageRuntime } = await import('./runtime');
97
+ const { EngageRuntime } = await import('./runtime.js');
98
98
  this.runtime = new EngageRuntime({
99
99
  transport: this.transport,
100
100
  deviceId: this.ctx?.deviceId ?? '',
@@ -121,7 +121,7 @@ export class EngageFeature {
121
121
  }
122
122
  /** Dev/QA — clear on-device engage state (throttle/session/test-seen) so campaigns show again. */
123
123
  async resetInAppState() {
124
- const { clearEngageState } = await import('./throttle');
124
+ const { clearEngageState } = await import('./throttle.js');
125
125
  clearEngageState();
126
126
  }
127
127
  // ── Host-/producer-driven triggers (forward live, or buffer during cold-start) ──
@@ -4,7 +4,7 @@
4
4
  * permission prompt, no rasterizer); voice uses MediaRecorder + optional Web Speech transcription.
5
5
  * Every capture is feature-detected and degrades to null on unsupported/denied — never throws.
6
6
  */
7
- import { type AttachmentDraft } from './types';
7
+ import { type AttachmentDraft } from './types.js';
8
8
  /** Capture a masked DOM snapshot of the current document as the "screenshot" attachment. Reuses the
9
9
  * replay engine's serializer so the same masking applies — raw PII never leaves the page. */
10
10
  export declare function captureScreenshot(mask: boolean): Promise<AttachmentDraft | null>;
@@ -4,7 +4,7 @@
4
4
  * permission prompt, no rasterizer); voice uses MediaRecorder + optional Web Speech transcription.
5
5
  * Every capture is feature-detected and degrades to null on unsupported/denied — never throws.
6
6
  */
7
- import { VOICE_MAX_DURATION_MS } from './types';
7
+ import { VOICE_MAX_DURATION_MS } from './types.js';
8
8
  const SNAPSHOT_MIME = 'application/scalebun-snapshot+json';
9
9
  /** Capture a masked DOM snapshot of the current document as the "screenshot" attachment. Reuses the
10
10
  * replay engine's serializer so the same masking applies — raw PII never leaves the page. */
@@ -13,8 +13,8 @@ export async function captureScreenshot(mask) {
13
13
  return null;
14
14
  try {
15
15
  const [{ takeSnapshot }, { NodeRegistry }] = await Promise.all([
16
- import('../replay/engine/snapshot'),
17
- import('../replay/engine/nodeRegistry'),
16
+ import('../replay/engine/snapshot.js'),
17
+ import('../replay/engine/nodeRegistry.js'),
18
18
  ]);
19
19
  const snapshot = takeSnapshot(document, new NodeRegistry(), { maskAllText: mask, maskAllInputs: mask });
20
20
  const blob = new Blob([JSON.stringify(snapshot)], { type: SNAPSHOT_MIME });
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * Lives in a lazy chunk: it serves 8 of 100 variants and core has ~250 B of headroom.
14
14
  */
15
- import type { InAppGameAction, InAppGameRequest, InAppGameResult, InAppGameVariantKey } from './gameTypes';
15
+ import type { InAppGameAction, InAppGameRequest, InAppGameResult, InAppGameVariantKey } from './gameTypes.js';
16
16
  type Dict = Record<string, unknown>;
17
17
  export declare function isInAppGameVariant(value: unknown): value is InAppGameVariantKey;
18
18
  export declare function inAppGameActionFor(value: unknown): InAppGameAction | null;
@@ -13,8 +13,8 @@
13
13
  *
14
14
  * Lazy by construction — imported only from `richInApp`, which is itself in the engage chunk.
15
15
  */
16
- import type { BlockOutcome } from '../inapp/blocks/blocks';
17
- import type { InAppGameRequestHandler } from './gameTypes';
16
+ import type { BlockOutcome } from '../inapp/blocks/blocks.js';
17
+ import type { InAppGameRequestHandler } from './gameTypes.js';
18
18
  type Dict = Record<string, unknown>;
19
19
  export interface GameResolverInput {
20
20
  campaignId: string;
@@ -1,4 +1,4 @@
1
- import { attemptIdFor, authoredPreviewGameResult, buildInAppGameRequest, inAppGameAllowedOutcomeIds, isInAppGameVariant, validateInAppGameResult, } from './gameLogic';
1
+ import { attemptIdFor, authoredPreviewGameResult, buildInAppGameRequest, inAppGameAllowedOutcomeIds, isInAppGameVariant, validateInAppGameResult, } from './gameLogic.js';
2
2
  const str = (v) => (typeof v === 'string' ? v.trim() : '');
3
3
  /** Module-scope so two messages on one page cannot mint the same attempt id. */
4
4
  let attemptSequence = 0;
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * A host that never calls `registerSlot` sees exactly today's behaviour.
18
18
  */
19
- import { SlotRegistry } from '../inapp/core/slots';
19
+ import { SlotRegistry } from '../inapp/core/slots.js';
20
20
  /**
21
21
  * One registry for the whole page.
22
22
  *
@@ -16,7 +16,7 @@
16
16
  *
17
17
  * A host that never calls `registerSlot` sees exactly today's behaviour.
18
18
  */
19
- import { SlotRegistry } from '../inapp/core/slots';
19
+ import { SlotRegistry } from '../inapp/core/slots.js';
20
20
  /**
21
21
  * One registry for the whole page.
22
22
  *
@@ -4,7 +4,7 @@
4
4
  * optionals default, and a malformed campaign/message is DROPPED rather than crashing. Pure, total, and
5
5
  * dependency-free (web-SDK core rule) — no `as`-cast of network data leaks past this file.
6
6
  */
7
- import type { EngageConfigResponse } from './types';
7
+ import type { EngageConfigResponse } from './types.js';
8
8
  /** Coerce the raw `/sdk/engage/config` JSON into a typed, safe `EngageConfigResponse`. Never throws;
9
9
  * malformed campaigns/messages are dropped; missing arrays become []. */
10
10
  export declare function normalizeConfig(raw: unknown): EngageConfigResponse;
@@ -10,9 +10,9 @@
10
10
  * The mechanic mapping mirrors the dashboard preview's webPreviewVariant.ts (archetype → block), so
11
11
  * what the author previews is what the SDK now ships.
12
12
  */
13
- import type { Variant } from '../inapp/types';
14
- import type { InAppMessage, InAppRenderSpec } from './types';
15
- import type { InAppGameRequestHandler } from './gameTypes';
13
+ import type { Variant } from '../inapp/types.js';
14
+ import type { InAppMessage, InAppRenderSpec } from './types.js';
15
+ import type { InAppGameRequestHandler } from './gameTypes.js';
16
16
  /** Why a message did not take the rich path — reported rather than swallowed (C4). */
17
17
  export interface RichDegrade {
18
18
  variantKey?: string;
@@ -1,7 +1,7 @@
1
- import { readableOn, sanitizeTheme } from './theme';
2
- import { makeGameResolver } from './gameResolver';
3
- import { engageSlots, hasInAppSlot, slotNameFor } from './inappSlots';
4
- import { inAppGameActionFor } from './gameLogic';
1
+ import { readableOn, sanitizeTheme } from './theme.js';
2
+ import { makeGameResolver } from './gameResolver.js';
3
+ import { engageSlots, hasInAppSlot, slotNameFor } from './inappSlots.js';
4
+ import { inAppGameActionFor } from './gameLogic.js';
5
5
  const rec = (v) => (v && typeof v === 'object' && !Array.isArray(v) ? v : {});
6
6
  const str = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
7
7
  const num = (v, d) => (typeof v === 'number' && Number.isFinite(v) ? v : d);
@@ -976,8 +976,8 @@ function applyEngageTheme(el, spec) {
976
976
  */
977
977
  export async function renderRichInApp(m, spec, cb) {
978
978
  const [{ renderVariant }, { ShadowHost }] = await Promise.all([
979
- import('../inapp/surfaces/render'),
980
- import('../inapp/core/host'),
979
+ import('../inapp/surfaces/render.js'),
980
+ import('../inapp/core/host.js'),
981
981
  ]);
982
982
  const host = new ShadowHost();
983
983
  host.ensure();
@@ -3,9 +3,9 @@
3
3
  * campaign is actually active, so it stays out of the core bundle. Orchestrates: trigger fires →
4
4
  * throttle gate → render widget → beacons (shown/started) + response + lifecycle events.
5
5
  */
6
- import { type LifecycleKind } from './triggerEngine';
7
- import type { EngageTransport } from './transport';
8
- import type { EngageCampaign, SurveyResult, InAppMessage, RatingEventType, RatingEventInput, ReplayCoord } from './types';
6
+ import { type LifecycleKind } from './triggerEngine.js';
7
+ import type { EngageTransport } from './transport.js';
8
+ import type { EngageCampaign, SurveyResult, InAppMessage, RatingEventType, RatingEventInput, ReplayCoord } from './types.js';
9
9
  export type SubmitStage = 'success' | 'error';
10
10
  export interface RuntimeDeps {
11
11
  transport: EngageTransport;
@@ -3,12 +3,12 @@
3
3
  * campaign is actually active, so it stays out of the core bundle. Orchestrates: trigger fires →
4
4
  * throttle gate → render widget → beacons (shown/started) + response + lifecycle events.
5
5
  */
6
- import { TriggerEngine } from './triggerEngine';
7
- import { passesThrottle, recordShown, recordCompleted, hasTestSeen, markTestSeen } from './throttle';
8
- import { mountSurvey } from './widget/SurveyWidget';
9
- import { mountInApp } from './widget/InAppWidget';
10
- import { isRichInApp, renderRichInApp, setInAppGameHandler } from './richInApp';
11
- import { fnv1a32 } from '../config/bucketing';
6
+ import { TriggerEngine } from './triggerEngine.js';
7
+ import { passesThrottle, recordShown, recordCompleted, hasTestSeen, markTestSeen } from './throttle.js';
8
+ import { mountSurvey } from './widget/SurveyWidget.js';
9
+ import { mountInApp } from './widget/InAppWidget.js';
10
+ import { isRichInApp, renderRichInApp, setInAppGameHandler } from './richInApp.js';
11
+ import { fnv1a32 } from '../config/bucketing.js';
12
12
  export class EngageRuntime {
13
13
  constructor(deps) {
14
14
  this.deps = deps;
@@ -53,7 +53,7 @@ export class EngageRuntime {
53
53
  async syncShake() {
54
54
  const wanted = (this.shakeInCampaigns || this.shakeInMessages) && !!this.deps.signal;
55
55
  if (wanted && !this.shakeTeardown) {
56
- const { installShakeDetector } = await import('./shakeDetector');
56
+ const { installShakeDetector } = await import('./shakeDetector.js');
57
57
  // Re-check: an interleaved setMessages/stop may have flipped the flag while the chunk loaded.
58
58
  if ((this.shakeInCampaigns || this.shakeInMessages) && this.deps.signal && !this.shakeTeardown) {
59
59
  this.shakeTeardown = installShakeDetector((r) => this.deps.signal(r));
@@ -10,7 +10,7 @@
10
10
  * denial isn't re-prompted. Android/desktop need no permission (desktop simply never fires motion
11
11
  * events → the whole thing is a silent no-op). Every failure path degrades to no-op, never throws.
12
12
  */
13
- import { now } from '../../core';
13
+ import { now } from '../../core/index.js';
14
14
  const SHAKE_THRESHOLD_G = 2.3; // total |a| in g — matches native (Android/iOS 2.3)
15
15
  const SHAKE_COOLDOWN_MS = 1500;
16
16
  const G = 9.80665; // m/s² per g; `devicemotion` reports m/s², native normalizes the same way
@@ -4,7 +4,7 @@
4
4
  * server does NOT track: showOnce · cooldownHours · maxImpressions — all REVISION-SCOPED (an author
5
5
  * editing the campaign bumps `revision`, which invalidates the old counters so the new design shows).
6
6
  */
7
- import type { Throttle } from './types';
7
+ import type { Throttle } from './types.js';
8
8
  /** Anything frequency-capped (surveys + in-app messages both satisfy this). */
9
9
  export interface ThrottleTarget {
10
10
  id: string;
@@ -1,4 +1,4 @@
1
- import type { EngageResponseItem, EngageEventItem, EngageImpressionItem, RatingEventItem, AttachmentPresignItem, AttachmentPresignResult } from './types';
1
+ import type { EngageResponseItem, EngageEventItem, EngageImpressionItem, RatingEventItem, AttachmentPresignItem, AttachmentPresignResult } from './types.js';
2
2
  export interface ConfigQuery {
3
3
  platform?: string;
4
4
  appVersion?: string;
@@ -3,7 +3,7 @@
3
3
  * active survey config, then post impressions (shown/started), responses, and lifecycle events
4
4
  * (impression/completion/dismiss). All best-effort + never throws into the host.
5
5
  */
6
- import { PersistentBuffer } from '../../storage/idb';
6
+ import { PersistentBuffer } from '../../storage/idb.js';
7
7
  /**
8
8
  * Cap on parked (undelivered) payloads.
9
9
  *
@@ -8,7 +8,7 @@
8
8
  * time_in_app ← a foreground timer · session_start / app_open / n_sessions / before_logout ← lifecycle
9
9
  * Honors targetScreen + delaySeconds (and, on cold-start replay, the REMAINING delay via `elapsed`).
10
10
  */
11
- import type { TriggerConfig } from './types';
11
+ import type { TriggerConfig } from './types.js';
12
12
  /** The minimal shape the engine needs — both surveys and in-app messages satisfy it. */
13
13
  export interface Triggerable {
14
14
  id: string;
@@ -1,4 +1,4 @@
1
- import type { InAppLayout, InAppRenderSpec, InAppCta } from '../types';
1
+ import type { InAppLayout, InAppRenderSpec, InAppCta } from '../types.js';
2
2
  export interface InAppHandle {
3
3
  close(): void;
4
4
  }
@@ -3,7 +3,7 @@
3
3
  * fullscreen / bottom_sheet / top_banner / bottom_banner / tooltip / toast) from an InAppRenderSpec
4
4
  * inside an isolated SHADOW DOM. Reports click (with the CTA) + dismiss. Zero deps, hand-built DOM.
5
5
  */
6
- import { applyNonce } from '../../../core';
6
+ import { applyNonce } from '../../../core/index.js';
7
7
  const BACKDROP = ['modal', 'fullscreen', 'bottom_sheet'];
8
8
  function positionCss(layout) {
9
9
  switch (layout) {
@@ -34,23 +34,23 @@ export function mountInApp(layout, spec, cb) {
34
34
  const accent = spec.style?.accent ?? '#111827';
35
35
  const backdrop = BACKDROP.includes(layout);
36
36
  const style = applyNonce(document.createElement('style'));
37
- style.textContent = `
38
- :host{all:initial}
39
- .ov{position:fixed;inset:0;display:flex;z-index:2147483600;
40
- background:${backdrop ? 'rgba(0,0,0,.28)' : 'transparent'};
41
- pointer-events:${backdrop ? 'auto' : 'none'};
42
- font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
43
- .card{pointer-events:auto;background:${bg};color:${fg};box-sizing:border-box;padding:16px 18px;
44
- box-shadow:0 8px 40px rgba(0,0,0,.22);position:relative}
45
- img{width:100%;border-radius:8px;margin-bottom:10px;display:block}
46
- .t{font-size:16px;font-weight:700;margin:0 0 6px}
47
- .b{font-size:14px;line-height:1.45;margin:0 0 12px}
48
- .coupon{display:inline-block;font-family:monospace;font-size:13px;border:1px dashed ${accent};
49
- border-radius:6px;padding:4px 10px;margin:0 0 12px}
50
- .acts{display:flex;gap:10px;justify-content:flex-end;align-items:center}
51
- .x{position:absolute;top:8px;right:10px;background:none;border:none;color:${fg};opacity:.5;font-size:18px;cursor:pointer;line-height:1}
52
- .sec{background:none;border:none;color:${fg};opacity:.7;font-size:13px;cursor:pointer}
53
- .cta{background:${accent};color:#fff;border:none;border-radius:9px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer}
37
+ style.textContent = `
38
+ :host{all:initial}
39
+ .ov{position:fixed;inset:0;display:flex;z-index:2147483600;
40
+ background:${backdrop ? 'rgba(0,0,0,.28)' : 'transparent'};
41
+ pointer-events:${backdrop ? 'auto' : 'none'};
42
+ font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
43
+ .card{pointer-events:auto;background:${bg};color:${fg};box-sizing:border-box;padding:16px 18px;
44
+ box-shadow:0 8px 40px rgba(0,0,0,.22);position:relative}
45
+ img{width:100%;border-radius:8px;margin-bottom:10px;display:block}
46
+ .t{font-size:16px;font-weight:700;margin:0 0 6px}
47
+ .b{font-size:14px;line-height:1.45;margin:0 0 12px}
48
+ .coupon{display:inline-block;font-family:monospace;font-size:13px;border:1px dashed ${accent};
49
+ border-radius:6px;padding:4px 10px;margin:0 0 12px}
50
+ .acts{display:flex;gap:10px;justify-content:flex-end;align-items:center}
51
+ .x{position:absolute;top:8px;right:10px;background:none;border:none;color:${fg};opacity:.5;font-size:18px;cursor:pointer;line-height:1}
52
+ .sec{background:none;border:none;color:${fg};opacity:.7;font-size:13px;cursor:pointer}
53
+ .cta{background:${accent};color:#fff;border:none;border-radius:9px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer}
54
54
  ${positionCss(layout)}`;
55
55
  root.appendChild(style);
56
56
  const ov = document.createElement('div');
@@ -1,4 +1,4 @@
1
- import type { EngageCampaign, SurveyResult } from '../types';
1
+ import type { EngageCampaign, SurveyResult } from '../types.js';
2
2
  export interface SurveyHandle {
3
3
  close(): void;
4
4
  }
@@ -4,8 +4,8 @@
4
4
  * collects the answer, and reports started/submit/dismiss. Accessible: role=dialog, focus-trapped,
5
5
  * Esc to dismiss. Zero third-party deps (hand-built DOM). Loaded only inside the lazy engage chunk.
6
6
  */
7
- import { injectStyle } from '../../../core';
8
- import { resolveTheme } from '../theme';
7
+ import { injectStyle } from '../../../core/index.js';
8
+ import { resolveTheme } from '../theme.js';
9
9
  /** Derive the questions to render. Multi-question campaigns carry `questions`; simple types map to one. */
10
10
  function resolveQuestions(c) {
11
11
  if (c.questions && c.questions.length)
@@ -47,68 +47,68 @@ function paginate(questions) {
47
47
  * `margin-inline-start` / logical properties rather than `margin-right` so RTL mirrors correctly from
48
48
  * the `dir` attribute alone, with no second stylesheet.
49
49
  */
50
- const STYLE = `
51
- :host{all:initial}
52
- .ov{position:fixed;inset:0;display:flex;align-items:flex-end;justify-content:center;
53
- background:var(--sb-backdrop);font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;pointer-events:auto}
54
- @media(min-width:640px){.ov{align-items:center}}
55
- .card{background:var(--sb-cardBg);color:var(--sb-bodyColor);width:100%;max-width:var(--sb-maxWidth);
56
- border-radius:var(--sb-radius) var(--sb-radius) 0 0;
57
- padding:var(--sb-pad);box-shadow:0 -8px 40px rgba(0,0,0,.2);box-sizing:border-box}
58
- @media(min-width:640px){.card{border-radius:var(--sb-radius);margin:16px}}
59
- .q{font-size:15px;font-weight:600;margin:0 0 var(--sb-gap);color:var(--sb-titleColor)}
60
-
61
- /* ── Appearance: position ──
62
- Centre and corner are WEB placements. The bottom sheet stays the default and the mobile-width
63
- behaviour is untouched: below 640px a corner toast would cover most of the screen anyway, so it
64
- falls back to the sheet rather than pretending to be something it can't be at that size. */
65
- :host([data-position=center]) .ov{align-items:center}
66
- :host([data-position=center]) .card{border-radius:var(--sb-radius);margin:16px}
67
- @media(min-width:640px){
68
- :host([data-position=corner]) .ov{align-items:flex-end;justify-content:flex-end;background:transparent;
69
- pointer-events:none;padding:20px}
70
- :host([data-position=corner]) .card{border-radius:var(--sb-radius);pointer-events:auto;
71
- max-width:min(var(--sb-maxWidth),380px);box-shadow:0 12px 44px rgba(0,0,0,.24)}
72
- }
73
-
74
- /* ── Appearance: separators ──
75
- The reason this exists: stacked questions ran together as one form with no visual break. Applied
76
- between question BLOCKS only (never above the first), so a single-question survey is unchanged. */
77
- .qb + .qb{margin-top:var(--sb-qGap)}
78
- :host([data-separators=line]) .qb + .qb{border-top:1px solid var(--sb-border);padding-top:var(--sb-qGap)}
79
- :host([data-separators=card]) .qb{border:1px solid var(--sb-border);border-radius:calc(var(--sb-radius) / 2);
80
- padding:var(--sb-gap);background:var(--sb-fieldBg)}
81
-
82
- /* Page position, shown only when there is more than one page — a "1 of 1" is noise. */
83
- .pg{font-size:12px;color:var(--sb-muted);margin:0 0 var(--sb-gap)}
84
- .back{background:none;border:none;color:var(--sb-muted);font-size:13px;cursor:pointer;padding:0}
85
- .row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px}
86
- .opt{flex:1 1 auto;min-width:34px;padding:9px 6px;border:1px solid var(--sb-border);border-radius:9px;
87
- background:var(--sb-fieldBg);font-size:14px;cursor:pointer;text-align:center;color:var(--sb-bodyColor)}
88
- .opt[aria-pressed=true]{background:var(--sb-accent);color:var(--sb-ctaText);border-color:var(--sb-accent)}
89
- .emoji{font-size:24px;border:none;background:none;cursor:pointer;flex:1;opacity:.55}
90
- .emoji[aria-pressed=true]{opacity:1;transform:scale(1.15)}
91
- textarea{width:100%;min-height:72px;border:1px solid var(--sb-border);border-radius:9px;padding:9px;font-size:14px;
92
- box-sizing:border-box;resize:vertical;color:var(--sb-bodyColor);background:var(--sb-fieldBg)}
93
- .foot{display:flex;gap:10px;justify-content:flex-end;align-items:center;margin-top:4px}
94
- .x{margin-inline-end:auto;background:none;border:none;color:var(--sb-muted);font-size:13px;cursor:pointer}
95
- .go{background:var(--sb-accent);color:var(--sb-ctaText);border:none;border-radius:9px;padding:9px 18px;
96
- font-size:14px;font-weight:600;cursor:pointer}
97
- .go:disabled{background:var(--sb-disabledBg);color:var(--sb-disabledFg);cursor:default}
98
- .scale-labels{display:flex;justify-content:space-between;font-size:11px;color:var(--sb-disabledFg);margin:-8px 0 14px}
99
- .att{display:flex;gap:8px;align-items:center;margin-bottom:14px;flex-wrap:wrap}
100
- .attbtn{padding:9px 14px;border:1px solid var(--sb-border);border-radius:9px;background:var(--sb-fieldBg);
101
- font-size:14px;cursor:pointer;color:var(--sb-bodyColor)}
102
- .attbtn[data-done=true]{background:var(--sb-couponBg);border-color:var(--sb-couponBorder);color:var(--sb-couponFg)}
103
- .atthint{font-size:12px;color:var(--sb-disabledFg)}
104
- /* Intro + thank-you states. The dashboard has authored startScreen / thankYouScreen since the
105
- builder shipped, and its preview has always drawn both — but no renderer existed on either SDK, so
106
- the copy was stored and silently dropped. Optional: a campaign without them behaves exactly as
107
- before, opening straight onto the first question.
108
- (No backticks in this comment — it lives inside a template literal.) */
109
- .lede{font-size:13px;color:var(--sb-muted);margin:-8px 0 16px}
110
- .thanks{text-align:center;padding:6px 0 2px}
111
- .thanks .q{margin-bottom:0}
50
+ const STYLE = `
51
+ :host{all:initial}
52
+ .ov{position:fixed;inset:0;display:flex;align-items:flex-end;justify-content:center;
53
+ background:var(--sb-backdrop);font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;pointer-events:auto}
54
+ @media(min-width:640px){.ov{align-items:center}}
55
+ .card{background:var(--sb-cardBg);color:var(--sb-bodyColor);width:100%;max-width:var(--sb-maxWidth);
56
+ border-radius:var(--sb-radius) var(--sb-radius) 0 0;
57
+ padding:var(--sb-pad);box-shadow:0 -8px 40px rgba(0,0,0,.2);box-sizing:border-box}
58
+ @media(min-width:640px){.card{border-radius:var(--sb-radius);margin:16px}}
59
+ .q{font-size:15px;font-weight:600;margin:0 0 var(--sb-gap);color:var(--sb-titleColor)}
60
+
61
+ /* ── Appearance: position ──
62
+ Centre and corner are WEB placements. The bottom sheet stays the default and the mobile-width
63
+ behaviour is untouched: below 640px a corner toast would cover most of the screen anyway, so it
64
+ falls back to the sheet rather than pretending to be something it can't be at that size. */
65
+ :host([data-position=center]) .ov{align-items:center}
66
+ :host([data-position=center]) .card{border-radius:var(--sb-radius);margin:16px}
67
+ @media(min-width:640px){
68
+ :host([data-position=corner]) .ov{align-items:flex-end;justify-content:flex-end;background:transparent;
69
+ pointer-events:none;padding:20px}
70
+ :host([data-position=corner]) .card{border-radius:var(--sb-radius);pointer-events:auto;
71
+ max-width:min(var(--sb-maxWidth),380px);box-shadow:0 12px 44px rgba(0,0,0,.24)}
72
+ }
73
+
74
+ /* ── Appearance: separators ──
75
+ The reason this exists: stacked questions ran together as one form with no visual break. Applied
76
+ between question BLOCKS only (never above the first), so a single-question survey is unchanged. */
77
+ .qb + .qb{margin-top:var(--sb-qGap)}
78
+ :host([data-separators=line]) .qb + .qb{border-top:1px solid var(--sb-border);padding-top:var(--sb-qGap)}
79
+ :host([data-separators=card]) .qb{border:1px solid var(--sb-border);border-radius:calc(var(--sb-radius) / 2);
80
+ padding:var(--sb-gap);background:var(--sb-fieldBg)}
81
+
82
+ /* Page position, shown only when there is more than one page — a "1 of 1" is noise. */
83
+ .pg{font-size:12px;color:var(--sb-muted);margin:0 0 var(--sb-gap)}
84
+ .back{background:none;border:none;color:var(--sb-muted);font-size:13px;cursor:pointer;padding:0}
85
+ .row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px}
86
+ .opt{flex:1 1 auto;min-width:34px;padding:9px 6px;border:1px solid var(--sb-border);border-radius:9px;
87
+ background:var(--sb-fieldBg);font-size:14px;cursor:pointer;text-align:center;color:var(--sb-bodyColor)}
88
+ .opt[aria-pressed=true]{background:var(--sb-accent);color:var(--sb-ctaText);border-color:var(--sb-accent)}
89
+ .emoji{font-size:24px;border:none;background:none;cursor:pointer;flex:1;opacity:.55}
90
+ .emoji[aria-pressed=true]{opacity:1;transform:scale(1.15)}
91
+ textarea{width:100%;min-height:72px;border:1px solid var(--sb-border);border-radius:9px;padding:9px;font-size:14px;
92
+ box-sizing:border-box;resize:vertical;color:var(--sb-bodyColor);background:var(--sb-fieldBg)}
93
+ .foot{display:flex;gap:10px;justify-content:flex-end;align-items:center;margin-top:4px}
94
+ .x{margin-inline-end:auto;background:none;border:none;color:var(--sb-muted);font-size:13px;cursor:pointer}
95
+ .go{background:var(--sb-accent);color:var(--sb-ctaText);border:none;border-radius:9px;padding:9px 18px;
96
+ font-size:14px;font-weight:600;cursor:pointer}
97
+ .go:disabled{background:var(--sb-disabledBg);color:var(--sb-disabledFg);cursor:default}
98
+ .scale-labels{display:flex;justify-content:space-between;font-size:11px;color:var(--sb-disabledFg);margin:-8px 0 14px}
99
+ .att{display:flex;gap:8px;align-items:center;margin-bottom:14px;flex-wrap:wrap}
100
+ .attbtn{padding:9px 14px;border:1px solid var(--sb-border);border-radius:9px;background:var(--sb-fieldBg);
101
+ font-size:14px;cursor:pointer;color:var(--sb-bodyColor)}
102
+ .attbtn[data-done=true]{background:var(--sb-couponBg);border-color:var(--sb-couponBorder);color:var(--sb-couponFg)}
103
+ .atthint{font-size:12px;color:var(--sb-disabledFg)}
104
+ /* Intro + thank-you states. The dashboard has authored startScreen / thankYouScreen since the
105
+ builder shipped, and its preview has always drawn both — but no renderer existed on either SDK, so
106
+ the copy was stored and silently dropped. Optional: a campaign without them behaves exactly as
107
+ before, opening straight onto the first question.
108
+ (No backticks in this comment — it lives inside a template literal.) */
109
+ .lede{font-size:13px;color:var(--sb-muted);margin:-8px 0 16px}
110
+ .thanks{text-align:center;padding:6px 0 2px}
111
+ .thanks .q{margin-bottom:0}
112
112
  `;
113
113
  export function mountSurvey(campaign, cb) {
114
114
  const host = document.createElement('div');
@@ -374,7 +374,7 @@ prior) {
374
374
  btn.addEventListener('click', async () => {
375
375
  btn.disabled = true;
376
376
  try {
377
- const { captureScreenshot } = await import('../attachments');
377
+ const { captureScreenshot } = await import('../attachments.js');
378
378
  const d = await captureScreenshot(true); // masked (privacy-safe default)
379
379
  if (d) {
380
380
  onAttachment(d);
@@ -406,7 +406,7 @@ prior) {
406
406
  btn.addEventListener('click', async () => {
407
407
  if (!recorder) {
408
408
  btn.disabled = true;
409
- const { startVoiceRecording } = await import('../attachments');
409
+ const { startVoiceRecording } = await import('../attachments.js');
410
410
  recorder = await startVoiceRecording();
411
411
  btn.disabled = false;
412
412
  if (!recorder) {
@@ -4,7 +4,7 @@
4
4
  * (captureError) is NOT — it's dev-intentional, not a loop.
5
5
  */
6
6
  import type { IFeature, FeatureContext } from '@scalebun/sdk-contracts';
7
- import { type ScopeOverride } from './scope';
7
+ import { type ScopeOverride } from './scope.js';
8
8
  /** Self-observability snapshot (M5) — the SDK's own error-pipeline health. */
9
9
  export interface ErrorStats {
10
10
  /** emitted to the pipeline */