@wcstack/camera 1.14.0 → 1.16.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.d.ts CHANGED
@@ -1,17 +1,3 @@
1
- interface ITagNames {
2
- readonly camera: string;
3
- readonly recorder: string;
4
- }
5
- interface IWritableTagNames {
6
- camera?: string;
7
- recorder?: string;
8
- }
9
- interface IConfig {
10
- readonly tagNames: ITagNames;
11
- }
12
- interface IWritableConfig {
13
- tagNames?: IWritableTagNames;
14
- }
15
1
  interface IWcBindableProperty {
16
2
  readonly name: string;
17
3
  readonly event: string;
@@ -27,11 +13,27 @@ interface IWcBindableCommand {
27
13
  }
28
14
  interface IWcBindable {
29
15
  readonly protocol: "wc-bindable";
30
- readonly version: number;
31
- readonly properties: IWcBindableProperty[];
32
- readonly inputs?: IWcBindableInput[];
33
- readonly commands?: IWcBindableCommand[];
16
+ readonly version: 1;
17
+ readonly properties: readonly IWcBindableProperty[];
18
+ readonly inputs?: readonly IWcBindableInput[];
19
+ readonly commands?: readonly IWcBindableCommand[];
20
+ }
21
+
22
+ interface ITagNames {
23
+ readonly camera: string;
24
+ readonly recorder: string;
34
25
  }
26
+ interface IWritableTagNames {
27
+ camera?: string;
28
+ recorder?: string;
29
+ }
30
+ interface IConfig {
31
+ readonly tagNames: ITagNames;
32
+ }
33
+ interface IWritableConfig {
34
+ tagNames?: IWritableTagNames;
35
+ }
36
+
35
37
  /**
36
38
  * Permission state for camera / microphone, mirroring the Permissions API
37
39
  * `PermissionState` plus `"unsupported"` for environments without
@@ -384,6 +386,7 @@ declare class RecorderCore extends EventTarget {
384
386
  private _timeslice;
385
387
  private _startTime;
386
388
  private _gen;
389
+ private _ready;
387
390
  constructor(target?: EventTarget);
388
391
  get recording(): boolean;
389
392
  get paused(): boolean;
@@ -392,6 +395,7 @@ declare class RecorderCore extends EventTarget {
392
395
  get blob(): Blob | null;
393
396
  get objectURL(): string | null;
394
397
  get error(): WcsMediaErrorDetail | null;
398
+ get ready(): Promise<void>;
395
399
  private _setRecording;
396
400
  private _setPaused;
397
401
  private _setDuration;
@@ -415,6 +419,13 @@ declare class RecorderCore extends EventTarget {
415
419
  stop(): void;
416
420
  pause(): void;
417
421
  resume(): void;
422
+ /**
423
+ * Establish monitoring (§3.5). Recording is command-driven — there is no listener
424
+ * or subscription to set up here (the borrowed stream arrives via attachStream and
425
+ * recording is driven by start()/stop()), so observe() is an idempotent no-op that
426
+ * resolves once ready. dispose() is its teardown counterpart.
427
+ */
428
+ observe(): Promise<void>;
418
429
  /** Stop in-flight recording, revoke the last object URL, drop the borrowed stream. */
419
430
  dispose(): void;
420
431
  private _assembleBlob;
@@ -436,9 +447,12 @@ declare class RecorderCore extends EventTarget {
436
447
  * properties — a settled `Blob` is a value and may flow through state.
437
448
  */
438
449
  declare class WcsRecorder extends HTMLElement {
450
+ static hasConnectedCallbackPromise: boolean;
439
451
  static wcBindable: IWcBindable;
440
452
  private _core;
453
+ private _connectedCallbackPromise;
441
454
  constructor();
455
+ get connectedCallbackPromise(): Promise<void>;
442
456
  get mimeType(): string;
443
457
  set mimeType(value: string);
444
458
  get timeslice(): number;
package/dist/index.esm.js CHANGED
@@ -807,6 +807,9 @@ class RecorderCore extends EventTarget {
807
807
  // dispose(); each MediaRecorder callback bails if stale so a torn-down/restarted
808
808
  // recorder's late event cannot mutate state.
809
809
  _gen = 0;
810
+ // SSR: recording is command-driven (attachStream/start), so there is no
811
+ // asynchronous probe to await — readiness is immediate.
812
+ _ready = Promise.resolve();
810
813
  constructor(target) {
811
814
  super();
812
815
  this._target = target ?? this;
@@ -820,6 +823,7 @@ class RecorderCore extends EventTarget {
820
823
  get blob() { return this._blob; }
821
824
  get objectURL() { return this._objectURL; }
822
825
  get error() { return this._error; }
826
+ get ready() { return this._ready; }
823
827
  // --- State setters ---
824
828
  _setRecording(v) {
825
829
  if (this._recording === v)
@@ -968,6 +972,15 @@ class RecorderCore extends EventTarget {
968
972
  this._recorder.resume();
969
973
  }
970
974
  }
975
+ /**
976
+ * Establish monitoring (§3.5). Recording is command-driven — there is no listener
977
+ * or subscription to set up here (the borrowed stream arrives via attachStream and
978
+ * recording is driven by start()/stop()), so observe() is an idempotent no-op that
979
+ * resolves once ready. dispose() is its teardown counterpart.
980
+ */
981
+ observe() {
982
+ return this._ready;
983
+ }
971
984
  /** Stop in-flight recording, revoke the last object URL, drop the borrowed stream. */
972
985
  dispose() {
973
986
  // Bump the generation first so the native stop()'s onstop (gen-guarded) does
@@ -1048,6 +1061,7 @@ class RecorderCore extends EventTarget {
1048
1061
  * properties — a settled `Blob` is a value and may flow through state.
1049
1062
  */
1050
1063
  class WcsRecorder extends HTMLElement {
1064
+ static hasConnectedCallbackPromise = true;
1051
1065
  static wcBindable = {
1052
1066
  ...RecorderCore.wcBindable,
1053
1067
  // `mimeType` deliberately appears on TWO surfaces: as an output `property`
@@ -1065,10 +1079,14 @@ class WcsRecorder extends HTMLElement {
1065
1079
  commands: RecorderCore.wcBindable.commands,
1066
1080
  };
1067
1081
  _core;
1082
+ _connectedCallbackPromise = Promise.resolve();
1068
1083
  constructor() {
1069
1084
  super();
1070
1085
  this._core = new RecorderCore(this);
1071
1086
  }
1087
+ get connectedCallbackPromise() {
1088
+ return this._connectedCallbackPromise;
1089
+ }
1072
1090
  // --- Attribute accessors ---
1073
1091
  // `mimeType` is an OUTPUT value property (the Core-resolved recording type,
1074
1092
  // published via `wcs-recorder:mimetype-changed`), so the getter delegates to the
@@ -1128,6 +1146,7 @@ class WcsRecorder extends HTMLElement {
1128
1146
  // --- Lifecycle ---
1129
1147
  connectedCallback() {
1130
1148
  this.style.display = "none";
1149
+ this._connectedCallbackPromise = this._core.observe();
1131
1150
  }
1132
1151
  disconnectedCallback() {
1133
1152
  this._core.dispose();
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/media/getUserMedia.ts","../src/media/permission.ts","../src/core/CameraCore.ts","../src/components/Camera.ts","../src/core/RecorderCore.ts","../src/components/Recorder.ts","../src/registerComponents.ts","../src/bootstrapCamera.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n camera: string;\n recorder: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n camera: \"wcs-camera\",\n recorder: \"wcs-recorder\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by registerComponents (read at call time so\n// setConfig() takes effect without re-import). Public consumers get the deep-frozen\n// clone from getConfig().\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { CameraConstraints, MediaDeviceSnapshot, WcsMediaErrorDetail } from \"../types.js\";\n\n/** True when getUserMedia is reachable (secure context with a media-devices impl). */\nexport function hasMediaDevices(): boolean {\n return typeof navigator !== \"undefined\"\n && !!navigator.mediaDevices\n && typeof navigator.mediaDevices.getUserMedia === \"function\";\n}\n\n/** True when MediaRecorder is available in this environment. */\nexport function hasMediaRecorder(): boolean {\n return typeof globalThis !== \"undefined\"\n && typeof (globalThis as { MediaRecorder?: unknown }).MediaRecorder === \"function\";\n}\n\n/**\n * Translate a getUserMedia constraints object from the declarative\n * CameraConstraints surface. Always requests a video track; `audio` opts the\n * microphone in. `deviceId` (exact) takes precedence over `facingMode`.\n */\nexport function buildConstraints(c: CameraConstraints): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n if (c.deviceId) {\n video.deviceId = { exact: c.deviceId };\n } else if (c.facingMode) {\n video.facingMode = c.facingMode;\n }\n if (typeof c.width === \"number\") video.width = c.width;\n if (typeof c.height === \"number\") video.height = c.height;\n const hasVideoConstraint = Object.keys(video).length > 0;\n return {\n video: hasVideoConstraint ? video : true,\n audio: c.audio === true,\n };\n}\n\n/** Normalize any thrown getUserMedia / MediaRecorder failure into a flat detail. */\nexport function normalizeMediaError(error: unknown): WcsMediaErrorDetail {\n if (error && typeof error === \"object\" && \"name\" in error) {\n const name = String((error as { name: unknown }).name) || \"Error\";\n const message = \"message\" in error && (error as { message?: unknown }).message\n ? String((error as { message: unknown }).message)\n : `Media request failed: ${name}.`;\n return { name, message };\n }\n return { name: \"Error\", message: \"Media request failed.\" };\n}\n\nconst UNSUPPORTED_ERROR: WcsMediaErrorDetail = {\n name: \"unsupported\",\n message: \"getUserMedia is not available (requires a secure context).\",\n};\n\nexport interface UserMediaResult {\n stream?: MediaStream;\n error?: WcsMediaErrorDetail;\n}\n\n/**\n * Request a media stream. Never throws — resolves with `{ stream }` on success or\n * `{ error }` (normalized) on failure / when the API is unavailable.\n */\nexport async function requestUserMedia(constraints: MediaStreamConstraints): Promise<UserMediaResult> {\n if (!hasMediaDevices()) {\n return { error: UNSUPPORTED_ERROR };\n }\n try {\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\n return { stream };\n } catch (error) {\n return { error: normalizeMediaError(error) };\n }\n}\n\n/** Stop every track of a stream, releasing the camera/microphone hardware. */\nexport function stopAllTracks(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) {\n track.stop();\n }\n}\n\n/**\n * Enumerate video input devices as plain snapshots. Labels are only populated\n * after a grant, so this is refreshed post-acquisition. Never throws.\n */\nexport async function enumerateVideoDevices(): Promise<MediaDeviceSnapshot[]> {\n if (!hasMediaDevices() || typeof navigator.mediaDevices.enumerateDevices !== \"function\") {\n return [];\n }\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === \"videoinput\")\n .map((d) => ({\n deviceId: d.deviceId,\n label: d.label,\n groupId: d.groupId,\n kind: d.kind,\n }));\n } catch {\n return [];\n }\n}\n","import { MediaPermissionState } from \"../types.js\";\n\n/**\n * Live monitor for a single media permission (`camera` or `microphone`) via the\n * Permissions API. Mirrors PermissionCore's two-phase pattern: an initial query\n * plus a live `change` subscription, guarded by a monotonic generation so a query\n * superseded by a rapid dispose/observe never attaches a stale listener.\n *\n * When the Permissions API is absent or rejects the descriptor (e.g. Firefox does\n * not accept the `camera` / `microphone` descriptor), the watcher reports\n * `\"unsupported\"`. The CameraCore then refines the state from the getUserMedia\n * outcome (granted on success, denied on NotAllowedError).\n */\nexport class MediaPermissionWatcher {\n private _name: \"camera\" | \"microphone\";\n private _onChange: (state: MediaPermissionState) => void;\n private _status: PermissionStatus | null = null;\n private _gen: number = 0;\n private _subscribed: boolean = false;\n\n constructor(name: \"camera\" | \"microphone\", onChange: (state: MediaPermissionState) => void) {\n this._name = name;\n this._onChange = onChange;\n }\n\n /** Issue the initial query and subscribe to live changes. Resolves when settled. */\n observe(): Promise<void> {\n if (this._subscribed) return Promise.resolve();\n if (typeof navigator === \"undefined\" || !navigator.permissions\n || typeof navigator.permissions.query !== \"function\") {\n this._onChange(\"unsupported\");\n return Promise.resolve();\n }\n this._subscribed = true;\n const gen = ++this._gen;\n return navigator.permissions\n .query({ name: this._name as PermissionName })\n .then(\n (status) => {\n if (gen !== this._gen) return;\n this._status = status;\n this._onChange(status.state as MediaPermissionState);\n status.addEventListener(\"change\", this._onStatusChange);\n },\n () => {\n if (gen !== this._gen) return;\n this._onChange(\"unsupported\");\n },\n );\n }\n\n /** Detach the live listener and invalidate any in-flight query. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n if (this._status) {\n this._status.removeEventListener(\"change\", this._onStatusChange);\n this._status = null;\n }\n }\n\n private _onStatusChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._onChange(status.state as MediaPermissionState);\n };\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot,\n MediaPermissionState, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport {\n buildConstraints, enumerateVideoDevices, hasMediaDevices, requestUserMedia, stopAllTracks,\n} from \"../media/getUserMedia.js\";\nimport { MediaPermissionWatcher } from \"../media/permission.js\";\n\n/**\n * Headless camera-capture primitive. Wraps getUserMedia + the Permissions API and\n * exposes a `MediaStream` through the wc-bindable protocol — but the live stream is\n * NEVER published as a reactive value. It is a non-serializable live handle: it\n * flows out only via the `wcs-camera:stream-ready` event so a consumer (the preview\n * `<video>`, a `<wcs-recorder>`) can bind it directly to an element property,\n * bypassing serializable state. See docs/camera-recorder-tag-design.md §1/§2.\n *\n * The observable value surface is strictly derived data: `active` (is a stream\n * live — the \"actual\" half of the desired/actual pair), `permission` /\n * `audioPermission` (two-phase: Permissions API monitor + getUserMedia outcome),\n * `deviceId` / `devices`, and `error`. Failures never throw — they surface through\n * `error`.\n */\nexport class CameraCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"active\", event: \"wcs-camera:active-changed\" },\n { name: \"permission\", event: \"wcs-camera:permission-changed\" },\n { name: \"audioPermission\", event: \"wcs-camera:audio-permission-changed\" },\n { name: \"deviceId\", event: \"wcs-camera:device-changed\" },\n { name: \"devices\", event: \"wcs-camera:devices-changed\" },\n { name: \"error\", event: \"wcs-camera:error\" },\n // Direct-channel handle: event-token only — never bound as a reactive value.\n { name: \"streamReady\", event: \"wcs-camera:stream-ready\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token: a bare signal (detail is always null) — surface detail, not the raw Event.\n { name: \"ended\", event: \"wcs-camera:ended\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"switchCamera\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _active: boolean = false;\n private _permission: MediaPermissionState = \"prompt\";\n private _audioPermission: MediaPermissionState | null = null;\n private _deviceId: string | null = null;\n private _devices: MediaDeviceSnapshot[] = [];\n private _error: WcsMediaErrorDetail | null = null;\n\n // The live stream — internal only, never a reactive value (see class docs).\n private _stream: MediaStream | null = null;\n\n // desired/actual split (wakelock-style): `_desired` is whether the user wants the\n // camera on; `_active` is whether a stream is actually live. The OS can revoke a\n // track (device unplugged / taken by another app) — actual drops while desired\n // stays true, so a later resume()/visibility-restore can re-acquire.\n private _desired: boolean = false;\n\n private _constraints: CameraConstraints = {};\n\n // Monotonic id of the current acquisition lifecycle. Bumped by every acquire and\n // by dispose(). Each in-flight getUserMedia captures it and, on resolve, bails\n // (stopping the just-acquired stream) when superseded — so a constraints change\n // mid-acquire (switchMap-style restart) cannot leave an orphaned stream live.\n private _gen: number = 0;\n\n private _subscribed: boolean = false;\n private _camWatcher: MediaPermissionWatcher | null = null;\n private _micWatcher: MediaPermissionWatcher | null = null;\n\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get active(): boolean { return this._active; }\n get permission(): MediaPermissionState { return this._permission; }\n get audioPermission(): MediaPermissionState | null { return this._audioPermission; }\n get deviceId(): string | null { return this._deviceId; }\n get devices(): MediaDeviceSnapshot[] { return this._devices; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n get ready(): Promise<void> { return this._ready; }\n\n // --- State setters with event dispatch (same-value guarded) ---\n\n private _setActive(active: boolean): void {\n if (this._active === active) return;\n this._active = active;\n this._dispatch(\"wcs-camera:active-changed\", active);\n }\n\n private _setPermission(state: MediaPermissionState): void {\n if (this._permission === state) return;\n this._permission = state;\n this._dispatch(\"wcs-camera:permission-changed\", state);\n }\n\n private _setAudioPermission(state: MediaPermissionState | null): void {\n if (this._audioPermission === state) return;\n this._audioPermission = state;\n this._dispatch(\"wcs-camera:audio-permission-changed\", state);\n }\n\n private _setDeviceId(id: string | null): void {\n if (this._deviceId === id) return;\n this._deviceId = id;\n this._dispatch(\"wcs-camera:device-changed\", id);\n }\n\n private _setDevices(devices: MediaDeviceSnapshot[]): void {\n if (this._devicesEqual(this._devices, devices)) return;\n this._devices = devices;\n this._dispatch(\"wcs-camera:devices-changed\", devices);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event (e.g. retrying getUserMedia and failing again must re-notify), so\n // unlike the value setters this is not content-deduped. Only the null→null\n // transition is collapsed (clearing an already-clear error stays silent). The guard\n // is written on null explicitly so it does NOT depend on callers passing a fresh\n // object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-camera:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n private _devicesEqual(a: MediaDeviceSnapshot[], b: MediaDeviceSnapshot[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].deviceId !== b[i].deviceId || a[i].label !== b[i].label) return false;\n }\n return true;\n }\n\n // --- Public API ---\n\n /**\n * Begin observing permissions for the given constraints. Idempotent while\n * already subscribed. The first call (or one after dispose()) starts the\n * camera/microphone permission monitors. Acquisition itself is driven separately\n * by start() / autostart — observing does not prompt.\n */\n observe(constraints: CameraConstraints): Promise<void> {\n this._constraints = { ...constraints };\n if (!this._subscribed) {\n this._subscribed = true;\n this._ready = this._initPermissions();\n } else {\n // Already live: track the latest constraints and fold any newly-started\n // microphone query into `ready` so awaiting observe() guarantees its initial\n // permission state — symmetric with _initPermissions() on the first observe.\n this._ready = this._ready.then(() => this._reconcileAudioWatcher());\n }\n return this._ready;\n }\n\n /** Acquire the camera (sets desired=true). Prompts on first use. */\n start(): void {\n this._desired = true;\n this._restart();\n }\n\n /** Release the camera (sets desired=false), stopping all tracks. */\n stop(): void {\n this._desired = false;\n this._release(false);\n }\n\n /**\n * Toggle facingMode (user ↔ environment) and re-acquire if active. This is the\n * headless, DOM-free path: it flips the Core's internal `_constraints` (the single\n * source of truth for a standalone Core) and re-acquires while desired.\n *\n * Note: the `<wcs-camera>` Shell does NOT delegate to this — it keeps the DOM\n * attributes authoritative and drives its own single re-acquire (see Camera.ts\n * switchCamera). Both reach the same end state; the split exists because the Shell\n * must keep its declared attributes in sync, which a Core has no notion of.\n */\n switchCamera(): void {\n const next: FacingMode = this._constraints.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._constraints = { ...this._constraints, facingMode: next, deviceId: undefined };\n if (this._desired) {\n this._restart();\n }\n }\n\n /**\n * Suspend the live stream while keeping `desired` — for page-hidden. Stops tracks\n * (clearing the hardware indicator) but remembers that the camera should resume.\n *\n * Bumps `_gen` to supersede any in-flight acquire: without this, an acquire that\n * resolves *after* the page went hidden would assign `_stream` and set active —\n * re-lighting the camera behind a no-op suspend (the stream had not been assigned\n * yet, so the `if (_stream)` release below could not reach it). The superseded\n * acquire stops its just-acquired orphan stream on resolve (see `_acquire`).\n */\n suspend(): void {\n this._gen++;\n if (this._stream) {\n this._release(false);\n }\n }\n\n /** Re-acquire if the camera is desired but not currently active — for page-visible. */\n resume(): void {\n if (this._desired && !this._active) {\n this._restart();\n }\n }\n\n /** Tear down: stop the stream and detach permission listeners. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n this._desired = false;\n this._release(true);\n if (hasMediaDevices() && typeof navigator.mediaDevices.removeEventListener === \"function\") {\n navigator.mediaDevices.removeEventListener(\"devicechange\", this._onDeviceChange);\n }\n this._camWatcher?.dispose();\n this._micWatcher?.dispose();\n this._camWatcher = null;\n this._micWatcher = null;\n }\n\n // --- Internal ---\n\n private _initPermissions(): Promise<void> {\n if (!hasMediaDevices()) {\n this._setPermission(\"unsupported\");\n return Promise.resolve();\n }\n this._camWatcher = new MediaPermissionWatcher(\"camera\", (s) => this._setPermission(s));\n const tasks: Promise<void>[] = [this._camWatcher.observe()];\n if (this._constraints.audio) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n tasks.push(this._micWatcher.observe());\n }\n // Track hot-plug: refresh the device list when a camera is added/removed.\n if (typeof navigator.mediaDevices.addEventListener === \"function\") {\n navigator.mediaDevices.addEventListener(\"devicechange\", this._onDeviceChange);\n }\n return Promise.all(tasks).then(() => undefined);\n }\n\n private _onDeviceChange = (): void => {\n void enumerateVideoDevices().then((devices) => {\n // Guard against a late resolution after dispose.\n if (this._subscribed) this._setDevices(devices);\n });\n };\n\n // Bring the microphone watcher in line with the latest `audio` constraint when\n // observe() is called again on an already-live Core. Returns the new watcher's\n // initial-query promise (so observe() can fold it into `ready`); resolved when\n // nothing started.\n private _reconcileAudioWatcher(): Promise<void> {\n if (this._constraints.audio && !this._micWatcher && hasMediaDevices()) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n return this._micWatcher.observe();\n } else if (!this._constraints.audio && this._micWatcher) {\n this._micWatcher.dispose();\n this._micWatcher = null;\n this._setAudioPermission(null);\n }\n return Promise.resolve();\n }\n\n private _restart(): void {\n this._release(false);\n void this._acquire();\n }\n\n private async _acquire(): Promise<void> {\n const gen = ++this._gen;\n const constraints = buildConstraints(this._constraints);\n const { stream, error } = await requestUserMedia(constraints);\n\n // Superseded by a newer acquire (rapid restart) or disposed while in flight:\n // stop the orphan stream and bail without mutating state. The `ended` listeners\n // are attached only AFTER this gen check (below), so stopping the orphan here\n // cannot fire _onTrackEnded — no spurious `ended` event / state mutation. Keep\n // this stop strictly before listener attachment if the order is ever refactored.\n if (gen !== this._gen) {\n stopAllTracks(stream ?? null);\n return;\n }\n\n if (error) {\n this._setError(error);\n if (error.name === \"NotAllowedError\") {\n this._setPermission(\"denied\");\n // Hard denial: drop `desired` so a later visibility-restore (resume()) does\n // not silently re-attempt getUserMedia on every page-visible. A transient\n // failure (NotReadableError = device busy) keeps `desired` so it can recover.\n this._desired = false;\n } else if (error.name === \"unsupported\") {\n this._setPermission(\"unsupported\");\n this._desired = false;\n }\n this._setActive(false);\n return;\n }\n\n const live = stream as MediaStream;\n this._stream = live;\n for (const track of live.getTracks()) {\n track.addEventListener(\"ended\", this._onTrackEnded);\n }\n this._setError(null);\n // getUserMedia success is authoritative for permission.\n this._setPermission(\"granted\");\n // Only assert mic-granted when the grant actually produced an audio track. With\n // today's boolean `audio` this is equivalent to `_constraints.audio`, but it stays\n // correct under a future non-mandatory `{ audio: {...} }` constraint where the\n // browser may grant video while omitting audio.\n if (this._constraints.audio && live.getAudioTracks().length > 0) {\n this._setAudioPermission(\"granted\");\n }\n this._updateDeviceId(live);\n this._setActive(true);\n // Publish the live handle for the direct element→element channel.\n this._dispatch(\"wcs-camera:stream-ready\", live);\n // Labels become available after a grant; refresh the device list.\n const devices = await enumerateVideoDevices();\n if (gen === this._gen) {\n this._setDevices(devices);\n }\n }\n\n private _release(silent: boolean): void {\n if (!this._stream) return;\n for (const track of this._stream.getTracks()) {\n track.removeEventListener(\"ended\", this._onTrackEnded);\n }\n stopAllTracks(this._stream);\n this._stream = null;\n if (silent) {\n this._active = false;\n } else {\n this._setActive(false);\n }\n }\n\n private _onTrackEnded = (): void => {\n // OS revoked a track (unplug / taken by another app). actual drops; desired\n // stays true so resume()/visibility-restore can re-acquire.\n this._release(false);\n this._dispatch(\"wcs-camera:ended\", null);\n };\n\n private _updateDeviceId(stream: MediaStream): void {\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack && typeof videoTrack.getSettings === \"function\") {\n const settings = videoTrack.getSettings();\n this._setDeviceId(settings.deviceId ?? null);\n }\n }\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot, MediaPermissionState,\n WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { CameraCore } from \"../core/CameraCore.js\";\n\n/**\n * `<wcs-camera>` — declarative camera capture with a built-in preview.\n *\n * The element owns a `<video>` in its shadow root and assigns the live\n * `MediaStream` to `video.srcObject` internally, so the non-serializable handle\n * never crosses the state boundary (design §1, case B). For consumers (a\n * `<wcs-recorder>`, an external `<video>`), the stream is also published via the\n * `wcs-camera:stream-ready` event-token for the direct element→element channel\n * (design §2).\n *\n * Acquisition is explicit: `start()` / the `autostart` attribute prompt and\n * acquire; merely connecting does not. While the page is hidden the stream is\n * suspended (clearing the camera indicator) and re-acquired on return, unless\n * `keep-alive` is set (e.g. during recording).\n */\nexport class WcsCamera extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...CameraCore.wcBindable,\n inputs: [\n { name: \"audio\", attribute: \"audio\" },\n { name: \"facingMode\", attribute: \"facing-mode\" },\n { name: \"deviceId\", attribute: \"device-id\" },\n { name: \"width\", attribute: \"width\" },\n { name: \"height\", attribute: \"height\" },\n { name: \"autostart\", attribute: \"autostart\" },\n { name: \"keepAlive\", attribute: \"keep-alive\" },\n ],\n commands: CameraCore.wcBindable.commands,\n };\n\n // `autostart` and `keep-alive` are intentionally NOT observed: `autostart` is a\n // connect-time-only acquire trigger (read once in connectedCallback; flipping it\n // later is meaningless), and `keep-alive` is read fresh on every visibilitychange\n // (_onVisibilityChange), so it never needs to drive a re-acquire. The observed set\n // is exactly the constraints that reshape the requested track.\n static observedAttributes = [\"facing-mode\", \"device-id\", \"audio\", \"width\", \"height\"];\n\n private _core: CameraCore;\n private _video: HTMLVideoElement;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _connected: boolean = false;\n // True while switchCamera() rewrites several attributes at once. Each setAttribute /\n // removeAttribute fires its own attributeChangedCallback synchronously; without this\n // guard the FIRST change would re-acquire with the not-yet-updated constraints (and\n // tear active down so the later change's re-acquire is skipped). We suppress the\n // per-attribute re-acquire and drive a single one with the final constraints.\n private _batchingAttrs: boolean = false;\n\n constructor() {\n super();\n this._core = new CameraCore(this);\n const root = this.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = \":host{display:inline-block}video{display:block;width:100%;height:100%}\";\n this._video = document.createElement(\"video\");\n this._video.autoplay = true;\n this._video.muted = true;\n this._video.setAttribute(\"playsinline\", \"\");\n this._video.setAttribute(\"part\", \"video\");\n root.append(style, this._video);\n // Bind the live handle to the preview internally — never through state. These\n // self-listeners are intentionally not removed on disconnect (asymmetric with the\n // document-level `visibilitychange` in connect/disconnectedCallback): the target\n // is `this`, so the listeners are collected together with the element — there is\n // no external reference to leak. The visibility listener, by contrast, lives on\n // `document` (outlives the element) and MUST be detached.\n this.addEventListener(\"wcs-camera:stream-ready\", this._onStreamReady as EventListener);\n this.addEventListener(\"wcs-camera:active-changed\", this._onActiveChanged as EventListener);\n }\n\n // --- Attribute accessors ---\n\n get audio(): boolean { return this.hasAttribute(\"audio\"); }\n set audio(value: boolean) { this._toggleAttr(\"audio\", value); }\n\n get facingMode(): FacingMode {\n return this.getAttribute(\"facing-mode\") === \"environment\" ? \"environment\" : \"user\";\n }\n set facingMode(value: FacingMode) { this.setAttribute(\"facing-mode\", value); }\n\n get deviceId(): string { return this.getAttribute(\"device-id\") ?? \"\"; }\n set deviceId(value: string) { this.setAttribute(\"device-id\", value); }\n\n get width(): number { return this._numberAttr(\"width\"); }\n set width(value: number) { this.setAttribute(\"width\", String(value)); }\n\n get height(): number { return this._numberAttr(\"height\"); }\n set height(value: number) { this.setAttribute(\"height\", String(value)); }\n\n get autostart(): boolean { return this.hasAttribute(\"autostart\"); }\n set autostart(value: boolean) { this._toggleAttr(\"autostart\", value); }\n\n get keepAlive(): boolean { return this.hasAttribute(\"keep-alive\"); }\n set keepAlive(value: boolean) { this._toggleAttr(\"keep-alive\", value); }\n\n /** The internal preview `<video>` (for advanced styling/measurement). */\n get videoElement(): HTMLVideoElement { return this._video; }\n\n // --- Core delegated getters ---\n\n get active(): boolean { return this._core.active; }\n get permission(): MediaPermissionState { return this._core.permission; }\n get audioPermission(): MediaPermissionState | null { return this._core.audioPermission; }\n get devices(): MediaDeviceSnapshot[] { return this._core.devices; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n get connectedCallbackPromise(): Promise<void> { return this._connectedCallbackPromise; }\n\n // --- Commands ---\n\n start(): void { this._core.start(); }\n stop(): void { this._core.stop(); }\n\n /**\n * Toggle the front/back camera by updating the DOM attributes (the single source\n * of truth), not just the Core's internal constraints. Deliberately does NOT call\n * `CameraCore.switchCamera()` (which would mutate the Core's constraints behind the\n * DOM's back, leaving the declared attributes stale). `device-id` is removed because\n * it would otherwise take precedence over `facing-mode` (see buildConstraints) —\n * leaving it pinned would silently undo the switch on the next re-acquire. Both\n * attribute writes are batched (see `_batchingAttrs`) so they drive exactly ONE\n * re-acquire here, with the final constraints — never an early acquire on a\n * half-updated state. The DOM and the live camera stay in agreement.\n */\n switchCamera(): void {\n const next: FacingMode = this.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._batchingAttrs = true;\n try {\n this.removeAttribute(\"device-id\");\n this.setAttribute(\"facing-mode\", next);\n } finally {\n this._batchingAttrs = false;\n }\n // Sync the (now-final) constraints and re-acquire once when a stream is live —\n // the same `active`-guarded restart attributeChangedCallback performs.\n this._core.observe(this._constraints());\n if (this._core.active) {\n this._core.start();\n }\n }\n\n // --- Internal ---\n\n private _toggleAttr(name: string, value: boolean): void {\n if (value) {\n this.setAttribute(name, \"\");\n } else {\n this.removeAttribute(name);\n }\n }\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _constraints(): CameraConstraints {\n const c: CameraConstraints = { audio: this.audio, facingMode: this.facingMode };\n if (this.deviceId) c.deviceId = this.deviceId;\n if (Number.isFinite(this.width)) c.width = this.width;\n if (Number.isFinite(this.height)) c.height = this.height;\n return c;\n }\n\n private _onStreamReady = (event: CustomEvent): void => {\n this._video.srcObject = event.detail as MediaStream;\n };\n\n private _onActiveChanged = (event: CustomEvent): void => {\n // Clear the preview when the stream is released so the last frame does not stick.\n if (event.detail === false) {\n this._video.srcObject = null;\n }\n };\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this._connected = true;\n this._connectedCallbackPromise = this._core.observe(this._constraints());\n if (this.autostart) {\n this._core.start();\n }\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n }\n\n disconnectedCallback(): void {\n this._connected = false;\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._core.dispose();\n }\n\n attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void {\n if (!this._connected || oldValue === newValue) return;\n // While switchCamera() batches several attribute writes, defer to its single\n // post-batch re-acquire — otherwise the first write would acquire on stale\n // constraints (see `_batchingAttrs`).\n if (this._batchingAttrs) return;\n // Track the new constraints (and reconcile the microphone watcher).\n this._core.observe(this._constraints());\n // A constraints change re-acquires (switchMap-style restart) only when a stream\n // is actually live. `active` is the deliberate guard (not `desired`): `active`\n // implies `desired` (a stream only goes live under desired), so re-acquiring\n // cannot spuriously re-`desired` a stopped camera. Guarding on `active` rather\n // than `desired` also avoids force-acquiring while suspended/hidden (desired but\n // not active) — the visibility handler owns resume there.\n if (this._core.active) {\n this._core.start();\n }\n }\n\n private _onVisibilityChange = (): void => {\n if (this.keepAlive) return;\n if (document.visibilityState === \"hidden\") {\n this._core.suspend();\n } else {\n this._core.resume();\n }\n };\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail, WcsRecordedDetail,\n} from \"../types.js\";\nimport { hasMediaRecorder, normalizeMediaError } from \"../media/getUserMedia.js\";\n\n/**\n * Headless media-recording primitive. Wraps MediaRecorder, consuming a borrowed\n * `MediaStream` (received via `attachStream` over the direct channel — see\n * docs/camera-recorder-tag-design.md §2) and producing a `Blob` clip.\n *\n * Ownership: the stream is BORROWED, never owned. The Core never stops its tracks\n * — that is the camera's job (the acquirer owns release). Stopping here would tear\n * down a stream that may still be previewing.\n *\n * Non-goal: the Core does NOT subscribe to the borrowed tracks' `ended` and does NOT\n * auto-terminate a recording when the underlying stream dies (camera stop / OS\n * revoke / switchCamera re-acquire). Reacting would mean reaching into a stream we do\n * not own. The MediaRecorder keeps running against the now-dead source until an\n * explicit stop(); the consumer (which owns the camera lifecycle) is responsible for\n * stopping the recording when it tears the stream down. stop() then assembles\n * whatever chunks were captured — never throws.\n *\n * Output: `dataavailable` chunks are collected and assembled into one `Blob` on\n * stop, published via `wcs-recorder:recorded`. The `Blob` is structured-clone\n * friendly (a settled value, unlike MediaStream) so it may flow through state.\n * `objectURL` is a managed string — the Core revokes the previous URL before\n * issuing a new one and on dispose. Failures never throw.\n */\nexport class RecorderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"recording\", event: \"wcs-recorder:recording-changed\" },\n { name: \"paused\", event: \"wcs-recorder:paused-changed\" },\n { name: \"duration\", event: \"wcs-recorder:duration-changed\" },\n { name: \"mimeType\", event: \"wcs-recorder:mimetype-changed\" },\n { name: \"blob\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.blob ?? null },\n { name: \"objectURL\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.objectURL ?? null },\n { name: \"error\", event: \"wcs-recorder:error\" },\n // event-token: detail = the assembled clip { blob, objectURL, mimeType, duration }.\n { name: \"recorded\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token (timeslice mode): detail = the streamed Blob chunk.\n { name: \"dataavailable\", event: \"wcs-recorder:dataavailable\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"attachStream\" },\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _recording: boolean = false;\n private _paused: boolean = false;\n private _duration: number = 0;\n private _mimeType: string = \"\";\n private _blob: Blob | null = null;\n private _objectURL: string | null = null;\n private _error: WcsMediaErrorDetail | null = null;\n\n private _recorder: MediaRecorder | null = null;\n private _stream: MediaStream | null = null; // borrowed — never stopped here\n private _chunks: Blob[] = [];\n private _timeslice: boolean = false;\n private _startTime: number = 0;\n\n // Monotonic id of the current recording lifecycle. Bumped by every start() and\n // dispose(); each MediaRecorder callback bails if stale so a torn-down/restarted\n // recorder's late event cannot mutate state.\n private _gen: number = 0;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get recording(): boolean { return this._recording; }\n get paused(): boolean { return this._paused; }\n // Finalized at stop/pause only — there is no live ticking timer, so this stays 0\n // from start() until the first pause()/stop() (see _elapsed / onstop / onpause).\n get duration(): number { return this._duration; }\n get mimeType(): string { return this._mimeType; }\n get blob(): Blob | null { return this._blob; }\n get objectURL(): string | null { return this._objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n\n // --- State setters ---\n\n private _setRecording(v: boolean): void {\n if (this._recording === v) return;\n this._recording = v;\n this._dispatch(\"wcs-recorder:recording-changed\", v);\n }\n\n private _setPaused(v: boolean): void {\n if (this._paused === v) return;\n this._paused = v;\n this._dispatch(\"wcs-recorder:paused-changed\", v);\n }\n\n private _setDuration(v: number): void {\n if (this._duration === v) return;\n this._duration = v;\n this._dispatch(\"wcs-recorder:duration-changed\", v);\n }\n\n private _setMimeType(v: string): void {\n if (this._mimeType === v) return;\n this._mimeType = v;\n this._dispatch(\"wcs-recorder:mimetype-changed\", v);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event, so unlike the value setters this is not content-deduped. Only the\n // null→null transition is collapsed (clearing an already-clear error stays silent).\n // The guard is written on null explicitly so it does NOT depend on callers passing a\n // fresh object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-recorder:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Borrow a stream for recording (the direct-channel sink). Synchronous, no\n * await: the live handle is captured by reference and never stored in state.\n * Does NOT stop any previously-borrowed stream — ownership stays with the camera.\n *\n * Re-attaching mid-recording takes effect on the NEXT start(), not the current\n * recording: the live MediaRecorder was already constructed around the previous\n * stream and keeps recording it until stop(). The borrowed reference is swapped so\n * the following start() uses the new stream.\n */\n attachStream(stream: MediaStream): void {\n this._stream = stream;\n }\n\n /** Start recording the borrowed stream. Never throws — surfaces `error`. */\n start(options: RecorderOptions = {}): void {\n if (!hasMediaRecorder()) {\n this._setError({ name: \"unsupported\", message: \"MediaRecorder is not available in this environment.\" });\n return;\n }\n if (!this._stream) {\n this._setError({ name: \"NoStreamError\", message: \"No stream attached. Wire a camera's stream-ready to attachStream first.\" });\n return;\n }\n if (this._recording) return;\n\n const recOptions: MediaRecorderOptions = {};\n if (options.mimeType && this._isTypeSupported(options.mimeType)) {\n recOptions.mimeType = options.mimeType;\n }\n if (typeof options.audioBitsPerSecond === \"number\") recOptions.audioBitsPerSecond = options.audioBitsPerSecond;\n if (typeof options.videoBitsPerSecond === \"number\") recOptions.videoBitsPerSecond = options.videoBitsPerSecond;\n\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(this._stream, recOptions);\n } catch (error) {\n this._setError(normalizeMediaError(error));\n return;\n }\n\n const gen = ++this._gen;\n this._chunks = [];\n this._timeslice = typeof options.timeslice === \"number\" && options.timeslice > 0;\n\n recorder.ondataavailable = (event: BlobEvent): void => {\n if (gen !== this._gen) return;\n if (event.data && event.data.size > 0) {\n this._chunks.push(event.data);\n if (this._timeslice) {\n this._dispatch(\"wcs-recorder:dataavailable\", event.data);\n }\n }\n };\n recorder.onstop = (): void => {\n if (gen !== this._gen) return;\n // When stopped while paused, `_duration` was already finalized in onpause —\n // recomputing _elapsed() here would wrongly include the paused gap (the\n // clock kept running but `_startTime` was not advanced). Keep the held value.\n if (!this._paused) this._setDuration(this._elapsed());\n this._assembleBlob();\n this._setPaused(false);\n this._setRecording(false);\n };\n recorder.onerror = (event: Event): void => {\n if (gen !== this._gen) return;\n const err = (event as unknown as { error?: unknown }).error;\n this._setError(normalizeMediaError(err ?? { name: \"RecorderError\" }));\n };\n recorder.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setDuration(this._elapsed());\n this._setPaused(true);\n };\n recorder.onresume = (): void => {\n if (gen !== this._gen) return;\n this._startTime = this._now() - this._duration;\n this._setPaused(false);\n };\n\n this._recorder = recorder;\n this._setError(null);\n this._setMimeType(recorder.mimeType || recOptions.mimeType || \"\");\n this._startTime = this._now();\n this._setDuration(0);\n recorder.start(this._timeslice ? options.timeslice : undefined);\n this._setRecording(true);\n }\n\n /** Stop recording; the assembled Blob is published from the recorder's onstop. */\n stop(): void {\n if (this._recorder && this._recorder.state !== \"inactive\") {\n this._recorder.stop();\n }\n }\n\n pause(): void {\n if (this._recorder && this._recorder.state === \"recording\") {\n this._recorder.pause();\n }\n }\n\n resume(): void {\n if (this._recorder && this._recorder.state === \"paused\") {\n this._recorder.resume();\n }\n }\n\n /** Stop in-flight recording, revoke the last object URL, drop the borrowed stream. */\n dispose(): void {\n // Bump the generation first so the native stop()'s onstop (gen-guarded) does\n // not run on a disposed Core — then reset the recording flags directly here.\n this._gen++;\n if (this._recorder && this._recorder.state !== \"inactive\") {\n try {\n this._recorder.stop();\n } catch {\n // A recorder already torn down by the environment must not throw here.\n }\n }\n this._recorder = null;\n this._revokeUrl();\n // Drop the borrowed reference WITHOUT stopping its tracks — the camera owns it.\n this._stream = null;\n // Reset transient recording state silently (onstop was gen-guarded out above).\n this._recording = false;\n this._paused = false;\n }\n\n // --- Internal ---\n\n private _assembleBlob(): void {\n const blob = new Blob(this._chunks, this._mimeType ? { type: this._mimeType } : undefined);\n this._chunks = [];\n // The PREVIOUS clip's object URL is revoked here, before minting the new one — so\n // any consumer still pointing a `<video src>` at the old URL will break once a new\n // recording completes. Consumers must follow the latest `objectURL`/`recorded`\n // value and not pin a stale URL. (The `blob` is unaffected — prefer flowing it.)\n this._revokeUrl();\n const objectURL = this._createUrl(blob);\n this._blob = blob;\n this._objectURL = objectURL;\n const detail: WcsRecordedDetail = {\n blob,\n objectURL,\n mimeType: this._mimeType,\n duration: this._duration,\n };\n this._dispatch(\"wcs-recorder:recorded\", detail);\n }\n\n private _revokeUrl(): void {\n if (this._objectURL && typeof URL !== \"undefined\" && typeof URL.revokeObjectURL === \"function\") {\n URL.revokeObjectURL(this._objectURL);\n }\n this._objectURL = null;\n }\n\n private _createUrl(blob: Blob): string {\n if (typeof URL !== \"undefined\" && typeof URL.createObjectURL === \"function\") {\n return URL.createObjectURL(blob);\n }\n return \"\";\n }\n\n // Only called after hasMediaRecorder() has confirmed the API, so MediaRecorder\n // and its isTypeSupported are present.\n private _isTypeSupported(type: string): boolean {\n const MR = (globalThis as { MediaRecorder: { isTypeSupported(t: string): boolean } }).MediaRecorder;\n return MR.isTypeSupported(type);\n }\n\n // performance.now() is universally available wherever MediaRecorder runs\n // (browsers and the happy-dom test env), so no fallback guard is needed.\n private _now(): number {\n return performance.now();\n }\n\n private _elapsed(): number {\n return Math.max(0, Math.round(this._now() - this._startTime));\n }\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { RecorderCore } from \"../core/RecorderCore.js\";\n\n/**\n * `<wcs-recorder>` — declarative media recording. Wraps RecorderCore and records a\n * borrowed `MediaStream` received via the `attachStream` command (the direct\n * channel from `<wcs-camera>`'s `stream-ready`). It never owns or stops the stream.\n *\n * Recording parameters (`mime-type` / `timeslice` / bitrates) are mirrored\n * attributes. The assembled clip is published as `wcs-recorder:recorded`\n * (`{ blob, objectURL, mimeType, duration }`) and the `blob` / `objectURL` value\n * properties — a settled `Blob` is a value and may flow through state.\n */\nexport class WcsRecorder extends HTMLElement {\n static wcBindable: IWcBindable = {\n ...RecorderCore.wcBindable,\n // `mimeType` deliberately appears on TWO surfaces: as an output `property`\n // (inherited from RecorderCore — the browser-resolved recording type, event\n // `mimetype-changed`) and as an `input` (the `mime-type` request attribute). They\n // share a base name but are distinct directions: the property is read-only output\n // (getter → Core), the input is the write-only request (setter → attribute, read\n // back in _options()). See README \"request vs. resolved\".\n inputs: [\n { name: \"mimeType\", attribute: \"mime-type\" },\n { name: \"timeslice\", attribute: \"timeslice\" },\n { name: \"audioBitsPerSecond\", attribute: \"audio-bits\" },\n { name: \"videoBitsPerSecond\", attribute: \"video-bits\" },\n ],\n commands: RecorderCore.wcBindable.commands,\n };\n\n private _core: RecorderCore;\n\n constructor() {\n super();\n this._core = new RecorderCore(this);\n }\n\n // --- Attribute accessors ---\n\n // `mimeType` is an OUTPUT value property (the Core-resolved recording type,\n // published via `wcs-recorder:mimetype-changed`), so the getter delegates to the\n // Core — NOT the `mime-type` input attribute. The attribute is a *request*: the\n // browser may pick a different type, or fill one in when none was requested, and\n // bindings must read the actual value. The input side is read straight from the\n // attribute in `_options()`. The setter still writes the request attribute.\n get mimeType(): string { return this._core.mimeType; }\n set mimeType(value: string) { this.setAttribute(\"mime-type\", value); }\n\n get timeslice(): number { return this._numberAttr(\"timeslice\"); }\n set timeslice(value: number) { this.setAttribute(\"timeslice\", String(value)); }\n\n get audioBitsPerSecond(): number { return this._numberAttr(\"audio-bits\"); }\n set audioBitsPerSecond(value: number) { this.setAttribute(\"audio-bits\", String(value)); }\n\n get videoBitsPerSecond(): number { return this._numberAttr(\"video-bits\"); }\n set videoBitsPerSecond(value: number) { this.setAttribute(\"video-bits\", String(value)); }\n\n // --- Core delegated getters ---\n\n get recording(): boolean { return this._core.recording; }\n get paused(): boolean { return this._core.paused; }\n get duration(): number { return this._core.duration; }\n get blob(): Blob | null { return this._core.blob; }\n get objectURL(): string | null { return this._core.objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n // --- Commands ---\n\n /** Borrow a stream (the direct-channel sink). */\n attachStream(stream: MediaStream): void {\n this._core.attachStream(stream);\n }\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void { this._core.stop(); }\n pause(): void { this._core.pause(); }\n resume(): void { this._core.resume(); }\n\n // --- Internal ---\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _options(): RecorderOptions {\n const o: RecorderOptions = {};\n // Read the requested type from the input attribute directly — `get mimeType()` is\n // the resolved OUTPUT value, not the request.\n const requested = this.getAttribute(\"mime-type\") ?? \"\";\n if (requested) o.mimeType = requested;\n if (Number.isFinite(this.timeslice)) o.timeslice = this.timeslice;\n if (Number.isFinite(this.audioBitsPerSecond)) o.audioBitsPerSecond = this.audioBitsPerSecond;\n if (Number.isFinite(this.videoBitsPerSecond)) o.videoBitsPerSecond = this.videoBitsPerSecond;\n return o;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsCamera } from \"./components/Camera.js\";\nimport { WcsRecorder } from \"./components/Recorder.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.camera)) {\n customElements.define(config.tagNames.camera, WcsCamera);\n }\n if (!customElements.get(config.tagNames.recorder)) {\n customElements.define(config.tagNames.recorder, WcsRecorder);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapCamera(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/media/getUserMedia.ts","../src/media/permission.ts","../src/core/CameraCore.ts","../src/components/Camera.ts","../src/core/RecorderCore.ts","../src/components/Recorder.ts","../src/registerComponents.ts","../src/bootstrapCamera.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n camera: string;\n recorder: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n camera: \"wcs-camera\",\n recorder: \"wcs-recorder\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by registerComponents (read at call time so\n// setConfig() takes effect without re-import). Public consumers get the deep-frozen\n// clone from getConfig().\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { CameraConstraints, MediaDeviceSnapshot, WcsMediaErrorDetail } from \"../types.js\";\n\n/** True when getUserMedia is reachable (secure context with a media-devices impl). */\nexport function hasMediaDevices(): boolean {\n return typeof navigator !== \"undefined\"\n && !!navigator.mediaDevices\n && typeof navigator.mediaDevices.getUserMedia === \"function\";\n}\n\n/** True when MediaRecorder is available in this environment. */\nexport function hasMediaRecorder(): boolean {\n return typeof globalThis !== \"undefined\"\n && typeof (globalThis as { MediaRecorder?: unknown }).MediaRecorder === \"function\";\n}\n\n/**\n * Translate a getUserMedia constraints object from the declarative\n * CameraConstraints surface. Always requests a video track; `audio` opts the\n * microphone in. `deviceId` (exact) takes precedence over `facingMode`.\n */\nexport function buildConstraints(c: CameraConstraints): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n if (c.deviceId) {\n video.deviceId = { exact: c.deviceId };\n } else if (c.facingMode) {\n video.facingMode = c.facingMode;\n }\n if (typeof c.width === \"number\") video.width = c.width;\n if (typeof c.height === \"number\") video.height = c.height;\n const hasVideoConstraint = Object.keys(video).length > 0;\n return {\n video: hasVideoConstraint ? video : true,\n audio: c.audio === true,\n };\n}\n\n/** Normalize any thrown getUserMedia / MediaRecorder failure into a flat detail. */\nexport function normalizeMediaError(error: unknown): WcsMediaErrorDetail {\n if (error && typeof error === \"object\" && \"name\" in error) {\n const name = String((error as { name: unknown }).name) || \"Error\";\n const message = \"message\" in error && (error as { message?: unknown }).message\n ? String((error as { message: unknown }).message)\n : `Media request failed: ${name}.`;\n return { name, message };\n }\n return { name: \"Error\", message: \"Media request failed.\" };\n}\n\nconst UNSUPPORTED_ERROR: WcsMediaErrorDetail = {\n name: \"unsupported\",\n message: \"getUserMedia is not available (requires a secure context).\",\n};\n\nexport interface UserMediaResult {\n stream?: MediaStream;\n error?: WcsMediaErrorDetail;\n}\n\n/**\n * Request a media stream. Never throws — resolves with `{ stream }` on success or\n * `{ error }` (normalized) on failure / when the API is unavailable.\n */\nexport async function requestUserMedia(constraints: MediaStreamConstraints): Promise<UserMediaResult> {\n if (!hasMediaDevices()) {\n return { error: UNSUPPORTED_ERROR };\n }\n try {\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\n return { stream };\n } catch (error) {\n return { error: normalizeMediaError(error) };\n }\n}\n\n/** Stop every track of a stream, releasing the camera/microphone hardware. */\nexport function stopAllTracks(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) {\n track.stop();\n }\n}\n\n/**\n * Enumerate video input devices as plain snapshots. Labels are only populated\n * after a grant, so this is refreshed post-acquisition. Never throws.\n */\nexport async function enumerateVideoDevices(): Promise<MediaDeviceSnapshot[]> {\n if (!hasMediaDevices() || typeof navigator.mediaDevices.enumerateDevices !== \"function\") {\n return [];\n }\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === \"videoinput\")\n .map((d) => ({\n deviceId: d.deviceId,\n label: d.label,\n groupId: d.groupId,\n kind: d.kind,\n }));\n } catch {\n return [];\n }\n}\n","import { MediaPermissionState } from \"../types.js\";\n\n/**\n * Live monitor for a single media permission (`camera` or `microphone`) via the\n * Permissions API. Mirrors PermissionCore's two-phase pattern: an initial query\n * plus a live `change` subscription, guarded by a monotonic generation so a query\n * superseded by a rapid dispose/observe never attaches a stale listener.\n *\n * When the Permissions API is absent or rejects the descriptor (e.g. Firefox does\n * not accept the `camera` / `microphone` descriptor), the watcher reports\n * `\"unsupported\"`. The CameraCore then refines the state from the getUserMedia\n * outcome (granted on success, denied on NotAllowedError).\n */\nexport class MediaPermissionWatcher {\n private _name: \"camera\" | \"microphone\";\n private _onChange: (state: MediaPermissionState) => void;\n private _status: PermissionStatus | null = null;\n private _gen: number = 0;\n private _subscribed: boolean = false;\n\n constructor(name: \"camera\" | \"microphone\", onChange: (state: MediaPermissionState) => void) {\n this._name = name;\n this._onChange = onChange;\n }\n\n /** Issue the initial query and subscribe to live changes. Resolves when settled. */\n observe(): Promise<void> {\n if (this._subscribed) return Promise.resolve();\n if (typeof navigator === \"undefined\" || !navigator.permissions\n || typeof navigator.permissions.query !== \"function\") {\n this._onChange(\"unsupported\");\n return Promise.resolve();\n }\n this._subscribed = true;\n const gen = ++this._gen;\n return navigator.permissions\n .query({ name: this._name as PermissionName })\n .then(\n (status) => {\n if (gen !== this._gen) return;\n this._status = status;\n this._onChange(status.state as MediaPermissionState);\n status.addEventListener(\"change\", this._onStatusChange);\n },\n () => {\n if (gen !== this._gen) return;\n this._onChange(\"unsupported\");\n },\n );\n }\n\n /** Detach the live listener and invalidate any in-flight query. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n if (this._status) {\n this._status.removeEventListener(\"change\", this._onStatusChange);\n this._status = null;\n }\n }\n\n private _onStatusChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._onChange(status.state as MediaPermissionState);\n };\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot,\n MediaPermissionState, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport {\n buildConstraints, enumerateVideoDevices, hasMediaDevices, requestUserMedia, stopAllTracks,\n} from \"../media/getUserMedia.js\";\nimport { MediaPermissionWatcher } from \"../media/permission.js\";\n\n/**\n * Headless camera-capture primitive. Wraps getUserMedia + the Permissions API and\n * exposes a `MediaStream` through the wc-bindable protocol — but the live stream is\n * NEVER published as a reactive value. It is a non-serializable live handle: it\n * flows out only via the `wcs-camera:stream-ready` event so a consumer (the preview\n * `<video>`, a `<wcs-recorder>`) can bind it directly to an element property,\n * bypassing serializable state. See docs/camera-recorder-tag-design.md §1/§2.\n *\n * The observable value surface is strictly derived data: `active` (is a stream\n * live — the \"actual\" half of the desired/actual pair), `permission` /\n * `audioPermission` (two-phase: Permissions API monitor + getUserMedia outcome),\n * `deviceId` / `devices`, and `error`. Failures never throw — they surface through\n * `error`.\n */\nexport class CameraCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"active\", event: \"wcs-camera:active-changed\" },\n { name: \"permission\", event: \"wcs-camera:permission-changed\" },\n { name: \"audioPermission\", event: \"wcs-camera:audio-permission-changed\" },\n { name: \"deviceId\", event: \"wcs-camera:device-changed\" },\n { name: \"devices\", event: \"wcs-camera:devices-changed\" },\n { name: \"error\", event: \"wcs-camera:error\" },\n // Direct-channel handle: event-token only — never bound as a reactive value.\n { name: \"streamReady\", event: \"wcs-camera:stream-ready\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token: a bare signal (detail is always null) — surface detail, not the raw Event.\n { name: \"ended\", event: \"wcs-camera:ended\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"switchCamera\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _active: boolean = false;\n private _permission: MediaPermissionState = \"prompt\";\n private _audioPermission: MediaPermissionState | null = null;\n private _deviceId: string | null = null;\n private _devices: MediaDeviceSnapshot[] = [];\n private _error: WcsMediaErrorDetail | null = null;\n\n // The live stream — internal only, never a reactive value (see class docs).\n private _stream: MediaStream | null = null;\n\n // desired/actual split (wakelock-style): `_desired` is whether the user wants the\n // camera on; `_active` is whether a stream is actually live. The OS can revoke a\n // track (device unplugged / taken by another app) — actual drops while desired\n // stays true, so a later resume()/visibility-restore can re-acquire.\n private _desired: boolean = false;\n\n private _constraints: CameraConstraints = {};\n\n // Monotonic id of the current acquisition lifecycle. Bumped by every acquire and\n // by dispose(). Each in-flight getUserMedia captures it and, on resolve, bails\n // (stopping the just-acquired stream) when superseded — so a constraints change\n // mid-acquire (switchMap-style restart) cannot leave an orphaned stream live.\n private _gen: number = 0;\n\n private _subscribed: boolean = false;\n private _camWatcher: MediaPermissionWatcher | null = null;\n private _micWatcher: MediaPermissionWatcher | null = null;\n\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get active(): boolean { return this._active; }\n get permission(): MediaPermissionState { return this._permission; }\n get audioPermission(): MediaPermissionState | null { return this._audioPermission; }\n get deviceId(): string | null { return this._deviceId; }\n get devices(): MediaDeviceSnapshot[] { return this._devices; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n get ready(): Promise<void> { return this._ready; }\n\n // --- State setters with event dispatch (same-value guarded) ---\n\n private _setActive(active: boolean): void {\n if (this._active === active) return;\n this._active = active;\n this._dispatch(\"wcs-camera:active-changed\", active);\n }\n\n private _setPermission(state: MediaPermissionState): void {\n if (this._permission === state) return;\n this._permission = state;\n this._dispatch(\"wcs-camera:permission-changed\", state);\n }\n\n private _setAudioPermission(state: MediaPermissionState | null): void {\n if (this._audioPermission === state) return;\n this._audioPermission = state;\n this._dispatch(\"wcs-camera:audio-permission-changed\", state);\n }\n\n private _setDeviceId(id: string | null): void {\n if (this._deviceId === id) return;\n this._deviceId = id;\n this._dispatch(\"wcs-camera:device-changed\", id);\n }\n\n private _setDevices(devices: MediaDeviceSnapshot[]): void {\n if (this._devicesEqual(this._devices, devices)) return;\n this._devices = devices;\n this._dispatch(\"wcs-camera:devices-changed\", devices);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event (e.g. retrying getUserMedia and failing again must re-notify), so\n // unlike the value setters this is not content-deduped. Only the null→null\n // transition is collapsed (clearing an already-clear error stays silent). The guard\n // is written on null explicitly so it does NOT depend on callers passing a fresh\n // object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-camera:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n private _devicesEqual(a: MediaDeviceSnapshot[], b: MediaDeviceSnapshot[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].deviceId !== b[i].deviceId || a[i].label !== b[i].label) return false;\n }\n return true;\n }\n\n // --- Public API ---\n\n /**\n * Begin observing permissions for the given constraints. Idempotent while\n * already subscribed. The first call (or one after dispose()) starts the\n * camera/microphone permission monitors. Acquisition itself is driven separately\n * by start() / autostart — observing does not prompt.\n */\n observe(constraints: CameraConstraints): Promise<void> {\n this._constraints = { ...constraints };\n if (!this._subscribed) {\n this._subscribed = true;\n this._ready = this._initPermissions();\n } else {\n // Already live: track the latest constraints and fold any newly-started\n // microphone query into `ready` so awaiting observe() guarantees its initial\n // permission state — symmetric with _initPermissions() on the first observe.\n this._ready = this._ready.then(() => this._reconcileAudioWatcher());\n }\n return this._ready;\n }\n\n /** Acquire the camera (sets desired=true). Prompts on first use. */\n start(): void {\n this._desired = true;\n this._restart();\n }\n\n /** Release the camera (sets desired=false), stopping all tracks. */\n stop(): void {\n this._desired = false;\n this._release(false);\n }\n\n /**\n * Toggle facingMode (user ↔ environment) and re-acquire if active. This is the\n * headless, DOM-free path: it flips the Core's internal `_constraints` (the single\n * source of truth for a standalone Core) and re-acquires while desired.\n *\n * Note: the `<wcs-camera>` Shell does NOT delegate to this — it keeps the DOM\n * attributes authoritative and drives its own single re-acquire (see Camera.ts\n * switchCamera). Both reach the same end state; the split exists because the Shell\n * must keep its declared attributes in sync, which a Core has no notion of.\n */\n switchCamera(): void {\n const next: FacingMode = this._constraints.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._constraints = { ...this._constraints, facingMode: next, deviceId: undefined };\n if (this._desired) {\n this._restart();\n }\n }\n\n /**\n * Suspend the live stream while keeping `desired` — for page-hidden. Stops tracks\n * (clearing the hardware indicator) but remembers that the camera should resume.\n *\n * Bumps `_gen` to supersede any in-flight acquire: without this, an acquire that\n * resolves *after* the page went hidden would assign `_stream` and set active —\n * re-lighting the camera behind a no-op suspend (the stream had not been assigned\n * yet, so the `if (_stream)` release below could not reach it). The superseded\n * acquire stops its just-acquired orphan stream on resolve (see `_acquire`).\n */\n suspend(): void {\n this._gen++;\n if (this._stream) {\n this._release(false);\n }\n }\n\n /** Re-acquire if the camera is desired but not currently active — for page-visible. */\n resume(): void {\n if (this._desired && !this._active) {\n this._restart();\n }\n }\n\n /** Tear down: stop the stream and detach permission listeners. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n this._desired = false;\n this._release(true);\n if (hasMediaDevices() && typeof navigator.mediaDevices.removeEventListener === \"function\") {\n navigator.mediaDevices.removeEventListener(\"devicechange\", this._onDeviceChange);\n }\n this._camWatcher?.dispose();\n this._micWatcher?.dispose();\n this._camWatcher = null;\n this._micWatcher = null;\n }\n\n // --- Internal ---\n\n private _initPermissions(): Promise<void> {\n if (!hasMediaDevices()) {\n this._setPermission(\"unsupported\");\n return Promise.resolve();\n }\n this._camWatcher = new MediaPermissionWatcher(\"camera\", (s) => this._setPermission(s));\n const tasks: Promise<void>[] = [this._camWatcher.observe()];\n if (this._constraints.audio) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n tasks.push(this._micWatcher.observe());\n }\n // Track hot-plug: refresh the device list when a camera is added/removed.\n if (typeof navigator.mediaDevices.addEventListener === \"function\") {\n navigator.mediaDevices.addEventListener(\"devicechange\", this._onDeviceChange);\n }\n return Promise.all(tasks).then(() => undefined);\n }\n\n private _onDeviceChange = (): void => {\n void enumerateVideoDevices().then((devices) => {\n // Guard against a late resolution after dispose.\n if (this._subscribed) this._setDevices(devices);\n });\n };\n\n // Bring the microphone watcher in line with the latest `audio` constraint when\n // observe() is called again on an already-live Core. Returns the new watcher's\n // initial-query promise (so observe() can fold it into `ready`); resolved when\n // nothing started.\n private _reconcileAudioWatcher(): Promise<void> {\n if (this._constraints.audio && !this._micWatcher && hasMediaDevices()) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n return this._micWatcher.observe();\n } else if (!this._constraints.audio && this._micWatcher) {\n this._micWatcher.dispose();\n this._micWatcher = null;\n this._setAudioPermission(null);\n }\n return Promise.resolve();\n }\n\n private _restart(): void {\n this._release(false);\n void this._acquire();\n }\n\n private async _acquire(): Promise<void> {\n const gen = ++this._gen;\n const constraints = buildConstraints(this._constraints);\n const { stream, error } = await requestUserMedia(constraints);\n\n // Superseded by a newer acquire (rapid restart) or disposed while in flight:\n // stop the orphan stream and bail without mutating state. The `ended` listeners\n // are attached only AFTER this gen check (below), so stopping the orphan here\n // cannot fire _onTrackEnded — no spurious `ended` event / state mutation. Keep\n // this stop strictly before listener attachment if the order is ever refactored.\n if (gen !== this._gen) {\n stopAllTracks(stream ?? null);\n return;\n }\n\n if (error) {\n this._setError(error);\n if (error.name === \"NotAllowedError\") {\n this._setPermission(\"denied\");\n // Hard denial: drop `desired` so a later visibility-restore (resume()) does\n // not silently re-attempt getUserMedia on every page-visible. A transient\n // failure (NotReadableError = device busy) keeps `desired` so it can recover.\n this._desired = false;\n } else if (error.name === \"unsupported\") {\n this._setPermission(\"unsupported\");\n this._desired = false;\n }\n this._setActive(false);\n return;\n }\n\n const live = stream as MediaStream;\n this._stream = live;\n for (const track of live.getTracks()) {\n track.addEventListener(\"ended\", this._onTrackEnded);\n }\n this._setError(null);\n // getUserMedia success is authoritative for permission.\n this._setPermission(\"granted\");\n // Only assert mic-granted when the grant actually produced an audio track. With\n // today's boolean `audio` this is equivalent to `_constraints.audio`, but it stays\n // correct under a future non-mandatory `{ audio: {...} }` constraint where the\n // browser may grant video while omitting audio.\n if (this._constraints.audio && live.getAudioTracks().length > 0) {\n this._setAudioPermission(\"granted\");\n }\n this._updateDeviceId(live);\n this._setActive(true);\n // Publish the live handle for the direct element→element channel.\n this._dispatch(\"wcs-camera:stream-ready\", live);\n // Labels become available after a grant; refresh the device list.\n const devices = await enumerateVideoDevices();\n if (gen === this._gen) {\n this._setDevices(devices);\n }\n }\n\n private _release(silent: boolean): void {\n if (!this._stream) return;\n for (const track of this._stream.getTracks()) {\n track.removeEventListener(\"ended\", this._onTrackEnded);\n }\n stopAllTracks(this._stream);\n this._stream = null;\n if (silent) {\n this._active = false;\n } else {\n this._setActive(false);\n }\n }\n\n private _onTrackEnded = (): void => {\n // OS revoked a track (unplug / taken by another app). actual drops; desired\n // stays true so resume()/visibility-restore can re-acquire.\n this._release(false);\n this._dispatch(\"wcs-camera:ended\", null);\n };\n\n private _updateDeviceId(stream: MediaStream): void {\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack && typeof videoTrack.getSettings === \"function\") {\n const settings = videoTrack.getSettings();\n this._setDeviceId(settings.deviceId ?? null);\n }\n }\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot, MediaPermissionState,\n WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { CameraCore } from \"../core/CameraCore.js\";\n\n/**\n * `<wcs-camera>` — declarative camera capture with a built-in preview.\n *\n * The element owns a `<video>` in its shadow root and assigns the live\n * `MediaStream` to `video.srcObject` internally, so the non-serializable handle\n * never crosses the state boundary (design §1, case B). For consumers (a\n * `<wcs-recorder>`, an external `<video>`), the stream is also published via the\n * `wcs-camera:stream-ready` event-token for the direct element→element channel\n * (design §2).\n *\n * Acquisition is explicit: `start()` / the `autostart` attribute prompt and\n * acquire; merely connecting does not. While the page is hidden the stream is\n * suspended (clearing the camera indicator) and re-acquired on return, unless\n * `keep-alive` is set (e.g. during recording).\n */\nexport class WcsCamera extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...CameraCore.wcBindable,\n inputs: [\n { name: \"audio\", attribute: \"audio\" },\n { name: \"facingMode\", attribute: \"facing-mode\" },\n { name: \"deviceId\", attribute: \"device-id\" },\n { name: \"width\", attribute: \"width\" },\n { name: \"height\", attribute: \"height\" },\n { name: \"autostart\", attribute: \"autostart\" },\n { name: \"keepAlive\", attribute: \"keep-alive\" },\n ],\n commands: CameraCore.wcBindable.commands,\n };\n\n // `autostart` and `keep-alive` are intentionally NOT observed: `autostart` is a\n // connect-time-only acquire trigger (read once in connectedCallback; flipping it\n // later is meaningless), and `keep-alive` is read fresh on every visibilitychange\n // (_onVisibilityChange), so it never needs to drive a re-acquire. The observed set\n // is exactly the constraints that reshape the requested track.\n static observedAttributes = [\"facing-mode\", \"device-id\", \"audio\", \"width\", \"height\"];\n\n private _core: CameraCore;\n private _video: HTMLVideoElement;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _connected: boolean = false;\n // True while switchCamera() rewrites several attributes at once. Each setAttribute /\n // removeAttribute fires its own attributeChangedCallback synchronously; without this\n // guard the FIRST change would re-acquire with the not-yet-updated constraints (and\n // tear active down so the later change's re-acquire is skipped). We suppress the\n // per-attribute re-acquire and drive a single one with the final constraints.\n private _batchingAttrs: boolean = false;\n\n constructor() {\n super();\n this._core = new CameraCore(this);\n const root = this.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = \":host{display:inline-block}video{display:block;width:100%;height:100%}\";\n this._video = document.createElement(\"video\");\n this._video.autoplay = true;\n this._video.muted = true;\n this._video.setAttribute(\"playsinline\", \"\");\n this._video.setAttribute(\"part\", \"video\");\n root.append(style, this._video);\n // Bind the live handle to the preview internally — never through state. These\n // self-listeners are intentionally not removed on disconnect (asymmetric with the\n // document-level `visibilitychange` in connect/disconnectedCallback): the target\n // is `this`, so the listeners are collected together with the element — there is\n // no external reference to leak. The visibility listener, by contrast, lives on\n // `document` (outlives the element) and MUST be detached.\n this.addEventListener(\"wcs-camera:stream-ready\", this._onStreamReady as EventListener);\n this.addEventListener(\"wcs-camera:active-changed\", this._onActiveChanged as EventListener);\n }\n\n // --- Attribute accessors ---\n\n get audio(): boolean { return this.hasAttribute(\"audio\"); }\n set audio(value: boolean) { this._toggleAttr(\"audio\", value); }\n\n get facingMode(): FacingMode {\n return this.getAttribute(\"facing-mode\") === \"environment\" ? \"environment\" : \"user\";\n }\n set facingMode(value: FacingMode) { this.setAttribute(\"facing-mode\", value); }\n\n get deviceId(): string { return this.getAttribute(\"device-id\") ?? \"\"; }\n set deviceId(value: string) { this.setAttribute(\"device-id\", value); }\n\n get width(): number { return this._numberAttr(\"width\"); }\n set width(value: number) { this.setAttribute(\"width\", String(value)); }\n\n get height(): number { return this._numberAttr(\"height\"); }\n set height(value: number) { this.setAttribute(\"height\", String(value)); }\n\n get autostart(): boolean { return this.hasAttribute(\"autostart\"); }\n set autostart(value: boolean) { this._toggleAttr(\"autostart\", value); }\n\n get keepAlive(): boolean { return this.hasAttribute(\"keep-alive\"); }\n set keepAlive(value: boolean) { this._toggleAttr(\"keep-alive\", value); }\n\n /** The internal preview `<video>` (for advanced styling/measurement). */\n get videoElement(): HTMLVideoElement { return this._video; }\n\n // --- Core delegated getters ---\n\n get active(): boolean { return this._core.active; }\n get permission(): MediaPermissionState { return this._core.permission; }\n get audioPermission(): MediaPermissionState | null { return this._core.audioPermission; }\n get devices(): MediaDeviceSnapshot[] { return this._core.devices; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n get connectedCallbackPromise(): Promise<void> { return this._connectedCallbackPromise; }\n\n // --- Commands ---\n\n start(): void { this._core.start(); }\n stop(): void { this._core.stop(); }\n\n /**\n * Toggle the front/back camera by updating the DOM attributes (the single source\n * of truth), not just the Core's internal constraints. Deliberately does NOT call\n * `CameraCore.switchCamera()` (which would mutate the Core's constraints behind the\n * DOM's back, leaving the declared attributes stale). `device-id` is removed because\n * it would otherwise take precedence over `facing-mode` (see buildConstraints) —\n * leaving it pinned would silently undo the switch on the next re-acquire. Both\n * attribute writes are batched (see `_batchingAttrs`) so they drive exactly ONE\n * re-acquire here, with the final constraints — never an early acquire on a\n * half-updated state. The DOM and the live camera stay in agreement.\n */\n switchCamera(): void {\n const next: FacingMode = this.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._batchingAttrs = true;\n try {\n this.removeAttribute(\"device-id\");\n this.setAttribute(\"facing-mode\", next);\n } finally {\n this._batchingAttrs = false;\n }\n // Sync the (now-final) constraints and re-acquire once when a stream is live —\n // the same `active`-guarded restart attributeChangedCallback performs.\n this._core.observe(this._constraints());\n if (this._core.active) {\n this._core.start();\n }\n }\n\n // --- Internal ---\n\n private _toggleAttr(name: string, value: boolean): void {\n if (value) {\n this.setAttribute(name, \"\");\n } else {\n this.removeAttribute(name);\n }\n }\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _constraints(): CameraConstraints {\n const c: CameraConstraints = { audio: this.audio, facingMode: this.facingMode };\n if (this.deviceId) c.deviceId = this.deviceId;\n if (Number.isFinite(this.width)) c.width = this.width;\n if (Number.isFinite(this.height)) c.height = this.height;\n return c;\n }\n\n private _onStreamReady = (event: CustomEvent): void => {\n this._video.srcObject = event.detail as MediaStream;\n };\n\n private _onActiveChanged = (event: CustomEvent): void => {\n // Clear the preview when the stream is released so the last frame does not stick.\n if (event.detail === false) {\n this._video.srcObject = null;\n }\n };\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this._connected = true;\n this._connectedCallbackPromise = this._core.observe(this._constraints());\n if (this.autostart) {\n this._core.start();\n }\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n }\n\n disconnectedCallback(): void {\n this._connected = false;\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._core.dispose();\n }\n\n attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void {\n if (!this._connected || oldValue === newValue) return;\n // While switchCamera() batches several attribute writes, defer to its single\n // post-batch re-acquire — otherwise the first write would acquire on stale\n // constraints (see `_batchingAttrs`).\n if (this._batchingAttrs) return;\n // Track the new constraints (and reconcile the microphone watcher).\n this._core.observe(this._constraints());\n // A constraints change re-acquires (switchMap-style restart) only when a stream\n // is actually live. `active` is the deliberate guard (not `desired`): `active`\n // implies `desired` (a stream only goes live under desired), so re-acquiring\n // cannot spuriously re-`desired` a stopped camera. Guarding on `active` rather\n // than `desired` also avoids force-acquiring while suspended/hidden (desired but\n // not active) — the visibility handler owns resume there.\n if (this._core.active) {\n this._core.start();\n }\n }\n\n private _onVisibilityChange = (): void => {\n if (this.keepAlive) return;\n if (document.visibilityState === \"hidden\") {\n this._core.suspend();\n } else {\n this._core.resume();\n }\n };\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail, WcsRecordedDetail,\n} from \"../types.js\";\nimport { hasMediaRecorder, normalizeMediaError } from \"../media/getUserMedia.js\";\n\n/**\n * Headless media-recording primitive. Wraps MediaRecorder, consuming a borrowed\n * `MediaStream` (received via `attachStream` over the direct channel — see\n * docs/camera-recorder-tag-design.md §2) and producing a `Blob` clip.\n *\n * Ownership: the stream is BORROWED, never owned. The Core never stops its tracks\n * — that is the camera's job (the acquirer owns release). Stopping here would tear\n * down a stream that may still be previewing.\n *\n * Non-goal: the Core does NOT subscribe to the borrowed tracks' `ended` and does NOT\n * auto-terminate a recording when the underlying stream dies (camera stop / OS\n * revoke / switchCamera re-acquire). Reacting would mean reaching into a stream we do\n * not own. The MediaRecorder keeps running against the now-dead source until an\n * explicit stop(); the consumer (which owns the camera lifecycle) is responsible for\n * stopping the recording when it tears the stream down. stop() then assembles\n * whatever chunks were captured — never throws.\n *\n * Output: `dataavailable` chunks are collected and assembled into one `Blob` on\n * stop, published via `wcs-recorder:recorded`. The `Blob` is structured-clone\n * friendly (a settled value, unlike MediaStream) so it may flow through state.\n * `objectURL` is a managed string — the Core revokes the previous URL before\n * issuing a new one and on dispose. Failures never throw.\n */\nexport class RecorderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"recording\", event: \"wcs-recorder:recording-changed\" },\n { name: \"paused\", event: \"wcs-recorder:paused-changed\" },\n { name: \"duration\", event: \"wcs-recorder:duration-changed\" },\n { name: \"mimeType\", event: \"wcs-recorder:mimetype-changed\" },\n { name: \"blob\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.blob ?? null },\n { name: \"objectURL\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.objectURL ?? null },\n { name: \"error\", event: \"wcs-recorder:error\" },\n // event-token: detail = the assembled clip { blob, objectURL, mimeType, duration }.\n { name: \"recorded\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token (timeslice mode): detail = the streamed Blob chunk.\n { name: \"dataavailable\", event: \"wcs-recorder:dataavailable\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"attachStream\" },\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _recording: boolean = false;\n private _paused: boolean = false;\n private _duration: number = 0;\n private _mimeType: string = \"\";\n private _blob: Blob | null = null;\n private _objectURL: string | null = null;\n private _error: WcsMediaErrorDetail | null = null;\n\n private _recorder: MediaRecorder | null = null;\n private _stream: MediaStream | null = null; // borrowed — never stopped here\n private _chunks: Blob[] = [];\n private _timeslice: boolean = false;\n private _startTime: number = 0;\n\n // Monotonic id of the current recording lifecycle. Bumped by every start() and\n // dispose(); each MediaRecorder callback bails if stale so a torn-down/restarted\n // recorder's late event cannot mutate state.\n private _gen: number = 0;\n\n // SSR: recording is command-driven (attachStream/start), so there is no\n // asynchronous probe to await — readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get recording(): boolean { return this._recording; }\n get paused(): boolean { return this._paused; }\n // Finalized at stop/pause only — there is no live ticking timer, so this stays 0\n // from start() until the first pause()/stop() (see _elapsed / onstop / onpause).\n get duration(): number { return this._duration; }\n get mimeType(): string { return this._mimeType; }\n get blob(): Blob | null { return this._blob; }\n get objectURL(): string | null { return this._objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n get ready(): Promise<void> { return this._ready; }\n\n // --- State setters ---\n\n private _setRecording(v: boolean): void {\n if (this._recording === v) return;\n this._recording = v;\n this._dispatch(\"wcs-recorder:recording-changed\", v);\n }\n\n private _setPaused(v: boolean): void {\n if (this._paused === v) return;\n this._paused = v;\n this._dispatch(\"wcs-recorder:paused-changed\", v);\n }\n\n private _setDuration(v: number): void {\n if (this._duration === v) return;\n this._duration = v;\n this._dispatch(\"wcs-recorder:duration-changed\", v);\n }\n\n private _setMimeType(v: string): void {\n if (this._mimeType === v) return;\n this._mimeType = v;\n this._dispatch(\"wcs-recorder:mimetype-changed\", v);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event, so unlike the value setters this is not content-deduped. Only the\n // null→null transition is collapsed (clearing an already-clear error stays silent).\n // The guard is written on null explicitly so it does NOT depend on callers passing a\n // fresh object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-recorder:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Borrow a stream for recording (the direct-channel sink). Synchronous, no\n * await: the live handle is captured by reference and never stored in state.\n * Does NOT stop any previously-borrowed stream — ownership stays with the camera.\n *\n * Re-attaching mid-recording takes effect on the NEXT start(), not the current\n * recording: the live MediaRecorder was already constructed around the previous\n * stream and keeps recording it until stop(). The borrowed reference is swapped so\n * the following start() uses the new stream.\n */\n attachStream(stream: MediaStream): void {\n this._stream = stream;\n }\n\n /** Start recording the borrowed stream. Never throws — surfaces `error`. */\n start(options: RecorderOptions = {}): void {\n if (!hasMediaRecorder()) {\n this._setError({ name: \"unsupported\", message: \"MediaRecorder is not available in this environment.\" });\n return;\n }\n if (!this._stream) {\n this._setError({ name: \"NoStreamError\", message: \"No stream attached. Wire a camera's stream-ready to attachStream first.\" });\n return;\n }\n if (this._recording) return;\n\n const recOptions: MediaRecorderOptions = {};\n if (options.mimeType && this._isTypeSupported(options.mimeType)) {\n recOptions.mimeType = options.mimeType;\n }\n if (typeof options.audioBitsPerSecond === \"number\") recOptions.audioBitsPerSecond = options.audioBitsPerSecond;\n if (typeof options.videoBitsPerSecond === \"number\") recOptions.videoBitsPerSecond = options.videoBitsPerSecond;\n\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(this._stream, recOptions);\n } catch (error) {\n this._setError(normalizeMediaError(error));\n return;\n }\n\n const gen = ++this._gen;\n this._chunks = [];\n this._timeslice = typeof options.timeslice === \"number\" && options.timeslice > 0;\n\n recorder.ondataavailable = (event: BlobEvent): void => {\n if (gen !== this._gen) return;\n if (event.data && event.data.size > 0) {\n this._chunks.push(event.data);\n if (this._timeslice) {\n this._dispatch(\"wcs-recorder:dataavailable\", event.data);\n }\n }\n };\n recorder.onstop = (): void => {\n if (gen !== this._gen) return;\n // When stopped while paused, `_duration` was already finalized in onpause —\n // recomputing _elapsed() here would wrongly include the paused gap (the\n // clock kept running but `_startTime` was not advanced). Keep the held value.\n if (!this._paused) this._setDuration(this._elapsed());\n this._assembleBlob();\n this._setPaused(false);\n this._setRecording(false);\n };\n recorder.onerror = (event: Event): void => {\n if (gen !== this._gen) return;\n const err = (event as unknown as { error?: unknown }).error;\n this._setError(normalizeMediaError(err ?? { name: \"RecorderError\" }));\n };\n recorder.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setDuration(this._elapsed());\n this._setPaused(true);\n };\n recorder.onresume = (): void => {\n if (gen !== this._gen) return;\n this._startTime = this._now() - this._duration;\n this._setPaused(false);\n };\n\n this._recorder = recorder;\n this._setError(null);\n this._setMimeType(recorder.mimeType || recOptions.mimeType || \"\");\n this._startTime = this._now();\n this._setDuration(0);\n recorder.start(this._timeslice ? options.timeslice : undefined);\n this._setRecording(true);\n }\n\n /** Stop recording; the assembled Blob is published from the recorder's onstop. */\n stop(): void {\n if (this._recorder && this._recorder.state !== \"inactive\") {\n this._recorder.stop();\n }\n }\n\n pause(): void {\n if (this._recorder && this._recorder.state === \"recording\") {\n this._recorder.pause();\n }\n }\n\n resume(): void {\n if (this._recorder && this._recorder.state === \"paused\") {\n this._recorder.resume();\n }\n }\n\n /**\n * Establish monitoring (§3.5). Recording is command-driven — there is no listener\n * or subscription to set up here (the borrowed stream arrives via attachStream and\n * recording is driven by start()/stop()), so observe() is an idempotent no-op that\n * resolves once ready. dispose() is its teardown counterpart.\n */\n observe(): Promise<void> {\n return this._ready;\n }\n\n /** Stop in-flight recording, revoke the last object URL, drop the borrowed stream. */\n dispose(): void {\n // Bump the generation first so the native stop()'s onstop (gen-guarded) does\n // not run on a disposed Core — then reset the recording flags directly here.\n this._gen++;\n if (this._recorder && this._recorder.state !== \"inactive\") {\n try {\n this._recorder.stop();\n } catch {\n // A recorder already torn down by the environment must not throw here.\n }\n }\n this._recorder = null;\n this._revokeUrl();\n // Drop the borrowed reference WITHOUT stopping its tracks — the camera owns it.\n this._stream = null;\n // Reset transient recording state silently (onstop was gen-guarded out above).\n this._recording = false;\n this._paused = false;\n }\n\n // --- Internal ---\n\n private _assembleBlob(): void {\n const blob = new Blob(this._chunks, this._mimeType ? { type: this._mimeType } : undefined);\n this._chunks = [];\n // The PREVIOUS clip's object URL is revoked here, before minting the new one — so\n // any consumer still pointing a `<video src>` at the old URL will break once a new\n // recording completes. Consumers must follow the latest `objectURL`/`recorded`\n // value and not pin a stale URL. (The `blob` is unaffected — prefer flowing it.)\n this._revokeUrl();\n const objectURL = this._createUrl(blob);\n this._blob = blob;\n this._objectURL = objectURL;\n const detail: WcsRecordedDetail = {\n blob,\n objectURL,\n mimeType: this._mimeType,\n duration: this._duration,\n };\n this._dispatch(\"wcs-recorder:recorded\", detail);\n }\n\n private _revokeUrl(): void {\n if (this._objectURL && typeof URL !== \"undefined\" && typeof URL.revokeObjectURL === \"function\") {\n URL.revokeObjectURL(this._objectURL);\n }\n this._objectURL = null;\n }\n\n private _createUrl(blob: Blob): string {\n if (typeof URL !== \"undefined\" && typeof URL.createObjectURL === \"function\") {\n return URL.createObjectURL(blob);\n }\n return \"\";\n }\n\n // Only called after hasMediaRecorder() has confirmed the API, so MediaRecorder\n // and its isTypeSupported are present.\n private _isTypeSupported(type: string): boolean {\n const MR = (globalThis as { MediaRecorder: { isTypeSupported(t: string): boolean } }).MediaRecorder;\n return MR.isTypeSupported(type);\n }\n\n // performance.now() is universally available wherever MediaRecorder runs\n // (browsers and the happy-dom test env), so no fallback guard is needed.\n private _now(): number {\n return performance.now();\n }\n\n private _elapsed(): number {\n return Math.max(0, Math.round(this._now() - this._startTime));\n }\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { RecorderCore } from \"../core/RecorderCore.js\";\n\n/**\n * `<wcs-recorder>` — declarative media recording. Wraps RecorderCore and records a\n * borrowed `MediaStream` received via the `attachStream` command (the direct\n * channel from `<wcs-camera>`'s `stream-ready`). It never owns or stops the stream.\n *\n * Recording parameters (`mime-type` / `timeslice` / bitrates) are mirrored\n * attributes. The assembled clip is published as `wcs-recorder:recorded`\n * (`{ blob, objectURL, mimeType, duration }`) and the `blob` / `objectURL` value\n * properties — a settled `Blob` is a value and may flow through state.\n */\nexport class WcsRecorder extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...RecorderCore.wcBindable,\n // `mimeType` deliberately appears on TWO surfaces: as an output `property`\n // (inherited from RecorderCore — the browser-resolved recording type, event\n // `mimetype-changed`) and as an `input` (the `mime-type` request attribute). They\n // share a base name but are distinct directions: the property is read-only output\n // (getter → Core), the input is the write-only request (setter → attribute, read\n // back in _options()). See README \"request vs. resolved\".\n inputs: [\n { name: \"mimeType\", attribute: \"mime-type\" },\n { name: \"timeslice\", attribute: \"timeslice\" },\n { name: \"audioBitsPerSecond\", attribute: \"audio-bits\" },\n { name: \"videoBitsPerSecond\", attribute: \"video-bits\" },\n ],\n commands: RecorderCore.wcBindable.commands,\n };\n\n private _core: RecorderCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new RecorderCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n // `mimeType` is an OUTPUT value property (the Core-resolved recording type,\n // published via `wcs-recorder:mimetype-changed`), so the getter delegates to the\n // Core — NOT the `mime-type` input attribute. The attribute is a *request*: the\n // browser may pick a different type, or fill one in when none was requested, and\n // bindings must read the actual value. The input side is read straight from the\n // attribute in `_options()`. The setter still writes the request attribute.\n get mimeType(): string { return this._core.mimeType; }\n set mimeType(value: string) { this.setAttribute(\"mime-type\", value); }\n\n get timeslice(): number { return this._numberAttr(\"timeslice\"); }\n set timeslice(value: number) { this.setAttribute(\"timeslice\", String(value)); }\n\n get audioBitsPerSecond(): number { return this._numberAttr(\"audio-bits\"); }\n set audioBitsPerSecond(value: number) { this.setAttribute(\"audio-bits\", String(value)); }\n\n get videoBitsPerSecond(): number { return this._numberAttr(\"video-bits\"); }\n set videoBitsPerSecond(value: number) { this.setAttribute(\"video-bits\", String(value)); }\n\n // --- Core delegated getters ---\n\n get recording(): boolean { return this._core.recording; }\n get paused(): boolean { return this._core.paused; }\n get duration(): number { return this._core.duration; }\n get blob(): Blob | null { return this._core.blob; }\n get objectURL(): string | null { return this._core.objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n // --- Commands ---\n\n /** Borrow a stream (the direct-channel sink). */\n attachStream(stream: MediaStream): void {\n this._core.attachStream(stream);\n }\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void { this._core.stop(); }\n pause(): void { this._core.pause(); }\n resume(): void { this._core.resume(); }\n\n // --- Internal ---\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _options(): RecorderOptions {\n const o: RecorderOptions = {};\n // Read the requested type from the input attribute directly — `get mimeType()` is\n // the resolved OUTPUT value, not the request.\n const requested = this.getAttribute(\"mime-type\") ?? \"\";\n if (requested) o.mimeType = requested;\n if (Number.isFinite(this.timeslice)) o.timeslice = this.timeslice;\n if (Number.isFinite(this.audioBitsPerSecond)) o.audioBitsPerSecond = this.audioBitsPerSecond;\n if (Number.isFinite(this.videoBitsPerSecond)) o.videoBitsPerSecond = this.videoBitsPerSecond;\n return o;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsCamera } from \"./components/Camera.js\";\nimport { WcsRecorder } from \"./components/Recorder.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.camera)) {\n customElements.define(config.tagNames.camera, WcsCamera);\n }\n if (!customElements.get(config.tagNames.recorder)) {\n customElements.define(config.tagNames.recorder, WcsRecorder);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapCamera(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e={tagNames:{camera:"wcs-camera",recorder:"wcs-recorder"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const i of Object.keys(e))t(e[i]);return e}function i(e){if(null===e||"object"!=typeof e)return e;const t={};for(const s of Object.keys(e))t[s]=i(e[s]);return t}let s=null;const r=e;function n(){return s||(s=t(i(e))),s}function a(){return"undefined"!=typeof navigator&&!!navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getUserMedia}function o(e){if(e&&"object"==typeof e&&"name"in e){const t=String(e.name)||"Error";return{name:t,message:"message"in e&&e.message?String(e.message):`Media request failed: ${t}.`}}return{name:"Error",message:"Media request failed."}}const c={name:"unsupported",message:"getUserMedia is not available (requires a secure context)."};function d(e){if(e)for(const t of e.getTracks())t.stop()}async function h(){if(!a()||"function"!=typeof navigator.mediaDevices.enumerateDevices)return[];try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>"videoinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label,groupId:e.groupId,kind:e.kind}))}catch{return[]}}class u{_name;_onChange;_status=null;_gen=0;_subscribed=!1;constructor(e,t){this._name=e,this._onChange=t}observe(){if(this._subscribed)return Promise.resolve();if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return this._onChange("unsupported"),Promise.resolve();this._subscribed=!0;const e=++this._gen;return navigator.permissions.query({name:this._name}).then(t=>{e===this._gen&&(this._status=t,this._onChange(t.state),t.addEventListener("change",this._onStatusChange))},()=>{e===this._gen&&this._onChange("unsupported")})}dispose(){this._subscribed=!1,this._gen++,this._status&&(this._status.removeEventListener("change",this._onStatusChange),this._status=null)}_onStatusChange=e=>{const t=e.target;this._onChange(t.state)}}class _ extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-camera:active-changed"},{name:"permission",event:"wcs-camera:permission-changed"},{name:"audioPermission",event:"wcs-camera:audio-permission-changed"},{name:"deviceId",event:"wcs-camera:device-changed"},{name:"devices",event:"wcs-camera:devices-changed"},{name:"error",event:"wcs-camera:error"},{name:"streamReady",event:"wcs-camera:stream-ready",getter:e=>e.detail},{name:"ended",event:"wcs-camera:ended",getter:e=>e.detail}],commands:[{name:"start"},{name:"stop"},{name:"switchCamera"}]};_target;_active=!1;_permission="prompt";_audioPermission=null;_deviceId=null;_devices=[];_error=null;_stream=null;_desired=!1;_constraints={};_gen=0;_subscribed=!1;_camWatcher=null;_micWatcher=null;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get active(){return this._active}get permission(){return this._permission}get audioPermission(){return this._audioPermission}get deviceId(){return this._deviceId}get devices(){return this._devices}get error(){return this._error}get ready(){return this._ready}_setActive(e){this._active!==e&&(this._active=e,this._dispatch("wcs-camera:active-changed",e))}_setPermission(e){this._permission!==e&&(this._permission=e,this._dispatch("wcs-camera:permission-changed",e))}_setAudioPermission(e){this._audioPermission!==e&&(this._audioPermission=e,this._dispatch("wcs-camera:audio-permission-changed",e))}_setDeviceId(e){this._deviceId!==e&&(this._deviceId=e,this._dispatch("wcs-camera:device-changed",e))}_setDevices(e){this._devicesEqual(this._devices,e)||(this._devices=e,this._dispatch("wcs-camera:devices-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._dispatch("wcs-camera:error",e))}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}_devicesEqual(e,t){if(e.length!==t.length)return!1;for(let i=0;i<e.length;i++)if(e[i].deviceId!==t[i].deviceId||e[i].label!==t[i].label)return!1;return!0}observe(e){return this._constraints={...e},this._subscribed?this._ready=this._ready.then(()=>this._reconcileAudioWatcher()):(this._subscribed=!0,this._ready=this._initPermissions()),this._ready}start(){this._desired=!0,this._restart()}stop(){this._desired=!1,this._release(!1)}switchCamera(){const e="environment"===this._constraints.facingMode?"user":"environment";this._constraints={...this._constraints,facingMode:e,deviceId:void 0},this._desired&&this._restart()}suspend(){this._gen++,this._stream&&this._release(!1)}resume(){this._desired&&!this._active&&this._restart()}dispose(){this._subscribed=!1,this._gen++,this._desired=!1,this._release(!0),a()&&"function"==typeof navigator.mediaDevices.removeEventListener&&navigator.mediaDevices.removeEventListener("devicechange",this._onDeviceChange),this._camWatcher?.dispose(),this._micWatcher?.dispose(),this._camWatcher=null,this._micWatcher=null}_initPermissions(){if(!a())return this._setPermission("unsupported"),Promise.resolve();this._camWatcher=new u("camera",e=>this._setPermission(e));const e=[this._camWatcher.observe()];return this._constraints.audio&&(this._micWatcher=new u("microphone",e=>this._setAudioPermission(e)),e.push(this._micWatcher.observe())),"function"==typeof navigator.mediaDevices.addEventListener&&navigator.mediaDevices.addEventListener("devicechange",this._onDeviceChange),Promise.all(e).then(()=>{})}_onDeviceChange=()=>{h().then(e=>{this._subscribed&&this._setDevices(e)})};_reconcileAudioWatcher(){return this._constraints.audio&&!this._micWatcher&&a()?(this._micWatcher=new u("microphone",e=>this._setAudioPermission(e)),this._micWatcher.observe()):(!this._constraints.audio&&this._micWatcher&&(this._micWatcher.dispose(),this._micWatcher=null,this._setAudioPermission(null)),Promise.resolve())}_restart(){this._release(!1),this._acquire()}async _acquire(){const e=++this._gen,t=function(e){const t={};return e.deviceId?t.deviceId={exact:e.deviceId}:e.facingMode&&(t.facingMode=e.facingMode),"number"==typeof e.width&&(t.width=e.width),"number"==typeof e.height&&(t.height=e.height),{video:!(Object.keys(t).length>0)||t,audio:!0===e.audio}}(this._constraints),{stream:i,error:s}=await async function(e){if(!a())return{error:c};try{return{stream:await navigator.mediaDevices.getUserMedia(e)}}catch(e){return{error:o(e)}}}(t);if(e!==this._gen)return void d(i??null);if(s)return this._setError(s),"NotAllowedError"===s.name?(this._setPermission("denied"),this._desired=!1):"unsupported"===s.name&&(this._setPermission("unsupported"),this._desired=!1),void this._setActive(!1);const r=i;this._stream=r;for(const e of r.getTracks())e.addEventListener("ended",this._onTrackEnded);this._setError(null),this._setPermission("granted"),this._constraints.audio&&r.getAudioTracks().length>0&&this._setAudioPermission("granted"),this._updateDeviceId(r),this._setActive(!0),this._dispatch("wcs-camera:stream-ready",r);const n=await h();e===this._gen&&this._setDevices(n)}_release(e){if(this._stream){for(const e of this._stream.getTracks())e.removeEventListener("ended",this._onTrackEnded);d(this._stream),this._stream=null,e?this._active=!1:this._setActive(!1)}}_onTrackEnded=()=>{this._release(!1),this._dispatch("wcs-camera:ended",null)};_updateDeviceId(e){const t=e.getVideoTracks()[0];if(t&&"function"==typeof t.getSettings){const e=t.getSettings();this._setDeviceId(e.deviceId??null)}}}class m extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={..._.wcBindable,inputs:[{name:"audio",attribute:"audio"},{name:"facingMode",attribute:"facing-mode"},{name:"deviceId",attribute:"device-id"},{name:"width",attribute:"width"},{name:"height",attribute:"height"},{name:"autostart",attribute:"autostart"},{name:"keepAlive",attribute:"keep-alive"}],commands:_.wcBindable.commands};static observedAttributes=["facing-mode","device-id","audio","width","height"];_core;_video;_connectedCallbackPromise=Promise.resolve();_connected=!1;_batchingAttrs=!1;constructor(){super(),this._core=new _(this);const e=this.attachShadow({mode:"open"}),t=document.createElement("style");t.textContent=":host{display:inline-block}video{display:block;width:100%;height:100%}",this._video=document.createElement("video"),this._video.autoplay=!0,this._video.muted=!0,this._video.setAttribute("playsinline",""),this._video.setAttribute("part","video"),e.append(t,this._video),this.addEventListener("wcs-camera:stream-ready",this._onStreamReady),this.addEventListener("wcs-camera:active-changed",this._onActiveChanged)}get audio(){return this.hasAttribute("audio")}set audio(e){this._toggleAttr("audio",e)}get facingMode(){return"environment"===this.getAttribute("facing-mode")?"environment":"user"}set facingMode(e){this.setAttribute("facing-mode",e)}get deviceId(){return this.getAttribute("device-id")??""}set deviceId(e){this.setAttribute("device-id",e)}get width(){return this._numberAttr("width")}set width(e){this.setAttribute("width",String(e))}get height(){return this._numberAttr("height")}set height(e){this.setAttribute("height",String(e))}get autostart(){return this.hasAttribute("autostart")}set autostart(e){this._toggleAttr("autostart",e)}get keepAlive(){return this.hasAttribute("keep-alive")}set keepAlive(e){this._toggleAttr("keep-alive",e)}get videoElement(){return this._video}get active(){return this._core.active}get permission(){return this._core.permission}get audioPermission(){return this._core.audioPermission}get devices(){return this._core.devices}get error(){return this._core.error}get connectedCallbackPromise(){return this._connectedCallbackPromise}start(){this._core.start()}stop(){this._core.stop()}switchCamera(){const e="environment"===this.facingMode?"user":"environment";this._batchingAttrs=!0;try{this.removeAttribute("device-id"),this.setAttribute("facing-mode",e)}finally{this._batchingAttrs=!1}this._core.observe(this._constraints()),this._core.active&&this._core.start()}_toggleAttr(e,t){t?this.setAttribute(e,""):this.removeAttribute(e)}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_constraints(){const e={audio:this.audio,facingMode:this.facingMode};return this.deviceId&&(e.deviceId=this.deviceId),Number.isFinite(this.width)&&(e.width=this.width),Number.isFinite(this.height)&&(e.height=this.height),e}_onStreamReady=e=>{this._video.srcObject=e.detail};_onActiveChanged=e=>{!1===e.detail&&(this._video.srcObject=null)};connectedCallback(){this._connected=!0,this._connectedCallbackPromise=this._core.observe(this._constraints()),this.autostart&&this._core.start(),document.addEventListener("visibilitychange",this._onVisibilityChange)}disconnectedCallback(){this._connected=!1,document.removeEventListener("visibilitychange",this._onVisibilityChange),this._core.dispose()}attributeChangedCallback(e,t,i){this._connected&&t!==i&&(this._batchingAttrs||(this._core.observe(this._constraints()),this._core.active&&this._core.start()))}_onVisibilityChange=()=>{this.keepAlive||("hidden"===document.visibilityState?this._core.suspend():this._core.resume())}}class l extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"recording",event:"wcs-recorder:recording-changed"},{name:"paused",event:"wcs-recorder:paused-changed"},{name:"duration",event:"wcs-recorder:duration-changed"},{name:"mimeType",event:"wcs-recorder:mimetype-changed"},{name:"blob",event:"wcs-recorder:recorded",getter:e=>e.detail?.blob??null},{name:"objectURL",event:"wcs-recorder:recorded",getter:e=>e.detail?.objectURL??null},{name:"error",event:"wcs-recorder:error"},{name:"recorded",event:"wcs-recorder:recorded",getter:e=>e.detail},{name:"dataavailable",event:"wcs-recorder:dataavailable",getter:e=>e.detail}],commands:[{name:"attachStream"},{name:"start"},{name:"stop"},{name:"pause"},{name:"resume"}]};_target;_recording=!1;_paused=!1;_duration=0;_mimeType="";_blob=null;_objectURL=null;_error=null;_recorder=null;_stream=null;_chunks=[];_timeslice=!1;_startTime=0;_gen=0;constructor(e){super(),this._target=e??this}get recording(){return this._recording}get paused(){return this._paused}get duration(){return this._duration}get mimeType(){return this._mimeType}get blob(){return this._blob}get objectURL(){return this._objectURL}get error(){return this._error}_setRecording(e){this._recording!==e&&(this._recording=e,this._dispatch("wcs-recorder:recording-changed",e))}_setPaused(e){this._paused!==e&&(this._paused=e,this._dispatch("wcs-recorder:paused-changed",e))}_setDuration(e){this._duration!==e&&(this._duration=e,this._dispatch("wcs-recorder:duration-changed",e))}_setMimeType(e){this._mimeType!==e&&(this._mimeType=e,this._dispatch("wcs-recorder:mimetype-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._dispatch("wcs-recorder:error",e))}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}attachStream(e){this._stream=e}start(e={}){if("undefined"==typeof globalThis||"function"!=typeof globalThis.MediaRecorder)return void this._setError({name:"unsupported",message:"MediaRecorder is not available in this environment."});if(!this._stream)return void this._setError({name:"NoStreamError",message:"No stream attached. Wire a camera's stream-ready to attachStream first."});if(this._recording)return;const t={};let i;e.mimeType&&this._isTypeSupported(e.mimeType)&&(t.mimeType=e.mimeType),"number"==typeof e.audioBitsPerSecond&&(t.audioBitsPerSecond=e.audioBitsPerSecond),"number"==typeof e.videoBitsPerSecond&&(t.videoBitsPerSecond=e.videoBitsPerSecond);try{i=new MediaRecorder(this._stream,t)}catch(e){return void this._setError(o(e))}const s=++this._gen;this._chunks=[],this._timeslice="number"==typeof e.timeslice&&e.timeslice>0,i.ondataavailable=e=>{s===this._gen&&e.data&&e.data.size>0&&(this._chunks.push(e.data),this._timeslice&&this._dispatch("wcs-recorder:dataavailable",e.data))},i.onstop=()=>{s===this._gen&&(this._paused||this._setDuration(this._elapsed()),this._assembleBlob(),this._setPaused(!1),this._setRecording(!1))},i.onerror=e=>{if(s!==this._gen)return;const t=e.error;this._setError(o(t??{name:"RecorderError"}))},i.onpause=()=>{s===this._gen&&(this._setDuration(this._elapsed()),this._setPaused(!0))},i.onresume=()=>{s===this._gen&&(this._startTime=this._now()-this._duration,this._setPaused(!1))},this._recorder=i,this._setError(null),this._setMimeType(i.mimeType||t.mimeType||""),this._startTime=this._now(),this._setDuration(0),i.start(this._timeslice?e.timeslice:void 0),this._setRecording(!0)}stop(){this._recorder&&"inactive"!==this._recorder.state&&this._recorder.stop()}pause(){this._recorder&&"recording"===this._recorder.state&&this._recorder.pause()}resume(){this._recorder&&"paused"===this._recorder.state&&this._recorder.resume()}dispose(){if(this._gen++,this._recorder&&"inactive"!==this._recorder.state)try{this._recorder.stop()}catch{}this._recorder=null,this._revokeUrl(),this._stream=null,this._recording=!1,this._paused=!1}_assembleBlob(){const e=new Blob(this._chunks,this._mimeType?{type:this._mimeType}:void 0);this._chunks=[],this._revokeUrl();const t=this._createUrl(e);this._blob=e,this._objectURL=t;const i={blob:e,objectURL:t,mimeType:this._mimeType,duration:this._duration};this._dispatch("wcs-recorder:recorded",i)}_revokeUrl(){this._objectURL&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(this._objectURL),this._objectURL=null}_createUrl(e){return"undefined"!=typeof URL&&"function"==typeof URL.createObjectURL?URL.createObjectURL(e):""}_isTypeSupported(e){return globalThis.MediaRecorder.isTypeSupported(e)}_now(){return performance.now()}_elapsed(){return Math.max(0,Math.round(this._now()-this._startTime))}}class g extends HTMLElement{static wcBindable={...l.wcBindable,inputs:[{name:"mimeType",attribute:"mime-type"},{name:"timeslice",attribute:"timeslice"},{name:"audioBitsPerSecond",attribute:"audio-bits"},{name:"videoBitsPerSecond",attribute:"video-bits"}],commands:l.wcBindable.commands};_core;constructor(){super(),this._core=new l(this)}get mimeType(){return this._core.mimeType}set mimeType(e){this.setAttribute("mime-type",e)}get timeslice(){return this._numberAttr("timeslice")}set timeslice(e){this.setAttribute("timeslice",String(e))}get audioBitsPerSecond(){return this._numberAttr("audio-bits")}set audioBitsPerSecond(e){this.setAttribute("audio-bits",String(e))}get videoBitsPerSecond(){return this._numberAttr("video-bits")}set videoBitsPerSecond(e){this.setAttribute("video-bits",String(e))}get recording(){return this._core.recording}get paused(){return this._core.paused}get duration(){return this._core.duration}get blob(){return this._core.blob}get objectURL(){return this._core.objectURL}get error(){return this._core.error}attachStream(e){this._core.attachStream(e)}start(){this._core.start(this._options())}stop(){this._core.stop()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_options(){const e={},t=this.getAttribute("mime-type")??"";return t&&(e.mimeType=t),Number.isFinite(this.timeslice)&&(e.timeslice=this.timeslice),Number.isFinite(this.audioBitsPerSecond)&&(e.audioBitsPerSecond=this.audioBitsPerSecond),Number.isFinite(this.videoBitsPerSecond)&&(e.videoBitsPerSecond=this.videoBitsPerSecond),e}connectedCallback(){this.style.display="none"}disconnectedCallback(){this._core.dispose()}}function v(t){var i;t&&((i=t).tagNames&&Object.assign(e.tagNames,i.tagNames),s=null),customElements.get(r.tagNames.camera)||customElements.define(r.tagNames.camera,m),customElements.get(r.tagNames.recorder)||customElements.define(r.tagNames.recorder,g)}export{_ as CameraCore,l as RecorderCore,m as WcsCamera,g as WcsRecorder,v as bootstrapCamera,n as getConfig};
1
+ const e={tagNames:{camera:"wcs-camera",recorder:"wcs-recorder"}};function t(e){if(null===e||"object"!=typeof e)return e;Object.freeze(e);for(const i of Object.keys(e))t(e[i]);return e}function i(e){if(null===e||"object"!=typeof e)return e;const t={};for(const s of Object.keys(e))t[s]=i(e[s]);return t}let s=null;const r=e;function n(){return s||(s=t(i(e))),s}function a(){return"undefined"!=typeof navigator&&!!navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.getUserMedia}function o(e){if(e&&"object"==typeof e&&"name"in e){const t=String(e.name)||"Error";return{name:t,message:"message"in e&&e.message?String(e.message):`Media request failed: ${t}.`}}return{name:"Error",message:"Media request failed."}}const c={name:"unsupported",message:"getUserMedia is not available (requires a secure context)."};function d(e){if(e)for(const t of e.getTracks())t.stop()}async function h(){if(!a()||"function"!=typeof navigator.mediaDevices.enumerateDevices)return[];try{return(await navigator.mediaDevices.enumerateDevices()).filter(e=>"videoinput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label,groupId:e.groupId,kind:e.kind}))}catch{return[]}}class u{_name;_onChange;_status=null;_gen=0;_subscribed=!1;constructor(e,t){this._name=e,this._onChange=t}observe(){if(this._subscribed)return Promise.resolve();if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return this._onChange("unsupported"),Promise.resolve();this._subscribed=!0;const e=++this._gen;return navigator.permissions.query({name:this._name}).then(t=>{e===this._gen&&(this._status=t,this._onChange(t.state),t.addEventListener("change",this._onStatusChange))},()=>{e===this._gen&&this._onChange("unsupported")})}dispose(){this._subscribed=!1,this._gen++,this._status&&(this._status.removeEventListener("change",this._onStatusChange),this._status=null)}_onStatusChange=e=>{const t=e.target;this._onChange(t.state)}}class _ extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"active",event:"wcs-camera:active-changed"},{name:"permission",event:"wcs-camera:permission-changed"},{name:"audioPermission",event:"wcs-camera:audio-permission-changed"},{name:"deviceId",event:"wcs-camera:device-changed"},{name:"devices",event:"wcs-camera:devices-changed"},{name:"error",event:"wcs-camera:error"},{name:"streamReady",event:"wcs-camera:stream-ready",getter:e=>e.detail},{name:"ended",event:"wcs-camera:ended",getter:e=>e.detail}],commands:[{name:"start"},{name:"stop"},{name:"switchCamera"}]};_target;_active=!1;_permission="prompt";_audioPermission=null;_deviceId=null;_devices=[];_error=null;_stream=null;_desired=!1;_constraints={};_gen=0;_subscribed=!1;_camWatcher=null;_micWatcher=null;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get active(){return this._active}get permission(){return this._permission}get audioPermission(){return this._audioPermission}get deviceId(){return this._deviceId}get devices(){return this._devices}get error(){return this._error}get ready(){return this._ready}_setActive(e){this._active!==e&&(this._active=e,this._dispatch("wcs-camera:active-changed",e))}_setPermission(e){this._permission!==e&&(this._permission=e,this._dispatch("wcs-camera:permission-changed",e))}_setAudioPermission(e){this._audioPermission!==e&&(this._audioPermission=e,this._dispatch("wcs-camera:audio-permission-changed",e))}_setDeviceId(e){this._deviceId!==e&&(this._deviceId=e,this._dispatch("wcs-camera:device-changed",e))}_setDevices(e){this._devicesEqual(this._devices,e)||(this._devices=e,this._dispatch("wcs-camera:devices-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._dispatch("wcs-camera:error",e))}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}_devicesEqual(e,t){if(e.length!==t.length)return!1;for(let i=0;i<e.length;i++)if(e[i].deviceId!==t[i].deviceId||e[i].label!==t[i].label)return!1;return!0}observe(e){return this._constraints={...e},this._subscribed?this._ready=this._ready.then(()=>this._reconcileAudioWatcher()):(this._subscribed=!0,this._ready=this._initPermissions()),this._ready}start(){this._desired=!0,this._restart()}stop(){this._desired=!1,this._release(!1)}switchCamera(){const e="environment"===this._constraints.facingMode?"user":"environment";this._constraints={...this._constraints,facingMode:e,deviceId:void 0},this._desired&&this._restart()}suspend(){this._gen++,this._stream&&this._release(!1)}resume(){this._desired&&!this._active&&this._restart()}dispose(){this._subscribed=!1,this._gen++,this._desired=!1,this._release(!0),a()&&"function"==typeof navigator.mediaDevices.removeEventListener&&navigator.mediaDevices.removeEventListener("devicechange",this._onDeviceChange),this._camWatcher?.dispose(),this._micWatcher?.dispose(),this._camWatcher=null,this._micWatcher=null}_initPermissions(){if(!a())return this._setPermission("unsupported"),Promise.resolve();this._camWatcher=new u("camera",e=>this._setPermission(e));const e=[this._camWatcher.observe()];return this._constraints.audio&&(this._micWatcher=new u("microphone",e=>this._setAudioPermission(e)),e.push(this._micWatcher.observe())),"function"==typeof navigator.mediaDevices.addEventListener&&navigator.mediaDevices.addEventListener("devicechange",this._onDeviceChange),Promise.all(e).then(()=>{})}_onDeviceChange=()=>{h().then(e=>{this._subscribed&&this._setDevices(e)})};_reconcileAudioWatcher(){return this._constraints.audio&&!this._micWatcher&&a()?(this._micWatcher=new u("microphone",e=>this._setAudioPermission(e)),this._micWatcher.observe()):(!this._constraints.audio&&this._micWatcher&&(this._micWatcher.dispose(),this._micWatcher=null,this._setAudioPermission(null)),Promise.resolve())}_restart(){this._release(!1),this._acquire()}async _acquire(){const e=++this._gen,t=function(e){const t={};return e.deviceId?t.deviceId={exact:e.deviceId}:e.facingMode&&(t.facingMode=e.facingMode),"number"==typeof e.width&&(t.width=e.width),"number"==typeof e.height&&(t.height=e.height),{video:!(Object.keys(t).length>0)||t,audio:!0===e.audio}}(this._constraints),{stream:i,error:s}=await async function(e){if(!a())return{error:c};try{return{stream:await navigator.mediaDevices.getUserMedia(e)}}catch(e){return{error:o(e)}}}(t);if(e!==this._gen)return void d(i??null);if(s)return this._setError(s),"NotAllowedError"===s.name?(this._setPermission("denied"),this._desired=!1):"unsupported"===s.name&&(this._setPermission("unsupported"),this._desired=!1),void this._setActive(!1);const r=i;this._stream=r;for(const e of r.getTracks())e.addEventListener("ended",this._onTrackEnded);this._setError(null),this._setPermission("granted"),this._constraints.audio&&r.getAudioTracks().length>0&&this._setAudioPermission("granted"),this._updateDeviceId(r),this._setActive(!0),this._dispatch("wcs-camera:stream-ready",r);const n=await h();e===this._gen&&this._setDevices(n)}_release(e){if(this._stream){for(const e of this._stream.getTracks())e.removeEventListener("ended",this._onTrackEnded);d(this._stream),this._stream=null,e?this._active=!1:this._setActive(!1)}}_onTrackEnded=()=>{this._release(!1),this._dispatch("wcs-camera:ended",null)};_updateDeviceId(e){const t=e.getVideoTracks()[0];if(t&&"function"==typeof t.getSettings){const e=t.getSettings();this._setDeviceId(e.deviceId??null)}}}class m extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={..._.wcBindable,inputs:[{name:"audio",attribute:"audio"},{name:"facingMode",attribute:"facing-mode"},{name:"deviceId",attribute:"device-id"},{name:"width",attribute:"width"},{name:"height",attribute:"height"},{name:"autostart",attribute:"autostart"},{name:"keepAlive",attribute:"keep-alive"}],commands:_.wcBindable.commands};static observedAttributes=["facing-mode","device-id","audio","width","height"];_core;_video;_connectedCallbackPromise=Promise.resolve();_connected=!1;_batchingAttrs=!1;constructor(){super(),this._core=new _(this);const e=this.attachShadow({mode:"open"}),t=document.createElement("style");t.textContent=":host{display:inline-block}video{display:block;width:100%;height:100%}",this._video=document.createElement("video"),this._video.autoplay=!0,this._video.muted=!0,this._video.setAttribute("playsinline",""),this._video.setAttribute("part","video"),e.append(t,this._video),this.addEventListener("wcs-camera:stream-ready",this._onStreamReady),this.addEventListener("wcs-camera:active-changed",this._onActiveChanged)}get audio(){return this.hasAttribute("audio")}set audio(e){this._toggleAttr("audio",e)}get facingMode(){return"environment"===this.getAttribute("facing-mode")?"environment":"user"}set facingMode(e){this.setAttribute("facing-mode",e)}get deviceId(){return this.getAttribute("device-id")??""}set deviceId(e){this.setAttribute("device-id",e)}get width(){return this._numberAttr("width")}set width(e){this.setAttribute("width",String(e))}get height(){return this._numberAttr("height")}set height(e){this.setAttribute("height",String(e))}get autostart(){return this.hasAttribute("autostart")}set autostart(e){this._toggleAttr("autostart",e)}get keepAlive(){return this.hasAttribute("keep-alive")}set keepAlive(e){this._toggleAttr("keep-alive",e)}get videoElement(){return this._video}get active(){return this._core.active}get permission(){return this._core.permission}get audioPermission(){return this._core.audioPermission}get devices(){return this._core.devices}get error(){return this._core.error}get connectedCallbackPromise(){return this._connectedCallbackPromise}start(){this._core.start()}stop(){this._core.stop()}switchCamera(){const e="environment"===this.facingMode?"user":"environment";this._batchingAttrs=!0;try{this.removeAttribute("device-id"),this.setAttribute("facing-mode",e)}finally{this._batchingAttrs=!1}this._core.observe(this._constraints()),this._core.active&&this._core.start()}_toggleAttr(e,t){t?this.setAttribute(e,""):this.removeAttribute(e)}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_constraints(){const e={audio:this.audio,facingMode:this.facingMode};return this.deviceId&&(e.deviceId=this.deviceId),Number.isFinite(this.width)&&(e.width=this.width),Number.isFinite(this.height)&&(e.height=this.height),e}_onStreamReady=e=>{this._video.srcObject=e.detail};_onActiveChanged=e=>{!1===e.detail&&(this._video.srcObject=null)};connectedCallback(){this._connected=!0,this._connectedCallbackPromise=this._core.observe(this._constraints()),this.autostart&&this._core.start(),document.addEventListener("visibilitychange",this._onVisibilityChange)}disconnectedCallback(){this._connected=!1,document.removeEventListener("visibilitychange",this._onVisibilityChange),this._core.dispose()}attributeChangedCallback(e,t,i){this._connected&&t!==i&&(this._batchingAttrs||(this._core.observe(this._constraints()),this._core.active&&this._core.start()))}_onVisibilityChange=()=>{this.keepAlive||("hidden"===document.visibilityState?this._core.suspend():this._core.resume())}}class l extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"recording",event:"wcs-recorder:recording-changed"},{name:"paused",event:"wcs-recorder:paused-changed"},{name:"duration",event:"wcs-recorder:duration-changed"},{name:"mimeType",event:"wcs-recorder:mimetype-changed"},{name:"blob",event:"wcs-recorder:recorded",getter:e=>e.detail?.blob??null},{name:"objectURL",event:"wcs-recorder:recorded",getter:e=>e.detail?.objectURL??null},{name:"error",event:"wcs-recorder:error"},{name:"recorded",event:"wcs-recorder:recorded",getter:e=>e.detail},{name:"dataavailable",event:"wcs-recorder:dataavailable",getter:e=>e.detail}],commands:[{name:"attachStream"},{name:"start"},{name:"stop"},{name:"pause"},{name:"resume"}]};_target;_recording=!1;_paused=!1;_duration=0;_mimeType="";_blob=null;_objectURL=null;_error=null;_recorder=null;_stream=null;_chunks=[];_timeslice=!1;_startTime=0;_gen=0;_ready=Promise.resolve();constructor(e){super(),this._target=e??this}get recording(){return this._recording}get paused(){return this._paused}get duration(){return this._duration}get mimeType(){return this._mimeType}get blob(){return this._blob}get objectURL(){return this._objectURL}get error(){return this._error}get ready(){return this._ready}_setRecording(e){this._recording!==e&&(this._recording=e,this._dispatch("wcs-recorder:recording-changed",e))}_setPaused(e){this._paused!==e&&(this._paused=e,this._dispatch("wcs-recorder:paused-changed",e))}_setDuration(e){this._duration!==e&&(this._duration=e,this._dispatch("wcs-recorder:duration-changed",e))}_setMimeType(e){this._mimeType!==e&&(this._mimeType=e,this._dispatch("wcs-recorder:mimetype-changed",e))}_setError(e){null===e&&null===this._error||(this._error=e,this._dispatch("wcs-recorder:error",e))}_dispatch(e,t){this._target.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0}))}attachStream(e){this._stream=e}start(e={}){if("undefined"==typeof globalThis||"function"!=typeof globalThis.MediaRecorder)return void this._setError({name:"unsupported",message:"MediaRecorder is not available in this environment."});if(!this._stream)return void this._setError({name:"NoStreamError",message:"No stream attached. Wire a camera's stream-ready to attachStream first."});if(this._recording)return;const t={};let i;e.mimeType&&this._isTypeSupported(e.mimeType)&&(t.mimeType=e.mimeType),"number"==typeof e.audioBitsPerSecond&&(t.audioBitsPerSecond=e.audioBitsPerSecond),"number"==typeof e.videoBitsPerSecond&&(t.videoBitsPerSecond=e.videoBitsPerSecond);try{i=new MediaRecorder(this._stream,t)}catch(e){return void this._setError(o(e))}const s=++this._gen;this._chunks=[],this._timeslice="number"==typeof e.timeslice&&e.timeslice>0,i.ondataavailable=e=>{s===this._gen&&e.data&&e.data.size>0&&(this._chunks.push(e.data),this._timeslice&&this._dispatch("wcs-recorder:dataavailable",e.data))},i.onstop=()=>{s===this._gen&&(this._paused||this._setDuration(this._elapsed()),this._assembleBlob(),this._setPaused(!1),this._setRecording(!1))},i.onerror=e=>{if(s!==this._gen)return;const t=e.error;this._setError(o(t??{name:"RecorderError"}))},i.onpause=()=>{s===this._gen&&(this._setDuration(this._elapsed()),this._setPaused(!0))},i.onresume=()=>{s===this._gen&&(this._startTime=this._now()-this._duration,this._setPaused(!1))},this._recorder=i,this._setError(null),this._setMimeType(i.mimeType||t.mimeType||""),this._startTime=this._now(),this._setDuration(0),i.start(this._timeslice?e.timeslice:void 0),this._setRecording(!0)}stop(){this._recorder&&"inactive"!==this._recorder.state&&this._recorder.stop()}pause(){this._recorder&&"recording"===this._recorder.state&&this._recorder.pause()}resume(){this._recorder&&"paused"===this._recorder.state&&this._recorder.resume()}observe(){return this._ready}dispose(){if(this._gen++,this._recorder&&"inactive"!==this._recorder.state)try{this._recorder.stop()}catch{}this._recorder=null,this._revokeUrl(),this._stream=null,this._recording=!1,this._paused=!1}_assembleBlob(){const e=new Blob(this._chunks,this._mimeType?{type:this._mimeType}:void 0);this._chunks=[],this._revokeUrl();const t=this._createUrl(e);this._blob=e,this._objectURL=t;const i={blob:e,objectURL:t,mimeType:this._mimeType,duration:this._duration};this._dispatch("wcs-recorder:recorded",i)}_revokeUrl(){this._objectURL&&"undefined"!=typeof URL&&"function"==typeof URL.revokeObjectURL&&URL.revokeObjectURL(this._objectURL),this._objectURL=null}_createUrl(e){return"undefined"!=typeof URL&&"function"==typeof URL.createObjectURL?URL.createObjectURL(e):""}_isTypeSupported(e){return globalThis.MediaRecorder.isTypeSupported(e)}_now(){return performance.now()}_elapsed(){return Math.max(0,Math.round(this._now()-this._startTime))}}class g extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...l.wcBindable,inputs:[{name:"mimeType",attribute:"mime-type"},{name:"timeslice",attribute:"timeslice"},{name:"audioBitsPerSecond",attribute:"audio-bits"},{name:"videoBitsPerSecond",attribute:"video-bits"}],commands:l.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new l(this)}get connectedCallbackPromise(){return this._connectedCallbackPromise}get mimeType(){return this._core.mimeType}set mimeType(e){this.setAttribute("mime-type",e)}get timeslice(){return this._numberAttr("timeslice")}set timeslice(e){this.setAttribute("timeslice",String(e))}get audioBitsPerSecond(){return this._numberAttr("audio-bits")}set audioBitsPerSecond(e){this.setAttribute("audio-bits",String(e))}get videoBitsPerSecond(){return this._numberAttr("video-bits")}set videoBitsPerSecond(e){this.setAttribute("video-bits",String(e))}get recording(){return this._core.recording}get paused(){return this._core.paused}get duration(){return this._core.duration}get blob(){return this._core.blob}get objectURL(){return this._core.objectURL}get error(){return this._core.error}attachStream(e){this._core.attachStream(e)}start(){this._core.start(this._options())}stop(){this._core.stop()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(e){const t=this.getAttribute(e);if(null===t||""===t.trim())return NaN;const i=Number(t);return Number.isFinite(i)?i:NaN}_options(){const e={},t=this.getAttribute("mime-type")??"";return t&&(e.mimeType=t),Number.isFinite(this.timeslice)&&(e.timeslice=this.timeslice),Number.isFinite(this.audioBitsPerSecond)&&(e.audioBitsPerSecond=this.audioBitsPerSecond),Number.isFinite(this.videoBitsPerSecond)&&(e.videoBitsPerSecond=this.videoBitsPerSecond),e}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}function v(t){var i;t&&((i=t).tagNames&&Object.assign(e.tagNames,i.tagNames),s=null),customElements.get(r.tagNames.camera)||customElements.define(r.tagNames.camera,m),customElements.get(r.tagNames.recorder)||customElements.define(r.tagNames.recorder,g)}export{_ as CameraCore,l as RecorderCore,m as WcsCamera,g as WcsRecorder,v as bootstrapCamera,n as getConfig};
2
2
  //# sourceMappingURL=index.esm.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/media/getUserMedia.ts","../src/media/permission.ts","../src/core/CameraCore.ts","../src/components/Camera.ts","../src/core/RecorderCore.ts","../src/components/Recorder.ts","../src/bootstrapCamera.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n camera: string;\n recorder: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n camera: \"wcs-camera\",\n recorder: \"wcs-recorder\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by registerComponents (read at call time so\n// setConfig() takes effect without re-import). Public consumers get the deep-frozen\n// clone from getConfig().\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { CameraConstraints, MediaDeviceSnapshot, WcsMediaErrorDetail } from \"../types.js\";\n\n/** True when getUserMedia is reachable (secure context with a media-devices impl). */\nexport function hasMediaDevices(): boolean {\n return typeof navigator !== \"undefined\"\n && !!navigator.mediaDevices\n && typeof navigator.mediaDevices.getUserMedia === \"function\";\n}\n\n/** True when MediaRecorder is available in this environment. */\nexport function hasMediaRecorder(): boolean {\n return typeof globalThis !== \"undefined\"\n && typeof (globalThis as { MediaRecorder?: unknown }).MediaRecorder === \"function\";\n}\n\n/**\n * Translate a getUserMedia constraints object from the declarative\n * CameraConstraints surface. Always requests a video track; `audio` opts the\n * microphone in. `deviceId` (exact) takes precedence over `facingMode`.\n */\nexport function buildConstraints(c: CameraConstraints): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n if (c.deviceId) {\n video.deviceId = { exact: c.deviceId };\n } else if (c.facingMode) {\n video.facingMode = c.facingMode;\n }\n if (typeof c.width === \"number\") video.width = c.width;\n if (typeof c.height === \"number\") video.height = c.height;\n const hasVideoConstraint = Object.keys(video).length > 0;\n return {\n video: hasVideoConstraint ? video : true,\n audio: c.audio === true,\n };\n}\n\n/** Normalize any thrown getUserMedia / MediaRecorder failure into a flat detail. */\nexport function normalizeMediaError(error: unknown): WcsMediaErrorDetail {\n if (error && typeof error === \"object\" && \"name\" in error) {\n const name = String((error as { name: unknown }).name) || \"Error\";\n const message = \"message\" in error && (error as { message?: unknown }).message\n ? String((error as { message: unknown }).message)\n : `Media request failed: ${name}.`;\n return { name, message };\n }\n return { name: \"Error\", message: \"Media request failed.\" };\n}\n\nconst UNSUPPORTED_ERROR: WcsMediaErrorDetail = {\n name: \"unsupported\",\n message: \"getUserMedia is not available (requires a secure context).\",\n};\n\nexport interface UserMediaResult {\n stream?: MediaStream;\n error?: WcsMediaErrorDetail;\n}\n\n/**\n * Request a media stream. Never throws — resolves with `{ stream }` on success or\n * `{ error }` (normalized) on failure / when the API is unavailable.\n */\nexport async function requestUserMedia(constraints: MediaStreamConstraints): Promise<UserMediaResult> {\n if (!hasMediaDevices()) {\n return { error: UNSUPPORTED_ERROR };\n }\n try {\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\n return { stream };\n } catch (error) {\n return { error: normalizeMediaError(error) };\n }\n}\n\n/** Stop every track of a stream, releasing the camera/microphone hardware. */\nexport function stopAllTracks(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) {\n track.stop();\n }\n}\n\n/**\n * Enumerate video input devices as plain snapshots. Labels are only populated\n * after a grant, so this is refreshed post-acquisition. Never throws.\n */\nexport async function enumerateVideoDevices(): Promise<MediaDeviceSnapshot[]> {\n if (!hasMediaDevices() || typeof navigator.mediaDevices.enumerateDevices !== \"function\") {\n return [];\n }\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === \"videoinput\")\n .map((d) => ({\n deviceId: d.deviceId,\n label: d.label,\n groupId: d.groupId,\n kind: d.kind,\n }));\n } catch {\n return [];\n }\n}\n","import { MediaPermissionState } from \"../types.js\";\n\n/**\n * Live monitor for a single media permission (`camera` or `microphone`) via the\n * Permissions API. Mirrors PermissionCore's two-phase pattern: an initial query\n * plus a live `change` subscription, guarded by a monotonic generation so a query\n * superseded by a rapid dispose/observe never attaches a stale listener.\n *\n * When the Permissions API is absent or rejects the descriptor (e.g. Firefox does\n * not accept the `camera` / `microphone` descriptor), the watcher reports\n * `\"unsupported\"`. The CameraCore then refines the state from the getUserMedia\n * outcome (granted on success, denied on NotAllowedError).\n */\nexport class MediaPermissionWatcher {\n private _name: \"camera\" | \"microphone\";\n private _onChange: (state: MediaPermissionState) => void;\n private _status: PermissionStatus | null = null;\n private _gen: number = 0;\n private _subscribed: boolean = false;\n\n constructor(name: \"camera\" | \"microphone\", onChange: (state: MediaPermissionState) => void) {\n this._name = name;\n this._onChange = onChange;\n }\n\n /** Issue the initial query and subscribe to live changes. Resolves when settled. */\n observe(): Promise<void> {\n if (this._subscribed) return Promise.resolve();\n if (typeof navigator === \"undefined\" || !navigator.permissions\n || typeof navigator.permissions.query !== \"function\") {\n this._onChange(\"unsupported\");\n return Promise.resolve();\n }\n this._subscribed = true;\n const gen = ++this._gen;\n return navigator.permissions\n .query({ name: this._name as PermissionName })\n .then(\n (status) => {\n if (gen !== this._gen) return;\n this._status = status;\n this._onChange(status.state as MediaPermissionState);\n status.addEventListener(\"change\", this._onStatusChange);\n },\n () => {\n if (gen !== this._gen) return;\n this._onChange(\"unsupported\");\n },\n );\n }\n\n /** Detach the live listener and invalidate any in-flight query. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n if (this._status) {\n this._status.removeEventListener(\"change\", this._onStatusChange);\n this._status = null;\n }\n }\n\n private _onStatusChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._onChange(status.state as MediaPermissionState);\n };\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot,\n MediaPermissionState, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport {\n buildConstraints, enumerateVideoDevices, hasMediaDevices, requestUserMedia, stopAllTracks,\n} from \"../media/getUserMedia.js\";\nimport { MediaPermissionWatcher } from \"../media/permission.js\";\n\n/**\n * Headless camera-capture primitive. Wraps getUserMedia + the Permissions API and\n * exposes a `MediaStream` through the wc-bindable protocol — but the live stream is\n * NEVER published as a reactive value. It is a non-serializable live handle: it\n * flows out only via the `wcs-camera:stream-ready` event so a consumer (the preview\n * `<video>`, a `<wcs-recorder>`) can bind it directly to an element property,\n * bypassing serializable state. See docs/camera-recorder-tag-design.md §1/§2.\n *\n * The observable value surface is strictly derived data: `active` (is a stream\n * live — the \"actual\" half of the desired/actual pair), `permission` /\n * `audioPermission` (two-phase: Permissions API monitor + getUserMedia outcome),\n * `deviceId` / `devices`, and `error`. Failures never throw — they surface through\n * `error`.\n */\nexport class CameraCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"active\", event: \"wcs-camera:active-changed\" },\n { name: \"permission\", event: \"wcs-camera:permission-changed\" },\n { name: \"audioPermission\", event: \"wcs-camera:audio-permission-changed\" },\n { name: \"deviceId\", event: \"wcs-camera:device-changed\" },\n { name: \"devices\", event: \"wcs-camera:devices-changed\" },\n { name: \"error\", event: \"wcs-camera:error\" },\n // Direct-channel handle: event-token only — never bound as a reactive value.\n { name: \"streamReady\", event: \"wcs-camera:stream-ready\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token: a bare signal (detail is always null) — surface detail, not the raw Event.\n { name: \"ended\", event: \"wcs-camera:ended\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"switchCamera\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _active: boolean = false;\n private _permission: MediaPermissionState = \"prompt\";\n private _audioPermission: MediaPermissionState | null = null;\n private _deviceId: string | null = null;\n private _devices: MediaDeviceSnapshot[] = [];\n private _error: WcsMediaErrorDetail | null = null;\n\n // The live stream — internal only, never a reactive value (see class docs).\n private _stream: MediaStream | null = null;\n\n // desired/actual split (wakelock-style): `_desired` is whether the user wants the\n // camera on; `_active` is whether a stream is actually live. The OS can revoke a\n // track (device unplugged / taken by another app) — actual drops while desired\n // stays true, so a later resume()/visibility-restore can re-acquire.\n private _desired: boolean = false;\n\n private _constraints: CameraConstraints = {};\n\n // Monotonic id of the current acquisition lifecycle. Bumped by every acquire and\n // by dispose(). Each in-flight getUserMedia captures it and, on resolve, bails\n // (stopping the just-acquired stream) when superseded — so a constraints change\n // mid-acquire (switchMap-style restart) cannot leave an orphaned stream live.\n private _gen: number = 0;\n\n private _subscribed: boolean = false;\n private _camWatcher: MediaPermissionWatcher | null = null;\n private _micWatcher: MediaPermissionWatcher | null = null;\n\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get active(): boolean { return this._active; }\n get permission(): MediaPermissionState { return this._permission; }\n get audioPermission(): MediaPermissionState | null { return this._audioPermission; }\n get deviceId(): string | null { return this._deviceId; }\n get devices(): MediaDeviceSnapshot[] { return this._devices; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n get ready(): Promise<void> { return this._ready; }\n\n // --- State setters with event dispatch (same-value guarded) ---\n\n private _setActive(active: boolean): void {\n if (this._active === active) return;\n this._active = active;\n this._dispatch(\"wcs-camera:active-changed\", active);\n }\n\n private _setPermission(state: MediaPermissionState): void {\n if (this._permission === state) return;\n this._permission = state;\n this._dispatch(\"wcs-camera:permission-changed\", state);\n }\n\n private _setAudioPermission(state: MediaPermissionState | null): void {\n if (this._audioPermission === state) return;\n this._audioPermission = state;\n this._dispatch(\"wcs-camera:audio-permission-changed\", state);\n }\n\n private _setDeviceId(id: string | null): void {\n if (this._deviceId === id) return;\n this._deviceId = id;\n this._dispatch(\"wcs-camera:device-changed\", id);\n }\n\n private _setDevices(devices: MediaDeviceSnapshot[]): void {\n if (this._devicesEqual(this._devices, devices)) return;\n this._devices = devices;\n this._dispatch(\"wcs-camera:devices-changed\", devices);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event (e.g. retrying getUserMedia and failing again must re-notify), so\n // unlike the value setters this is not content-deduped. Only the null→null\n // transition is collapsed (clearing an already-clear error stays silent). The guard\n // is written on null explicitly so it does NOT depend on callers passing a fresh\n // object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-camera:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n private _devicesEqual(a: MediaDeviceSnapshot[], b: MediaDeviceSnapshot[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].deviceId !== b[i].deviceId || a[i].label !== b[i].label) return false;\n }\n return true;\n }\n\n // --- Public API ---\n\n /**\n * Begin observing permissions for the given constraints. Idempotent while\n * already subscribed. The first call (or one after dispose()) starts the\n * camera/microphone permission monitors. Acquisition itself is driven separately\n * by start() / autostart — observing does not prompt.\n */\n observe(constraints: CameraConstraints): Promise<void> {\n this._constraints = { ...constraints };\n if (!this._subscribed) {\n this._subscribed = true;\n this._ready = this._initPermissions();\n } else {\n // Already live: track the latest constraints and fold any newly-started\n // microphone query into `ready` so awaiting observe() guarantees its initial\n // permission state — symmetric with _initPermissions() on the first observe.\n this._ready = this._ready.then(() => this._reconcileAudioWatcher());\n }\n return this._ready;\n }\n\n /** Acquire the camera (sets desired=true). Prompts on first use. */\n start(): void {\n this._desired = true;\n this._restart();\n }\n\n /** Release the camera (sets desired=false), stopping all tracks. */\n stop(): void {\n this._desired = false;\n this._release(false);\n }\n\n /**\n * Toggle facingMode (user ↔ environment) and re-acquire if active. This is the\n * headless, DOM-free path: it flips the Core's internal `_constraints` (the single\n * source of truth for a standalone Core) and re-acquires while desired.\n *\n * Note: the `<wcs-camera>` Shell does NOT delegate to this — it keeps the DOM\n * attributes authoritative and drives its own single re-acquire (see Camera.ts\n * switchCamera). Both reach the same end state; the split exists because the Shell\n * must keep its declared attributes in sync, which a Core has no notion of.\n */\n switchCamera(): void {\n const next: FacingMode = this._constraints.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._constraints = { ...this._constraints, facingMode: next, deviceId: undefined };\n if (this._desired) {\n this._restart();\n }\n }\n\n /**\n * Suspend the live stream while keeping `desired` — for page-hidden. Stops tracks\n * (clearing the hardware indicator) but remembers that the camera should resume.\n *\n * Bumps `_gen` to supersede any in-flight acquire: without this, an acquire that\n * resolves *after* the page went hidden would assign `_stream` and set active —\n * re-lighting the camera behind a no-op suspend (the stream had not been assigned\n * yet, so the `if (_stream)` release below could not reach it). The superseded\n * acquire stops its just-acquired orphan stream on resolve (see `_acquire`).\n */\n suspend(): void {\n this._gen++;\n if (this._stream) {\n this._release(false);\n }\n }\n\n /** Re-acquire if the camera is desired but not currently active — for page-visible. */\n resume(): void {\n if (this._desired && !this._active) {\n this._restart();\n }\n }\n\n /** Tear down: stop the stream and detach permission listeners. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n this._desired = false;\n this._release(true);\n if (hasMediaDevices() && typeof navigator.mediaDevices.removeEventListener === \"function\") {\n navigator.mediaDevices.removeEventListener(\"devicechange\", this._onDeviceChange);\n }\n this._camWatcher?.dispose();\n this._micWatcher?.dispose();\n this._camWatcher = null;\n this._micWatcher = null;\n }\n\n // --- Internal ---\n\n private _initPermissions(): Promise<void> {\n if (!hasMediaDevices()) {\n this._setPermission(\"unsupported\");\n return Promise.resolve();\n }\n this._camWatcher = new MediaPermissionWatcher(\"camera\", (s) => this._setPermission(s));\n const tasks: Promise<void>[] = [this._camWatcher.observe()];\n if (this._constraints.audio) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n tasks.push(this._micWatcher.observe());\n }\n // Track hot-plug: refresh the device list when a camera is added/removed.\n if (typeof navigator.mediaDevices.addEventListener === \"function\") {\n navigator.mediaDevices.addEventListener(\"devicechange\", this._onDeviceChange);\n }\n return Promise.all(tasks).then(() => undefined);\n }\n\n private _onDeviceChange = (): void => {\n void enumerateVideoDevices().then((devices) => {\n // Guard against a late resolution after dispose.\n if (this._subscribed) this._setDevices(devices);\n });\n };\n\n // Bring the microphone watcher in line with the latest `audio` constraint when\n // observe() is called again on an already-live Core. Returns the new watcher's\n // initial-query promise (so observe() can fold it into `ready`); resolved when\n // nothing started.\n private _reconcileAudioWatcher(): Promise<void> {\n if (this._constraints.audio && !this._micWatcher && hasMediaDevices()) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n return this._micWatcher.observe();\n } else if (!this._constraints.audio && this._micWatcher) {\n this._micWatcher.dispose();\n this._micWatcher = null;\n this._setAudioPermission(null);\n }\n return Promise.resolve();\n }\n\n private _restart(): void {\n this._release(false);\n void this._acquire();\n }\n\n private async _acquire(): Promise<void> {\n const gen = ++this._gen;\n const constraints = buildConstraints(this._constraints);\n const { stream, error } = await requestUserMedia(constraints);\n\n // Superseded by a newer acquire (rapid restart) or disposed while in flight:\n // stop the orphan stream and bail without mutating state. The `ended` listeners\n // are attached only AFTER this gen check (below), so stopping the orphan here\n // cannot fire _onTrackEnded — no spurious `ended` event / state mutation. Keep\n // this stop strictly before listener attachment if the order is ever refactored.\n if (gen !== this._gen) {\n stopAllTracks(stream ?? null);\n return;\n }\n\n if (error) {\n this._setError(error);\n if (error.name === \"NotAllowedError\") {\n this._setPermission(\"denied\");\n // Hard denial: drop `desired` so a later visibility-restore (resume()) does\n // not silently re-attempt getUserMedia on every page-visible. A transient\n // failure (NotReadableError = device busy) keeps `desired` so it can recover.\n this._desired = false;\n } else if (error.name === \"unsupported\") {\n this._setPermission(\"unsupported\");\n this._desired = false;\n }\n this._setActive(false);\n return;\n }\n\n const live = stream as MediaStream;\n this._stream = live;\n for (const track of live.getTracks()) {\n track.addEventListener(\"ended\", this._onTrackEnded);\n }\n this._setError(null);\n // getUserMedia success is authoritative for permission.\n this._setPermission(\"granted\");\n // Only assert mic-granted when the grant actually produced an audio track. With\n // today's boolean `audio` this is equivalent to `_constraints.audio`, but it stays\n // correct under a future non-mandatory `{ audio: {...} }` constraint where the\n // browser may grant video while omitting audio.\n if (this._constraints.audio && live.getAudioTracks().length > 0) {\n this._setAudioPermission(\"granted\");\n }\n this._updateDeviceId(live);\n this._setActive(true);\n // Publish the live handle for the direct element→element channel.\n this._dispatch(\"wcs-camera:stream-ready\", live);\n // Labels become available after a grant; refresh the device list.\n const devices = await enumerateVideoDevices();\n if (gen === this._gen) {\n this._setDevices(devices);\n }\n }\n\n private _release(silent: boolean): void {\n if (!this._stream) return;\n for (const track of this._stream.getTracks()) {\n track.removeEventListener(\"ended\", this._onTrackEnded);\n }\n stopAllTracks(this._stream);\n this._stream = null;\n if (silent) {\n this._active = false;\n } else {\n this._setActive(false);\n }\n }\n\n private _onTrackEnded = (): void => {\n // OS revoked a track (unplug / taken by another app). actual drops; desired\n // stays true so resume()/visibility-restore can re-acquire.\n this._release(false);\n this._dispatch(\"wcs-camera:ended\", null);\n };\n\n private _updateDeviceId(stream: MediaStream): void {\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack && typeof videoTrack.getSettings === \"function\") {\n const settings = videoTrack.getSettings();\n this._setDeviceId(settings.deviceId ?? null);\n }\n }\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot, MediaPermissionState,\n WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { CameraCore } from \"../core/CameraCore.js\";\n\n/**\n * `<wcs-camera>` — declarative camera capture with a built-in preview.\n *\n * The element owns a `<video>` in its shadow root and assigns the live\n * `MediaStream` to `video.srcObject` internally, so the non-serializable handle\n * never crosses the state boundary (design §1, case B). For consumers (a\n * `<wcs-recorder>`, an external `<video>`), the stream is also published via the\n * `wcs-camera:stream-ready` event-token for the direct element→element channel\n * (design §2).\n *\n * Acquisition is explicit: `start()` / the `autostart` attribute prompt and\n * acquire; merely connecting does not. While the page is hidden the stream is\n * suspended (clearing the camera indicator) and re-acquired on return, unless\n * `keep-alive` is set (e.g. during recording).\n */\nexport class WcsCamera extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...CameraCore.wcBindable,\n inputs: [\n { name: \"audio\", attribute: \"audio\" },\n { name: \"facingMode\", attribute: \"facing-mode\" },\n { name: \"deviceId\", attribute: \"device-id\" },\n { name: \"width\", attribute: \"width\" },\n { name: \"height\", attribute: \"height\" },\n { name: \"autostart\", attribute: \"autostart\" },\n { name: \"keepAlive\", attribute: \"keep-alive\" },\n ],\n commands: CameraCore.wcBindable.commands,\n };\n\n // `autostart` and `keep-alive` are intentionally NOT observed: `autostart` is a\n // connect-time-only acquire trigger (read once in connectedCallback; flipping it\n // later is meaningless), and `keep-alive` is read fresh on every visibilitychange\n // (_onVisibilityChange), so it never needs to drive a re-acquire. The observed set\n // is exactly the constraints that reshape the requested track.\n static observedAttributes = [\"facing-mode\", \"device-id\", \"audio\", \"width\", \"height\"];\n\n private _core: CameraCore;\n private _video: HTMLVideoElement;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _connected: boolean = false;\n // True while switchCamera() rewrites several attributes at once. Each setAttribute /\n // removeAttribute fires its own attributeChangedCallback synchronously; without this\n // guard the FIRST change would re-acquire with the not-yet-updated constraints (and\n // tear active down so the later change's re-acquire is skipped). We suppress the\n // per-attribute re-acquire and drive a single one with the final constraints.\n private _batchingAttrs: boolean = false;\n\n constructor() {\n super();\n this._core = new CameraCore(this);\n const root = this.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = \":host{display:inline-block}video{display:block;width:100%;height:100%}\";\n this._video = document.createElement(\"video\");\n this._video.autoplay = true;\n this._video.muted = true;\n this._video.setAttribute(\"playsinline\", \"\");\n this._video.setAttribute(\"part\", \"video\");\n root.append(style, this._video);\n // Bind the live handle to the preview internally — never through state. These\n // self-listeners are intentionally not removed on disconnect (asymmetric with the\n // document-level `visibilitychange` in connect/disconnectedCallback): the target\n // is `this`, so the listeners are collected together with the element — there is\n // no external reference to leak. The visibility listener, by contrast, lives on\n // `document` (outlives the element) and MUST be detached.\n this.addEventListener(\"wcs-camera:stream-ready\", this._onStreamReady as EventListener);\n this.addEventListener(\"wcs-camera:active-changed\", this._onActiveChanged as EventListener);\n }\n\n // --- Attribute accessors ---\n\n get audio(): boolean { return this.hasAttribute(\"audio\"); }\n set audio(value: boolean) { this._toggleAttr(\"audio\", value); }\n\n get facingMode(): FacingMode {\n return this.getAttribute(\"facing-mode\") === \"environment\" ? \"environment\" : \"user\";\n }\n set facingMode(value: FacingMode) { this.setAttribute(\"facing-mode\", value); }\n\n get deviceId(): string { return this.getAttribute(\"device-id\") ?? \"\"; }\n set deviceId(value: string) { this.setAttribute(\"device-id\", value); }\n\n get width(): number { return this._numberAttr(\"width\"); }\n set width(value: number) { this.setAttribute(\"width\", String(value)); }\n\n get height(): number { return this._numberAttr(\"height\"); }\n set height(value: number) { this.setAttribute(\"height\", String(value)); }\n\n get autostart(): boolean { return this.hasAttribute(\"autostart\"); }\n set autostart(value: boolean) { this._toggleAttr(\"autostart\", value); }\n\n get keepAlive(): boolean { return this.hasAttribute(\"keep-alive\"); }\n set keepAlive(value: boolean) { this._toggleAttr(\"keep-alive\", value); }\n\n /** The internal preview `<video>` (for advanced styling/measurement). */\n get videoElement(): HTMLVideoElement { return this._video; }\n\n // --- Core delegated getters ---\n\n get active(): boolean { return this._core.active; }\n get permission(): MediaPermissionState { return this._core.permission; }\n get audioPermission(): MediaPermissionState | null { return this._core.audioPermission; }\n get devices(): MediaDeviceSnapshot[] { return this._core.devices; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n get connectedCallbackPromise(): Promise<void> { return this._connectedCallbackPromise; }\n\n // --- Commands ---\n\n start(): void { this._core.start(); }\n stop(): void { this._core.stop(); }\n\n /**\n * Toggle the front/back camera by updating the DOM attributes (the single source\n * of truth), not just the Core's internal constraints. Deliberately does NOT call\n * `CameraCore.switchCamera()` (which would mutate the Core's constraints behind the\n * DOM's back, leaving the declared attributes stale). `device-id` is removed because\n * it would otherwise take precedence over `facing-mode` (see buildConstraints) —\n * leaving it pinned would silently undo the switch on the next re-acquire. Both\n * attribute writes are batched (see `_batchingAttrs`) so they drive exactly ONE\n * re-acquire here, with the final constraints — never an early acquire on a\n * half-updated state. The DOM and the live camera stay in agreement.\n */\n switchCamera(): void {\n const next: FacingMode = this.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._batchingAttrs = true;\n try {\n this.removeAttribute(\"device-id\");\n this.setAttribute(\"facing-mode\", next);\n } finally {\n this._batchingAttrs = false;\n }\n // Sync the (now-final) constraints and re-acquire once when a stream is live —\n // the same `active`-guarded restart attributeChangedCallback performs.\n this._core.observe(this._constraints());\n if (this._core.active) {\n this._core.start();\n }\n }\n\n // --- Internal ---\n\n private _toggleAttr(name: string, value: boolean): void {\n if (value) {\n this.setAttribute(name, \"\");\n } else {\n this.removeAttribute(name);\n }\n }\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _constraints(): CameraConstraints {\n const c: CameraConstraints = { audio: this.audio, facingMode: this.facingMode };\n if (this.deviceId) c.deviceId = this.deviceId;\n if (Number.isFinite(this.width)) c.width = this.width;\n if (Number.isFinite(this.height)) c.height = this.height;\n return c;\n }\n\n private _onStreamReady = (event: CustomEvent): void => {\n this._video.srcObject = event.detail as MediaStream;\n };\n\n private _onActiveChanged = (event: CustomEvent): void => {\n // Clear the preview when the stream is released so the last frame does not stick.\n if (event.detail === false) {\n this._video.srcObject = null;\n }\n };\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this._connected = true;\n this._connectedCallbackPromise = this._core.observe(this._constraints());\n if (this.autostart) {\n this._core.start();\n }\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n }\n\n disconnectedCallback(): void {\n this._connected = false;\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._core.dispose();\n }\n\n attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void {\n if (!this._connected || oldValue === newValue) return;\n // While switchCamera() batches several attribute writes, defer to its single\n // post-batch re-acquire — otherwise the first write would acquire on stale\n // constraints (see `_batchingAttrs`).\n if (this._batchingAttrs) return;\n // Track the new constraints (and reconcile the microphone watcher).\n this._core.observe(this._constraints());\n // A constraints change re-acquires (switchMap-style restart) only when a stream\n // is actually live. `active` is the deliberate guard (not `desired`): `active`\n // implies `desired` (a stream only goes live under desired), so re-acquiring\n // cannot spuriously re-`desired` a stopped camera. Guarding on `active` rather\n // than `desired` also avoids force-acquiring while suspended/hidden (desired but\n // not active) — the visibility handler owns resume there.\n if (this._core.active) {\n this._core.start();\n }\n }\n\n private _onVisibilityChange = (): void => {\n if (this.keepAlive) return;\n if (document.visibilityState === \"hidden\") {\n this._core.suspend();\n } else {\n this._core.resume();\n }\n };\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail, WcsRecordedDetail,\n} from \"../types.js\";\nimport { hasMediaRecorder, normalizeMediaError } from \"../media/getUserMedia.js\";\n\n/**\n * Headless media-recording primitive. Wraps MediaRecorder, consuming a borrowed\n * `MediaStream` (received via `attachStream` over the direct channel — see\n * docs/camera-recorder-tag-design.md §2) and producing a `Blob` clip.\n *\n * Ownership: the stream is BORROWED, never owned. The Core never stops its tracks\n * — that is the camera's job (the acquirer owns release). Stopping here would tear\n * down a stream that may still be previewing.\n *\n * Non-goal: the Core does NOT subscribe to the borrowed tracks' `ended` and does NOT\n * auto-terminate a recording when the underlying stream dies (camera stop / OS\n * revoke / switchCamera re-acquire). Reacting would mean reaching into a stream we do\n * not own. The MediaRecorder keeps running against the now-dead source until an\n * explicit stop(); the consumer (which owns the camera lifecycle) is responsible for\n * stopping the recording when it tears the stream down. stop() then assembles\n * whatever chunks were captured — never throws.\n *\n * Output: `dataavailable` chunks are collected and assembled into one `Blob` on\n * stop, published via `wcs-recorder:recorded`. The `Blob` is structured-clone\n * friendly (a settled value, unlike MediaStream) so it may flow through state.\n * `objectURL` is a managed string — the Core revokes the previous URL before\n * issuing a new one and on dispose. Failures never throw.\n */\nexport class RecorderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"recording\", event: \"wcs-recorder:recording-changed\" },\n { name: \"paused\", event: \"wcs-recorder:paused-changed\" },\n { name: \"duration\", event: \"wcs-recorder:duration-changed\" },\n { name: \"mimeType\", event: \"wcs-recorder:mimetype-changed\" },\n { name: \"blob\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.blob ?? null },\n { name: \"objectURL\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.objectURL ?? null },\n { name: \"error\", event: \"wcs-recorder:error\" },\n // event-token: detail = the assembled clip { blob, objectURL, mimeType, duration }.\n { name: \"recorded\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token (timeslice mode): detail = the streamed Blob chunk.\n { name: \"dataavailable\", event: \"wcs-recorder:dataavailable\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"attachStream\" },\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _recording: boolean = false;\n private _paused: boolean = false;\n private _duration: number = 0;\n private _mimeType: string = \"\";\n private _blob: Blob | null = null;\n private _objectURL: string | null = null;\n private _error: WcsMediaErrorDetail | null = null;\n\n private _recorder: MediaRecorder | null = null;\n private _stream: MediaStream | null = null; // borrowed — never stopped here\n private _chunks: Blob[] = [];\n private _timeslice: boolean = false;\n private _startTime: number = 0;\n\n // Monotonic id of the current recording lifecycle. Bumped by every start() and\n // dispose(); each MediaRecorder callback bails if stale so a torn-down/restarted\n // recorder's late event cannot mutate state.\n private _gen: number = 0;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get recording(): boolean { return this._recording; }\n get paused(): boolean { return this._paused; }\n // Finalized at stop/pause only — there is no live ticking timer, so this stays 0\n // from start() until the first pause()/stop() (see _elapsed / onstop / onpause).\n get duration(): number { return this._duration; }\n get mimeType(): string { return this._mimeType; }\n get blob(): Blob | null { return this._blob; }\n get objectURL(): string | null { return this._objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n\n // --- State setters ---\n\n private _setRecording(v: boolean): void {\n if (this._recording === v) return;\n this._recording = v;\n this._dispatch(\"wcs-recorder:recording-changed\", v);\n }\n\n private _setPaused(v: boolean): void {\n if (this._paused === v) return;\n this._paused = v;\n this._dispatch(\"wcs-recorder:paused-changed\", v);\n }\n\n private _setDuration(v: number): void {\n if (this._duration === v) return;\n this._duration = v;\n this._dispatch(\"wcs-recorder:duration-changed\", v);\n }\n\n private _setMimeType(v: string): void {\n if (this._mimeType === v) return;\n this._mimeType = v;\n this._dispatch(\"wcs-recorder:mimetype-changed\", v);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event, so unlike the value setters this is not content-deduped. Only the\n // null→null transition is collapsed (clearing an already-clear error stays silent).\n // The guard is written on null explicitly so it does NOT depend on callers passing a\n // fresh object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-recorder:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Borrow a stream for recording (the direct-channel sink). Synchronous, no\n * await: the live handle is captured by reference and never stored in state.\n * Does NOT stop any previously-borrowed stream — ownership stays with the camera.\n *\n * Re-attaching mid-recording takes effect on the NEXT start(), not the current\n * recording: the live MediaRecorder was already constructed around the previous\n * stream and keeps recording it until stop(). The borrowed reference is swapped so\n * the following start() uses the new stream.\n */\n attachStream(stream: MediaStream): void {\n this._stream = stream;\n }\n\n /** Start recording the borrowed stream. Never throws — surfaces `error`. */\n start(options: RecorderOptions = {}): void {\n if (!hasMediaRecorder()) {\n this._setError({ name: \"unsupported\", message: \"MediaRecorder is not available in this environment.\" });\n return;\n }\n if (!this._stream) {\n this._setError({ name: \"NoStreamError\", message: \"No stream attached. Wire a camera's stream-ready to attachStream first.\" });\n return;\n }\n if (this._recording) return;\n\n const recOptions: MediaRecorderOptions = {};\n if (options.mimeType && this._isTypeSupported(options.mimeType)) {\n recOptions.mimeType = options.mimeType;\n }\n if (typeof options.audioBitsPerSecond === \"number\") recOptions.audioBitsPerSecond = options.audioBitsPerSecond;\n if (typeof options.videoBitsPerSecond === \"number\") recOptions.videoBitsPerSecond = options.videoBitsPerSecond;\n\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(this._stream, recOptions);\n } catch (error) {\n this._setError(normalizeMediaError(error));\n return;\n }\n\n const gen = ++this._gen;\n this._chunks = [];\n this._timeslice = typeof options.timeslice === \"number\" && options.timeslice > 0;\n\n recorder.ondataavailable = (event: BlobEvent): void => {\n if (gen !== this._gen) return;\n if (event.data && event.data.size > 0) {\n this._chunks.push(event.data);\n if (this._timeslice) {\n this._dispatch(\"wcs-recorder:dataavailable\", event.data);\n }\n }\n };\n recorder.onstop = (): void => {\n if (gen !== this._gen) return;\n // When stopped while paused, `_duration` was already finalized in onpause —\n // recomputing _elapsed() here would wrongly include the paused gap (the\n // clock kept running but `_startTime` was not advanced). Keep the held value.\n if (!this._paused) this._setDuration(this._elapsed());\n this._assembleBlob();\n this._setPaused(false);\n this._setRecording(false);\n };\n recorder.onerror = (event: Event): void => {\n if (gen !== this._gen) return;\n const err = (event as unknown as { error?: unknown }).error;\n this._setError(normalizeMediaError(err ?? { name: \"RecorderError\" }));\n };\n recorder.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setDuration(this._elapsed());\n this._setPaused(true);\n };\n recorder.onresume = (): void => {\n if (gen !== this._gen) return;\n this._startTime = this._now() - this._duration;\n this._setPaused(false);\n };\n\n this._recorder = recorder;\n this._setError(null);\n this._setMimeType(recorder.mimeType || recOptions.mimeType || \"\");\n this._startTime = this._now();\n this._setDuration(0);\n recorder.start(this._timeslice ? options.timeslice : undefined);\n this._setRecording(true);\n }\n\n /** Stop recording; the assembled Blob is published from the recorder's onstop. */\n stop(): void {\n if (this._recorder && this._recorder.state !== \"inactive\") {\n this._recorder.stop();\n }\n }\n\n pause(): void {\n if (this._recorder && this._recorder.state === \"recording\") {\n this._recorder.pause();\n }\n }\n\n resume(): void {\n if (this._recorder && this._recorder.state === \"paused\") {\n this._recorder.resume();\n }\n }\n\n /** Stop in-flight recording, revoke the last object URL, drop the borrowed stream. */\n dispose(): void {\n // Bump the generation first so the native stop()'s onstop (gen-guarded) does\n // not run on a disposed Core — then reset the recording flags directly here.\n this._gen++;\n if (this._recorder && this._recorder.state !== \"inactive\") {\n try {\n this._recorder.stop();\n } catch {\n // A recorder already torn down by the environment must not throw here.\n }\n }\n this._recorder = null;\n this._revokeUrl();\n // Drop the borrowed reference WITHOUT stopping its tracks — the camera owns it.\n this._stream = null;\n // Reset transient recording state silently (onstop was gen-guarded out above).\n this._recording = false;\n this._paused = false;\n }\n\n // --- Internal ---\n\n private _assembleBlob(): void {\n const blob = new Blob(this._chunks, this._mimeType ? { type: this._mimeType } : undefined);\n this._chunks = [];\n // The PREVIOUS clip's object URL is revoked here, before minting the new one — so\n // any consumer still pointing a `<video src>` at the old URL will break once a new\n // recording completes. Consumers must follow the latest `objectURL`/`recorded`\n // value and not pin a stale URL. (The `blob` is unaffected — prefer flowing it.)\n this._revokeUrl();\n const objectURL = this._createUrl(blob);\n this._blob = blob;\n this._objectURL = objectURL;\n const detail: WcsRecordedDetail = {\n blob,\n objectURL,\n mimeType: this._mimeType,\n duration: this._duration,\n };\n this._dispatch(\"wcs-recorder:recorded\", detail);\n }\n\n private _revokeUrl(): void {\n if (this._objectURL && typeof URL !== \"undefined\" && typeof URL.revokeObjectURL === \"function\") {\n URL.revokeObjectURL(this._objectURL);\n }\n this._objectURL = null;\n }\n\n private _createUrl(blob: Blob): string {\n if (typeof URL !== \"undefined\" && typeof URL.createObjectURL === \"function\") {\n return URL.createObjectURL(blob);\n }\n return \"\";\n }\n\n // Only called after hasMediaRecorder() has confirmed the API, so MediaRecorder\n // and its isTypeSupported are present.\n private _isTypeSupported(type: string): boolean {\n const MR = (globalThis as { MediaRecorder: { isTypeSupported(t: string): boolean } }).MediaRecorder;\n return MR.isTypeSupported(type);\n }\n\n // performance.now() is universally available wherever MediaRecorder runs\n // (browsers and the happy-dom test env), so no fallback guard is needed.\n private _now(): number {\n return performance.now();\n }\n\n private _elapsed(): number {\n return Math.max(0, Math.round(this._now() - this._startTime));\n }\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { RecorderCore } from \"../core/RecorderCore.js\";\n\n/**\n * `<wcs-recorder>` — declarative media recording. Wraps RecorderCore and records a\n * borrowed `MediaStream` received via the `attachStream` command (the direct\n * channel from `<wcs-camera>`'s `stream-ready`). It never owns or stops the stream.\n *\n * Recording parameters (`mime-type` / `timeslice` / bitrates) are mirrored\n * attributes. The assembled clip is published as `wcs-recorder:recorded`\n * (`{ blob, objectURL, mimeType, duration }`) and the `blob` / `objectURL` value\n * properties — a settled `Blob` is a value and may flow through state.\n */\nexport class WcsRecorder extends HTMLElement {\n static wcBindable: IWcBindable = {\n ...RecorderCore.wcBindable,\n // `mimeType` deliberately appears on TWO surfaces: as an output `property`\n // (inherited from RecorderCore — the browser-resolved recording type, event\n // `mimetype-changed`) and as an `input` (the `mime-type` request attribute). They\n // share a base name but are distinct directions: the property is read-only output\n // (getter → Core), the input is the write-only request (setter → attribute, read\n // back in _options()). See README \"request vs. resolved\".\n inputs: [\n { name: \"mimeType\", attribute: \"mime-type\" },\n { name: \"timeslice\", attribute: \"timeslice\" },\n { name: \"audioBitsPerSecond\", attribute: \"audio-bits\" },\n { name: \"videoBitsPerSecond\", attribute: \"video-bits\" },\n ],\n commands: RecorderCore.wcBindable.commands,\n };\n\n private _core: RecorderCore;\n\n constructor() {\n super();\n this._core = new RecorderCore(this);\n }\n\n // --- Attribute accessors ---\n\n // `mimeType` is an OUTPUT value property (the Core-resolved recording type,\n // published via `wcs-recorder:mimetype-changed`), so the getter delegates to the\n // Core — NOT the `mime-type` input attribute. The attribute is a *request*: the\n // browser may pick a different type, or fill one in when none was requested, and\n // bindings must read the actual value. The input side is read straight from the\n // attribute in `_options()`. The setter still writes the request attribute.\n get mimeType(): string { return this._core.mimeType; }\n set mimeType(value: string) { this.setAttribute(\"mime-type\", value); }\n\n get timeslice(): number { return this._numberAttr(\"timeslice\"); }\n set timeslice(value: number) { this.setAttribute(\"timeslice\", String(value)); }\n\n get audioBitsPerSecond(): number { return this._numberAttr(\"audio-bits\"); }\n set audioBitsPerSecond(value: number) { this.setAttribute(\"audio-bits\", String(value)); }\n\n get videoBitsPerSecond(): number { return this._numberAttr(\"video-bits\"); }\n set videoBitsPerSecond(value: number) { this.setAttribute(\"video-bits\", String(value)); }\n\n // --- Core delegated getters ---\n\n get recording(): boolean { return this._core.recording; }\n get paused(): boolean { return this._core.paused; }\n get duration(): number { return this._core.duration; }\n get blob(): Blob | null { return this._core.blob; }\n get objectURL(): string | null { return this._core.objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n // --- Commands ---\n\n /** Borrow a stream (the direct-channel sink). */\n attachStream(stream: MediaStream): void {\n this._core.attachStream(stream);\n }\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void { this._core.stop(); }\n pause(): void { this._core.pause(); }\n resume(): void { this._core.resume(); }\n\n // --- Internal ---\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _options(): RecorderOptions {\n const o: RecorderOptions = {};\n // Read the requested type from the input attribute directly — `get mimeType()` is\n // the resolved OUTPUT value, not the request.\n const requested = this.getAttribute(\"mime-type\") ?? \"\";\n if (requested) o.mimeType = requested;\n if (Number.isFinite(this.timeslice)) o.timeslice = this.timeslice;\n if (Number.isFinite(this.audioBitsPerSecond)) o.audioBitsPerSecond = this.audioBitsPerSecond;\n if (Number.isFinite(this.videoBitsPerSecond)) o.videoBitsPerSecond = this.videoBitsPerSecond;\n return o;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapCamera(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsCamera } from \"./components/Camera.js\";\nimport { WcsRecorder } from \"./components/Recorder.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.camera)) {\n customElements.define(config.tagNames.camera, WcsCamera);\n }\n if (!customElements.get(config.tagNames.recorder)) {\n customElements.define(config.tagNames.recorder, WcsRecorder);\n }\n}\n"],"names":["_config","tagNames","camera","recorder","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","hasMediaDevices","navigator","mediaDevices","getUserMedia","normalizeMediaError","error","name","String","message","UNSUPPORTED_ERROR","stopAllTracks","stream","track","getTracks","stop","async","enumerateVideoDevices","enumerateDevices","filter","d","kind","map","deviceId","label","groupId","MediaPermissionWatcher","_name","_onChange","_status","_gen","_subscribed","constructor","onChange","this","observe","Promise","resolve","permissions","query","gen","then","status","state","addEventListener","_onStatusChange","dispose","removeEventListener","event","target","CameraCore","EventTarget","static","protocol","version","properties","getter","e","detail","commands","_target","_active","_permission","_audioPermission","_deviceId","_devices","_error","_stream","_desired","_constraints","_camWatcher","_micWatcher","_ready","super","active","permission","audioPermission","devices","ready","_setActive","_dispatch","_setPermission","_setAudioPermission","_setDeviceId","id","_setDevices","_devicesEqual","_setError","type","dispatchEvent","CustomEvent","bubbles","a","b","length","i","constraints","_reconcileAudioWatcher","_initPermissions","start","_restart","_release","switchCamera","next","facingMode","undefined","suspend","resume","_onDeviceChange","s","tasks","audio","push","all","_acquire","c","video","exact","width","height","buildConstraints","requestUserMedia","live","_onTrackEnded","getAudioTracks","_updateDeviceId","silent","videoTrack","getVideoTracks","getSettings","settings","WcsCamera","HTMLElement","wcBindable","inputs","attribute","_core","_video","_connectedCallbackPromise","_connected","_batchingAttrs","root","attachShadow","mode","style","document","createElement","textContent","autoplay","muted","setAttribute","append","_onStreamReady","_onActiveChanged","hasAttribute","value","_toggleAttr","getAttribute","_numberAttr","autostart","keepAlive","videoElement","connectedCallbackPromise","removeAttribute","attr","trim","NaN","parsed","Number","isFinite","srcObject","connectedCallback","_onVisibilityChange","disconnectedCallback","attributeChangedCallback","oldValue","newValue","visibilityState","RecorderCore","blob","objectURL","_recording","_paused","_duration","_mimeType","_blob","_objectURL","_recorder","_chunks","_timeslice","_startTime","recording","paused","duration","mimeType","_setRecording","v","_setPaused","_setDuration","_setMimeType","attachStream","options","globalThis","MediaRecorder","recOptions","_isTypeSupported","audioBitsPerSecond","videoBitsPerSecond","timeslice","ondataavailable","data","size","onstop","_elapsed","_assembleBlob","onerror","err","onpause","onresume","_now","pause","_revokeUrl","Blob","_createUrl","URL","revokeObjectURL","createObjectURL","isTypeSupported","performance","now","Math","max","round","WcsRecorder","_options","o","requested","display","bootstrapCamera","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/media/getUserMedia.ts","../src/media/permission.ts","../src/core/CameraCore.ts","../src/components/Camera.ts","../src/core/RecorderCore.ts","../src/components/Recorder.ts","../src/bootstrapCamera.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n camera: string;\n recorder: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n camera: \"wcs-camera\",\n recorder: \"wcs-recorder\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by registerComponents (read at call time so\n// setConfig() takes effect without re-import). Public consumers get the deep-frozen\n// clone from getConfig().\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { CameraConstraints, MediaDeviceSnapshot, WcsMediaErrorDetail } from \"../types.js\";\n\n/** True when getUserMedia is reachable (secure context with a media-devices impl). */\nexport function hasMediaDevices(): boolean {\n return typeof navigator !== \"undefined\"\n && !!navigator.mediaDevices\n && typeof navigator.mediaDevices.getUserMedia === \"function\";\n}\n\n/** True when MediaRecorder is available in this environment. */\nexport function hasMediaRecorder(): boolean {\n return typeof globalThis !== \"undefined\"\n && typeof (globalThis as { MediaRecorder?: unknown }).MediaRecorder === \"function\";\n}\n\n/**\n * Translate a getUserMedia constraints object from the declarative\n * CameraConstraints surface. Always requests a video track; `audio` opts the\n * microphone in. `deviceId` (exact) takes precedence over `facingMode`.\n */\nexport function buildConstraints(c: CameraConstraints): MediaStreamConstraints {\n const video: MediaTrackConstraints = {};\n if (c.deviceId) {\n video.deviceId = { exact: c.deviceId };\n } else if (c.facingMode) {\n video.facingMode = c.facingMode;\n }\n if (typeof c.width === \"number\") video.width = c.width;\n if (typeof c.height === \"number\") video.height = c.height;\n const hasVideoConstraint = Object.keys(video).length > 0;\n return {\n video: hasVideoConstraint ? video : true,\n audio: c.audio === true,\n };\n}\n\n/** Normalize any thrown getUserMedia / MediaRecorder failure into a flat detail. */\nexport function normalizeMediaError(error: unknown): WcsMediaErrorDetail {\n if (error && typeof error === \"object\" && \"name\" in error) {\n const name = String((error as { name: unknown }).name) || \"Error\";\n const message = \"message\" in error && (error as { message?: unknown }).message\n ? String((error as { message: unknown }).message)\n : `Media request failed: ${name}.`;\n return { name, message };\n }\n return { name: \"Error\", message: \"Media request failed.\" };\n}\n\nconst UNSUPPORTED_ERROR: WcsMediaErrorDetail = {\n name: \"unsupported\",\n message: \"getUserMedia is not available (requires a secure context).\",\n};\n\nexport interface UserMediaResult {\n stream?: MediaStream;\n error?: WcsMediaErrorDetail;\n}\n\n/**\n * Request a media stream. Never throws — resolves with `{ stream }` on success or\n * `{ error }` (normalized) on failure / when the API is unavailable.\n */\nexport async function requestUserMedia(constraints: MediaStreamConstraints): Promise<UserMediaResult> {\n if (!hasMediaDevices()) {\n return { error: UNSUPPORTED_ERROR };\n }\n try {\n const stream = await navigator.mediaDevices.getUserMedia(constraints);\n return { stream };\n } catch (error) {\n return { error: normalizeMediaError(error) };\n }\n}\n\n/** Stop every track of a stream, releasing the camera/microphone hardware. */\nexport function stopAllTracks(stream: MediaStream | null): void {\n if (!stream) return;\n for (const track of stream.getTracks()) {\n track.stop();\n }\n}\n\n/**\n * Enumerate video input devices as plain snapshots. Labels are only populated\n * after a grant, so this is refreshed post-acquisition. Never throws.\n */\nexport async function enumerateVideoDevices(): Promise<MediaDeviceSnapshot[]> {\n if (!hasMediaDevices() || typeof navigator.mediaDevices.enumerateDevices !== \"function\") {\n return [];\n }\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n return devices\n .filter((d) => d.kind === \"videoinput\")\n .map((d) => ({\n deviceId: d.deviceId,\n label: d.label,\n groupId: d.groupId,\n kind: d.kind,\n }));\n } catch {\n return [];\n }\n}\n","import { MediaPermissionState } from \"../types.js\";\n\n/**\n * Live monitor for a single media permission (`camera` or `microphone`) via the\n * Permissions API. Mirrors PermissionCore's two-phase pattern: an initial query\n * plus a live `change` subscription, guarded by a monotonic generation so a query\n * superseded by a rapid dispose/observe never attaches a stale listener.\n *\n * When the Permissions API is absent or rejects the descriptor (e.g. Firefox does\n * not accept the `camera` / `microphone` descriptor), the watcher reports\n * `\"unsupported\"`. The CameraCore then refines the state from the getUserMedia\n * outcome (granted on success, denied on NotAllowedError).\n */\nexport class MediaPermissionWatcher {\n private _name: \"camera\" | \"microphone\";\n private _onChange: (state: MediaPermissionState) => void;\n private _status: PermissionStatus | null = null;\n private _gen: number = 0;\n private _subscribed: boolean = false;\n\n constructor(name: \"camera\" | \"microphone\", onChange: (state: MediaPermissionState) => void) {\n this._name = name;\n this._onChange = onChange;\n }\n\n /** Issue the initial query and subscribe to live changes. Resolves when settled. */\n observe(): Promise<void> {\n if (this._subscribed) return Promise.resolve();\n if (typeof navigator === \"undefined\" || !navigator.permissions\n || typeof navigator.permissions.query !== \"function\") {\n this._onChange(\"unsupported\");\n return Promise.resolve();\n }\n this._subscribed = true;\n const gen = ++this._gen;\n return navigator.permissions\n .query({ name: this._name as PermissionName })\n .then(\n (status) => {\n if (gen !== this._gen) return;\n this._status = status;\n this._onChange(status.state as MediaPermissionState);\n status.addEventListener(\"change\", this._onStatusChange);\n },\n () => {\n if (gen !== this._gen) return;\n this._onChange(\"unsupported\");\n },\n );\n }\n\n /** Detach the live listener and invalidate any in-flight query. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n if (this._status) {\n this._status.removeEventListener(\"change\", this._onStatusChange);\n this._status = null;\n }\n }\n\n private _onStatusChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._onChange(status.state as MediaPermissionState);\n };\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot,\n MediaPermissionState, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport {\n buildConstraints, enumerateVideoDevices, hasMediaDevices, requestUserMedia, stopAllTracks,\n} from \"../media/getUserMedia.js\";\nimport { MediaPermissionWatcher } from \"../media/permission.js\";\n\n/**\n * Headless camera-capture primitive. Wraps getUserMedia + the Permissions API and\n * exposes a `MediaStream` through the wc-bindable protocol — but the live stream is\n * NEVER published as a reactive value. It is a non-serializable live handle: it\n * flows out only via the `wcs-camera:stream-ready` event so a consumer (the preview\n * `<video>`, a `<wcs-recorder>`) can bind it directly to an element property,\n * bypassing serializable state. See docs/camera-recorder-tag-design.md §1/§2.\n *\n * The observable value surface is strictly derived data: `active` (is a stream\n * live — the \"actual\" half of the desired/actual pair), `permission` /\n * `audioPermission` (two-phase: Permissions API monitor + getUserMedia outcome),\n * `deviceId` / `devices`, and `error`. Failures never throw — they surface through\n * `error`.\n */\nexport class CameraCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"active\", event: \"wcs-camera:active-changed\" },\n { name: \"permission\", event: \"wcs-camera:permission-changed\" },\n { name: \"audioPermission\", event: \"wcs-camera:audio-permission-changed\" },\n { name: \"deviceId\", event: \"wcs-camera:device-changed\" },\n { name: \"devices\", event: \"wcs-camera:devices-changed\" },\n { name: \"error\", event: \"wcs-camera:error\" },\n // Direct-channel handle: event-token only — never bound as a reactive value.\n { name: \"streamReady\", event: \"wcs-camera:stream-ready\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token: a bare signal (detail is always null) — surface detail, not the raw Event.\n { name: \"ended\", event: \"wcs-camera:ended\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"switchCamera\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _active: boolean = false;\n private _permission: MediaPermissionState = \"prompt\";\n private _audioPermission: MediaPermissionState | null = null;\n private _deviceId: string | null = null;\n private _devices: MediaDeviceSnapshot[] = [];\n private _error: WcsMediaErrorDetail | null = null;\n\n // The live stream — internal only, never a reactive value (see class docs).\n private _stream: MediaStream | null = null;\n\n // desired/actual split (wakelock-style): `_desired` is whether the user wants the\n // camera on; `_active` is whether a stream is actually live. The OS can revoke a\n // track (device unplugged / taken by another app) — actual drops while desired\n // stays true, so a later resume()/visibility-restore can re-acquire.\n private _desired: boolean = false;\n\n private _constraints: CameraConstraints = {};\n\n // Monotonic id of the current acquisition lifecycle. Bumped by every acquire and\n // by dispose(). Each in-flight getUserMedia captures it and, on resolve, bails\n // (stopping the just-acquired stream) when superseded — so a constraints change\n // mid-acquire (switchMap-style restart) cannot leave an orphaned stream live.\n private _gen: number = 0;\n\n private _subscribed: boolean = false;\n private _camWatcher: MediaPermissionWatcher | null = null;\n private _micWatcher: MediaPermissionWatcher | null = null;\n\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get active(): boolean { return this._active; }\n get permission(): MediaPermissionState { return this._permission; }\n get audioPermission(): MediaPermissionState | null { return this._audioPermission; }\n get deviceId(): string | null { return this._deviceId; }\n get devices(): MediaDeviceSnapshot[] { return this._devices; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n get ready(): Promise<void> { return this._ready; }\n\n // --- State setters with event dispatch (same-value guarded) ---\n\n private _setActive(active: boolean): void {\n if (this._active === active) return;\n this._active = active;\n this._dispatch(\"wcs-camera:active-changed\", active);\n }\n\n private _setPermission(state: MediaPermissionState): void {\n if (this._permission === state) return;\n this._permission = state;\n this._dispatch(\"wcs-camera:permission-changed\", state);\n }\n\n private _setAudioPermission(state: MediaPermissionState | null): void {\n if (this._audioPermission === state) return;\n this._audioPermission = state;\n this._dispatch(\"wcs-camera:audio-permission-changed\", state);\n }\n\n private _setDeviceId(id: string | null): void {\n if (this._deviceId === id) return;\n this._deviceId = id;\n this._dispatch(\"wcs-camera:device-changed\", id);\n }\n\n private _setDevices(devices: MediaDeviceSnapshot[]): void {\n if (this._devicesEqual(this._devices, devices)) return;\n this._devices = devices;\n this._dispatch(\"wcs-camera:devices-changed\", devices);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event (e.g. retrying getUserMedia and failing again must re-notify), so\n // unlike the value setters this is not content-deduped. Only the null→null\n // transition is collapsed (clearing an already-clear error stays silent). The guard\n // is written on null explicitly so it does NOT depend on callers passing a fresh\n // object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-camera:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n private _devicesEqual(a: MediaDeviceSnapshot[], b: MediaDeviceSnapshot[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].deviceId !== b[i].deviceId || a[i].label !== b[i].label) return false;\n }\n return true;\n }\n\n // --- Public API ---\n\n /**\n * Begin observing permissions for the given constraints. Idempotent while\n * already subscribed. The first call (or one after dispose()) starts the\n * camera/microphone permission monitors. Acquisition itself is driven separately\n * by start() / autostart — observing does not prompt.\n */\n observe(constraints: CameraConstraints): Promise<void> {\n this._constraints = { ...constraints };\n if (!this._subscribed) {\n this._subscribed = true;\n this._ready = this._initPermissions();\n } else {\n // Already live: track the latest constraints and fold any newly-started\n // microphone query into `ready` so awaiting observe() guarantees its initial\n // permission state — symmetric with _initPermissions() on the first observe.\n this._ready = this._ready.then(() => this._reconcileAudioWatcher());\n }\n return this._ready;\n }\n\n /** Acquire the camera (sets desired=true). Prompts on first use. */\n start(): void {\n this._desired = true;\n this._restart();\n }\n\n /** Release the camera (sets desired=false), stopping all tracks. */\n stop(): void {\n this._desired = false;\n this._release(false);\n }\n\n /**\n * Toggle facingMode (user ↔ environment) and re-acquire if active. This is the\n * headless, DOM-free path: it flips the Core's internal `_constraints` (the single\n * source of truth for a standalone Core) and re-acquires while desired.\n *\n * Note: the `<wcs-camera>` Shell does NOT delegate to this — it keeps the DOM\n * attributes authoritative and drives its own single re-acquire (see Camera.ts\n * switchCamera). Both reach the same end state; the split exists because the Shell\n * must keep its declared attributes in sync, which a Core has no notion of.\n */\n switchCamera(): void {\n const next: FacingMode = this._constraints.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._constraints = { ...this._constraints, facingMode: next, deviceId: undefined };\n if (this._desired) {\n this._restart();\n }\n }\n\n /**\n * Suspend the live stream while keeping `desired` — for page-hidden. Stops tracks\n * (clearing the hardware indicator) but remembers that the camera should resume.\n *\n * Bumps `_gen` to supersede any in-flight acquire: without this, an acquire that\n * resolves *after* the page went hidden would assign `_stream` and set active —\n * re-lighting the camera behind a no-op suspend (the stream had not been assigned\n * yet, so the `if (_stream)` release below could not reach it). The superseded\n * acquire stops its just-acquired orphan stream on resolve (see `_acquire`).\n */\n suspend(): void {\n this._gen++;\n if (this._stream) {\n this._release(false);\n }\n }\n\n /** Re-acquire if the camera is desired but not currently active — for page-visible. */\n resume(): void {\n if (this._desired && !this._active) {\n this._restart();\n }\n }\n\n /** Tear down: stop the stream and detach permission listeners. */\n dispose(): void {\n this._subscribed = false;\n this._gen++;\n this._desired = false;\n this._release(true);\n if (hasMediaDevices() && typeof navigator.mediaDevices.removeEventListener === \"function\") {\n navigator.mediaDevices.removeEventListener(\"devicechange\", this._onDeviceChange);\n }\n this._camWatcher?.dispose();\n this._micWatcher?.dispose();\n this._camWatcher = null;\n this._micWatcher = null;\n }\n\n // --- Internal ---\n\n private _initPermissions(): Promise<void> {\n if (!hasMediaDevices()) {\n this._setPermission(\"unsupported\");\n return Promise.resolve();\n }\n this._camWatcher = new MediaPermissionWatcher(\"camera\", (s) => this._setPermission(s));\n const tasks: Promise<void>[] = [this._camWatcher.observe()];\n if (this._constraints.audio) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n tasks.push(this._micWatcher.observe());\n }\n // Track hot-plug: refresh the device list when a camera is added/removed.\n if (typeof navigator.mediaDevices.addEventListener === \"function\") {\n navigator.mediaDevices.addEventListener(\"devicechange\", this._onDeviceChange);\n }\n return Promise.all(tasks).then(() => undefined);\n }\n\n private _onDeviceChange = (): void => {\n void enumerateVideoDevices().then((devices) => {\n // Guard against a late resolution after dispose.\n if (this._subscribed) this._setDevices(devices);\n });\n };\n\n // Bring the microphone watcher in line with the latest `audio` constraint when\n // observe() is called again on an already-live Core. Returns the new watcher's\n // initial-query promise (so observe() can fold it into `ready`); resolved when\n // nothing started.\n private _reconcileAudioWatcher(): Promise<void> {\n if (this._constraints.audio && !this._micWatcher && hasMediaDevices()) {\n this._micWatcher = new MediaPermissionWatcher(\"microphone\", (s) => this._setAudioPermission(s));\n return this._micWatcher.observe();\n } else if (!this._constraints.audio && this._micWatcher) {\n this._micWatcher.dispose();\n this._micWatcher = null;\n this._setAudioPermission(null);\n }\n return Promise.resolve();\n }\n\n private _restart(): void {\n this._release(false);\n void this._acquire();\n }\n\n private async _acquire(): Promise<void> {\n const gen = ++this._gen;\n const constraints = buildConstraints(this._constraints);\n const { stream, error } = await requestUserMedia(constraints);\n\n // Superseded by a newer acquire (rapid restart) or disposed while in flight:\n // stop the orphan stream and bail without mutating state. The `ended` listeners\n // are attached only AFTER this gen check (below), so stopping the orphan here\n // cannot fire _onTrackEnded — no spurious `ended` event / state mutation. Keep\n // this stop strictly before listener attachment if the order is ever refactored.\n if (gen !== this._gen) {\n stopAllTracks(stream ?? null);\n return;\n }\n\n if (error) {\n this._setError(error);\n if (error.name === \"NotAllowedError\") {\n this._setPermission(\"denied\");\n // Hard denial: drop `desired` so a later visibility-restore (resume()) does\n // not silently re-attempt getUserMedia on every page-visible. A transient\n // failure (NotReadableError = device busy) keeps `desired` so it can recover.\n this._desired = false;\n } else if (error.name === \"unsupported\") {\n this._setPermission(\"unsupported\");\n this._desired = false;\n }\n this._setActive(false);\n return;\n }\n\n const live = stream as MediaStream;\n this._stream = live;\n for (const track of live.getTracks()) {\n track.addEventListener(\"ended\", this._onTrackEnded);\n }\n this._setError(null);\n // getUserMedia success is authoritative for permission.\n this._setPermission(\"granted\");\n // Only assert mic-granted when the grant actually produced an audio track. With\n // today's boolean `audio` this is equivalent to `_constraints.audio`, but it stays\n // correct under a future non-mandatory `{ audio: {...} }` constraint where the\n // browser may grant video while omitting audio.\n if (this._constraints.audio && live.getAudioTracks().length > 0) {\n this._setAudioPermission(\"granted\");\n }\n this._updateDeviceId(live);\n this._setActive(true);\n // Publish the live handle for the direct element→element channel.\n this._dispatch(\"wcs-camera:stream-ready\", live);\n // Labels become available after a grant; refresh the device list.\n const devices = await enumerateVideoDevices();\n if (gen === this._gen) {\n this._setDevices(devices);\n }\n }\n\n private _release(silent: boolean): void {\n if (!this._stream) return;\n for (const track of this._stream.getTracks()) {\n track.removeEventListener(\"ended\", this._onTrackEnded);\n }\n stopAllTracks(this._stream);\n this._stream = null;\n if (silent) {\n this._active = false;\n } else {\n this._setActive(false);\n }\n }\n\n private _onTrackEnded = (): void => {\n // OS revoked a track (unplug / taken by another app). actual drops; desired\n // stays true so resume()/visibility-restore can re-acquire.\n this._release(false);\n this._dispatch(\"wcs-camera:ended\", null);\n };\n\n private _updateDeviceId(stream: MediaStream): void {\n const videoTrack = stream.getVideoTracks()[0];\n if (videoTrack && typeof videoTrack.getSettings === \"function\") {\n const settings = videoTrack.getSettings();\n this._setDeviceId(settings.deviceId ?? null);\n }\n }\n}\n","import {\n IWcBindable, CameraConstraints, FacingMode, MediaDeviceSnapshot, MediaPermissionState,\n WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { CameraCore } from \"../core/CameraCore.js\";\n\n/**\n * `<wcs-camera>` — declarative camera capture with a built-in preview.\n *\n * The element owns a `<video>` in its shadow root and assigns the live\n * `MediaStream` to `video.srcObject` internally, so the non-serializable handle\n * never crosses the state boundary (design §1, case B). For consumers (a\n * `<wcs-recorder>`, an external `<video>`), the stream is also published via the\n * `wcs-camera:stream-ready` event-token for the direct element→element channel\n * (design §2).\n *\n * Acquisition is explicit: `start()` / the `autostart` attribute prompt and\n * acquire; merely connecting does not. While the page is hidden the stream is\n * suspended (clearing the camera indicator) and re-acquired on return, unless\n * `keep-alive` is set (e.g. during recording).\n */\nexport class WcsCamera extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...CameraCore.wcBindable,\n inputs: [\n { name: \"audio\", attribute: \"audio\" },\n { name: \"facingMode\", attribute: \"facing-mode\" },\n { name: \"deviceId\", attribute: \"device-id\" },\n { name: \"width\", attribute: \"width\" },\n { name: \"height\", attribute: \"height\" },\n { name: \"autostart\", attribute: \"autostart\" },\n { name: \"keepAlive\", attribute: \"keep-alive\" },\n ],\n commands: CameraCore.wcBindable.commands,\n };\n\n // `autostart` and `keep-alive` are intentionally NOT observed: `autostart` is a\n // connect-time-only acquire trigger (read once in connectedCallback; flipping it\n // later is meaningless), and `keep-alive` is read fresh on every visibilitychange\n // (_onVisibilityChange), so it never needs to drive a re-acquire. The observed set\n // is exactly the constraints that reshape the requested track.\n static observedAttributes = [\"facing-mode\", \"device-id\", \"audio\", \"width\", \"height\"];\n\n private _core: CameraCore;\n private _video: HTMLVideoElement;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n private _connected: boolean = false;\n // True while switchCamera() rewrites several attributes at once. Each setAttribute /\n // removeAttribute fires its own attributeChangedCallback synchronously; without this\n // guard the FIRST change would re-acquire with the not-yet-updated constraints (and\n // tear active down so the later change's re-acquire is skipped). We suppress the\n // per-attribute re-acquire and drive a single one with the final constraints.\n private _batchingAttrs: boolean = false;\n\n constructor() {\n super();\n this._core = new CameraCore(this);\n const root = this.attachShadow({ mode: \"open\" });\n const style = document.createElement(\"style\");\n style.textContent = \":host{display:inline-block}video{display:block;width:100%;height:100%}\";\n this._video = document.createElement(\"video\");\n this._video.autoplay = true;\n this._video.muted = true;\n this._video.setAttribute(\"playsinline\", \"\");\n this._video.setAttribute(\"part\", \"video\");\n root.append(style, this._video);\n // Bind the live handle to the preview internally — never through state. These\n // self-listeners are intentionally not removed on disconnect (asymmetric with the\n // document-level `visibilitychange` in connect/disconnectedCallback): the target\n // is `this`, so the listeners are collected together with the element — there is\n // no external reference to leak. The visibility listener, by contrast, lives on\n // `document` (outlives the element) and MUST be detached.\n this.addEventListener(\"wcs-camera:stream-ready\", this._onStreamReady as EventListener);\n this.addEventListener(\"wcs-camera:active-changed\", this._onActiveChanged as EventListener);\n }\n\n // --- Attribute accessors ---\n\n get audio(): boolean { return this.hasAttribute(\"audio\"); }\n set audio(value: boolean) { this._toggleAttr(\"audio\", value); }\n\n get facingMode(): FacingMode {\n return this.getAttribute(\"facing-mode\") === \"environment\" ? \"environment\" : \"user\";\n }\n set facingMode(value: FacingMode) { this.setAttribute(\"facing-mode\", value); }\n\n get deviceId(): string { return this.getAttribute(\"device-id\") ?? \"\"; }\n set deviceId(value: string) { this.setAttribute(\"device-id\", value); }\n\n get width(): number { return this._numberAttr(\"width\"); }\n set width(value: number) { this.setAttribute(\"width\", String(value)); }\n\n get height(): number { return this._numberAttr(\"height\"); }\n set height(value: number) { this.setAttribute(\"height\", String(value)); }\n\n get autostart(): boolean { return this.hasAttribute(\"autostart\"); }\n set autostart(value: boolean) { this._toggleAttr(\"autostart\", value); }\n\n get keepAlive(): boolean { return this.hasAttribute(\"keep-alive\"); }\n set keepAlive(value: boolean) { this._toggleAttr(\"keep-alive\", value); }\n\n /** The internal preview `<video>` (for advanced styling/measurement). */\n get videoElement(): HTMLVideoElement { return this._video; }\n\n // --- Core delegated getters ---\n\n get active(): boolean { return this._core.active; }\n get permission(): MediaPermissionState { return this._core.permission; }\n get audioPermission(): MediaPermissionState | null { return this._core.audioPermission; }\n get devices(): MediaDeviceSnapshot[] { return this._core.devices; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n get connectedCallbackPromise(): Promise<void> { return this._connectedCallbackPromise; }\n\n // --- Commands ---\n\n start(): void { this._core.start(); }\n stop(): void { this._core.stop(); }\n\n /**\n * Toggle the front/back camera by updating the DOM attributes (the single source\n * of truth), not just the Core's internal constraints. Deliberately does NOT call\n * `CameraCore.switchCamera()` (which would mutate the Core's constraints behind the\n * DOM's back, leaving the declared attributes stale). `device-id` is removed because\n * it would otherwise take precedence over `facing-mode` (see buildConstraints) —\n * leaving it pinned would silently undo the switch on the next re-acquire. Both\n * attribute writes are batched (see `_batchingAttrs`) so they drive exactly ONE\n * re-acquire here, with the final constraints — never an early acquire on a\n * half-updated state. The DOM and the live camera stay in agreement.\n */\n switchCamera(): void {\n const next: FacingMode = this.facingMode === \"environment\" ? \"user\" : \"environment\";\n this._batchingAttrs = true;\n try {\n this.removeAttribute(\"device-id\");\n this.setAttribute(\"facing-mode\", next);\n } finally {\n this._batchingAttrs = false;\n }\n // Sync the (now-final) constraints and re-acquire once when a stream is live —\n // the same `active`-guarded restart attributeChangedCallback performs.\n this._core.observe(this._constraints());\n if (this._core.active) {\n this._core.start();\n }\n }\n\n // --- Internal ---\n\n private _toggleAttr(name: string, value: boolean): void {\n if (value) {\n this.setAttribute(name, \"\");\n } else {\n this.removeAttribute(name);\n }\n }\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _constraints(): CameraConstraints {\n const c: CameraConstraints = { audio: this.audio, facingMode: this.facingMode };\n if (this.deviceId) c.deviceId = this.deviceId;\n if (Number.isFinite(this.width)) c.width = this.width;\n if (Number.isFinite(this.height)) c.height = this.height;\n return c;\n }\n\n private _onStreamReady = (event: CustomEvent): void => {\n this._video.srcObject = event.detail as MediaStream;\n };\n\n private _onActiveChanged = (event: CustomEvent): void => {\n // Clear the preview when the stream is released so the last frame does not stick.\n if (event.detail === false) {\n this._video.srcObject = null;\n }\n };\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this._connected = true;\n this._connectedCallbackPromise = this._core.observe(this._constraints());\n if (this.autostart) {\n this._core.start();\n }\n document.addEventListener(\"visibilitychange\", this._onVisibilityChange);\n }\n\n disconnectedCallback(): void {\n this._connected = false;\n document.removeEventListener(\"visibilitychange\", this._onVisibilityChange);\n this._core.dispose();\n }\n\n attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void {\n if (!this._connected || oldValue === newValue) return;\n // While switchCamera() batches several attribute writes, defer to its single\n // post-batch re-acquire — otherwise the first write would acquire on stale\n // constraints (see `_batchingAttrs`).\n if (this._batchingAttrs) return;\n // Track the new constraints (and reconcile the microphone watcher).\n this._core.observe(this._constraints());\n // A constraints change re-acquires (switchMap-style restart) only when a stream\n // is actually live. `active` is the deliberate guard (not `desired`): `active`\n // implies `desired` (a stream only goes live under desired), so re-acquiring\n // cannot spuriously re-`desired` a stopped camera. Guarding on `active` rather\n // than `desired` also avoids force-acquiring while suspended/hidden (desired but\n // not active) — the visibility handler owns resume there.\n if (this._core.active) {\n this._core.start();\n }\n }\n\n private _onVisibilityChange = (): void => {\n if (this.keepAlive) return;\n if (document.visibilityState === \"hidden\") {\n this._core.suspend();\n } else {\n this._core.resume();\n }\n };\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail, WcsRecordedDetail,\n} from \"../types.js\";\nimport { hasMediaRecorder, normalizeMediaError } from \"../media/getUserMedia.js\";\n\n/**\n * Headless media-recording primitive. Wraps MediaRecorder, consuming a borrowed\n * `MediaStream` (received via `attachStream` over the direct channel — see\n * docs/camera-recorder-tag-design.md §2) and producing a `Blob` clip.\n *\n * Ownership: the stream is BORROWED, never owned. The Core never stops its tracks\n * — that is the camera's job (the acquirer owns release). Stopping here would tear\n * down a stream that may still be previewing.\n *\n * Non-goal: the Core does NOT subscribe to the borrowed tracks' `ended` and does NOT\n * auto-terminate a recording when the underlying stream dies (camera stop / OS\n * revoke / switchCamera re-acquire). Reacting would mean reaching into a stream we do\n * not own. The MediaRecorder keeps running against the now-dead source until an\n * explicit stop(); the consumer (which owns the camera lifecycle) is responsible for\n * stopping the recording when it tears the stream down. stop() then assembles\n * whatever chunks were captured — never throws.\n *\n * Output: `dataavailable` chunks are collected and assembled into one `Blob` on\n * stop, published via `wcs-recorder:recorded`. The `Blob` is structured-clone\n * friendly (a settled value, unlike MediaStream) so it may flow through state.\n * `objectURL` is a managed string — the Core revokes the previous URL before\n * issuing a new one and on dispose. Failures never throw.\n */\nexport class RecorderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"recording\", event: \"wcs-recorder:recording-changed\" },\n { name: \"paused\", event: \"wcs-recorder:paused-changed\" },\n { name: \"duration\", event: \"wcs-recorder:duration-changed\" },\n { name: \"mimeType\", event: \"wcs-recorder:mimetype-changed\" },\n { name: \"blob\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.blob ?? null },\n { name: \"objectURL\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail?.objectURL ?? null },\n { name: \"error\", event: \"wcs-recorder:error\" },\n // event-token: detail = the assembled clip { blob, objectURL, mimeType, duration }.\n { name: \"recorded\", event: \"wcs-recorder:recorded\", getter: (e: Event) => (e as CustomEvent).detail },\n // event-token (timeslice mode): detail = the streamed Blob chunk.\n { name: \"dataavailable\", event: \"wcs-recorder:dataavailable\", getter: (e: Event) => (e as CustomEvent).detail },\n ],\n commands: [\n { name: \"attachStream\" },\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _recording: boolean = false;\n private _paused: boolean = false;\n private _duration: number = 0;\n private _mimeType: string = \"\";\n private _blob: Blob | null = null;\n private _objectURL: string | null = null;\n private _error: WcsMediaErrorDetail | null = null;\n\n private _recorder: MediaRecorder | null = null;\n private _stream: MediaStream | null = null; // borrowed — never stopped here\n private _chunks: Blob[] = [];\n private _timeslice: boolean = false;\n private _startTime: number = 0;\n\n // Monotonic id of the current recording lifecycle. Bumped by every start() and\n // dispose(); each MediaRecorder callback bails if stale so a torn-down/restarted\n // recorder's late event cannot mutate state.\n private _gen: number = 0;\n\n // SSR: recording is command-driven (attachStream/start), so there is no\n // asynchronous probe to await — readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get recording(): boolean { return this._recording; }\n get paused(): boolean { return this._paused; }\n // Finalized at stop/pause only — there is no live ticking timer, so this stays 0\n // from start() until the first pause()/stop() (see _elapsed / onstop / onpause).\n get duration(): number { return this._duration; }\n get mimeType(): string { return this._mimeType; }\n get blob(): Blob | null { return this._blob; }\n get objectURL(): string | null { return this._objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._error; }\n get ready(): Promise<void> { return this._ready; }\n\n // --- State setters ---\n\n private _setRecording(v: boolean): void {\n if (this._recording === v) return;\n this._recording = v;\n this._dispatch(\"wcs-recorder:recording-changed\", v);\n }\n\n private _setPaused(v: boolean): void {\n if (this._paused === v) return;\n this._paused = v;\n this._dispatch(\"wcs-recorder:paused-changed\", v);\n }\n\n private _setDuration(v: number): void {\n if (this._duration === v) return;\n this._duration = v;\n this._dispatch(\"wcs-recorder:duration-changed\", v);\n }\n\n private _setMimeType(v: string): void {\n if (this._mimeType === v) return;\n this._mimeType = v;\n this._dispatch(\"wcs-recorder:mimetype-changed\", v);\n }\n\n // Errors are dispatched on EVERY non-null occurrence by design — each failure is a\n // distinct event, so unlike the value setters this is not content-deduped. Only the\n // null→null transition is collapsed (clearing an already-clear error stays silent).\n // The guard is written on null explicitly so it does NOT depend on callers passing a\n // fresh object: a reused/cached non-null detail would still re-notify.\n private _setError(error: WcsMediaErrorDetail | null): void {\n if (error === null && this._error === null) return;\n this._error = error;\n this._dispatch(\"wcs-recorder:error\", error);\n }\n\n private _dispatch(type: string, detail: unknown): void {\n this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Borrow a stream for recording (the direct-channel sink). Synchronous, no\n * await: the live handle is captured by reference and never stored in state.\n * Does NOT stop any previously-borrowed stream — ownership stays with the camera.\n *\n * Re-attaching mid-recording takes effect on the NEXT start(), not the current\n * recording: the live MediaRecorder was already constructed around the previous\n * stream and keeps recording it until stop(). The borrowed reference is swapped so\n * the following start() uses the new stream.\n */\n attachStream(stream: MediaStream): void {\n this._stream = stream;\n }\n\n /** Start recording the borrowed stream. Never throws — surfaces `error`. */\n start(options: RecorderOptions = {}): void {\n if (!hasMediaRecorder()) {\n this._setError({ name: \"unsupported\", message: \"MediaRecorder is not available in this environment.\" });\n return;\n }\n if (!this._stream) {\n this._setError({ name: \"NoStreamError\", message: \"No stream attached. Wire a camera's stream-ready to attachStream first.\" });\n return;\n }\n if (this._recording) return;\n\n const recOptions: MediaRecorderOptions = {};\n if (options.mimeType && this._isTypeSupported(options.mimeType)) {\n recOptions.mimeType = options.mimeType;\n }\n if (typeof options.audioBitsPerSecond === \"number\") recOptions.audioBitsPerSecond = options.audioBitsPerSecond;\n if (typeof options.videoBitsPerSecond === \"number\") recOptions.videoBitsPerSecond = options.videoBitsPerSecond;\n\n let recorder: MediaRecorder;\n try {\n recorder = new MediaRecorder(this._stream, recOptions);\n } catch (error) {\n this._setError(normalizeMediaError(error));\n return;\n }\n\n const gen = ++this._gen;\n this._chunks = [];\n this._timeslice = typeof options.timeslice === \"number\" && options.timeslice > 0;\n\n recorder.ondataavailable = (event: BlobEvent): void => {\n if (gen !== this._gen) return;\n if (event.data && event.data.size > 0) {\n this._chunks.push(event.data);\n if (this._timeslice) {\n this._dispatch(\"wcs-recorder:dataavailable\", event.data);\n }\n }\n };\n recorder.onstop = (): void => {\n if (gen !== this._gen) return;\n // When stopped while paused, `_duration` was already finalized in onpause —\n // recomputing _elapsed() here would wrongly include the paused gap (the\n // clock kept running but `_startTime` was not advanced). Keep the held value.\n if (!this._paused) this._setDuration(this._elapsed());\n this._assembleBlob();\n this._setPaused(false);\n this._setRecording(false);\n };\n recorder.onerror = (event: Event): void => {\n if (gen !== this._gen) return;\n const err = (event as unknown as { error?: unknown }).error;\n this._setError(normalizeMediaError(err ?? { name: \"RecorderError\" }));\n };\n recorder.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setDuration(this._elapsed());\n this._setPaused(true);\n };\n recorder.onresume = (): void => {\n if (gen !== this._gen) return;\n this._startTime = this._now() - this._duration;\n this._setPaused(false);\n };\n\n this._recorder = recorder;\n this._setError(null);\n this._setMimeType(recorder.mimeType || recOptions.mimeType || \"\");\n this._startTime = this._now();\n this._setDuration(0);\n recorder.start(this._timeslice ? options.timeslice : undefined);\n this._setRecording(true);\n }\n\n /** Stop recording; the assembled Blob is published from the recorder's onstop. */\n stop(): void {\n if (this._recorder && this._recorder.state !== \"inactive\") {\n this._recorder.stop();\n }\n }\n\n pause(): void {\n if (this._recorder && this._recorder.state === \"recording\") {\n this._recorder.pause();\n }\n }\n\n resume(): void {\n if (this._recorder && this._recorder.state === \"paused\") {\n this._recorder.resume();\n }\n }\n\n /**\n * Establish monitoring (§3.5). Recording is command-driven — there is no listener\n * or subscription to set up here (the borrowed stream arrives via attachStream and\n * recording is driven by start()/stop()), so observe() is an idempotent no-op that\n * resolves once ready. dispose() is its teardown counterpart.\n */\n observe(): Promise<void> {\n return this._ready;\n }\n\n /** Stop in-flight recording, revoke the last object URL, drop the borrowed stream. */\n dispose(): void {\n // Bump the generation first so the native stop()'s onstop (gen-guarded) does\n // not run on a disposed Core — then reset the recording flags directly here.\n this._gen++;\n if (this._recorder && this._recorder.state !== \"inactive\") {\n try {\n this._recorder.stop();\n } catch {\n // A recorder already torn down by the environment must not throw here.\n }\n }\n this._recorder = null;\n this._revokeUrl();\n // Drop the borrowed reference WITHOUT stopping its tracks — the camera owns it.\n this._stream = null;\n // Reset transient recording state silently (onstop was gen-guarded out above).\n this._recording = false;\n this._paused = false;\n }\n\n // --- Internal ---\n\n private _assembleBlob(): void {\n const blob = new Blob(this._chunks, this._mimeType ? { type: this._mimeType } : undefined);\n this._chunks = [];\n // The PREVIOUS clip's object URL is revoked here, before minting the new one — so\n // any consumer still pointing a `<video src>` at the old URL will break once a new\n // recording completes. Consumers must follow the latest `objectURL`/`recorded`\n // value and not pin a stale URL. (The `blob` is unaffected — prefer flowing it.)\n this._revokeUrl();\n const objectURL = this._createUrl(blob);\n this._blob = blob;\n this._objectURL = objectURL;\n const detail: WcsRecordedDetail = {\n blob,\n objectURL,\n mimeType: this._mimeType,\n duration: this._duration,\n };\n this._dispatch(\"wcs-recorder:recorded\", detail);\n }\n\n private _revokeUrl(): void {\n if (this._objectURL && typeof URL !== \"undefined\" && typeof URL.revokeObjectURL === \"function\") {\n URL.revokeObjectURL(this._objectURL);\n }\n this._objectURL = null;\n }\n\n private _createUrl(blob: Blob): string {\n if (typeof URL !== \"undefined\" && typeof URL.createObjectURL === \"function\") {\n return URL.createObjectURL(blob);\n }\n return \"\";\n }\n\n // Only called after hasMediaRecorder() has confirmed the API, so MediaRecorder\n // and its isTypeSupported are present.\n private _isTypeSupported(type: string): boolean {\n const MR = (globalThis as { MediaRecorder: { isTypeSupported(t: string): boolean } }).MediaRecorder;\n return MR.isTypeSupported(type);\n }\n\n // performance.now() is universally available wherever MediaRecorder runs\n // (browsers and the happy-dom test env), so no fallback guard is needed.\n private _now(): number {\n return performance.now();\n }\n\n private _elapsed(): number {\n return Math.max(0, Math.round(this._now() - this._startTime));\n }\n}\n","import {\n IWcBindable, RecorderOptions, WcsMediaErrorDetail,\n} from \"../types.js\";\nimport { RecorderCore } from \"../core/RecorderCore.js\";\n\n/**\n * `<wcs-recorder>` — declarative media recording. Wraps RecorderCore and records a\n * borrowed `MediaStream` received via the `attachStream` command (the direct\n * channel from `<wcs-camera>`'s `stream-ready`). It never owns or stops the stream.\n *\n * Recording parameters (`mime-type` / `timeslice` / bitrates) are mirrored\n * attributes. The assembled clip is published as `wcs-recorder:recorded`\n * (`{ blob, objectURL, mimeType, duration }`) and the `blob` / `objectURL` value\n * properties — a settled `Blob` is a value and may flow through state.\n */\nexport class WcsRecorder extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...RecorderCore.wcBindable,\n // `mimeType` deliberately appears on TWO surfaces: as an output `property`\n // (inherited from RecorderCore — the browser-resolved recording type, event\n // `mimetype-changed`) and as an `input` (the `mime-type` request attribute). They\n // share a base name but are distinct directions: the property is read-only output\n // (getter → Core), the input is the write-only request (setter → attribute, read\n // back in _options()). See README \"request vs. resolved\".\n inputs: [\n { name: \"mimeType\", attribute: \"mime-type\" },\n { name: \"timeslice\", attribute: \"timeslice\" },\n { name: \"audioBitsPerSecond\", attribute: \"audio-bits\" },\n { name: \"videoBitsPerSecond\", attribute: \"video-bits\" },\n ],\n commands: RecorderCore.wcBindable.commands,\n };\n\n private _core: RecorderCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new RecorderCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n // `mimeType` is an OUTPUT value property (the Core-resolved recording type,\n // published via `wcs-recorder:mimetype-changed`), so the getter delegates to the\n // Core — NOT the `mime-type` input attribute. The attribute is a *request*: the\n // browser may pick a different type, or fill one in when none was requested, and\n // bindings must read the actual value. The input side is read straight from the\n // attribute in `_options()`. The setter still writes the request attribute.\n get mimeType(): string { return this._core.mimeType; }\n set mimeType(value: string) { this.setAttribute(\"mime-type\", value); }\n\n get timeslice(): number { return this._numberAttr(\"timeslice\"); }\n set timeslice(value: number) { this.setAttribute(\"timeslice\", String(value)); }\n\n get audioBitsPerSecond(): number { return this._numberAttr(\"audio-bits\"); }\n set audioBitsPerSecond(value: number) { this.setAttribute(\"audio-bits\", String(value)); }\n\n get videoBitsPerSecond(): number { return this._numberAttr(\"video-bits\"); }\n set videoBitsPerSecond(value: number) { this.setAttribute(\"video-bits\", String(value)); }\n\n // --- Core delegated getters ---\n\n get recording(): boolean { return this._core.recording; }\n get paused(): boolean { return this._core.paused; }\n get duration(): number { return this._core.duration; }\n get blob(): Blob | null { return this._core.blob; }\n get objectURL(): string | null { return this._core.objectURL; }\n get error(): WcsMediaErrorDetail | null { return this._core.error; }\n\n // --- Commands ---\n\n /** Borrow a stream (the direct-channel sink). */\n attachStream(stream: MediaStream): void {\n this._core.attachStream(stream);\n }\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void { this._core.stop(); }\n pause(): void { this._core.pause(); }\n resume(): void { this._core.resume(); }\n\n // --- Internal ---\n\n private _numberAttr(name: string): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return NaN;\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : NaN;\n }\n\n private _options(): RecorderOptions {\n const o: RecorderOptions = {};\n // Read the requested type from the input attribute directly — `get mimeType()` is\n // the resolved OUTPUT value, not the request.\n const requested = this.getAttribute(\"mime-type\") ?? \"\";\n if (requested) o.mimeType = requested;\n if (Number.isFinite(this.timeslice)) o.timeslice = this.timeslice;\n if (Number.isFinite(this.audioBitsPerSecond)) o.audioBitsPerSecond = this.audioBitsPerSecond;\n if (Number.isFinite(this.videoBitsPerSecond)) o.videoBitsPerSecond = this.videoBitsPerSecond;\n return o;\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapCamera(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsCamera } from \"./components/Camera.js\";\nimport { WcsRecorder } from \"./components/Recorder.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.camera)) {\n customElements.define(config.tagNames.camera, WcsCamera);\n }\n if (!customElements.get(config.tagNames.recorder)) {\n customElements.define(config.tagNames.recorder, WcsRecorder);\n }\n}\n"],"names":["_config","tagNames","camera","recorder","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","hasMediaDevices","navigator","mediaDevices","getUserMedia","normalizeMediaError","error","name","String","message","UNSUPPORTED_ERROR","stopAllTracks","stream","track","getTracks","stop","async","enumerateVideoDevices","enumerateDevices","filter","d","kind","map","deviceId","label","groupId","MediaPermissionWatcher","_name","_onChange","_status","_gen","_subscribed","constructor","onChange","this","observe","Promise","resolve","permissions","query","gen","then","status","state","addEventListener","_onStatusChange","dispose","removeEventListener","event","target","CameraCore","EventTarget","static","protocol","version","properties","getter","e","detail","commands","_target","_active","_permission","_audioPermission","_deviceId","_devices","_error","_stream","_desired","_constraints","_camWatcher","_micWatcher","_ready","super","active","permission","audioPermission","devices","ready","_setActive","_dispatch","_setPermission","_setAudioPermission","_setDeviceId","id","_setDevices","_devicesEqual","_setError","type","dispatchEvent","CustomEvent","bubbles","a","b","length","i","constraints","_reconcileAudioWatcher","_initPermissions","start","_restart","_release","switchCamera","next","facingMode","undefined","suspend","resume","_onDeviceChange","s","tasks","audio","push","all","_acquire","c","video","exact","width","height","buildConstraints","requestUserMedia","live","_onTrackEnded","getAudioTracks","_updateDeviceId","silent","videoTrack","getVideoTracks","getSettings","settings","WcsCamera","HTMLElement","wcBindable","inputs","attribute","_core","_video","_connectedCallbackPromise","_connected","_batchingAttrs","root","attachShadow","mode","style","document","createElement","textContent","autoplay","muted","setAttribute","append","_onStreamReady","_onActiveChanged","hasAttribute","value","_toggleAttr","getAttribute","_numberAttr","autostart","keepAlive","videoElement","connectedCallbackPromise","removeAttribute","attr","trim","NaN","parsed","Number","isFinite","srcObject","connectedCallback","_onVisibilityChange","disconnectedCallback","attributeChangedCallback","oldValue","newValue","visibilityState","RecorderCore","blob","objectURL","_recording","_paused","_duration","_mimeType","_blob","_objectURL","_recorder","_chunks","_timeslice","_startTime","recording","paused","duration","mimeType","_setRecording","v","_setPaused","_setDuration","_setMimeType","attachStream","options","globalThis","MediaRecorder","recOptions","_isTypeSupported","audioBitsPerSecond","videoBitsPerSecond","timeslice","ondataavailable","data","size","onstop","_elapsed","_assembleBlob","onerror","err","onpause","onresume","_now","pause","_revokeUrl","Blob","_createUrl","URL","revokeObjectURL","createObjectURL","isTypeSupported","performance","now","Math","max","round","WcsRecorder","_options","o","requested","display","bootstrapCamera","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"AASA,MAAMA,EAA2B,CAC/BC,SAAU,CACRC,OAAQ,aACRC,SAAU,iBAId,SAASC,EAAcC,GACrB,GAAY,OAARA,GAA+B,iBAARA,EAAkB,OAAOA,EACpDC,OAAOC,OAAOF,GACd,IAAK,MAAMG,KAAOF,OAAOG,KAAKJ,GAC5BD,EAAYC,EAAgCG,IAE9C,OAAOH,CACT,CAEA,SAASK,EAAaL,GACpB,GAAY,OAARA,GAA+B,iBAARA,EAAkB,OAAOA,EACpD,MAAMM,EAAiC,CAAA,EACvC,IAAK,MAAMH,KAAOF,OAAOG,KAAKJ,GAC5BM,EAAMH,GAAOE,EAAWL,EAAgCG,IAE1D,OAAOG,CACT,CAEA,IAAIC,EAA+B,KAK5B,MAAMC,EAAkBb,WAEfc,IAId,OAHKF,IACHA,EAAeR,EAAWM,EAAUV,KAE/BY,CACT,UC3CgBG,IACd,MAA4B,oBAAdC,aACPA,UAAUC,cACmC,mBAAxCD,UAAUC,aAAaC,YACrC,CA8BM,SAAUC,EAAoBC,GAClC,GAAIA,GAA0B,iBAAVA,GAAsB,SAAUA,EAAO,CACzD,MAAMC,EAAOC,OAAQF,EAA4BC,OAAS,QAI1D,MAAO,CAAEA,OAAME,QAHC,YAAaH,GAAUA,EAAgCG,QACnED,OAAQF,EAA+BG,SACvC,yBAAyBF,KAE/B,CACA,MAAO,CAAEA,KAAM,QAASE,QAAS,wBACnC,CAEA,MAAMC,EAAyC,CAC7CH,KAAM,cACNE,QAAS,8DAyBL,SAAUE,EAAcC,GAC5B,GAAKA,EACL,IAAK,MAAMC,KAASD,EAAOE,YACzBD,EAAME,MAEV,CAMOC,eAAeC,IACpB,IAAKhB,KAAwE,mBAA5CC,UAAUC,aAAae,iBACtD,MAAO,GAET,IAEE,aADsBhB,UAAUC,aAAae,oBAE1CC,OAAQC,GAAiB,eAAXA,EAAEC,MAChBC,IAAKF,IAAC,CACLG,SAAUH,EAAEG,SACZC,MAAOJ,EAAEI,MACTC,QAASL,EAAEK,QACXJ,KAAMD,EAAEC,OAEd,CAAE,MACA,MAAO,EACT,CACF,OC1FaK,EACHC,MACAC,UACAC,QAAmC,KACnCC,KAAe,EACfC,aAAuB,EAE/B,WAAAC,CAAYzB,EAA+B0B,GACzCC,KAAKP,MAAQpB,EACb2B,KAAKN,UAAYK,CACnB,CAGA,OAAAE,GACE,GAAID,KAAKH,YAAa,OAAOK,QAAQC,UACrC,GAAyB,oBAAdnC,YAA8BA,UAAUoC,aACP,mBAAhCpC,UAAUoC,YAAYC,MAEhC,OADAL,KAAKN,UAAU,eACRQ,QAAQC,UAEjBH,KAAKH,aAAc,EACnB,MAAMS,IAAQN,KAAKJ,KACnB,OAAO5B,UAAUoC,YACdC,MAAM,CAAEhC,KAAM2B,KAAKP,QACnBc,KACEC,IACKF,IAAQN,KAAKJ,OACjBI,KAAKL,QAAUa,EACfR,KAAKN,UAAUc,EAAOC,OACtBD,EAAOE,iBAAiB,SAAUV,KAAKW,mBAEzC,KACML,IAAQN,KAAKJ,MACjBI,KAAKN,UAAU,gBAGvB,CAGA,OAAAkB,GACEZ,KAAKH,aAAc,EACnBG,KAAKJ,OACDI,KAAKL,UACPK,KAAKL,QAAQkB,oBAAoB,SAAUb,KAAKW,iBAChDX,KAAKL,QAAU,KAEnB,CAEQgB,gBAAmBG,IACzB,MAAMN,EAASM,EAAMC,OACrBf,KAAKN,UAAUc,EAAOC,QCxCpB,MAAOO,UAAmBC,YAC9BC,kBAAiC,CAC/BC,SAAU,cACVC,QAAS,EACTC,WAAY,CACV,CAAEhD,KAAM,SAAUyC,MAAO,6BACzB,CAAEzC,KAAM,aAAcyC,MAAO,iCAC7B,CAAEzC,KAAM,kBAAmByC,MAAO,uCAClC,CAAEzC,KAAM,WAAYyC,MAAO,6BAC3B,CAAEzC,KAAM,UAAWyC,MAAO,8BAC1B,CAAEzC,KAAM,QAASyC,MAAO,oBAExB,CAAEzC,KAAM,cAAeyC,MAAO,0BAA2BQ,OAASC,GAAcA,EAAkBC,QAElG,CAAEnD,KAAM,QAASyC,MAAO,mBAAoBQ,OAASC,GAAcA,EAAkBC,SAEvFC,SAAU,CACR,CAAEpD,KAAM,SACR,CAAEA,KAAM,QACR,CAAEA,KAAM,kBAIJqD,QAEAC,SAAmB,EACnBC,YAAoC,SACpCC,iBAAgD,KAChDC,UAA2B,KAC3BC,SAAkC,GAClCC,OAAqC,KAGrCC,QAA8B,KAM9BC,UAAoB,EAEpBC,aAAkC,CAAA,EAMlCvC,KAAe,EAEfC,aAAuB,EACvBuC,YAA6C,KAC7CC,YAA6C,KAE7CC,OAAwBpC,QAAQC,UAExC,WAAAL,CAAYiB,GACVwB,QACAvC,KAAK0B,QAAUX,GAAUf,IAC3B,CAEA,UAAIwC,GAAoB,OAAOxC,KAAK2B,OAAS,CAC7C,cAAIc,GAAqC,OAAOzC,KAAK4B,WAAa,CAClE,mBAAIc,GAAiD,OAAO1C,KAAK6B,gBAAkB,CACnF,YAAIxC,GAA4B,OAAOW,KAAK8B,SAAW,CACvD,WAAIa,GAAmC,OAAO3C,KAAK+B,QAAU,CAC7D,SAAI3D,GAAsC,OAAO4B,KAAKgC,MAAQ,CAC9D,SAAIY,GAAyB,OAAO5C,KAAKsC,MAAQ,CAIzC,UAAAO,CAAWL,GACbxC,KAAK2B,UAAYa,IACrBxC,KAAK2B,QAAUa,EACfxC,KAAK8C,UAAU,4BAA6BN,GAC9C,CAEQ,cAAAO,CAAetC,GACjBT,KAAK4B,cAAgBnB,IACzBT,KAAK4B,YAAcnB,EACnBT,KAAK8C,UAAU,gCAAiCrC,GAClD,CAEQ,mBAAAuC,CAAoBvC,GACtBT,KAAK6B,mBAAqBpB,IAC9BT,KAAK6B,iBAAmBpB,EACxBT,KAAK8C,UAAU,sCAAuCrC,GACxD,CAEQ,YAAAwC,CAAaC,GACflD,KAAK8B,YAAcoB,IACvBlD,KAAK8B,UAAYoB,EACjBlD,KAAK8C,UAAU,4BAA6BI,GAC9C,CAEQ,WAAAC,CAAYR,GACd3C,KAAKoD,cAAcpD,KAAK+B,SAAUY,KACtC3C,KAAK+B,SAAWY,EAChB3C,KAAK8C,UAAU,6BAA8BH,GAC/C,CAQQ,SAAAU,CAAUjF,GACF,OAAVA,GAAkC,OAAhB4B,KAAKgC,SAC3BhC,KAAKgC,OAAS5D,EACd4B,KAAK8C,UAAU,mBAAoB1E,GACrC,CAEQ,SAAA0E,CAAUQ,EAAc9B,GAC9BxB,KAAK0B,QAAQ6B,cAAc,IAAIC,YAAYF,EAAM,CAAE9B,SAAQiC,SAAS,IACtE,CAEQ,aAAAL,CAAcM,EAA0BC,GAC9C,GAAID,EAAEE,SAAWD,EAAEC,OAAQ,OAAO,EAClC,IAAK,IAAIC,EAAI,EAAGA,EAAIH,EAAEE,OAAQC,IAC5B,GAAIH,EAAEG,GAAGxE,WAAasE,EAAEE,GAAGxE,UAAYqE,EAAEG,GAAGvE,QAAUqE,EAAEE,GAAGvE,MAAO,OAAO,EAE3E,OAAO,CACT,CAUA,OAAAW,CAAQ6D,GAWN,OAVA9D,KAAKmC,aAAe,IAAK2B,GACpB9D,KAAKH,YAORG,KAAKsC,OAAStC,KAAKsC,OAAO/B,KAAK,IAAMP,KAAK+D,2BAN1C/D,KAAKH,aAAc,EACnBG,KAAKsC,OAAStC,KAAKgE,oBAOdhE,KAAKsC,MACd,CAGA,KAAA2B,GACEjE,KAAKkC,UAAW,EAChBlC,KAAKkE,UACP,CAGA,IAAArF,GACEmB,KAAKkC,UAAW,EAChBlC,KAAKmE,UAAS,EAChB,CAYA,YAAAC,GACE,MAAMC,EAAoD,gBAAjCrE,KAAKmC,aAAamC,WAA+B,OAAS,cACnFtE,KAAKmC,aAAe,IAAKnC,KAAKmC,aAAcmC,WAAYD,EAAMhF,cAAUkF,GACpEvE,KAAKkC,UACPlC,KAAKkE,UAET,CAYA,OAAAM,GACExE,KAAKJ,OACDI,KAAKiC,SACPjC,KAAKmE,UAAS,EAElB,CAGA,MAAAM,GACMzE,KAAKkC,WAAalC,KAAK2B,SACzB3B,KAAKkE,UAET,CAGA,OAAAtD,GACEZ,KAAKH,aAAc,EACnBG,KAAKJ,OACLI,KAAKkC,UAAW,EAChBlC,KAAKmE,UAAS,GACVpG,KAA2E,mBAA/CC,UAAUC,aAAa4C,qBACrD7C,UAAUC,aAAa4C,oBAAoB,eAAgBb,KAAK0E,iBAElE1E,KAAKoC,aAAaxB,UAClBZ,KAAKqC,aAAazB,UAClBZ,KAAKoC,YAAc,KACnBpC,KAAKqC,YAAc,IACrB,CAIQ,gBAAA2B,GACN,IAAKjG,IAEH,OADAiC,KAAK+C,eAAe,eACb7C,QAAQC,UAEjBH,KAAKoC,YAAc,IAAI5C,EAAuB,SAAWmF,GAAM3E,KAAK+C,eAAe4B,IACnF,MAAMC,EAAyB,CAAC5E,KAAKoC,YAAYnC,WASjD,OARID,KAAKmC,aAAa0C,QACpB7E,KAAKqC,YAAc,IAAI7C,EAAuB,aAAemF,GAAM3E,KAAKgD,oBAAoB2B,IAC5FC,EAAME,KAAK9E,KAAKqC,YAAYpC,YAGyB,mBAA5CjC,UAAUC,aAAayC,kBAChC1C,UAAUC,aAAayC,iBAAiB,eAAgBV,KAAK0E,iBAExDxE,QAAQ6E,IAAIH,GAAOrE,KAAK,OACjC,CAEQmE,gBAAkB,KACnB3F,IAAwBwB,KAAMoC,IAE7B3C,KAAKH,aAAaG,KAAKmD,YAAYR,MAQnC,sBAAAoB,GACN,OAAI/D,KAAKmC,aAAa0C,QAAU7E,KAAKqC,aAAetE,KAClDiC,KAAKqC,YAAc,IAAI7C,EAAuB,aAAemF,GAAM3E,KAAKgD,oBAAoB2B,IACrF3E,KAAKqC,YAAYpC,aACdD,KAAKmC,aAAa0C,OAAS7E,KAAKqC,cAC1CrC,KAAKqC,YAAYzB,UACjBZ,KAAKqC,YAAc,KACnBrC,KAAKgD,oBAAoB,OAEpB9C,QAAQC,UACjB,CAEQ,QAAA+D,GACNlE,KAAKmE,UAAS,GACTnE,KAAKgF,UACZ,CAEQ,cAAMA,GACZ,MAAM1E,IAAQN,KAAKJ,KACbkE,EF5QJ,SAA2BmB,GAC/B,MAAMC,EAA+B,CAAA,EASrC,OARID,EAAE5F,SACJ6F,EAAM7F,SAAW,CAAE8F,MAAOF,EAAE5F,UACnB4F,EAAEX,aACXY,EAAMZ,WAAaW,EAAEX,YAEA,iBAAZW,EAAEG,QAAoBF,EAAME,MAAQH,EAAEG,OACzB,iBAAbH,EAAEI,SAAqBH,EAAMG,OAASJ,EAAEI,QAE5C,CACLH,QAFyB5H,OAAOG,KAAKyH,GAAOtB,OAAS,IAEzBsB,EAC5BL,OAAmB,IAAZI,EAAEJ,MAEb,CE8PwBS,CAAiBtF,KAAKmC,eACpCzD,OAAEA,EAAMN,MAAEA,SFnObU,eAAgCgF,GACrC,IAAK/F,IACH,MAAO,CAAEK,MAAOI,GAElB,IAEE,MAAO,CAAEE,aADYV,UAAUC,aAAaC,aAAa4F,GAE3D,CAAE,MAAO1F,GACP,MAAO,CAAEA,MAAOD,EAAoBC,GACtC,CACF,CEyNoCmH,CAAiBzB,GAOjD,GAAIxD,IAAQN,KAAKJ,KAEf,YADAnB,EAAcC,GAAU,MAI1B,GAAIN,EAaF,OAZA4B,KAAKqD,UAAUjF,GACI,oBAAfA,EAAMC,MACR2B,KAAK+C,eAAe,UAIpB/C,KAAKkC,UAAW,GACQ,gBAAf9D,EAAMC,OACf2B,KAAK+C,eAAe,eACpB/C,KAAKkC,UAAW,QAElBlC,KAAK6C,YAAW,GAIlB,MAAM2C,EAAO9G,EACbsB,KAAKiC,QAAUuD,EACf,IAAK,MAAM7G,KAAS6G,EAAK5G,YACvBD,EAAM+B,iBAAiB,QAASV,KAAKyF,eAEvCzF,KAAKqD,UAAU,MAEfrD,KAAK+C,eAAe,WAKhB/C,KAAKmC,aAAa0C,OAASW,EAAKE,iBAAiB9B,OAAS,GAC5D5D,KAAKgD,oBAAoB,WAE3BhD,KAAK2F,gBAAgBH,GACrBxF,KAAK6C,YAAW,GAEhB7C,KAAK8C,UAAU,0BAA2B0C,GAE1C,MAAM7C,QAAgB5D,IAClBuB,IAAQN,KAAKJ,MACfI,KAAKmD,YAAYR,EAErB,CAEQ,QAAAwB,CAASyB,GACf,GAAK5F,KAAKiC,QAAV,CACA,IAAK,MAAMtD,KAASqB,KAAKiC,QAAQrD,YAC/BD,EAAMkC,oBAAoB,QAASb,KAAKyF,eAE1ChH,EAAcuB,KAAKiC,SACnBjC,KAAKiC,QAAU,KACX2D,EACF5F,KAAK2B,SAAU,EAEf3B,KAAK6C,YAAW,EATC,CAWrB,CAEQ4C,cAAgB,KAGtBzF,KAAKmE,UAAS,GACdnE,KAAK8C,UAAU,mBAAoB,OAG7B,eAAA6C,CAAgBjH,GACtB,MAAMmH,EAAanH,EAAOoH,iBAAiB,GAC3C,GAAID,GAAgD,mBAA3BA,EAAWE,YAA4B,CAC9D,MAAMC,EAAWH,EAAWE,cAC5B/F,KAAKiD,aAAa+C,EAAS3G,UAAY,KACzC,CACF,EC7VI,MAAO4G,UAAkBC,YAC7BhF,oCAAqC,EACrCA,kBAAiC,IAC5BF,EAAWmF,WACdC,OAAQ,CACN,CAAE/H,KAAM,QAASgI,UAAW,SAC5B,CAAEhI,KAAM,aAAcgI,UAAW,eACjC,CAAEhI,KAAM,WAAYgI,UAAW,aAC/B,CAAEhI,KAAM,QAASgI,UAAW,SAC5B,CAAEhI,KAAM,SAAUgI,UAAW,UAC7B,CAAEhI,KAAM,YAAagI,UAAW,aAChC,CAAEhI,KAAM,YAAagI,UAAW,eAElC5E,SAAUT,EAAWmF,WAAW1E,UAQlCP,0BAA4B,CAAC,cAAe,YAAa,QAAS,QAAS,UAEnEoF,MACAC,OACAC,0BAA2CtG,QAAQC,UACnDsG,YAAsB,EAMtBC,gBAA0B,EAElC,WAAA5G,GACEyC,QACAvC,KAAKsG,MAAQ,IAAItF,EAAWhB,MAC5B,MAAM2G,EAAO3G,KAAK4G,aAAa,CAAEC,KAAM,SACjCC,EAAQC,SAASC,cAAc,SACrCF,EAAMG,YAAc,yEACpBjH,KAAKuG,OAASQ,SAASC,cAAc,SACrChH,KAAKuG,OAAOW,UAAW,EACvBlH,KAAKuG,OAAOY,OAAQ,EACpBnH,KAAKuG,OAAOa,aAAa,cAAe,IACxCpH,KAAKuG,OAAOa,aAAa,OAAQ,SACjCT,EAAKU,OAAOP,EAAO9G,KAAKuG,QAOxBvG,KAAKU,iBAAiB,0BAA2BV,KAAKsH,gBACtDtH,KAAKU,iBAAiB,4BAA6BV,KAAKuH,iBAC1D,CAIA,SAAI1C,GAAmB,OAAO7E,KAAKwH,aAAa,QAAU,CAC1D,SAAI3C,CAAM4C,GAAkBzH,KAAK0H,YAAY,QAASD,EAAQ,CAE9D,cAAInD,GACF,MAA4C,gBAArCtE,KAAK2H,aAAa,eAAmC,cAAgB,MAC9E,CACA,cAAIrD,CAAWmD,GAAqBzH,KAAKoH,aAAa,cAAeK,EAAQ,CAE7E,YAAIpI,GAAqB,OAAOW,KAAK2H,aAAa,cAAgB,EAAI,CACtE,YAAItI,CAASoI,GAAiBzH,KAAKoH,aAAa,YAAaK,EAAQ,CAErE,SAAIrC,GAAkB,OAAOpF,KAAK4H,YAAY,QAAU,CACxD,SAAIxC,CAAMqC,GAAiBzH,KAAKoH,aAAa,QAAS9I,OAAOmJ,GAAS,CAEtE,UAAIpC,GAAmB,OAAOrF,KAAK4H,YAAY,SAAW,CAC1D,UAAIvC,CAAOoC,GAAiBzH,KAAKoH,aAAa,SAAU9I,OAAOmJ,GAAS,CAExE,aAAII,GAAuB,OAAO7H,KAAKwH,aAAa,YAAc,CAClE,aAAIK,CAAUJ,GAAkBzH,KAAK0H,YAAY,YAAaD,EAAQ,CAEtE,aAAIK,GAAuB,OAAO9H,KAAKwH,aAAa,aAAe,CACnE,aAAIM,CAAUL,GAAkBzH,KAAK0H,YAAY,aAAcD,EAAQ,CAGvE,gBAAIM,GAAmC,OAAO/H,KAAKuG,MAAQ,CAI3D,UAAI/D,GAAoB,OAAOxC,KAAKsG,MAAM9D,MAAQ,CAClD,cAAIC,GAAqC,OAAOzC,KAAKsG,MAAM7D,UAAY,CACvE,mBAAIC,GAAiD,OAAO1C,KAAKsG,MAAM5D,eAAiB,CACxF,WAAIC,GAAmC,OAAO3C,KAAKsG,MAAM3D,OAAS,CAClE,SAAIvE,GAAsC,OAAO4B,KAAKsG,MAAMlI,KAAO,CAEnE,4BAAI4J,GAA4C,OAAOhI,KAAKwG,yBAA2B,CAIvF,KAAAvC,GAAgBjE,KAAKsG,MAAMrC,OAAS,CACpC,IAAApF,GAAemB,KAAKsG,MAAMzH,MAAQ,CAalC,YAAAuF,GACE,MAAMC,EAAuC,gBAApBrE,KAAKsE,WAA+B,OAAS,cACtEtE,KAAK0G,gBAAiB,EACtB,IACE1G,KAAKiI,gBAAgB,aACrBjI,KAAKoH,aAAa,cAAe/C,EACnC,SACErE,KAAK0G,gBAAiB,CACxB,CAGA1G,KAAKsG,MAAMrG,QAAQD,KAAKmC,gBACpBnC,KAAKsG,MAAM9D,QACbxC,KAAKsG,MAAMrC,OAEf,CAIQ,WAAAyD,CAAYrJ,EAAcoJ,GAC5BA,EACFzH,KAAKoH,aAAa/I,EAAM,IAExB2B,KAAKiI,gBAAgB5J,EAEzB,CAEQ,WAAAuJ,CAAYvJ,GAClB,MAAM6J,EAAOlI,KAAK2H,aAAatJ,GAC/B,GAAa,OAAT6J,GAAiC,KAAhBA,EAAKC,OAAe,OAAOC,IAChD,MAAMC,EAASC,OAAOJ,GACtB,OAAOI,OAAOC,SAASF,GAAUA,EAASD,GAC5C,CAEQ,YAAAjG,GACN,MAAM8C,EAAuB,CAAEJ,MAAO7E,KAAK6E,MAAOP,WAAYtE,KAAKsE,YAInE,OAHItE,KAAKX,WAAU4F,EAAE5F,SAAWW,KAAKX,UACjCiJ,OAAOC,SAASvI,KAAKoF,SAAQH,EAAEG,MAAQpF,KAAKoF,OAC5CkD,OAAOC,SAASvI,KAAKqF,UAASJ,EAAEI,OAASrF,KAAKqF,QAC3CJ,CACT,CAEQqC,eAAkBxG,IACxBd,KAAKuG,OAAOiC,UAAY1H,EAAMU,QAGxB+F,iBAAoBzG,KAEL,IAAjBA,EAAMU,SACRxB,KAAKuG,OAAOiC,UAAY,OAM5B,iBAAAC,GACEzI,KAAKyG,YAAa,EAClBzG,KAAKwG,0BAA4BxG,KAAKsG,MAAMrG,QAAQD,KAAKmC,gBACrDnC,KAAK6H,WACP7H,KAAKsG,MAAMrC,QAEb8C,SAASrG,iBAAiB,mBAAoBV,KAAK0I,oBACrD,CAEA,oBAAAC,GACE3I,KAAKyG,YAAa,EAClBM,SAASlG,oBAAoB,mBAAoBb,KAAK0I,qBACtD1I,KAAKsG,MAAM1F,SACb,CAEA,wBAAAgI,CAAyBnJ,EAAeoJ,EAAyBC,GAC1D9I,KAAKyG,YAAcoC,IAAaC,IAIjC9I,KAAK0G,iBAET1G,KAAKsG,MAAMrG,QAAQD,KAAKmC,gBAOpBnC,KAAKsG,MAAM9D,QACbxC,KAAKsG,MAAMrC,SAEf,CAEQyE,oBAAsB,KACxB1I,KAAK8H,YACwB,WAA7Bf,SAASgC,gBACX/I,KAAKsG,MAAM9B,UAEXxE,KAAKsG,MAAM7B,WCrMX,MAAOuE,UAAqB/H,YAChCC,kBAAiC,CAC/BC,SAAU,cACVC,QAAS,EACTC,WAAY,CACV,CAAEhD,KAAM,YAAayC,MAAO,kCAC5B,CAAEzC,KAAM,SAAUyC,MAAO,+BACzB,CAAEzC,KAAM,WAAYyC,MAAO,iCAC3B,CAAEzC,KAAM,WAAYyC,MAAO,iCAC3B,CAAEzC,KAAM,OAAQyC,MAAO,wBAAyBQ,OAASC,GAAcA,EAAkBC,QAAQyH,MAAQ,MACzG,CAAE5K,KAAM,YAAayC,MAAO,wBAAyBQ,OAASC,GAAcA,EAAkBC,QAAQ0H,WAAa,MACnH,CAAE7K,KAAM,QAASyC,MAAO,sBAExB,CAAEzC,KAAM,WAAYyC,MAAO,wBAAyBQ,OAASC,GAAcA,EAAkBC,QAE7F,CAAEnD,KAAM,gBAAiByC,MAAO,6BAA8BQ,OAASC,GAAcA,EAAkBC,SAEzGC,SAAU,CACR,CAAEpD,KAAM,gBACR,CAAEA,KAAM,SACR,CAAEA,KAAM,QACR,CAAEA,KAAM,SACR,CAAEA,KAAM,YAIJqD,QAEAyH,YAAsB,EACtBC,SAAmB,EACnBC,UAAoB,EACpBC,UAAoB,GACpBC,MAAqB,KACrBC,WAA4B,KAC5BxH,OAAqC,KAErCyH,UAAkC,KAClCxH,QAA8B,KAC9ByH,QAAkB,GAClBC,YAAsB,EACtBC,WAAqB,EAKrBhK,KAAe,EAIf0C,OAAwBpC,QAAQC,UAExC,WAAAL,CAAYiB,GACVwB,QACAvC,KAAK0B,QAAUX,GAAUf,IAC3B,CAEA,aAAI6J,GAAuB,OAAO7J,KAAKmJ,UAAY,CACnD,UAAIW,GAAoB,OAAO9J,KAAKoJ,OAAS,CAG7C,YAAIW,GAAqB,OAAO/J,KAAKqJ,SAAW,CAChD,YAAIW,GAAqB,OAAOhK,KAAKsJ,SAAW,CAChD,QAAIL,GAAsB,OAAOjJ,KAAKuJ,KAAO,CAC7C,aAAIL,GAA6B,OAAOlJ,KAAKwJ,UAAY,CACzD,SAAIpL,GAAsC,OAAO4B,KAAKgC,MAAQ,CAC9D,SAAIY,GAAyB,OAAO5C,KAAKsC,MAAQ,CAIzC,aAAA2H,CAAcC,GAChBlK,KAAKmJ,aAAee,IACxBlK,KAAKmJ,WAAae,EAClBlK,KAAK8C,UAAU,iCAAkCoH,GACnD,CAEQ,UAAAC,CAAWD,GACblK,KAAKoJ,UAAYc,IACrBlK,KAAKoJ,QAAUc,EACflK,KAAK8C,UAAU,8BAA+BoH,GAChD,CAEQ,YAAAE,CAAaF,GACflK,KAAKqJ,YAAca,IACvBlK,KAAKqJ,UAAYa,EACjBlK,KAAK8C,UAAU,gCAAiCoH,GAClD,CAEQ,YAAAG,CAAaH,GACflK,KAAKsJ,YAAcY,IACvBlK,KAAKsJ,UAAYY,EACjBlK,KAAK8C,UAAU,gCAAiCoH,GAClD,CAOQ,SAAA7G,CAAUjF,GACF,OAAVA,GAAkC,OAAhB4B,KAAKgC,SAC3BhC,KAAKgC,OAAS5D,EACd4B,KAAK8C,UAAU,qBAAsB1E,GACvC,CAEQ,SAAA0E,CAAUQ,EAAc9B,GAC9BxB,KAAK0B,QAAQ6B,cAAc,IAAIC,YAAYF,EAAM,CAAE9B,SAAQiC,SAAS,IACtE,CAcA,YAAA6G,CAAa5L,GACXsB,KAAKiC,QAAUvD,CACjB,CAGA,KAAAuF,CAAMsG,EAA2B,IAC/B,GJ/I2B,oBAAfC,YAC4D,mBAA7DA,WAA2CC,cIgJpD,YADAzK,KAAKqD,UAAU,CAAEhF,KAAM,cAAeE,QAAS,wDAGjD,IAAKyB,KAAKiC,QAER,YADAjC,KAAKqD,UAAU,CAAEhF,KAAM,gBAAiBE,QAAS,4EAGnD,GAAIyB,KAAKmJ,WAAY,OAErB,MAAMuB,EAAmC,CAAA,EAOzC,IAAIvN,EANAoN,EAAQP,UAAYhK,KAAK2K,iBAAiBJ,EAAQP,YACpDU,EAAWV,SAAWO,EAAQP,UAEU,iBAA/BO,EAAQK,qBAAiCF,EAAWE,mBAAqBL,EAAQK,oBAClD,iBAA/BL,EAAQM,qBAAiCH,EAAWG,mBAAqBN,EAAQM,oBAG5F,IACE1N,EAAW,IAAIsN,cAAczK,KAAKiC,QAASyI,EAC7C,CAAE,MAAOtM,GAEP,YADA4B,KAAKqD,UAAUlF,EAAoBC,GAErC,CAEA,MAAMkC,IAAQN,KAAKJ,KACnBI,KAAK0J,QAAU,GACf1J,KAAK2J,WAA0C,iBAAtBY,EAAQO,WAA0BP,EAAQO,UAAY,EAE/E3N,EAAS4N,gBAAmBjK,IACtBR,IAAQN,KAAKJ,MACbkB,EAAMkK,MAAQlK,EAAMkK,KAAKC,KAAO,IAClCjL,KAAK0J,QAAQ5E,KAAKhE,EAAMkK,MACpBhL,KAAK2J,YACP3J,KAAK8C,UAAU,6BAA8BhC,EAAMkK,QAIzD7N,EAAS+N,OAAS,KACZ5K,IAAQN,KAAKJ,OAIZI,KAAKoJ,SAASpJ,KAAKoK,aAAapK,KAAKmL,YAC1CnL,KAAKoL,gBACLpL,KAAKmK,YAAW,GAChBnK,KAAKiK,eAAc,KAErB9M,EAASkO,QAAWvK,IAClB,GAAIR,IAAQN,KAAKJ,KAAM,OACvB,MAAM0L,EAAOxK,EAAyC1C,MACtD4B,KAAKqD,UAAUlF,EAAoBmN,GAAO,CAAEjN,KAAM,oBAEpDlB,EAASoO,QAAU,KACbjL,IAAQN,KAAKJ,OACjBI,KAAKoK,aAAapK,KAAKmL,YACvBnL,KAAKmK,YAAW,KAElBhN,EAASqO,SAAW,KACdlL,IAAQN,KAAKJ,OACjBI,KAAK4J,WAAa5J,KAAKyL,OAASzL,KAAKqJ,UACrCrJ,KAAKmK,YAAW,KAGlBnK,KAAKyJ,UAAYtM,EACjB6C,KAAKqD,UAAU,MACfrD,KAAKqK,aAAalN,EAAS6M,UAAYU,EAAWV,UAAY,IAC9DhK,KAAK4J,WAAa5J,KAAKyL,OACvBzL,KAAKoK,aAAa,GAClBjN,EAAS8G,MAAMjE,KAAK2J,WAAaY,EAAQO,eAAYvG,GACrDvE,KAAKiK,eAAc,EACrB,CAGA,IAAApL,GACMmB,KAAKyJ,WAAsC,aAAzBzJ,KAAKyJ,UAAUhJ,OACnCT,KAAKyJ,UAAU5K,MAEnB,CAEA,KAAA6M,GACM1L,KAAKyJ,WAAsC,cAAzBzJ,KAAKyJ,UAAUhJ,OACnCT,KAAKyJ,UAAUiC,OAEnB,CAEA,MAAAjH,GACMzE,KAAKyJ,WAAsC,WAAzBzJ,KAAKyJ,UAAUhJ,OACnCT,KAAKyJ,UAAUhF,QAEnB,CAQA,OAAAxE,GACE,OAAOD,KAAKsC,MACd,CAGA,OAAA1B,GAIE,GADAZ,KAAKJ,OACDI,KAAKyJ,WAAsC,aAAzBzJ,KAAKyJ,UAAUhJ,MACnC,IACET,KAAKyJ,UAAU5K,MACjB,CAAE,MAEF,CAEFmB,KAAKyJ,UAAY,KACjBzJ,KAAK2L,aAEL3L,KAAKiC,QAAU,KAEfjC,KAAKmJ,YAAa,EAClBnJ,KAAKoJ,SAAU,CACjB,CAIQ,aAAAgC,GACN,MAAMnC,EAAO,IAAI2C,KAAK5L,KAAK0J,QAAS1J,KAAKsJ,UAAY,CAAEhG,KAAMtD,KAAKsJ,gBAAc/E,GAChFvE,KAAK0J,QAAU,GAKf1J,KAAK2L,aACL,MAAMzC,EAAYlJ,KAAK6L,WAAW5C,GAClCjJ,KAAKuJ,MAAQN,EACbjJ,KAAKwJ,WAAaN,EAClB,MAAM1H,EAA4B,CAChCyH,OACAC,YACAc,SAAUhK,KAAKsJ,UACfS,SAAU/J,KAAKqJ,WAEjBrJ,KAAK8C,UAAU,wBAAyBtB,EAC1C,CAEQ,UAAAmK,GACF3L,KAAKwJ,YAA6B,oBAARsC,KAAsD,mBAAxBA,IAAIC,iBAC9DD,IAAIC,gBAAgB/L,KAAKwJ,YAE3BxJ,KAAKwJ,WAAa,IACpB,CAEQ,UAAAqC,CAAW5C,GACjB,MAAmB,oBAAR6C,KAAsD,mBAAxBA,IAAIE,gBACpCF,IAAIE,gBAAgB/C,GAEtB,EACT,CAIQ,gBAAA0B,CAAiBrH,GAEvB,OADYkH,WAA0EC,cAC5EwB,gBAAgB3I,EAC5B,CAIQ,IAAAmI,GACN,OAAOS,YAAYC,KACrB,CAEQ,QAAAhB,GACN,OAAOiB,KAAKC,IAAI,EAAGD,KAAKE,MAAMtM,KAAKyL,OAASzL,KAAK4J,YACnD,ECzTI,MAAO2C,UAAoBrG,YAC/BhF,oCAAqC,EACrCA,kBAAiC,IAC5B8H,EAAa7C,WAOhBC,OAAQ,CACN,CAAE/H,KAAM,WAAYgI,UAAW,aAC/B,CAAEhI,KAAM,YAAagI,UAAW,aAChC,CAAEhI,KAAM,qBAAsBgI,UAAW,cACzC,CAAEhI,KAAM,qBAAsBgI,UAAW,eAE3C5E,SAAUuH,EAAa7C,WAAW1E,UAG5B6E,MACAE,0BAA2CtG,QAAQC,UAE3D,WAAAL,GACEyC,QACAvC,KAAKsG,MAAQ,IAAI0C,EAAahJ,KAChC,CAEA,4BAAIgI,GACF,OAAOhI,KAAKwG,yBACd,CAUA,YAAIwD,GAAqB,OAAOhK,KAAKsG,MAAM0D,QAAU,CACrD,YAAIA,CAASvC,GAAiBzH,KAAKoH,aAAa,YAAaK,EAAQ,CAErE,aAAIqD,GAAsB,OAAO9K,KAAK4H,YAAY,YAAc,CAChE,aAAIkD,CAAUrD,GAAiBzH,KAAKoH,aAAa,YAAa9I,OAAOmJ,GAAS,CAE9E,sBAAImD,GAA+B,OAAO5K,KAAK4H,YAAY,aAAe,CAC1E,sBAAIgD,CAAmBnD,GAAiBzH,KAAKoH,aAAa,aAAc9I,OAAOmJ,GAAS,CAExF,sBAAIoD,GAA+B,OAAO7K,KAAK4H,YAAY,aAAe,CAC1E,sBAAIiD,CAAmBpD,GAAiBzH,KAAKoH,aAAa,aAAc9I,OAAOmJ,GAAS,CAIxF,aAAIoC,GAAuB,OAAO7J,KAAKsG,MAAMuD,SAAW,CACxD,UAAIC,GAAoB,OAAO9J,KAAKsG,MAAMwD,MAAQ,CAClD,YAAIC,GAAqB,OAAO/J,KAAKsG,MAAMyD,QAAU,CACrD,QAAId,GAAsB,OAAOjJ,KAAKsG,MAAM2C,IAAM,CAClD,aAAIC,GAA6B,OAAOlJ,KAAKsG,MAAM4C,SAAW,CAC9D,SAAI9K,GAAsC,OAAO4B,KAAKsG,MAAMlI,KAAO,CAKnE,YAAAkM,CAAa5L,GACXsB,KAAKsG,MAAMgE,aAAa5L,EAC1B,CAEA,KAAAuF,GACEjE,KAAKsG,MAAMrC,MAAMjE,KAAKwM,WACxB,CAEA,IAAA3N,GAAemB,KAAKsG,MAAMzH,MAAQ,CAClC,KAAA6M,GAAgB1L,KAAKsG,MAAMoF,OAAS,CACpC,MAAAjH,GAAiBzE,KAAKsG,MAAM7B,QAAU,CAI9B,WAAAmD,CAAYvJ,GAClB,MAAM6J,EAAOlI,KAAK2H,aAAatJ,GAC/B,GAAa,OAAT6J,GAAiC,KAAhBA,EAAKC,OAAe,OAAOC,IAChD,MAAMC,EAASC,OAAOJ,GACtB,OAAOI,OAAOC,SAASF,GAAUA,EAASD,GAC5C,CAEQ,QAAAoE,GACN,MAAMC,EAAqB,CAAA,EAGrBC,EAAY1M,KAAK2H,aAAa,cAAgB,GAKpD,OAJI+E,IAAWD,EAAEzC,SAAW0C,GACxBpE,OAAOC,SAASvI,KAAK8K,aAAY2B,EAAE3B,UAAY9K,KAAK8K,WACpDxC,OAAOC,SAASvI,KAAK4K,sBAAqB6B,EAAE7B,mBAAqB5K,KAAK4K,oBACtEtC,OAAOC,SAASvI,KAAK6K,sBAAqB4B,EAAE5B,mBAAqB7K,KAAK6K,oBACnE4B,CACT,CAIA,iBAAAhE,GACEzI,KAAK8G,MAAM6F,QAAU,OACrB3M,KAAKwG,0BAA4BxG,KAAKsG,MAAMrG,SAC9C,CAEA,oBAAA0I,GACE3I,KAAKsG,MAAM1F,SACb,ECpHI,SAAUgM,EAAgBC,GP4C1B,IAAoBC,EO3CpBD,KP2CoBC,EO1CZD,GP2CM5P,UAChBK,OAAOyP,OAAO/P,EAAQC,SAAU6P,EAAc7P,UAEhDW,EAAe,MQ/CVoP,eAAeC,IAAIpP,EAAOZ,SAASC,SACtC8P,eAAeE,OAAOrP,EAAOZ,SAASC,OAAQ+I,GAE3C+G,eAAeC,IAAIpP,EAAOZ,SAASE,WACtC6P,eAAeE,OAAOrP,EAAOZ,SAASE,SAAUoP,EDApD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/camera",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "Declarative camera capture and media recording for Web Components. Binds live MediaStream handles directly to elements (never through serializable state) via getUserMedia + MediaRecorder.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",