@independo/capacitor-voice-recorder 8.1.0-dev.1 → 8.1.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +40 -30
  2. package/android/build.gradle +44 -1
  3. package/android/src/main/java/app/independo/capacitorvoicerecorder/VoiceRecorder.java +146 -0
  4. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/PermissionChecker.java +8 -0
  5. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecordDataMapper.java +32 -0
  6. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderAdapter.java +39 -0
  7. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderPlatform.java +25 -0
  8. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/CurrentRecordingStatus.java +9 -0
  9. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ErrorCodes.java +19 -0
  10. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/Messages.java +2 -1
  11. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/RecordData.java +15 -1
  12. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/RecordOptions.java +4 -0
  13. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ResponseFormat.java +18 -0
  14. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/ResponseGenerator.java +7 -1
  15. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/platform}/CustomMediaRecorder.java +33 -2
  16. package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/DefaultRecorderPlatform.java +86 -0
  17. package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/NotSupportedOsVersion.java +4 -0
  18. package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderService.java +144 -0
  19. package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderServiceException.java +23 -0
  20. package/dist/esm/adapters/VoiceRecorderWebAdapter.d.ts +23 -0
  21. package/dist/esm/adapters/VoiceRecorderWebAdapter.js +41 -0
  22. package/dist/esm/adapters/VoiceRecorderWebAdapter.js.map +1 -0
  23. package/dist/esm/core/error-codes.d.ts +4 -0
  24. package/dist/esm/core/error-codes.js +21 -0
  25. package/dist/esm/core/error-codes.js.map +1 -0
  26. package/dist/esm/core/recording-contract.d.ts +3 -0
  27. package/dist/esm/core/recording-contract.js +15 -0
  28. package/dist/esm/core/recording-contract.js.map +1 -0
  29. package/dist/esm/core/response-format.d.ts +8 -0
  30. package/dist/esm/core/response-format.js +17 -0
  31. package/dist/esm/core/response-format.js.map +1 -0
  32. package/dist/esm/platform/web/VoiceRecorderImpl.d.ts +45 -0
  33. package/dist/esm/{VoiceRecorderImpl.js → platform/web/VoiceRecorderImpl.js} +20 -2
  34. package/dist/esm/platform/web/VoiceRecorderImpl.js.map +1 -0
  35. package/dist/esm/platform/web/get-blob-duration.js.map +1 -0
  36. package/dist/esm/{predefined-web-responses.d.ts → platform/web/predefined-web-responses.d.ts} +12 -1
  37. package/dist/esm/{predefined-web-responses.js → platform/web/predefined-web-responses.js} +11 -0
  38. package/dist/esm/platform/web/predefined-web-responses.js.map +1 -0
  39. package/dist/esm/service/VoiceRecorderService.d.ts +47 -0
  40. package/dist/esm/service/VoiceRecorderService.js +60 -0
  41. package/dist/esm/service/VoiceRecorderService.js.map +1 -0
  42. package/dist/esm/web.d.ts +12 -1
  43. package/dist/esm/web.js +26 -12
  44. package/dist/esm/web.js.map +1 -1
  45. package/dist/plugin.cjs.js +200 -9
  46. package/dist/plugin.cjs.js.map +1 -1
  47. package/dist/plugin.js +200 -9
  48. package/dist/plugin.js.map +1 -1
  49. package/ios/Sources/VoiceRecorder/Adapters/DefaultRecorderPlatform.swift +33 -0
  50. package/ios/Sources/VoiceRecorder/Adapters/RecordDataMapper.swift +38 -0
  51. package/ios/Sources/VoiceRecorder/Adapters/RecorderAdapter.swift +24 -0
  52. package/ios/Sources/VoiceRecorder/Adapters/RecorderPlatform.swift +11 -0
  53. package/ios/Sources/VoiceRecorder/Bridge/VoiceRecorder.swift +172 -0
  54. package/ios/Sources/VoiceRecorder/{CurrentRecordingStatus.swift → Core/CurrentRecordingStatus.swift} +1 -0
  55. package/ios/Sources/VoiceRecorder/Core/ErrorCodes.swift +16 -0
  56. package/ios/Sources/VoiceRecorder/{Messages.swift → Core/Messages.swift} +1 -0
  57. package/ios/Sources/VoiceRecorder/{RecordData.swift → Core/RecordData.swift} +6 -0
  58. package/ios/Sources/VoiceRecorder/Core/RecordOptions.swift +11 -0
  59. package/ios/Sources/VoiceRecorder/Core/ResponseFormat.swift +22 -0
  60. package/ios/Sources/VoiceRecorder/{ResponseGenerator.swift → Core/ResponseGenerator.swift} +6 -0
  61. package/ios/Sources/VoiceRecorder/{CustomMediaRecorder.swift → Platform/CustomMediaRecorder.swift} +25 -1
  62. package/ios/Sources/VoiceRecorder/Service/VoiceRecorderService.swift +128 -0
  63. package/ios/Sources/VoiceRecorder/Service/VoiceRecorderServiceError.swift +14 -0
  64. package/package.json +10 -4
  65. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/CurrentRecordingStatus.java +0 -8
  66. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/NotSupportedOsVersion.java +0 -3
  67. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/RecordOptions.java +0 -3
  68. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/VoiceRecorder.java +0 -205
  69. package/dist/esm/VoiceRecorderImpl.d.ts +0 -27
  70. package/dist/esm/VoiceRecorderImpl.js.map +0 -1
  71. package/dist/esm/helper/get-blob-duration.js.map +0 -1
  72. package/dist/esm/predefined-web-responses.js.map +0 -1
  73. package/ios/Sources/VoiceRecorder/RecordOptions.swift +0 -8
  74. package/ios/Sources/VoiceRecorder/VoiceRecorder.swift +0 -170
  75. /package/dist/esm/{helper → platform/web}/get-blob-duration.d.ts +0 -0
  76. /package/dist/esm/{helper → platform/web}/get-blob-duration.js +0 -0
package/dist/plugin.js CHANGED
@@ -37,18 +37,28 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
37
37
  return durationP;
38
38
  }
39
39
 
40
+ /** Success wrapper for boolean plugin responses. */
40
41
  const successResponse = () => ({ value: true });
42
+ /** Failure wrapper for boolean plugin responses. */
41
43
  const failureResponse = () => ({ value: false });
44
+ /** Error for missing microphone permission. */
42
45
  const missingPermissionError = () => new Error('MISSING_PERMISSION');
46
+ /** Error for attempting to start while already recording. */
43
47
  const alreadyRecordingError = () => new Error('ALREADY_RECORDING');
