[
  {
    "path": ".github/workflows/publish.yaml",
    "content": "name: Publish Package to npmjs\non:\n  release:\n    types: [published]\njobs:\n  build:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      id-token: write\n    steps:\n      - uses: actions/checkout@v4      \n      - uses: actions/setup-node@v4\n        with:\n          node-version: '20.x'\n          registry-url: 'https://registry.npmjs.org'\n      - run: npm ci\n      - run: npm publish --provenance --access public\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# build directories\nlib\n"
  },
  {
    "path": ".npmignore",
    "content": "src"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Giridharan GM\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# react-media-recorder :o2: :video_camera: :microphone: :computer:\n\n`react-media-recorder` is a fully typed react component with render prop, or a react hook, that can be used to:\n\n- Record audio/video\n- Record screen\n\nusing [MediaRecorder API](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder).\n\n## Installation\n\n```\nnpm i react-media-recorder\n```\n\nor\n\n```\nyarn add react-media-recorder\n```\n\n## Usage\n\n```javascript\nimport { ReactMediaRecorder } from \"react-media-recorder\";\n\nconst RecordView = () => (\n  <div>\n    <ReactMediaRecorder\n      video\n      render={({ status, startRecording, stopRecording, mediaBlobUrl }) => (\n        <div>\n          <p>{status}</p>\n          <button onClick={startRecording}>Start Recording</button>\n          <button onClick={stopRecording}>Stop Recording</button>\n          <video src={mediaBlobUrl} controls autoPlay loop />\n        </div>\n      )}\n    />\n  </div>\n);\n```\n\nSince `react-media-recording` uses render prop, you can define what to render in the view. Just don't forget to wire the `startRecording`, `stopRecording` and `mediaBlobUrl` to your component.\n\n## Usage with react hooks\n\n```javascript\nimport { useReactMediaRecorder } from \"react-media-recorder\";\n\nconst RecordView = () => {\n  const { status, startRecording, stopRecording, mediaBlobUrl } =\n    useReactMediaRecorder({ video: true });\n\n  return (\n    <div>\n      <p>{status}</p>\n      <button onClick={startRecording}>Start Recording</button>\n      <button onClick={stopRecording}>Stop Recording</button>\n      <video src={mediaBlobUrl} controls autoPlay loop />\n    </div>\n  );\n};\n```\n\nThe hook receives an object as argument with the same ReactMediaRecorder options / props (except the `render` function).\n\n### Options / Props\n\n#### audio\n\nCan be either a boolean value or a [MediaTrackConstraints](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints) object.\n\ntype: `boolean` or `object`\ndefault: `true`\n\n#### blobPropertyBag\n\n[From MDN](https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob):\nAn optional `BlobPropertyBag` dictionary which may specify the following two attributes (for the `mediaBlob`):\n\n- `type`, that represents the MIME type of the content of the array that will be put in the blob.\n- `endings`, with a default value of \"transparent\", that specifies how strings containing the line ending character \\n are to be written out. It is one of the two values: \"native\", meaning that line ending characters are changed to match host OS filesystem convention, or \"transparent\", meaning that endings are stored in the blob without change\n\ntype: `object`\ndefault:\nif `video` is enabled,\n\n```\n{\n   type: \"video/mp4\"\n}\n```\n\nif there's only `audio` is enabled,\n\n```\n{\n  type: \"audio/wav\"\n}\n```\n\n#### customMediaStream\n\nA media stream object itself (optional)\n\n#### mediaRecorderOptions\n\nAn optional options object that will be passed to `MediaRecorder`. Please note that if you specify the MIME type via either `audio` or `video` prop _and_ through this `mediaRecorderOptions`, the `mediaRecorderOptions` have higher precedence.\n\ntype: `object`\ndefault: `{}`\n\n#### onStart\n\nA `function` that would get invoked when the MediaRecorder starts.\n\ntype: `function()`\ndefault: `() => null`\n\n#### onStop\n\nA `function` that would get invoked when the MediaRecorder stops. It'll provide the blob and the blob url as its params.\n\ntype: `function(blobUrl: string, blob: Blob)`\ndefault: `() => null`\n\n#### stopStreamsOnStop\n\nWhether to stop all streams on stop. By default, its `true`\n\n#### render\n\nA `function` which accepts an object containing fields: `status`, `startRecording`, `stopRecording` and`mediaBlob`. This function would return a react element/component.\n\ntype: `function`\ndefault: `() => null`\n\n#### screen\n\nA `boolean` value. Lets you to record your current screen. Not all browsers would support this. Please [check here](https://caniuse.com/#search=getDisplayMedia) for the availability. Please note that at the moment, the MediaRecorder won't record two alike streams at a time, if you provide both `screen` and `video` prop, the **screen capturing will take precedence** than the video capturing. But, you can provide the `video` prop (_as the MediaTrackConstraints_) which will then utilized by screen capture (for example, `height`, `width` etc..)\n\n#### video\n\nCan be either a boolean value or a [MediaTrackConstraints](https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints) object.\n\ntype: `boolean` or `object`\ndefault: `false`\n\n#### askPermissionOnMount\n\nA boolean value. If set to `true`, will ask media permission on mounting.\n\ntype: `boolean`\ndefault: `false`\n\n#### preferCurrentTab\n\nA boolean value. If set to `true`, the browser will offer the current tab as the most prominent capture source, i.e. as a separate \"This Tab\" option in the \"Choose what to share\" options presented to the user.\n\ntype: `boolean`\ndefault: `false`\n\n#### selfBrowserSurface\n\nAn enumerated value specifying whether the browser should allow the user to select the current tab for capture. Possible values are `include`, which hints that the browser should include the current tab in the choices offered for capture, and `exclude`, which hints that it should be excluded.\n\ntype: `undefined` | `'include'` | `'exclude'`;\ndefault: `undefined`\n\n### Props available in the `render` function\n\n#### error\n\nA string enum. Possible values:\n\n- `media_aborted`\n- `permission_denied`\n- `no_specified_media_found`\n- `media_in_use`\n- `invalid_media_constraints`\n- `no_constraints`\n- `recorder_error`\n\n#### status\n\nA string `enum`. Possible values:\n\n- `media_aborted`\n- `permission_denied`\n- `no_specified_media_found`\n- `media_in_use`\n- `invalid_media_constraints`\n- `no_constraints`\n- `recorder_error`\n- `idle`\n- `acquiring_media`\n- `recording`\n- `stopping`\n- `stopped`\n\n#### startRecording\n\nA `function`, which starts recording when invoked.\n\n#### pauseRecording\n\nA `function`, which pauses the recording when invoked.\n\n#### resumeRecording\n\nA `function`, which resumes the recording when invoked.\n\n#### stopRecording\n\nA `function`, which stops recording when invoked.\n\n#### muteAudio\n\nA `function`, which mutes the audio tracks when invoked.\n\n#### unmuteAudio\n\nA `function` which unmutes the audio tracks when invoked.\n\n#### mediaBlobUrl\n\nA `blob` url that can be wired to an `<audio />`, `<video />` or an `<a />` element.\n\n#### clearBlobUrl\n\nA `function` which clears the existing generated blob url (if any) and resets the workflow to its initial `idle` state.\n\n#### isMuted\n\nA boolean prop that tells whether the audio is muted or not.\n\n#### previewStream\n\nIf you want to create a live-preview of the video to the user, you can use this _stream_ and attach it to a `<video />` element. Please note that this is a **muted stream**. This is by design to get rid of internal microphone feedbacks on machines like laptop.\n\nFor example:\n\n```tsx\nconst VideoPreview = ({ stream }: { stream: MediaStream | null }) => {\n  const videoRef = useRef<HTMLVideoElement>(null);\n\n  useEffect(() => {\n    if (videoRef.current && stream) {\n      videoRef.current.srcObject = stream;\n    }\n  }, [stream]);\n  if (!stream) {\n    return null;\n  }\n  return <video ref={videoRef} width={500} height={500} autoPlay controls />;\n};\n\nconst App = () => (\n  <ReactMediaRecorder\n    video\n    render={({ previewStream }) => {\n      return <VideoPreview stream={previewStream} />;\n    }}\n  />\n);\n```\n\n#### previewAudioStream\n\nIf you want access to the live audio stream for use in sound visualisations, you can use this _stream_ as your audio source and extract data from it using the [AudioContext](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext) and [AnalyzerNode](https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode) features of the Web Audio API. Some javascript examples of how to do this can be found [here](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Visualizations_with_Web_Audio_API).\n\n## Contributing\n\nFeel free to submit a PR if you found a bug (I might've missed many! :grinning:) or if you want to enhance it further.\n\nThanks!. Happy Recording!\n"
  },
  {
    "path": "index.js",
    "content": "module.exports = require(\"./lib\");\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-media-recorder\",\n  \"version\": \"1.7.2\",\n  \"description\": \"A React component based on MediaRecorder() API to record audio/video streams\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"build\": \"tsc && jsmin -o ./lib/index.js ./lib/index.js\",\n    \"prepare\": \"npm run build\"\n  },\n  \"files\": [\n    \"lib\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/giridharangm/react-media-recorder.git\"\n  },\n  \"keywords\": [\n    \"react\",\n    \"recorder\",\n    \"voice recording\",\n    \"video recording\",\n    \"media recording\",\n    \"getusermedia\",\n    \"MediaRecorder\",\n    \"getDisplayMedia\",\n    \"screen recorder\",\n    \"video recorder\",\n    \"audio recorder\"\n  ],\n  \"author\": \"Giridharan GM\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/giridharangm/react-media-recorder/issues\"\n  },\n  \"homepage\": \"https://github.com/giridharangm/react-media-recorder#readme\",\n  \"devDependencies\": {\n    \"@types/react\": \"^16.9.11\",\n    \"jsmin\": \"^1.0.1\",\n    \"typescript\": \"^4.4.3\"\n  },\n  \"types\": \"./lib/index.d.ts\",\n  \"dependencies\": {\n    \"extendable-media-recorder\": \"^6.6.5\",\n    \"extendable-media-recorder-wav-encoder\": \"^7.0.68\"\n  }\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "import {\n  register,\n  MediaRecorder as ExtendableMediaRecorder,\n  IMediaRecorder,\n} from \"extendable-media-recorder\";\nimport { ReactElement, useCallback, useEffect, useRef, useState } from \"react\";\nimport { connect } from \"extendable-media-recorder-wav-encoder\";\n\nexport type ReactMediaRecorderRenderProps = {\n  error: string;\n  muteAudio: () => void;\n  unMuteAudio: () => void;\n  startRecording: () => void;\n  pauseRecording: () => void;\n  resumeRecording: () => void;\n  stopRecording: () => void;\n  mediaBlobUrl: undefined | string;\n  status: StatusMessages;\n  isAudioMuted: boolean;\n  previewStream: MediaStream | null;\n  previewAudioStream: MediaStream | null;\n  clearBlobUrl: () => void;\n};\n\nexport type ReactMediaRecorderHookProps = {\n  audio?: boolean | MediaTrackConstraints;\n  video?: boolean | MediaTrackConstraints;\n  screen?: boolean;\n  selfBrowserSurface?: SelfBrowserSurface;\n  preferCurrentTab?: PreferCurrentTab,\n  onStop?: (blobUrl: string, blob: Blob) => void;\n  onStart?: () => void;\n  blobPropertyBag?: BlobPropertyBag;\n  mediaRecorderOptions?: MediaRecorderOptions | undefined;\n  customMediaStream?: MediaStream | null;\n  stopStreamsOnStop?: boolean;\n  askPermissionOnMount?: boolean;\n};\nexport type ReactMediaRecorderProps = ReactMediaRecorderHookProps & {\n  render: (props: ReactMediaRecorderRenderProps) => ReactElement;\n};\n\n/**\n * Experimental (optional).\n * An enumerated value specifying whether the browser should allow the user to select the current tab for capture.\n * This helps to avoid the \"infinite hall of mirrors\" effect experienced when a video conferencing app inadvertently shares its own display.\n * Possible values are include, which hints that the browser should include the current tab in the choices offered for capture,\n * and exclude, which hints that it should be excluded.\n * A default value is not mandated by the spec.\n * See specs at: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#selfbrowsersurface\n */\nexport type SelfBrowserSurface = undefined | 'include' | 'exclude';\n\n/**\n * Experimental (optional).\n * A boolean; a value of true instructs the browser to offer the current tab as the most prominent capture source, i.e. as a separate \"This Tab\" option in the \"Choose what to share\" options presented to the user.\n * This is useful as many app types generally just want to share the current tab.\n * See specs at: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#prefercurrenttab\n */\nexport type PreferCurrentTab = true | false;\n\nexport type StatusMessages =\n    | \"media_aborted\"\n    | \"permission_denied\"\n    | \"no_specified_media_found\"\n    | \"media_in_use\"\n    | \"invalid_media_constraints\"\n    | \"no_constraints\"\n    | \"recorder_error\"\n    | \"idle\"\n    | \"acquiring_media\"\n    | \"delayed_start\"\n    | \"recording\"\n    | \"stopping\"\n    | \"stopped\"\n    | \"paused\";\n\nexport enum RecorderErrors {\n  AbortError = \"media_aborted\",\n  NotAllowedError = \"permission_denied\",\n  NotFoundError = \"no_specified_media_found\",\n  NotReadableError = \"media_in_use\",\n  OverconstrainedError = \"invalid_media_constraints\",\n  TypeError = \"no_constraints\",\n  NONE = \"\",\n  NO_RECORDER = \"recorder_error\",\n}\n\nexport function useReactMediaRecorder({\n  audio = true,\n  video = false,\n  selfBrowserSurface = undefined,\n  preferCurrentTab = false,\n  onStop = () => null,\n  onStart = () => null,\n  blobPropertyBag,\n  screen = false,\n  mediaRecorderOptions = undefined,\n  customMediaStream = null,\n  stopStreamsOnStop = true,\n  askPermissionOnMount = false,\n}: ReactMediaRecorderHookProps): ReactMediaRecorderRenderProps {\n  const mediaRecorder = useRef<IMediaRecorder | null >(null);\n  const mediaChunks = useRef<Blob[]>([]);\n  const mediaStream = useRef<MediaStream | null>(null);\n  const [status, setStatus] = useState<StatusMessages>(\"idle\");\n  const [isAudioMuted, setIsAudioMuted] = useState<boolean>(false);\n  const [mediaBlobUrl, setMediaBlobUrl] = useState<string | undefined>(\n      undefined\n  );\n  const [error, setError] = useState<keyof typeof RecorderErrors>(\"NONE\");\n  const [init, setInit] = useState(false);\n\n  useEffect(() => {\n    // avoid re-registering the encoder\n    if (init) {\n      return;\n    }\n\n    const setup = async () => {\n      try {\n        await register(await connect());\n      } catch (e) {\n        //\n      }\n    };\n\n    setup();\n    setInit(true);\n  }, []);\n\n  const getMediaStream = useCallback(async () => {\n    setStatus(\"acquiring_media\");\n    const requiredMedia: MediaStreamConstraints = {\n      audio: typeof audio === \"boolean\" ? !!audio : audio,\n      video: typeof video === \"boolean\" ? !!video : video,\n    };\n    try {\n      if (customMediaStream) {\n        mediaStream.current = customMediaStream;\n      } else if (screen) {\n        const stream = (await window.navigator.mediaDevices.getDisplayMedia({\n          video: video || true,\n          // @ts-ignore experimental feature, useful for Chrome\n          selfBrowserSurface,\n          preferCurrentTab\n        })) as MediaStream;\n        stream.getVideoTracks()[0].addEventListener(\"ended\", () => {\n          stopRecording();\n        });\n        if (audio) {\n          const audioStream = await window.navigator.mediaDevices.getUserMedia({\n            audio,\n          });\n\n          audioStream\n              .getAudioTracks()\n              .forEach((audioTrack) => stream.addTrack(audioTrack));\n        }\n        mediaStream.current = stream;\n      } else {\n        const stream = await window.navigator.mediaDevices.getUserMedia(\n            requiredMedia\n        );\n        mediaStream.current = stream;\n      }\n      setStatus(\"idle\");\n    } catch (error: any) {\n      setError(error.name);\n      setStatus(\"idle\");\n    }\n  }, [audio, video, screen]);\n\n  useEffect(() => {\n    if (!window.MediaRecorder) {\n      throw new Error(\"Unsupported Browser\");\n    }\n\n    if (screen) {\n      if (!window.navigator.mediaDevices.getDisplayMedia) {\n        throw new Error(\"This browser doesn't support screen capturing\");\n      }\n    }\n\n    const checkConstraints = (mediaType: MediaTrackConstraints) => {\n      const supportedMediaConstraints =\n          navigator.mediaDevices.getSupportedConstraints();\n      const unSupportedConstraints = Object.keys(mediaType).filter(\n          (constraint) =>\n              !(supportedMediaConstraints as { [key: string]: any })[constraint]\n      );\n\n      if (unSupportedConstraints.length > 0) {\n        console.error(\n            `The constraints ${unSupportedConstraints.join(\n                \",\"\n            )} doesn't support on this browser. Please check your ReactMediaRecorder component.`\n        );\n      }\n    };\n\n    if (typeof audio === \"object\") {\n      checkConstraints(audio);\n    }\n    if (typeof video === \"object\") {\n      checkConstraints(video);\n    }\n\n    if (mediaRecorderOptions && mediaRecorderOptions.mimeType) {\n      if (!MediaRecorder.isTypeSupported(mediaRecorderOptions.mimeType)) {\n        console.error(\n            `The specified MIME type you supplied for MediaRecorder doesn't support this browser`\n        );\n      }\n    }\n\n    if (!mediaStream.current && askPermissionOnMount) {\n      getMediaStream();\n    }\n\n    return () => {\n      if (mediaStream.current) {\n        const tracks = mediaStream.current.getTracks();\n        tracks.forEach((track) => track.clone().stop());\n      }\n    };\n  }, [\n    audio,\n    screen,\n    video,\n    getMediaStream,\n    mediaRecorderOptions,\n    askPermissionOnMount,\n  ]);\n\n  // Media Recorder Handlers\n\n  const startRecording = async () => {\n    setError(\"NONE\");\n    if (!mediaStream.current) {\n      await getMediaStream();\n    }\n    if (mediaStream.current) {\n      const isStreamEnded = mediaStream.current\n          .getTracks()\n          .some((track) => track.readyState === \"ended\");\n      if (isStreamEnded) {\n        await getMediaStream();\n      }\n\n      // User blocked the permissions (getMediaStream errored out)\n      if (!mediaStream.current.active) {\n        return;\n      }\n      mediaRecorder.current = new ExtendableMediaRecorder(\n          mediaStream.current,\n          mediaRecorderOptions || undefined\n      );\n      mediaRecorder.current.ondataavailable = onRecordingActive;\n      mediaRecorder.current.onstop = onRecordingStop;\n      mediaRecorder.current.onstart = onRecordingStart;\n      mediaRecorder.current.onerror = () => {\n        setError(\"NO_RECORDER\");\n        setStatus(\"idle\");\n      };\n      mediaRecorder.current.start();\n      setStatus(\"recording\");\n    }\n  };\n\n  const onRecordingActive = ({ data }: BlobEvent) => {\n    mediaChunks.current.push(data);\n  };\n\n  const onRecordingStart = () => {\n    onStart();\n  };\n\n  const onRecordingStop = () => {\n    const [chunk] = mediaChunks.current;\n    const blobProperty: BlobPropertyBag = Object.assign(\n        { type: chunk.type },\n        blobPropertyBag || (video ? { type: \"video/mp4\" } : { type: \"audio/wav\" })\n    );\n    const blob = new Blob(mediaChunks.current, blobProperty);\n    const url = URL.createObjectURL(blob);\n    setStatus(\"stopped\");\n    setMediaBlobUrl(url);\n    onStop(url, blob);\n  };\n\n  const muteAudio = (mute: boolean) => {\n    setIsAudioMuted(mute);\n    if (mediaStream.current) {\n      mediaStream.current\n          .getAudioTracks()\n          .forEach((audioTrack) => (audioTrack.enabled = !mute));\n    }\n  };\n\n  const pauseRecording = () => {\n    if (mediaRecorder.current && mediaRecorder.current.state === \"recording\") {\n      setStatus(\"paused\");\n      mediaRecorder.current.pause();\n    }\n  };\n  const resumeRecording = () => {\n    if (mediaRecorder.current && mediaRecorder.current.state === \"paused\") {\n      setStatus(\"recording\");\n      mediaRecorder.current.resume();\n    }\n  };\n\n  const stopRecording = () => {\n    if (mediaRecorder.current) {\n      if (mediaRecorder.current.state !== \"inactive\") {\n        setStatus(\"stopping\");\n        mediaRecorder.current.stop();\n        if (stopStreamsOnStop) {\n          mediaStream.current &&\n          mediaStream.current.getTracks().forEach((track) => track.stop());\n        }\n        mediaChunks.current = [];\n      }\n    }\n  };\n\n  return {\n    error: RecorderErrors[error],\n    muteAudio: () => muteAudio(true),\n    unMuteAudio: () => muteAudio(false),\n    startRecording,\n    pauseRecording,\n    resumeRecording,\n    stopRecording,\n    mediaBlobUrl,\n    status,\n    isAudioMuted,\n    previewStream: mediaStream.current\n        ? new MediaStream(mediaStream.current.getVideoTracks())\n        : null,\n    previewAudioStream: mediaStream.current\n        ? new MediaStream(mediaStream.current.getAudioTracks())\n        : null,\n    clearBlobUrl: () => {\n      if (mediaBlobUrl) {\n        URL.revokeObjectURL(mediaBlobUrl);\n      }\n      setMediaBlobUrl(undefined);\n      setStatus(\"idle\");\n    },\n  };\n}\n\nexport const ReactMediaRecorder = (props: ReactMediaRecorderProps) =>\n    props.render(useReactMediaRecorder(props));\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Basic Options */\n    // \"incremental\": true,                   /* Enable incremental compilation */\n    \"target\": \"es5\" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,\n    \"module\": \"commonjs\" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,\n    \"lib\": [\n      \"ES2015\",\n      \"DOM\"\n    ] /* Specify library files to be included in the compilation. */,\n    // \"allowJs\": true,                       /* Allow javascript files to be compiled. */\n    // \"checkJs\": true,                       /* Report errors in .js files. */\n    // \"jsx\": \"preserve\",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */\n    \"declaration\": true /* Generates corresponding '.d.ts' file. */,\n    // \"declarationMap\": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */\n    // \"sourceMap\": true,                     /* Generates corresponding '.map' file. */\n    // \"outFile\": \"./\",                       /* Concatenate and emit output to single file. */\n    \"outDir\": \"./lib\" /* Redirect output structure to the directory. */,\n    \"rootDir\": \"./src\" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,\n    // \"composite\": true,                     /* Enable project compilation */\n    // \"tsBuildInfoFile\": \"./\",               /* Specify file to store incremental compilation information */\n    // \"removeComments\": true,                /* Do not emit comments to output. */\n    // \"noEmit\": true,                        /* Do not emit outputs. */\n    // \"importHelpers\": true,                 /* Import emit helpers from 'tslib'. */\n    // \"downlevelIteration\": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */\n    // \"isolatedModules\": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */\n\n    /* Strict Type-Checking Options */\n    \"strict\": true /* Enable all strict type-checking options. */,\n    // \"noImplicitAny\": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */\n    // \"strictNullChecks\": true,              /* Enable strict null checks. */\n    // \"strictFunctionTypes\": true,           /* Enable strict checking of function types. */\n    // \"strictBindCallApply\": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n    // \"strictPropertyInitialization\": true,  /* Enable strict checking of property initialization in classes. */\n    // \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\n    // \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n    /* Additional Checks */\n    // \"noUnusedLocals\": true,                /* Report errors on unused locals. */\n    // \"noUnusedParameters\": true,            /* Report errors on unused parameters. */\n    // \"noImplicitReturns\": true,             /* Report error when not all code paths in function return a value. */\n    // \"noFallthroughCasesInSwitch\": true,    /* Report errors for fallthrough cases in switch statement. */\n\n    /* Module Resolution Options */\n    // \"moduleResolution\": \"node\",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */\n    // \"baseUrl\": \"./\",                       /* Base directory to resolve non-absolute module names. */\n    // \"paths\": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */\n    // \"rootDirs\": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */\n    // \"typeRoots\": [],                       /* List of folders to include type definitions from. */\n    // \"types\": [],                           /* Type declaration files to be included in compilation. */\n    // \"allowSyntheticDefaultImports\": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */\n    \"esModuleInterop\": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,\n    // \"preserveSymlinks\": true,              /* Do not resolve the real path of symlinks. */\n    // \"allowUmdGlobalAccess\": true,          /* Allow accessing UMD globals from modules. */\n\n    /* Source Map Options */\n    // \"sourceRoot\": \"\",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */\n    // \"mapRoot\": \"\",                         /* Specify the location where debugger should locate map files instead of generated locations. */\n    // \"inlineSourceMap\": true,               /* Emit a single file with source maps instead of having a separate file. */\n    // \"inlineSources\": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */\n\n    /* Experimental Options */\n    // \"experimentalDecorators\": true,        /* Enables experimental support for ES7 decorators. */\n    // \"emitDecoratorMetadata\": true,         /* Enables experimental support for emitting type metadata for decorators. */\n\n    /* Advanced Options */\n    \"forceConsistentCasingInFileNames\": true /* Disallow inconsistently-cased references to the same file. */\n  }\n}\n"
  }
]