@wcstack/camera 1.20.0 → 1.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -46,7 +46,7 @@ $on: {
46
46
 
47
47
  **コマンド:** `start()`・`stop()`・`switchCamera()`(前後カメラ切替)。
48
48
 
49
- **バインド可能な値:** `active`(ストリーム生存)・`permission` / `audioPermission`(`prompt`/`granted`/`denied`/`unsupported`)・`deviceId`・`devices`・`error`。
49
+ **バインド可能な値:** `active`(ストリーム生存)・`permission` / `audioPermission`(`prompt`/`granted`/`denied`/`unsupported`)・`deviceId`・`devices`・`error`・`errorInfo`(`WcsIoErrorInfo | null`——`error` から派生するシリアライズ可能な失敗分類。`wcs-camera:error-info-changed` で publish。下記「注意・落とし穴」参照)。
50
50
 
51
51
  **イベント(event-token):** `streamReady`(`wcs-camera:stream-ready`、detail = 生 `MediaStream`)・`error`・`ended`(OS によるトラック剥奪)。`streamReady` の「プロパティ」は event-token 配線用で、値としてバインドしないでください。
52
52
 
@@ -65,7 +65,7 @@ $on: {
65
65
 
66
66
  **コマンド:** `attachStream(stream)`・`start()`・`stop()`・`pause()`・`resume()`。
67
67
 
68
- **バインド可能な値:** `recording`・`paused`・`duration`(ms・下記注記参照)・`mimeType`(**解決後**の録画タイプ。要求した `mime-type` 属性と異なる、または未指定時にブラウザが補完した値)・`blob`・`objectURL`・`error`。
68
+ **バインド可能な値:** `recording`・`paused`・`duration`(ms・下記注記参照)・`mimeType`(**解決後**の録画タイプ。要求した `mime-type` 属性と異なる、または未指定時にブラウザが補完した値)・`blob`・`objectURL`・`error`・`errorInfo`(`WcsIoErrorInfo | null`——`error` から派生するシリアライズ可能な失敗分類。`wcs-recorder:error-info-changed` で publish。下記「注意・落とし穴」参照)。
69
69
 
70
70
  **イベント(event-token):** `recorded`(`wcs-recorder:recorded`、detail = `{ blob, objectURL, mimeType, duration }`)・`dataavailable`(`timeslice` モードのみ)・`error`。
71
71
 
@@ -147,6 +147,17 @@ wcs-recorder:state(paused) ~ .rec-dot { animation: none; opacity: .4; }
147
147
  - **カメラインジケータ=リーク検出器。** 終了後も点灯したままなら、トラックが停止されていません。
148
148
  - **ユーザージェスチャ。** 一部ブラウザは `getUserMedia` をユーザー操作起点で要求します。タイマーから撃つと無言で失敗することがあります(`error` に出ます・throw しません)。
149
149
  - **エラーは分類され throw されません:** `NotAllowedError`(拒否)・`NotFoundError`(デバイス無し)・`NotReadableError`(他アプリ使用中)・`OverconstrainedError`。
150
+ - **`errorInfo`——付加的な失敗分類。** `error` と並んで、`<wcs-camera>` / `<wcs-recorder>` はどちらも付加的なバインド可能出力 `errorInfo`(`WcsIoErrorInfo` = 安定した `code` / `phase` / `recoverable` / `message`)を公開します。同じ失敗から派生し、`wcs-camera:error-info-changed` / `wcs-recorder:error-info-changed` で publish されます。`error` の形状は不変で、`errorInfo` は `error` と完全に同期して遷移します(成功時に `null` へクリア)。両要素は 1 つの code セット(`core/mediaCapabilities.ts` の `WCS_MEDIA_ERROR_CODE`)を共有します:
151
+ - `capability-missing`(phase `probe`)——`getUserMedia` / `MediaRecorder` 非対応(非セキュアコンテキスト含む)。
152
+ - `not-allowed`(phase `start`)——`NotAllowedError` / `SecurityError`(権限拒否・feature-policy ブロック)。
153
+ - `not-found`(phase `start`)——`NotFoundError`(要求した種類のカメラ / マイクが存在しない)。
154
+ - `not-readable`(phase `start`)——`NotReadableError`(デバイス占有・ハードウェア障害)。
155
+ - `invalid-argument`(phase `start`)——`OverconstrainedError` / `NotSupportedError`(制約・mimeType が満たせない)。
156
+ - `invalid-state`(phase `start`)——`NoStreamError`(stream 未 attach で録画開始)。
157
+ - `aborted`(phase `execute`・`recoverable: true`)——`AbortError`(実行途中の中断、retry で回復しうる)。
158
+ - `media-error`(phase `execute`)——その他の実行時失敗(例: `RecorderError` / 想定外の `MediaRecorder` エラー)。
159
+
160
+ `WcsIoErrorInfo` 型と `WCS_MEDIA_ERROR_CODE` 定数は export されます。
150
161
  - **ストリームの所有権はカメラ側。** recorder は借用するだけ。録画中のカメラ切替は非対応(先に録画停止)。
151
162
  - **mimeType の対応はブラウザ差**(webm/mp4)。非対応の `mime-type` は無視され既定が使われます。
152
163
 
package/README.md CHANGED
@@ -46,7 +46,7 @@ Acquires a camera stream and renders a preview. Acquisition is **explicit** —
46
46
 
47
47
  **Commands:** `start()`, `stop()`, `switchCamera()` (toggle front/back).
48
48
 
49
- **Bindable values:** `active` (a stream is live), `permission` / `audioPermission` (`prompt`/`granted`/`denied`/`unsupported`), `deviceId`, `devices`, `error`.
49
+ **Bindable values:** `active` (a stream is live), `permission` / `audioPermission` (`prompt`/`granted`/`denied`/`unsupported`), `deviceId`, `devices`, `error`, `errorInfo` (`WcsIoErrorInfo | null` — a serializable failure taxonomy derived from `error`, published via `wcs-camera:error-info-changed`; see [Notes & gotchas](#notes--gotchas) below).
50
50
 
51
51
  **Events (event-token):** `streamReady` (`wcs-camera:stream-ready`, detail = the live `MediaStream`), `error`, `ended` (a track was revoked by the OS). The `streamReady` "property" exists for event-token wiring only — never bind it as a value.
52
52
 
@@ -65,7 +65,7 @@ Records a **borrowed** stream received via `attachStream` (the direct channel fr
65
65
 
66
66
  **Commands:** `attachStream(stream)`, `start()`, `stop()`, `pause()`, `resume()`.
67
67
 
68
- **Bindable values:** `recording`, `paused`, `duration` (ms — see note below), `mimeType` (the **resolved** recording type, which may differ from the requested `mime-type` attribute or be filled in when none was requested), `blob`, `objectURL`, `error`.
68
+ **Bindable values:** `recording`, `paused`, `duration` (ms — see note below), `mimeType` (the **resolved** recording type, which may differ from the requested `mime-type` attribute or be filled in when none was requested), `blob`, `objectURL`, `error`, `errorInfo` (`WcsIoErrorInfo | null` — a serializable failure taxonomy derived from `error`, published via `wcs-recorder:error-info-changed`; see [Notes & gotchas](#notes--gotchas) below).
69
69
 
70
70
  **Events (event-token):** `recorded` (`wcs-recorder:recorded`, detail = `{ blob, objectURL, mimeType, duration }`), `dataavailable` (only in `timeslice` mode), `error`.
71
71
 
@@ -148,6 +148,17 @@ DevTools open; they are not a supported styling hook.
148
148
  - **The camera indicator = a leak detector.** If it stays on after you are done, a track was not stopped.
149
149
  - **User gesture.** Some browsers require `getUserMedia` to be triggered by a user action; firing it from a timer may silently fail (surfaced via `error`, never thrown).
150
150
  - **Errors are classified, never thrown:** `NotAllowedError` (denied), `NotFoundError` (no device), `NotReadableError` (in use by another app), `OverconstrainedError`.
151
+ - **`errorInfo` — additive failure taxonomy.** Alongside `error`, both `<wcs-camera>` and `<wcs-recorder>` expose an **additive** bindable output `errorInfo` (`WcsIoErrorInfo` = a stable `code` / `phase` / `recoverable` / `message`), derived from the same failure and published via `wcs-camera:error-info-changed` / `wcs-recorder:error-info-changed`. The `error` shape is unchanged; `errorInfo` transitions exactly when `error` does (cleared to `null` on success). Both elements share one code set (`WCS_MEDIA_ERROR_CODE`, defined in `core/mediaCapabilities.ts`):
152
+ - `capability-missing` (phase `probe`) — `getUserMedia` / `MediaRecorder` unavailable, including non-secure context.
153
+ - `not-allowed` (phase `start`) — `NotAllowedError` / `SecurityError` (permission denied or feature-policy block).
154
+ - `not-found` (phase `start`) — `NotFoundError` (no camera / mic of the requested kind).
155
+ - `not-readable` (phase `start`) — `NotReadableError` (device busy or hardware fault).
156
+ - `invalid-argument` (phase `start`) — `OverconstrainedError` / `NotSupportedError` (constraints or mimeType unsatisfiable).
157
+ - `invalid-state` (phase `start`) — `NoStreamError` (recorder started with no stream attached).
158
+ - `aborted` (phase `execute`, `recoverable: true`) — `AbortError` (interrupted mid-flight, may recover on retry).
159
+ - `media-error` (phase `execute`) — any other runtime failure (e.g. `RecorderError` / unexpected `MediaRecorder` error).
160
+
161
+ The `WcsIoErrorInfo` type and the `WCS_MEDIA_ERROR_CODE` constants are exported.
151
162
  - **Stream ownership stays with the camera.** A recorder borrows it; switching cameras while recording is not supported (stop recording first).
152
163
  - **mimeType support varies** (webm/mp4). Unsupported `mime-type` values are ignored and the browser default is used.
153
164
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,14 @@
1
+ /** operation error の phase(taxonomy)。 */
2
+ type WcsIoErrorPhase = "probe" | "start" | "execute" | "decode" | "commit" | "dispose";
3
+ /** serializable な error info(non-cloneable な cause とは分離。DevTools / remote へは info のみ)。 */
4
+ interface WcsIoErrorInfo {
5
+ readonly code: string;
6
+ readonly phase: WcsIoErrorPhase;
7
+ readonly recoverable: boolean;
8
+ readonly capabilityId?: string;
9
+ readonly message: string;
10
+ }
11
+
1
12
  interface IWcBindableProperty {
2
13
  readonly name: string;
3
14
  readonly event: string;
@@ -13,7 +24,8 @@ interface IWcBindableCommand {
13
24
  }
14
25
  interface IWcBindable {
15
26
  readonly protocol: "wc-bindable";
16
- readonly version: 1;
27
+ /** Integer protocol version. All versions >= 1 are core-compatible. */
28
+ readonly version: number;
17
29
  readonly properties: readonly IWcBindableProperty[];
18
30
  readonly inputs?: readonly IWcBindableInput[];
19
31
  readonly commands?: readonly IWcBindableCommand[];
@@ -89,6 +101,8 @@ interface WcsCameraCoreValues {
89
101
  deviceId: string | null;
90
102
  devices: MediaDeviceSnapshot[];
91
103
  error: WcsMediaErrorDetail | null;
104
+ /** Serializable Phase 6 failure taxonomy derived from `error`, or null. */
105
+ errorInfo: WcsIoErrorInfo | null;
92
106
  }
93
107
  type WcsCameraValues = WcsCameraCoreValues;
94
108
  interface WcsCameraInputs {
@@ -153,6 +167,8 @@ interface WcsRecorderCoreValues {
153
167
  blob: Blob | null;
154
168
  objectURL: string | null;
155
169
  error: WcsMediaErrorDetail | null;
170
+ /** Serializable Phase 6 failure taxonomy derived from `error`, or null. */
171
+ errorInfo: WcsIoErrorInfo | null;
156
172
  }
157
173
  type WcsRecorderValues = WcsRecorderCoreValues;
158
174
  interface WcsRecorderInputs {
@@ -204,6 +220,7 @@ declare class CameraCore extends EventTarget {
204
220
  private _deviceId;
205
221
  private _devices;
206
222
  private _error;
223
+ private _errorInfo;
207
224
  private _stream;
208
225
  private _desired;
209
226
  private _constraints;
@@ -219,6 +236,13 @@ declare class CameraCore extends EventTarget {
219
236
  get deviceId(): string | null;
220
237
  get devices(): MediaDeviceSnapshot[];
221
238
  get error(): WcsMediaErrorDetail | null;
239
+ /**
240
+ * The last failure's serializable `WcsIoErrorInfo` (stable `code` / `phase` /
241
+ * `recoverable`), or null. Additive wc-bindable property (event
242
+ * `wcs-camera:error-info-changed`), derived from `error`; the existing `error`
243
+ * property/event are unchanged.
244
+ */
245
+ get errorInfo(): WcsIoErrorInfo | null;
222
246
  get ready(): Promise<void>;
223
247
  private _setActive;
224
248
  private _setPermission;
@@ -226,6 +250,7 @@ declare class CameraCore extends EventTarget {
226
250
  private _setDeviceId;
227
251
  private _setDevices;
228
252
  private _setError;
253
+ private _commitErrorInfo;
229
254
  private _dispatch;
230
255
  private _devicesEqual;
231
256
  /**
@@ -325,6 +350,8 @@ declare class WcsCamera extends HTMLElement {
325
350
  get audioPermission(): MediaPermissionState | null;
326
351
  get devices(): MediaDeviceSnapshot[];
327
352
  get error(): WcsMediaErrorDetail | null;
353
+ /** The last failure's serializable `WcsIoErrorInfo` (Phase 6 taxonomy), or null. */
354
+ get errorInfo(): WcsIoErrorInfo | null;
328
355
  get connectedCallbackPromise(): Promise<void>;
329
356
  start(): void;
330
357
  stop(): void;
@@ -384,6 +411,7 @@ declare class RecorderCore extends EventTarget {
384
411
  private _blob;
385
412
  private _objectURL;
386
413
  private _error;
414
+ private _errorInfo;
387
415
  private _recorder;
388
416
  private _stream;
389
417
  private _chunks;
@@ -399,12 +427,20 @@ declare class RecorderCore extends EventTarget {
399
427
  get blob(): Blob | null;
400
428
  get objectURL(): string | null;
401
429
  get error(): WcsMediaErrorDetail | null;
430
+ /**
431
+ * The last failure's serializable `WcsIoErrorInfo` (stable `code` / `phase` /
432
+ * `recoverable`), or null. Additive wc-bindable property (event
433
+ * `wcs-recorder:error-info-changed`), derived from `error`; the existing `error`
434
+ * property/event are unchanged.
435
+ */
436
+ get errorInfo(): WcsIoErrorInfo | null;
402
437
  get ready(): Promise<void>;
403
438
  private _setRecording;
404
439
  private _setPaused;
405
440
  private _setDuration;
406
441
  private _setMimeType;
407
442
  private _setError;
443
+ private _commitErrorInfo;
408
444
  private _dispatch;
409
445
  /**
410
446
  * Borrow a stream for recording (the direct-channel sink). Synchronous, no
@@ -475,6 +511,8 @@ declare class WcsRecorder extends HTMLElement {
475
511
  get blob(): Blob | null;
476
512
  get objectURL(): string | null;
477
513
  get error(): WcsMediaErrorDetail | null;
514
+ /** The last failure's serializable `WcsIoErrorInfo` (Phase 6 taxonomy), or null. */
515
+ get errorInfo(): WcsIoErrorInfo | null;
478
516
  /** Borrow a stream (the direct-channel sink). */
479
517
  attachStream(stream: MediaStream): void;
480
518
  start(): void;
@@ -487,5 +525,39 @@ declare class WcsRecorder extends HTMLElement {
487
525
  disconnectedCallback(): void;
488
526
  }
489
527
 
490
- export { CameraCore, RecorderCore, WcsCamera, WcsRecorder, bootstrapCamera, getConfig };
491
- export type { CameraConstraints, FacingMode, IWritableConfig, IWritableTagNames, MediaDeviceSnapshot, MediaPermissionState, RecorderOptions, WcsCameraCommands, WcsCameraCoreCommands, WcsCameraCoreValues, WcsCameraInputs, WcsCameraValues, WcsMediaErrorDetail, WcsRecordedDetail, WcsRecorderCommands, WcsRecorderCoreCommands, WcsRecorderCoreValues, WcsRecorderInputs, WcsRecorderValues };
528
+ /**
529
+ * mediaCapabilities.ts
530
+ *
531
+ * camera / recorder node 固有の error code(taxonomy)と derivation。汎用の error info
532
+ * 型は `./platformCapability.js`(/io-core/ から copy-distribution される生成ファイル)から
533
+ * import する。
534
+ *
535
+ * この 1 ファイルを CameraCore(getUserMedia)と RecorderCore(MediaRecorder)の両方が
536
+ * import する。両 Core は同一の error detail 型(`WcsMediaErrorDetail = { name, message }`、
537
+ * `.name` が DOMException 名 / "unsupported" sentinel / 各 Core 固有の合成名)を共有する
538
+ * ため、derivation も 1 本で両者を賄える。lane は持たず(getUserMedia は acquire を
539
+ * `_gen` で switchMap 済み、録画は command-driven)、error taxonomy(errorInfo)のみを採用する。
540
+ */
541
+
542
+ /** 安定した media(camera / recorder)error code(taxonomy)。値は公開キーとして固定。 */
543
+ declare const WCS_MEDIA_ERROR_CODE: {
544
+ /** getUserMedia / MediaRecorder API 不在(非セキュアコンテキスト含む) — "unsupported" sentinel。 */
545
+ readonly CapabilityMissing: "capability-missing";
546
+ /** `NotAllowedError` / `SecurityError` — 権限拒否・feature-policy ブロック。 */
547
+ readonly NotAllowed: "not-allowed";
548
+ /** `NotFoundError` — 要求した種類のデバイス(カメラ/マイク)が存在しない。 */
549
+ readonly NotFound: "not-found";
550
+ /** `NotReadableError` — デバイスがハードウェア障害/他アプリ占有で読めない。 */
551
+ readonly NotReadable: "not-readable";
552
+ /** `OverconstrainedError` / `NotSupportedError` — 制約・構成(mimeType 等)が満たせない。 */
553
+ readonly InvalidArgument: "invalid-argument";
554
+ /** `NoStreamError` — stream 未 attach で録画開始(前提状態の不備)。 */
555
+ readonly InvalidState: "invalid-state";
556
+ /** `AbortError` — 実行途中の中断(retry で回復しうる)。 */
557
+ readonly Aborted: "aborted";
558
+ /** その他の実行時失敗(`RecorderError` / 想定外の MediaRecorder エラー等)。 */
559
+ readonly MediaError: "media-error";
560
+ };
561
+
562
+ export { CameraCore, RecorderCore, WCS_MEDIA_ERROR_CODE, WcsCamera, WcsRecorder, bootstrapCamera, getConfig };
563
+ export type { CameraConstraints, FacingMode, IWritableConfig, IWritableTagNames, MediaDeviceSnapshot, MediaPermissionState, RecorderOptions, WcsCameraCommands, WcsCameraCoreCommands, WcsCameraCoreValues, WcsCameraInputs, WcsCameraValues, WcsIoErrorInfo, WcsIoErrorPhase, WcsMediaErrorDetail, WcsRecordedDetail, WcsRecorderCommands, WcsRecorderCoreCommands, WcsRecorderCoreValues, WcsRecorderInputs, WcsRecorderValues };
package/dist/index.esm.js CHANGED
@@ -198,6 +198,83 @@ class MediaPermissionWatcher {
198
198
  };
199
199
  }
200
200
 
201
+ /**
202
+ * mediaCapabilities.ts
203
+ *
204
+ * camera / recorder node 固有の error code(taxonomy)と derivation。汎用の error info
205
+ * 型は `./platformCapability.js`(/io-core/ から copy-distribution される生成ファイル)から
206
+ * import する。
207
+ *
208
+ * この 1 ファイルを CameraCore(getUserMedia)と RecorderCore(MediaRecorder)の両方が
209
+ * import する。両 Core は同一の error detail 型(`WcsMediaErrorDetail = { name, message }`、
210
+ * `.name` が DOMException 名 / "unsupported" sentinel / 各 Core 固有の合成名)を共有する
211
+ * ため、derivation も 1 本で両者を賄える。lane は持たず(getUserMedia は acquire を
212
+ * `_gen` で switchMap 済み、録画は command-driven)、error taxonomy(errorInfo)のみを採用する。
213
+ */
214
+ /** 安定した media(camera / recorder)error code(taxonomy)。値は公開キーとして固定。 */
215
+ const WCS_MEDIA_ERROR_CODE = {
216
+ /** getUserMedia / MediaRecorder API 不在(非セキュアコンテキスト含む) — "unsupported" sentinel。 */
217
+ CapabilityMissing: "capability-missing",
218
+ /** `NotAllowedError` / `SecurityError` — 権限拒否・feature-policy ブロック。 */
219
+ NotAllowed: "not-allowed",
220
+ /** `NotFoundError` — 要求した種類のデバイス(カメラ/マイク)が存在しない。 */
221
+ NotFound: "not-found",
222
+ /** `NotReadableError` — デバイスがハードウェア障害/他アプリ占有で読めない。 */
223
+ NotReadable: "not-readable",
224
+ /** `OverconstrainedError` / `NotSupportedError` — 制約・構成(mimeType 等)が満たせない。 */
225
+ InvalidArgument: "invalid-argument",
226
+ /** `NoStreamError` — stream 未 attach で録画開始(前提状態の不備)。 */
227
+ InvalidState: "invalid-state",
228
+ /** `AbortError` — 実行途中の中断(retry で回復しうる)。 */
229
+ Aborted: "aborted",
230
+ /** その他の実行時失敗(`RecorderError` / 想定外の MediaRecorder エラー等)。 */
231
+ MediaError: "media-error",
232
+ };
233
+ /**
234
+ * 正規化済み media error(`WcsMediaErrorDetail = { name, message }`)を serializable な
235
+ * error taxonomy に写す。`name` は DOMException 名 / "unsupported" sentinel / Core 固有の
236
+ * 合成名(`NoStreamError` / `RecorderError`)。公開 `error` shape は不変で、これはその
237
+ * 付加的な分類。
238
+ *
239
+ * - "unsupported" は利用直前の能力欠如 → phase="probe" / capability-missing。
240
+ * - `NotAllowedError` / `SecurityError` は取得開始時の権限拒否 → phase="start" /
241
+ * not-allowed。retry で回復しない。
242
+ * - `NotFoundError` は要求デバイス不在 → phase="start" / not-found。
243
+ * - `NotReadableError` はデバイス占有/ハードウェア障害 → phase="start" / not-readable。
244
+ * - `OverconstrainedError` / `NotSupportedError` は制約・構成が満たせない
245
+ * → phase="start" / invalid-argument。
246
+ * - `NoStreamError`(stream 未 attach で録画開始)は前提状態の不備 → phase="start" /
247
+ * invalid-state。
248
+ * - `AbortError` は実行途中の中断 → phase="execute" / aborted(recoverable=true)。
249
+ * - それ以外(`RecorderError` / runtime MediaRecorder エラー / "Error" fallback 等)は
250
+ * phase="execute" / media-error。
251
+ */
252
+ function deriveMediaErrorInfo(error) {
253
+ const { name, message } = error;
254
+ if (name === "unsupported") {
255
+ return { code: WCS_MEDIA_ERROR_CODE.CapabilityMissing, phase: "probe", recoverable: false, message };
256
+ }
257
+ if (name === "NotAllowedError" || name === "SecurityError") {
258
+ return { code: WCS_MEDIA_ERROR_CODE.NotAllowed, phase: "start", recoverable: false, message };
259
+ }
260
+ if (name === "NotFoundError") {
261
+ return { code: WCS_MEDIA_ERROR_CODE.NotFound, phase: "start", recoverable: false, message };
262
+ }
263
+ if (name === "NotReadableError") {
264
+ return { code: WCS_MEDIA_ERROR_CODE.NotReadable, phase: "start", recoverable: false, message };
265
+ }
266
+ if (name === "OverconstrainedError" || name === "NotSupportedError") {
267
+ return { code: WCS_MEDIA_ERROR_CODE.InvalidArgument, phase: "start", recoverable: false, message };
268
+ }
269
+ if (name === "NoStreamError") {
270
+ return { code: WCS_MEDIA_ERROR_CODE.InvalidState, phase: "start", recoverable: false, message };
271
+ }
272
+ if (name === "AbortError") {
273
+ return { code: WCS_MEDIA_ERROR_CODE.Aborted, phase: "execute", recoverable: true, message };
274
+ }
275
+ return { code: WCS_MEDIA_ERROR_CODE.MediaError, phase: "execute", recoverable: false, message };
276
+ }
277
+
201
278
  /**
202
279
  * Headless camera-capture primitive. Wraps getUserMedia + the Permissions API and
203
280
  * exposes a `MediaStream` through the wc-bindable protocol — but the live stream is
@@ -223,6 +300,12 @@ class CameraCore extends EventTarget {
223
300
  { name: "deviceId", event: "wcs-camera:device-changed" },
224
301
  { name: "devices", event: "wcs-camera:devices-changed" },
225
302
  { name: "error", event: "wcs-camera:error" },
303
+ // Serializable failure taxonomy (stable code / phase / recoverable), or null.
304
+ // Additive bindable output derived from `error` (the DOMException name /
305
+ // "unsupported" sentinel); the existing `error` property/event are unchanged.
306
+ // Fires wcs-camera:error-info-changed. No lane — acquisition is switchMap'd by
307
+ // `_gen`, so there is no per-node operation policy to attach here.
308
+ { name: "errorInfo", event: "wcs-camera:error-info-changed" },
226
309
  // Direct-channel handle: event-token only — never bound as a reactive value.
227
310
  { name: "streamReady", event: "wcs-camera:stream-ready", getter: (e) => e.detail },
228
311
  // event-token: a bare signal (detail is always null) — surface detail, not the raw Event.
@@ -241,6 +324,7 @@ class CameraCore extends EventTarget {
241
324
  _deviceId = null;
242
325
  _devices = [];
243
326
  _error = null;
327
+ _errorInfo = null;
244
328
  // The live stream — internal only, never a reactive value (see class docs).
245
329
  _stream = null;
246
330
  // desired/actual split (wakelock-style): `_desired` is whether the user wants the
@@ -268,6 +352,13 @@ class CameraCore extends EventTarget {
268
352
  get deviceId() { return this._deviceId; }
269
353
  get devices() { return this._devices; }
270
354
  get error() { return this._error; }
355
+ /**
356
+ * The last failure's serializable `WcsIoErrorInfo` (stable `code` / `phase` /
357
+ * `recoverable`), or null. Additive wc-bindable property (event
358
+ * `wcs-camera:error-info-changed`), derived from `error`; the existing `error`
359
+ * property/event are unchanged.
360
+ */
361
+ get errorInfo() { return this._errorInfo; }
271
362
  get ready() { return this._ready; }
272
363
  // --- State setters with event dispatch (same-value guarded) ---
273
364
  _setActive(active) {
@@ -310,8 +401,18 @@ class CameraCore extends EventTarget {
310
401
  if (error === null && this._error === null)
311
402
  return;
312
403
  this._error = error;
404
+ // Keep the additive `errorInfo` taxonomy in sync with `error`: derive from the
405
+ // error detail (or null on clear). Fires before the `error` event so an observer
406
+ // binding both sees the classification first, mirroring the io-node family.
407
+ this._commitErrorInfo(error === null ? null : deriveMediaErrorInfo(error));
313
408
  this._dispatch("wcs-camera:error", error);
314
409
  }
410
+ // Called only from _setError (which already collapses the null→null transition), so
411
+ // errorInfo transitions exactly when error does — no separate guard needed here.
412
+ _commitErrorInfo(info) {
413
+ this._errorInfo = info;
414
+ this._dispatch("wcs-camera:error-info-changed", info);
415
+ }
315
416
  _dispatch(type, detail) {
316
417
  this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));
317
418
  }
@@ -684,6 +785,8 @@ class WcsCamera extends HTMLElement {
684
785
  get audioPermission() { return this._core.audioPermission; }
685
786
  get devices() { return this._core.devices; }
686
787
  get error() { return this._core.error; }
788
+ /** The last failure's serializable `WcsIoErrorInfo` (Phase 6 taxonomy), or null. */
789
+ get errorInfo() { return this._core.errorInfo; }
687
790
  get connectedCallbackPromise() { return this._connectedCallbackPromise; }
688
791
  // --- Commands ---
689
792
  start() { this._core.start(); }
@@ -832,6 +935,12 @@ class RecorderCore extends EventTarget {
832
935
  { name: "blob", event: "wcs-recorder:recorded", getter: (e) => e.detail?.blob ?? null },
833
936
  { name: "objectURL", event: "wcs-recorder:recorded", getter: (e) => e.detail?.objectURL ?? null },
834
937
  { name: "error", event: "wcs-recorder:error" },
938
+ // Serializable failure taxonomy (stable code / phase / recoverable), or null.
939
+ // Additive bindable output derived from `error` (the DOMException name /
940
+ // "unsupported"/"NoStreamError"/"RecorderError" sentinel); the existing `error`
941
+ // property/event are unchanged. Fires wcs-recorder:error-info-changed. No lane —
942
+ // recording is command-driven.
943
+ { name: "errorInfo", event: "wcs-recorder:error-info-changed" },
835
944
  // event-token: detail = the assembled clip { blob, objectURL, mimeType, duration }.
836
945
  { name: "recorded", event: "wcs-recorder:recorded", getter: (e) => e.detail },
837
946
  // event-token (timeslice mode): detail = the streamed Blob chunk.
@@ -853,6 +962,7 @@ class RecorderCore extends EventTarget {
853
962
  _blob = null;
854
963
  _objectURL = null;
855
964
  _error = null;
965
+ _errorInfo = null;
856
966
  _recorder = null;
857
967
  _stream = null; // borrowed — never stopped here
858
968
  _chunks = [];
@@ -878,6 +988,13 @@ class RecorderCore extends EventTarget {
878
988
  get blob() { return this._blob; }
879
989
  get objectURL() { return this._objectURL; }
880
990
  get error() { return this._error; }
991
+ /**
992
+ * The last failure's serializable `WcsIoErrorInfo` (stable `code` / `phase` /
993
+ * `recoverable`), or null. Additive wc-bindable property (event
994
+ * `wcs-recorder:error-info-changed`), derived from `error`; the existing `error`
995
+ * property/event are unchanged.
996
+ */
997
+ get errorInfo() { return this._errorInfo; }
881
998
  get ready() { return this._ready; }
882
999
  // --- State setters ---
883
1000
  _setRecording(v) {
@@ -913,8 +1030,18 @@ class RecorderCore extends EventTarget {
913
1030
  if (error === null && this._error === null)
914
1031
  return;
915
1032
  this._error = error;
1033
+ // Keep the additive `errorInfo` taxonomy in sync with `error`: derive from the
1034
+ // error detail (or null on clear). Fires before the `error` event so an observer
1035
+ // binding both sees the classification first, mirroring the io-node family.
1036
+ this._commitErrorInfo(error === null ? null : deriveMediaErrorInfo(error));
916
1037
  this._dispatch("wcs-recorder:error", error);
917
1038
  }
1039
+ // Called only from _setError (which already collapses the null→null transition), so
1040
+ // errorInfo transitions exactly when error does — no separate guard needed here.
1041
+ _commitErrorInfo(info) {
1042
+ this._errorInfo = info;
1043
+ this._dispatch("wcs-recorder:error-info-changed", info);
1044
+ }
918
1045
  _dispatch(type, detail) {
919
1046
  this._target.dispatchEvent(new CustomEvent(type, { detail, bubbles: true }));
920
1047
  }
@@ -1220,6 +1347,8 @@ class WcsRecorder extends HTMLElement {
1220
1347
  get blob() { return this._core.blob; }
1221
1348
  get objectURL() { return this._core.objectURL; }
1222
1349
  get error() { return this._core.error; }
1350
+ /** The last failure's serializable `WcsIoErrorInfo` (Phase 6 taxonomy), or null. */
1351
+ get errorInfo() { return this._core.errorInfo; }
1223
1352
  // --- Commands ---
1224
1353
  /** Borrow a stream (the direct-channel sink). */
1225
1354
  attachStream(stream) {
@@ -1280,5 +1409,5 @@ function bootstrapCamera(userConfig) {
1280
1409
  registerComponents();
1281
1410
  }
1282
1411
 
1283
- export { CameraCore, RecorderCore, WcsCamera, WcsRecorder, bootstrapCamera, getConfig };
1412
+ export { CameraCore, RecorderCore, WCS_MEDIA_ERROR_CODE, WcsCamera, WcsRecorder, bootstrapCamera, getConfig };
1284
1413
  //# sourceMappingURL=index.esm.js.map