48
+ /** Error for devices that cannot record audio. */
44
49
  const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD');
50
+ /** Error for recorder start failures. */
45
51
  const failedToRecordError = () => new Error('FAILED_TO_RECORD');
52
+ /** Error for empty or zero-length recordings. */
46
53
  const emptyRecordingError = () => new Error('EMPTY_RECORDING');
54
+ /** Error for stopping without an active recording. */
47
55
  const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED');
56
+ /** Error for failures when fetching recording data. */
48
57
  const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING');
58
+ /** Error for browsers that do not support permission queries. */
49
59
  const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');
50
60
 
51
- // these mime types will be checked one by one in order until one of them is found to be supported by the current browser
61
+ /** Preferred MIME types to probe in order of fallback. */
52
62
  const POSSIBLE_MIME_TYPES = {
53
63
  'audio/aac': '.aac',
54
64
  'audio/webm;codecs=opus': '.ogg',
@@ -56,13 +66,19 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
56
66
  'audio/webm': '.ogg',
57
67
  'audio/ogg;codecs=opus': '.ogg',
58
68
  };
69
+ /** Creates a promise that never resolves. */
59
70
  const neverResolvingPromise = () => new Promise(() => undefined);
71
+ /** Browser implementation backed by MediaRecorder and Capacitor Filesystem. */
60
72
  class VoiceRecorderImpl {
61
73
  constructor() {
74
+ /** Active MediaRecorder instance, if recording. */
62
75
  this.mediaRecorder = null;
76
+ /** Collected data chunks from MediaRecorder. */
63
77
  this.chunks = [];
78
+ /** Promise resolved when the recorder stops and payload is ready. */
64
79
  this.pendingResult = neverResolvingPromise();
65
80
  }
81
+ /** Returns whether the browser can start a recording session. */
66
82
  static async canDeviceVoiceRecord() {
67
83
  var _a;
68
84
  if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) {
@@ -72,6 +88,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
72
88
  return successResponse();
73
89
  }
74
90
  }
91
+ /** Starts a recording session using MediaRecorder. */
75
92
  async startRecording(options) {
76
93
  if (this.mediaRecorder != null) {
77
94
  throw alreadyRecordingError();
@@ -89,6 +106,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
89
106
  .then((stream) => this.onSuccessfullyStartedRecording(stream, options))
90
107
  .catch(this.onFailedToStartRecording.bind(this));
91
108
  }
109
+ /** Stops the current recording and resolves the pending payload. */
92
110
  async stopRecording() {
93
111
  if (this.mediaRecorder == null) {
94
112
  throw recordingHasNotStartedError();
@@ -105,6 +123,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
105
123
  this.prepareInstanceForNextOperation();
106
124
  }
107
125
  }
126
+ /** Returns whether the browser has microphone permission. */
108
127
  static async hasAudioRecordingPermission() {
109
128
  // Safari does not support navigator.permissions.query
110
129
  if (!navigator.permissions.query) {
@@ -124,6 +143,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
124
143
  throw couldNotQueryPermissionStatusError();
125
144
  });
126
145
  }
146
+ /** Requests microphone permission from the browser. */
127
147
  static async requestAudioRecordingPermission() {
128
148
  const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());
129
149
  if (havingPermission.value) {
@@ -134,6 +154,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
134
154
  .then(() => successResponse())
135
155
  .catch(() => failureResponse());
136
156
  }
157
+ /** Pauses the recording session when supported. */
137
158
  pauseRecording() {
138
159
  if (this.mediaRecorder == null) {
139
160
  throw recordingHasNotStartedError();
@@ -146,6 +167,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
146
167
  return Promise.resolve(failureResponse());
147
168
  }
148
169
  }
170
+ /** Resumes a paused recording session when supported. */
149
171
  resumeRecording() {
150
172
  if (this.mediaRecorder == null) {
151
173
  throw recordingHasNotStartedError();
@@ -158,6 +180,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
158
180
  return Promise.resolve(failureResponse());
159
181
  }
160
182
  }
183
+ /** Returns the current recording status from MediaRecorder. */
161
184
  getCurrentStatus() {
162
185
  if (this.mediaRecorder == null) {
163
186
  return Promise.resolve({ status: 'NONE' });
@@ -172,12 +195,14 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
172
195
  return Promise.resolve({ status: 'NONE' });
173
196
  }
174
197
  }
198
+ /** Returns the first supported MIME type, if any. */
175
199
  static getSupportedMimeType() {
176
200
  if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null)
177
201
  return null;
178
202
  const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type));
179
203
  return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null;
180
204
  }
205
+ /** Initializes MediaRecorder and wires up handlers. */
181
206
  onSuccessfullyStartedRecording(stream, options) {
182
207
  this.pendingResult = new Promise((resolve, reject) => {
183
208
  this.mediaRecorder = new MediaRecorder(stream);
@@ -225,10 +250,12 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
225
250
  });
226
251
  return successResponse();
227
252
  }
253
+ /** Handles failures from getUserMedia. */
228
254
  onFailedToStartRecording() {
229
255
  this.prepareInstanceForNextOperation();
230
256
  throw failedToRecordError();
231
257
  }
258
+ /** Converts a Blob payload into a base64 string. */
232
259
  static blobToBase64(blob) {
233
260
  return new Promise((resolve) => {
234
261
  const reader = new FileReader();
@@ -241,6 +268,7 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
241
268
  reader.readAsDataURL(blob);
242
269
  });
243
270
  }
271
+ /** Resets state for the next recording attempt. */
244
272
  prepareInstanceForNextOperation() {
245
273
  if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {
246
274
  try {
@@ -256,34 +284,197 @@ var capacitorVoiceRecorder = (function (exports, core, filesystem, write_blob) {
256
284
  }
257
285
  }
258
286
 
259
- class VoiceRecorderWeb extends core.WebPlugin {
287
+ /** Web adapter that delegates to the browser-specific implementation. */
288
+ class VoiceRecorderWebAdapter {
260
289
  constructor() {
261
- super(...arguments);
262
- this.voiceRecorderInstance = new VoiceRecorderImpl();
290
+ /** Browser implementation that talks to MediaRecorder APIs. */
291
+ this.voiceRecorderImpl = new VoiceRecorderImpl();
263
292
  }
293
+ /** Checks whether the browser can record audio. */
264
294
  canDeviceVoiceRecord() {
265
295
  return VoiceRecorderImpl.canDeviceVoiceRecord();
266
296
  }
297
+ /** Returns whether the browser has microphone permission. */
267
298
  hasAudioRecordingPermission() {
268
299
  return VoiceRecorderImpl.hasAudioRecordingPermission();
269
300
  }
301
+ /** Requests microphone permission through the browser. */
270
302
  requestAudioRecordingPermission() {
271
303
  return VoiceRecorderImpl.requestAudioRecordingPermission();
272
304
  }
305
+ /** Starts a recording session using MediaRecorder. */
306
+ startRecording(options) {
307
+ return this.voiceRecorderImpl.startRecording(options);
308
+ }
309
+ /** Stops the recording session and returns the payload. */
310
+ stopRecording() {
311
+ return this.voiceRecorderImpl.stopRecording();
312
+ }
313
+ /** Pauses the recording session when supported. */
314
+ pauseRecording() {
315
+ return this.voiceRecorderImpl.pauseRecording();
316
+ }
317
+ /** Resumes a paused recording session when supported. */
318
+ resumeRecording() {
319
+ return this.voiceRecorderImpl.resumeRecording();
320
+ }
321
+ /** Returns the current recording state. */
322
+ getCurrentStatus() {
323
+ return this.voiceRecorderImpl.getCurrentStatus();
324
+ }
325
+ }
326
+
327
+ /** Default response shape when no config is provided. */
328
+ const DEFAULT_RESPONSE_FORMAT = 'legacy';
329
+ /** Parses a user-provided response format into a supported value. */
330
+ const resolveResponseFormat = (value) => {
331
+ if (typeof value === 'string' && value.toLowerCase() === 'normalized') {
332
+ return 'normalized';
333
+ }
334
+ return DEFAULT_RESPONSE_FORMAT;
335
+ };
336
+ /** Reads the response format from a Capacitor plugin config object. */
337
+ const getResponseFormatFromConfig = (config) => {
338
+ if (config && typeof config === 'object' && 'responseFormat' in config) {
339
+ return resolveResponseFormat(config.responseFormat);
340
+ }
341
+ return DEFAULT_RESPONSE_FORMAT;
342
+ };
343
+
344
+ /** Maps legacy error messages to canonical error codes. */
345
+ const legacyToCanonical = {
346
+ CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',
347
+ };
348
+ /** Normalizes legacy error messages into canonical error codes. */
349
+ const toCanonicalErrorCode = (legacyMessage) => {
350
+ var _a;
351
+ return (_a = legacyToCanonical[legacyMessage]) !== null && _a !== void 0 ? _a : legacyMessage;
352
+ };
353
+ /** Adds a canonical `code` field to Error-like objects when possible. */
354
+ const attachCanonicalErrorCode = (error) => {
355
+ if (!error || typeof error !== 'object') {
356
+ return;
357
+ }
358
+ const messageValue = error.message;
359
+ if (typeof messageValue !== 'string') {
360
+ return;
361
+ }
362
+ error.code = toCanonicalErrorCode(messageValue);
363
+ };
364
+
365
+ /** Normalizes recording payloads into a stable contract shape. */
366
+ const normalizeRecordingData = (data) => {
367
+ const { recordDataBase64, uri, msDuration, mimeType } = data.value;
368
+ const normalizedValue = { msDuration, mimeType };
369
+ const trimmedUri = typeof uri === 'string' && uri.length > 0 ? uri : undefined;
370
+ const trimmedBase64 = typeof recordDataBase64 === 'string' && recordDataBase64.length > 0 ? recordDataBase64 : undefined;
371
+ if (trimmedUri) {
372
+ normalizedValue.uri = trimmedUri;
373
+ }
374
+ else if (trimmedBase64) {
375
+ normalizedValue.recordDataBase64 = trimmedBase64;
376
+ }
377
+ return { value: normalizedValue };
378
+ };
379
+
380
+ /** Orchestrates platform calls and normalizes responses when requested. */
381
+ class VoiceRecorderService {
382
+ constructor(platform, responseFormat) {
383
+ this.platform = platform;
384
+ this.responseFormat = responseFormat;
385
+ }
386
+ /** Checks whether the device can record audio. */
387
+ canDeviceVoiceRecord() {
388
+ return this.execute(() => this.platform.canDeviceVoiceRecord());
389
+ }
390
+ /** Returns whether microphone permission is currently granted. */
391
+ hasAudioRecordingPermission() {
392
+ return this.execute(() => this.platform.hasAudioRecordingPermission());
393
+ }
394
+ /** Requests microphone permission from the user. */
395
+ requestAudioRecordingPermission() {
396
+ return this.execute(() => this.platform.requestAudioRecordingPermission());
397
+ }
398
+ /** Starts a recording session. */
399
+ startRecording(options) {
400
+ return this.execute(() => this.platform.startRecording(options));
401
+ }
402
+ /** Stops the recording session and formats the payload if needed. */
403
+ async stopRecording() {
404
+ return this.execute(async () => {
405
+ const data = await this.platform.stopRecording();
406
+ if (this.responseFormat === 'normalized') {
407
+ return normalizeRecordingData(data);
408
+ }
409
+ return data;
410
+ });
411
+ }
412
+ /** Pauses the recording session when supported. */
413
+ pauseRecording() {
414
+ return this.execute(() => this.platform.pauseRecording());
415
+ }
416
+ /** Resumes a paused recording session when supported. */
417
+ resumeRecording() {
418
+ return this.execute(() => this.platform.resumeRecording());
419
+ }
420
+ /** Returns the current recording state. */
421
+ getCurrentStatus() {
422
+ return this.execute(() => this.platform.getCurrentStatus());
423
+ }
424
+ /** Wraps calls to apply canonical error codes when requested. */
425
+ async execute(fn) {
426
+ try {
427
+ return await fn();
428
+ }
429
+ catch (error) {
430
+ if (this.responseFormat === 'normalized') {
431
+ attachCanonicalErrorCode(error);
432
+ }
433
+ throw error;
434
+ }
435
+ }
436
+ }
437
+
438
+ /** Web implementation of the VoiceRecorder Capacitor plugin. */
439
+ class VoiceRecorderWeb extends core.WebPlugin {
440
+ constructor() {
441
+ var _a, _b;
442
+ super();
443
+ const pluginConfig = (_b = (_a = core.Capacitor === null || core.Capacitor === void 0 ? void 0 : core.Capacitor.config) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.VoiceRecorder;
444
+ const responseFormat = getResponseFormatFromConfig(pluginConfig);
445
+ this.service = new VoiceRecorderService(new VoiceRecorderWebAdapter(), responseFormat);
446
+ }
447
+ /** Checks whether the browser can record audio. */
448
+ canDeviceVoiceRecord() {
449
+ return this.service.canDeviceVoiceRecord();
450
+ }
451
+ /** Returns whether microphone permission is currently granted. */
452
+ hasAudioRecordingPermission() {
453
+ return this.service.hasAudioRecordingPermission();
454
+ }
455
+ /** Requests microphone permission from the user. */
456
+ requestAudioRecordingPermission() {
457
+ return this.service.requestAudioRecordingPermission();
458
+ }
459
+ /** Starts a recording session. */
273
460
  startRecording(options) {
274
- return this.voiceRecorderInstance.startRecording(options);
461
+ return this.service.startRecording(options);
275
462
  }
463
+ /** Stops the current recording session and returns the payload. */
276
464
  stopRecording() {
277
- return this.voiceRecorderInstance.stopRecording();
465
+ return this.service.stopRecording();
278
466
  }
467
+ /** Pauses the recording session when supported. */
279
468
  pauseRecording() {
280
- return this.voiceRecorderInstance.pauseRecording();
469
+ return this.service.pauseRecording();
281
470
  }
471
+ /** Resumes a paused recording session when supported. */
282
472
  resumeRecording() {
283
- return this.voiceRecorderInstance.resumeRecording();
473
+ return this.service.resumeRecording();
284
474
  }
475
+ /** Returns the current recording state. */
285
476
  getCurrentStatus() {
286
- return this.voiceRecorderInstance.getCurrentStatus();
477
+ return this.service.getCurrentStatus();
287
478
  }
288
479
  }
289
480
 
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/helper/get-blob-duration.js","esm/predefined-web-responses.js","esm/VoiceRecorderImpl.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst VoiceRecorder = registerPlugin('VoiceRecorder', {\n web: () => import('./web').then((m) => new m.VoiceRecorderWeb()),\n});\nexport * from './definitions';\nexport { VoiceRecorder };\n//# sourceMappingURL=index.js.map","/**\n * @param {Blob | string} blob\n * @returns {Promise<number>} Blob duration in seconds.\n */\nexport default function getBlobDuration(blob) {\n const tempVideoEl = document.createElement('video');\n if (!tempVideoEl)\n throw new Error('Failed to create video element');\n const durationP = new Promise((resolve, reject) => {\n tempVideoEl.addEventListener('loadedmetadata', () => {\n // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=642012\n if (tempVideoEl.duration === Infinity) {\n tempVideoEl.currentTime = Number.MAX_SAFE_INTEGER;\n tempVideoEl.ontimeupdate = () => {\n tempVideoEl.ontimeupdate = null;\n resolve(tempVideoEl.duration);\n tempVideoEl.currentTime = 0;\n };\n }\n else {\n resolve(tempVideoEl.duration);\n }\n });\n tempVideoEl.onerror = (event) => {\n const error = event.error || new Error('Unknown error occurred');\n reject(error);\n };\n });\n tempVideoEl.src = typeof blob === 'string' ? blob : URL.createObjectURL(blob);\n return durationP;\n}\n//# sourceMappingURL=get-blob-duration.js.map","export const successResponse = () => ({ value: true });\nexport const failureResponse = () => ({ value: false });\nexport const missingPermissionError = () => new Error('MISSING_PERMISSION');\nexport const alreadyRecordingError = () => new Error('ALREADY_RECORDING');\nexport const microphoneBeingUsedError = () => new Error('MICROPHONE_BEING_USED');\nexport const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD');\nexport const failedToRecordError = () => new Error('FAILED_TO_RECORD');\nexport const emptyRecordingError = () => new Error('EMPTY_RECORDING');\nexport const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED');\nexport const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING');\nexport const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');\n//# sourceMappingURL=predefined-web-responses.js.map","import { Filesystem } from '@capacitor/filesystem';\nimport write_blob from 'capacitor-blob-writer';\nimport getBlobDuration from \"./helper/get-blob-duration\";\nimport { alreadyRecordingError, couldNotQueryPermissionStatusError, deviceCannotVoiceRecordError, emptyRecordingError, failedToFetchRecordingError, failedToRecordError, failureResponse, missingPermissionError, recordingHasNotStartedError, successResponse, } from './predefined-web-responses';\n// these mime types will be checked one by one in order until one of them is found to be supported by the current browser\nconst POSSIBLE_MIME_TYPES = {\n 'audio/aac': '.aac',\n 'audio/webm;codecs=opus': '.ogg',\n 'audio/mp4': '.mp3',\n 'audio/webm': '.ogg',\n 'audio/ogg;codecs=opus': '.ogg',\n};\nconst neverResolvingPromise = () => new Promise(() => undefined);\nexport class VoiceRecorderImpl {\n constructor() {\n this.mediaRecorder = null;\n this.chunks = [];\n this.pendingResult = neverResolvingPromise();\n }\n static async canDeviceVoiceRecord() {\n var _a;\n if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) {\n return failureResponse();\n }\n else {\n return successResponse();\n }\n }\n async startRecording(options) {\n if (this.mediaRecorder != null) {\n throw alreadyRecordingError();\n }\n const deviceCanRecord = await VoiceRecorderImpl.canDeviceVoiceRecord();\n if (!deviceCanRecord.value) {\n throw deviceCannotVoiceRecordError();\n }\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => successResponse());\n if (!havingPermission.value) {\n throw missingPermissionError();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then((stream) => this.onSuccessfullyStartedRecording(stream, options))\n .catch(this.onFailedToStartRecording.bind(this));\n }\n async stopRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n try {\n this.mediaRecorder.stop();\n this.mediaRecorder.stream.getTracks().forEach((track) => track.stop());\n return this.pendingResult;\n }\n catch (ignore) {\n throw failedToFetchRecordingError();\n }\n finally {\n this.prepareInstanceForNextOperation();\n }\n }\n static async hasAudioRecordingPermission() {\n // Safari does not support navigator.permissions.query\n if (!navigator.permissions.query) {\n if (navigator.mediaDevices !== undefined) {\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n }\n return navigator.permissions\n .query({ name: 'microphone' })\n .then((result) => ({ value: result.state === 'granted' }))\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n static async requestAudioRecordingPermission() {\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());\n if (havingPermission.value) {\n return successResponse();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => failureResponse());\n }\n pauseRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'recording') {\n this.mediaRecorder.pause();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n resumeRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'paused') {\n this.mediaRecorder.resume();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n getCurrentStatus() {\n if (this.mediaRecorder == null) {\n return Promise.resolve({ status: 'NONE' });\n }\n else if (this.mediaRecorder.state === 'recording') {\n return Promise.resolve({ status: 'RECORDING' });\n }\n else if (this.mediaRecorder.state === 'paused') {\n return Promise.resolve({ status: 'PAUSED' });\n }\n else {\n return Promise.resolve({ status: 'NONE' });\n }\n }\n static getSupportedMimeType() {\n if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null)\n return null;\n const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type));\n return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null;\n }\n onSuccessfullyStartedRecording(stream, options) {\n this.pendingResult = new Promise((resolve, reject) => {\n this.mediaRecorder = new MediaRecorder(stream);\n this.mediaRecorder.onerror = () => {\n this.prepareInstanceForNextOperation();\n reject(failedToRecordError());\n };\n this.mediaRecorder.onstop = async () => {\n var _a, _b, _c;\n const mimeType = VoiceRecorderImpl.getSupportedMimeType();\n if (mimeType == null) {\n this.prepareInstanceForNextOperation();\n reject(failedToFetchRecordingError());\n return;\n }\n const blobVoiceRecording = new Blob(this.chunks, { type: mimeType });\n if (blobVoiceRecording.size <= 0) {\n this.prepareInstanceForNextOperation();\n reject(emptyRecordingError());\n return;\n }\n let uri = undefined;\n let recordDataBase64 = '';\n if (options === null || options === void 0 ? void 0 : options.directory) {\n const subDirectory = (_c = (_b = (_a = options.subDirectory) === null || _a === void 0 ? void 0 : _a.match(/^\\/?(.+[^/])\\/?$/)) === null || _b === void 0 ? void 0 : _b[1]) !== null && _c !== void 0 ? _c : '';\n const path = `${subDirectory}/recording-${new Date().getTime()}${POSSIBLE_MIME_TYPES[mimeType]}`;\n await write_blob({\n blob: blobVoiceRecording,\n directory: options.directory,\n fast_mode: true,\n path,\n recursive: true,\n });\n ({ uri } = await Filesystem.getUri({ directory: options.directory, path }));\n }\n else {\n recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording);\n }\n const recordingDuration = await getBlobDuration(blobVoiceRecording);\n this.prepareInstanceForNextOperation();\n resolve({ value: { recordDataBase64, mimeType, msDuration: recordingDuration * 1000, uri } });\n };\n this.mediaRecorder.ondataavailable = (event) => this.chunks.push(event.data);\n this.mediaRecorder.start();\n });\n return successResponse();\n }\n onFailedToStartRecording() {\n this.prepareInstanceForNextOperation();\n throw failedToRecordError();\n }\n static blobToBase64(blob) {\n return new Promise((resolve) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const recordingResult = String(reader.result);\n const splitResult = recordingResult.split('base64,');\n const toResolve = splitResult.length > 1 ? splitResult[1] : recordingResult;\n resolve(toResolve.trim());\n };\n reader.readAsDataURL(blob);\n });\n }\n prepareInstanceForNextOperation() {\n if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {\n try {\n this.mediaRecorder.stop();\n }\n catch (ignore) {\n console.warn('Failed to stop recording during cleanup');\n }\n }\n this.pendingResult = neverResolvingPromise();\n this.mediaRecorder = null;\n this.chunks = [];\n }\n}\n//# sourceMappingURL=VoiceRecorderImpl.js.map","import { WebPlugin } from '@capacitor/core';\nimport { VoiceRecorderImpl } from './VoiceRecorderImpl';\nexport class VoiceRecorderWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.voiceRecorderInstance = new VoiceRecorderImpl();\n }\n canDeviceVoiceRecord() {\n return VoiceRecorderImpl.canDeviceVoiceRecord();\n }\n hasAudioRecordingPermission() {\n return VoiceRecorderImpl.hasAudioRecordingPermission();\n }\n requestAudioRecordingPermission() {\n return VoiceRecorderImpl.requestAudioRecordingPermission();\n }\n startRecording(options) {\n return this.voiceRecorderInstance.startRecording(options);\n }\n stopRecording() {\n return this.voiceRecorderInstance.stopRecording();\n }\n pauseRecording() {\n return this.voiceRecorderInstance.pauseRecording();\n }\n resumeRecording() {\n return this.voiceRecorderInstance.resumeRecording();\n }\n getCurrentStatus() {\n return this.voiceRecorderInstance.getCurrentStatus();\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","Filesystem","WebPlugin"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/platform/web/get-blob-duration.js","esm/platform/web/predefined-web-responses.js","esm/platform/web/VoiceRecorderImpl.js","esm/adapters/VoiceRecorderWebAdapter.js","esm/core/response-format.js","esm/core/error-codes.js","esm/core/recording-contract.js","esm/service/VoiceRecorderService.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst VoiceRecorder = registerPlugin('VoiceRecorder', {\n web: () => import('./web').then((m) => new m.VoiceRecorderWeb()),\n});\nexport * from './definitions';\nexport { VoiceRecorder };\n//# sourceMappingURL=index.js.map","/**\n * @param {Blob | string} blob\n * @returns {Promise<number>} Blob duration in seconds.\n */\nexport default function getBlobDuration(blob) {\n const tempVideoEl = document.createElement('video');\n if (!tempVideoEl)\n throw new Error('Failed to create video element');\n const durationP = new Promise((resolve, reject) => {\n tempVideoEl.addEventListener('loadedmetadata', () => {\n // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=642012\n if (tempVideoEl.duration === Infinity) {\n tempVideoEl.currentTime = Number.MAX_SAFE_INTEGER;\n tempVideoEl.ontimeupdate = () => {\n tempVideoEl.ontimeupdate = null;\n resolve(tempVideoEl.duration);\n tempVideoEl.currentTime = 0;\n };\n }\n else {\n resolve(tempVideoEl.duration);\n }\n });\n tempVideoEl.onerror = (event) => {\n const error = event.error || new Error('Unknown error occurred');\n reject(error);\n };\n });\n tempVideoEl.src = typeof blob === 'string' ? blob : URL.createObjectURL(blob);\n return durationP;\n}\n//# sourceMappingURL=get-blob-duration.js.map","/** Success wrapper for boolean plugin responses. */\nexport const successResponse = () => ({ value: true });\n/** Failure wrapper for boolean plugin responses. */\nexport const failureResponse = () => ({ value: false });\n/** Error for missing microphone permission. */\nexport const missingPermissionError = () => new Error('MISSING_PERMISSION');\n/** Error for attempting to start while already recording. */\nexport const alreadyRecordingError = () => new Error('ALREADY_RECORDING');\n/** Error for microphone in use by another app or recorder. */\nexport const microphoneBeingUsedError = () => new Error('MICROPHONE_BEING_USED');\n/** Error for devices that cannot record audio. */\nexport const deviceCannotVoiceRecordError = () => new Error('DEVICE_CANNOT_VOICE_RECORD');\n/** Error for recorder start failures. */\nexport const failedToRecordError = () => new Error('FAILED_TO_RECORD');\n/** Error for empty or zero-length recordings. */\nexport const emptyRecordingError = () => new Error('EMPTY_RECORDING');\n/** Error for stopping without an active recording. */\nexport const recordingHasNotStartedError = () => new Error('RECORDING_HAS_NOT_STARTED');\n/** Error for failures when fetching recording data. */\nexport const failedToFetchRecordingError = () => new Error('FAILED_TO_FETCH_RECORDING');\n/** Error for browsers that do not support permission queries. */\nexport const couldNotQueryPermissionStatusError = () => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');\n//# sourceMappingURL=predefined-web-responses.js.map","import { Filesystem } from '@capacitor/filesystem';\nimport write_blob from 'capacitor-blob-writer';\nimport getBlobDuration from './get-blob-duration';\nimport { alreadyRecordingError, couldNotQueryPermissionStatusError, deviceCannotVoiceRecordError, emptyRecordingError, failedToFetchRecordingError, failedToRecordError, failureResponse, missingPermissionError, recordingHasNotStartedError, successResponse, } from './predefined-web-responses';\n/** Preferred MIME types to probe in order of fallback. */\nconst POSSIBLE_MIME_TYPES = {\n 'audio/aac': '.aac',\n 'audio/webm;codecs=opus': '.ogg',\n 'audio/mp4': '.mp3',\n 'audio/webm': '.ogg',\n 'audio/ogg;codecs=opus': '.ogg',\n};\n/** Creates a promise that never resolves. */\nconst neverResolvingPromise = () => new Promise(() => undefined);\n/** Browser implementation backed by MediaRecorder and Capacitor Filesystem. */\nexport class VoiceRecorderImpl {\n constructor() {\n /** Active MediaRecorder instance, if recording. */\n this.mediaRecorder = null;\n /** Collected data chunks from MediaRecorder. */\n this.chunks = [];\n /** Promise resolved when the recorder stops and payload is ready. */\n this.pendingResult = neverResolvingPromise();\n }\n /** Returns whether the browser can start a recording session. */\n static async canDeviceVoiceRecord() {\n var _a;\n if (((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) == null || VoiceRecorderImpl.getSupportedMimeType() == null) {\n return failureResponse();\n }\n else {\n return successResponse();\n }\n }\n /** Starts a recording session using MediaRecorder. */\n async startRecording(options) {\n if (this.mediaRecorder != null) {\n throw alreadyRecordingError();\n }\n const deviceCanRecord = await VoiceRecorderImpl.canDeviceVoiceRecord();\n if (!deviceCanRecord.value) {\n throw deviceCannotVoiceRecordError();\n }\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => successResponse());\n if (!havingPermission.value) {\n throw missingPermissionError();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then((stream) => this.onSuccessfullyStartedRecording(stream, options))\n .catch(this.onFailedToStartRecording.bind(this));\n }\n /** Stops the current recording and resolves the pending payload. */\n async stopRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n try {\n this.mediaRecorder.stop();\n this.mediaRecorder.stream.getTracks().forEach((track) => track.stop());\n return this.pendingResult;\n }\n catch (ignore) {\n throw failedToFetchRecordingError();\n }\n finally {\n this.prepareInstanceForNextOperation();\n }\n }\n /** Returns whether the browser has microphone permission. */\n static async hasAudioRecordingPermission() {\n // Safari does not support navigator.permissions.query\n if (!navigator.permissions.query) {\n if (navigator.mediaDevices !== undefined) {\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n }\n return navigator.permissions\n .query({ name: 'microphone' })\n .then((result) => ({ value: result.state === 'granted' }))\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n /** Requests microphone permission from the browser. */\n static async requestAudioRecordingPermission() {\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());\n if (havingPermission.value) {\n return successResponse();\n }\n return navigator.mediaDevices\n .getUserMedia({ audio: true })\n .then(() => successResponse())\n .catch(() => failureResponse());\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'recording') {\n this.mediaRecorder.pause();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n else if (this.mediaRecorder.state === 'paused') {\n this.mediaRecorder.resume();\n return Promise.resolve(successResponse());\n }\n else {\n return Promise.resolve(failureResponse());\n }\n }\n /** Returns the current recording status from MediaRecorder. */\n getCurrentStatus() {\n if (this.mediaRecorder == null) {\n return Promise.resolve({ status: 'NONE' });\n }\n else if (this.mediaRecorder.state === 'recording') {\n return Promise.resolve({ status: 'RECORDING' });\n }\n else if (this.mediaRecorder.state === 'paused') {\n return Promise.resolve({ status: 'PAUSED' });\n }\n else {\n return Promise.resolve({ status: 'NONE' });\n }\n }\n /** Returns the first supported MIME type, if any. */\n static getSupportedMimeType() {\n if ((MediaRecorder === null || MediaRecorder === void 0 ? void 0 : MediaRecorder.isTypeSupported) == null)\n return null;\n const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type));\n return foundSupportedType !== null && foundSupportedType !== void 0 ? foundSupportedType : null;\n }\n /** Initializes MediaRecorder and wires up handlers. */\n onSuccessfullyStartedRecording(stream, options) {\n this.pendingResult = new Promise((resolve, reject) => {\n this.mediaRecorder = new MediaRecorder(stream);\n this.mediaRecorder.onerror = () => {\n this.prepareInstanceForNextOperation();\n reject(failedToRecordError());\n };\n this.mediaRecorder.onstop = async () => {\n var _a, _b, _c;\n const mimeType = VoiceRecorderImpl.getSupportedMimeType();\n if (mimeType == null) {\n this.prepareInstanceForNextOperation();\n reject(failedToFetchRecordingError());\n return;\n }\n const blobVoiceRecording = new Blob(this.chunks, { type: mimeType });\n if (blobVoiceRecording.size <= 0) {\n this.prepareInstanceForNextOperation();\n reject(emptyRecordingError());\n return;\n }\n let uri = undefined;\n let recordDataBase64 = '';\n if (options === null || options === void 0 ? void 0 : options.directory) {\n const subDirectory = (_c = (_b = (_a = options.subDirectory) === null || _a === void 0 ? void 0 : _a.match(/^\\/?(.+[^/])\\/?$/)) === null || _b === void 0 ? void 0 : _b[1]) !== null && _c !== void 0 ? _c : '';\n const path = `${subDirectory}/recording-${new Date().getTime()}${POSSIBLE_MIME_TYPES[mimeType]}`;\n await write_blob({\n blob: blobVoiceRecording,\n directory: options.directory,\n fast_mode: true,\n path,\n recursive: true,\n });\n ({ uri } = await Filesystem.getUri({ directory: options.directory, path }));\n }\n else {\n recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording);\n }\n const recordingDuration = await getBlobDuration(blobVoiceRecording);\n this.prepareInstanceForNextOperation();\n resolve({ value: { recordDataBase64, mimeType, msDuration: recordingDuration * 1000, uri } });\n };\n this.mediaRecorder.ondataavailable = (event) => this.chunks.push(event.data);\n this.mediaRecorder.start();\n });\n return successResponse();\n }\n /** Handles failures from getUserMedia. */\n onFailedToStartRecording() {\n this.prepareInstanceForNextOperation();\n throw failedToRecordError();\n }\n /** Converts a Blob payload into a base64 string. */\n static blobToBase64(blob) {\n return new Promise((resolve) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const recordingResult = String(reader.result);\n const splitResult = recordingResult.split('base64,');\n const toResolve = splitResult.length > 1 ? splitResult[1] : recordingResult;\n resolve(toResolve.trim());\n };\n reader.readAsDataURL(blob);\n });\n }\n /** Resets state for the next recording attempt. */\n prepareInstanceForNextOperation() {\n if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {\n try {\n this.mediaRecorder.stop();\n }\n catch (ignore) {\n console.warn('Failed to stop recording during cleanup');\n }\n }\n this.pendingResult = neverResolvingPromise();\n this.mediaRecorder = null;\n this.chunks = [];\n }\n}\n//# sourceMappingURL=VoiceRecorderImpl.js.map","import { VoiceRecorderImpl } from '../platform/web/VoiceRecorderImpl';\n/** Web adapter that delegates to the browser-specific implementation. */\nexport class VoiceRecorderWebAdapter {\n constructor() {\n /** Browser implementation that talks to MediaRecorder APIs. */\n this.voiceRecorderImpl = new VoiceRecorderImpl();\n }\n /** Checks whether the browser can record audio. */\n canDeviceVoiceRecord() {\n return VoiceRecorderImpl.canDeviceVoiceRecord();\n }\n /** Returns whether the browser has microphone permission. */\n hasAudioRecordingPermission() {\n return VoiceRecorderImpl.hasAudioRecordingPermission();\n }\n /** Requests microphone permission through the browser. */\n requestAudioRecordingPermission() {\n return VoiceRecorderImpl.requestAudioRecordingPermission();\n }\n /** Starts a recording session using MediaRecorder. */\n startRecording(options) {\n return this.voiceRecorderImpl.startRecording(options);\n }\n /** Stops the recording session and returns the payload. */\n stopRecording() {\n return this.voiceRecorderImpl.stopRecording();\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n return this.voiceRecorderImpl.pauseRecording();\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n return this.voiceRecorderImpl.resumeRecording();\n }\n /** Returns the current recording state. */\n getCurrentStatus() {\n return this.voiceRecorderImpl.getCurrentStatus();\n }\n}\n//# sourceMappingURL=VoiceRecorderWebAdapter.js.map","/** Default response shape when no config is provided. */\nexport const DEFAULT_RESPONSE_FORMAT = 'legacy';\n/** Parses a user-provided response format into a supported value. */\nexport const resolveResponseFormat = (value) => {\n if (typeof value === 'string' && value.toLowerCase() === 'normalized') {\n return 'normalized';\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n/** Reads the response format from a Capacitor plugin config object. */\nexport const getResponseFormatFromConfig = (config) => {\n if (config && typeof config === 'object' && 'responseFormat' in config) {\n return resolveResponseFormat(config.responseFormat);\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n//# sourceMappingURL=response-format.js.map","/** Maps legacy error messages to canonical error codes. */\nconst legacyToCanonical = {\n CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',\n};\n/** Normalizes legacy error messages into canonical error codes. */\nexport const toCanonicalErrorCode = (legacyMessage) => {\n var _a;\n return (_a = legacyToCanonical[legacyMessage]) !== null && _a !== void 0 ? _a : legacyMessage;\n};\n/** Adds a canonical `code` field to Error-like objects when possible. */\nexport const attachCanonicalErrorCode = (error) => {\n if (!error || typeof error !== 'object') {\n return;\n }\n const messageValue = error.message;\n if (typeof messageValue !== 'string') {\n return;\n }\n error.code = toCanonicalErrorCode(messageValue);\n};\n//# sourceMappingURL=error-codes.js.map","/** Normalizes recording payloads into a stable contract shape. */\nexport const normalizeRecordingData = (data) => {\n const { recordDataBase64, uri, msDuration, mimeType } = data.value;\n const normalizedValue = { msDuration, mimeType };\n const trimmedUri = typeof uri === 'string' && uri.length > 0 ? uri : undefined;\n const trimmedBase64 = typeof recordDataBase64 === 'string' && recordDataBase64.length > 0 ? recordDataBase64 : undefined;\n if (trimmedUri) {\n normalizedValue.uri = trimmedUri;\n }\n else if (trimmedBase64) {\n normalizedValue.recordDataBase64 = trimmedBase64;\n }\n return { value: normalizedValue };\n};\n//# sourceMappingURL=recording-contract.js.map","import { attachCanonicalErrorCode } from '../core/error-codes';\nimport { normalizeRecordingData } from '../core/recording-contract';\n/** Orchestrates platform calls and normalizes responses when requested. */\nexport class VoiceRecorderService {\n constructor(platform, responseFormat) {\n this.platform = platform;\n this.responseFormat = responseFormat;\n }\n /** Checks whether the device can record audio. */\n canDeviceVoiceRecord() {\n return this.execute(() => this.platform.canDeviceVoiceRecord());\n }\n /** Returns whether microphone permission is currently granted. */\n hasAudioRecordingPermission() {\n return this.execute(() => this.platform.hasAudioRecordingPermission());\n }\n /** Requests microphone permission from the user. */\n requestAudioRecordingPermission() {\n return this.execute(() => this.platform.requestAudioRecordingPermission());\n }\n /** Starts a recording session. */\n startRecording(options) {\n return this.execute(() => this.platform.startRecording(options));\n }\n /** Stops the recording session and formats the payload if needed. */\n async stopRecording() {\n return this.execute(async () => {\n const data = await this.platform.stopRecording();\n if (this.responseFormat === 'normalized') {\n return normalizeRecordingData(data);\n }\n return data;\n });\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n return this.execute(() => this.platform.pauseRecording());\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n return this.execute(() => this.platform.resumeRecording());\n }\n /** Returns the current recording state. */\n getCurrentStatus() {\n return this.execute(() => this.platform.getCurrentStatus());\n }\n /** Wraps calls to apply canonical error codes when requested. */\n async execute(fn) {\n try {\n return await fn();\n }\n catch (error) {\n if (this.responseFormat === 'normalized') {\n attachCanonicalErrorCode(error);\n }\n throw error;\n }\n }\n}\n//# sourceMappingURL=VoiceRecorderService.js.map","import { Capacitor, WebPlugin } from '@capacitor/core';\nimport { VoiceRecorderWebAdapter } from './adapters/VoiceRecorderWebAdapter';\nimport { getResponseFormatFromConfig } from './core/response-format';\nimport { VoiceRecorderService } from './service/VoiceRecorderService';\n/** Web implementation of the VoiceRecorder Capacitor plugin. */\nexport class VoiceRecorderWeb extends WebPlugin {\n constructor() {\n var _a, _b;\n super();\n const pluginConfig = (_b = (_a = Capacitor === null || Capacitor === void 0 ? void 0 : Capacitor.config) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.VoiceRecorder;\n const responseFormat = getResponseFormatFromConfig(pluginConfig);\n this.service = new VoiceRecorderService(new VoiceRecorderWebAdapter(), responseFormat);\n }\n /** Checks whether the browser can record audio. */\n canDeviceVoiceRecord() {\n return this.service.canDeviceVoiceRecord();\n }\n /** Returns whether microphone permission is currently granted. */\n hasAudioRecordingPermission() {\n return this.service.hasAudioRecordingPermission();\n }\n /** Requests microphone permission from the user. */\n requestAudioRecordingPermission() {\n return this.service.requestAudioRecordingPermission();\n }\n /** Starts a recording session. */\n startRecording(options) {\n return this.service.startRecording(options);\n }\n /** Stops the current recording session and returns the payload. */\n stopRecording() {\n return this.service.stopRecording();\n }\n /** Pauses the recording session when supported. */\n pauseRecording() {\n return this.service.pauseRecording();\n }\n /** Resumes a paused recording session when supported. */\n resumeRecording() {\n return this.service.resumeRecording();\n }\n /** Returns the current recording state. */\n getCurrentStatus() {\n return this.service.getCurrentStatus();\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","Filesystem","WebPlugin","Capacitor"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import Foundation
2
+ import AVFoundation
3
+
4
+ /// Default platform adapter for file IO and duration lookup.
5
+ final class DefaultRecorderPlatform: RecorderPlatform {
6
+ /// Returns whether the device can record audio.
7
+ func canDeviceVoiceRecord() -> Bool {
8
+ return true
9
+ }
10
+
11
+ /// Reads a file as base64, returning nil on failure.
12
+ func readFileAsBase64(_ filePath: URL?) -> String? {
13
+ guard let filePath = filePath else {
14
+ return nil
15
+ }
16
+
17
+ do {
18
+ let fileData = try Data(contentsOf: filePath)
19
+ return fileData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
20
+ } catch {
21
+ return nil
22
+ }
23
+ }
24
+
25
+ /// Returns the file duration in milliseconds, or -1 on failure.
26
+ func getDurationMs(_ filePath: URL?) -> Int {
27
+ guard let filePath = filePath else {
28
+ return -1
29
+ }
30
+
31
+ return Int(CMTimeGetSeconds(AVURLAsset(url: filePath).duration) * 1000)
32
+ }
33
+ }
@@ -0,0 +1,38 @@
1
+ import Foundation
2
+
3
+ /// Maps record data into legacy or normalized dictionary payloads.
4
+ struct RecordDataMapper {
5
+ /// Converts record data to the legacy payload shape.
6
+ static func toLegacyDictionary(_ recordData: RecordData) -> Dictionary<String, Any> {
7
+ return recordData.toDictionary()
8
+ }
9
+
10
+ /// Converts record data to the normalized payload shape.
11
+ static func toNormalizedDictionary(_ recordData: RecordData) -> Dictionary<String, Any> {
12
+ var normalized: Dictionary<String, Any> = [
13
+ "msDuration": recordData.msDuration,
14
+ "mimeType": recordData.mimeType,
15
+ ]
16
+
17
+ if let uri = normalizedUri(from: recordData.uri) {
18
+ normalized["uri"] = uri
19
+ } else if let base64 = recordData.recordDataBase64, !base64.isEmpty {
20
+ normalized["recordDataBase64"] = base64
21
+ }
22
+
23
+ return normalized
24
+ }
25
+
26
+ /// Normalizes legacy file paths into file:// URIs.
27
+ private static func normalizedUri(from legacyUri: String?) -> String? {
28
+ guard let legacyUri = legacyUri, !legacyUri.isEmpty else {
29
+ return nil
30
+ }
31
+
32
+ if legacyUri.hasPrefix("file://") {
33
+ return legacyUri
34
+ }
35
+
36
+ return URL(fileURLWithPath: legacyUri).absoluteString
37
+ }
38
+ }
@@ -0,0 +1,24 @@
1
+ import Foundation
2
+
3
+ /// Recorder abstraction used by the service layer.
4
+ protocol RecorderAdapter: AnyObject {
5
+ /// Options supplied when recording starts.
6
+ var options: RecordOptions? { get }
7
+ /// Callback invoked when interruptions begin.
8
+ var onInterruptionBegan: (() -> Void)? { get set }
9
+ /// Callback invoked when interruptions end.
10
+ var onInterruptionEnded: (() -> Void)? { get set }
11
+
12
+ /// Starts recording audio.
13
+ func startRecording(recordOptions: RecordOptions?) -> Bool
14
+ /// Stops recording and returns success via callback.
15
+ func stopRecording(completion: @escaping (Bool) -> Void)
16
+ /// Pauses recording if supported.
17
+ func pauseRecording() -> Bool
18
+ /// Resumes recording if supported.
19
+ func resumeRecording() -> Bool
20
+ /// Returns the current recording status.
21
+ func getCurrentStatus() -> CurrentRecordingStatus
22
+ /// Returns the output file for the current session.
23
+ func getOutputFile() -> URL
24
+ }
@@ -0,0 +1,11 @@
1
+ import Foundation
2
+
3
+ /// Platform abstraction for device and file operations.
4
+ protocol RecorderPlatform {
5
+ /// Returns whether the device can record audio.
6
+ func canDeviceVoiceRecord() -> Bool
7
+ /// Reads the file as base64, returning nil on failure.
8
+ func readFileAsBase64(_ filePath: URL?) -> String?
9
+ /// Returns the file duration in milliseconds, or -1 on failure.
10
+ func getDurationMs(_ filePath: URL?) -> Int
11
+ }