@easyv/biz-components 0.0.30 → 0.0.31

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 (172) hide show
  1. package/dist/_virtual/_commonjsHelpers.es.js +0 -25
  2. package/dist/_virtual/_commonjsHelpers.es.js.map +1 -1
  3. package/dist/_virtual/index.es2.js +2 -2
  4. package/dist/_virtual/index.es3.js +2 -2
  5. package/dist/_virtual/index.es4.js +2 -2
  6. package/dist/_virtual/index.es5.js +2 -2
  7. package/dist/_virtual/index.es6.js +2 -2
  8. package/dist/node_modules/.pnpm/classnames@2.3.2/node_modules/classnames/index.es.js +1 -1
  9. package/dist/node_modules/.pnpm/co-web-worker@1.0.1/node_modules/co-web-worker/index.es.js +1 -1
  10. package/dist/node_modules/.pnpm/prop-types@15.8.1/node_modules/prop-types/index.es.js +1 -1
  11. package/dist/node_modules/.pnpm/react-is@16.13.1/node_modules/react-is/index.es.js +1 -1
  12. package/dist/node_modules/.pnpm/style-to-js@1.1.16/node_modules/style-to-js/cjs/index.es.js +1 -1
  13. package/dist/node_modules/.pnpm/style-to-object@1.0.8/node_modules/style-to-object/cjs/index.es.js +1 -1
  14. package/dist/utils/xunFeiVoice/RecorderManager/RecorderManager.es.js +1 -15
  15. package/dist/utils/xunFeiVoice/RecorderManager/RecorderManager.es.js.map +1 -1
  16. package/dist/utils/xunFeiVoice/XFStreamVoiceManager.d.ts +12 -4
  17. package/dist/utils/xunFeiVoice/XFStreamVoiceManager.es.js +39 -26
  18. package/dist/utils/xunFeiVoice/XFStreamVoiceManager.es.js.map +1 -1
  19. package/dist/utils/xunFeiVoice/utils.d.ts +15 -0
  20. package/dist/utils/xunFeiVoice/utils.es.js +28 -0
  21. package/dist/utils/xunFeiVoice/utils.es.js.map +1 -1
  22. package/package.json +1 -1
  23. package/dist/_virtual/___vite-browser-external.es.js +0 -7
  24. package/dist/_virtual/___vite-browser-external.es.js.map +0 -1
  25. package/dist/_virtual/__vite-browser-external.es.js +0 -5
  26. package/dist/_virtual/__vite-browser-external.es.js.map +0 -1
  27. package/dist/_virtual/_commonjs-dynamic-modules.es.js +0 -7
  28. package/dist/_virtual/_commonjs-dynamic-modules.es.js.map +0 -1
  29. package/dist/_virtual/aes.es.js +0 -5
  30. package/dist/_virtual/aes.es.js.map +0 -1
  31. package/dist/_virtual/blowfish.es.js +0 -5
  32. package/dist/_virtual/blowfish.es.js.map +0 -1
  33. package/dist/_virtual/cipher-core.es.js +0 -5
  34. package/dist/_virtual/cipher-core.es.js.map +0 -1
  35. package/dist/_virtual/core.es.js +0 -5
  36. package/dist/_virtual/core.es.js.map +0 -1
  37. package/dist/_virtual/enc-base64.es.js +0 -5
  38. package/dist/_virtual/enc-base64.es.js.map +0 -1
  39. package/dist/_virtual/enc-base64url.es.js +0 -5
  40. package/dist/_virtual/enc-base64url.es.js.map +0 -1
  41. package/dist/_virtual/enc-utf16.es.js +0 -5
  42. package/dist/_virtual/enc-utf16.es.js.map +0 -1
  43. package/dist/_virtual/evpkdf.es.js +0 -5
  44. package/dist/_virtual/evpkdf.es.js.map +0 -1
  45. package/dist/_virtual/format-hex.es.js +0 -5
  46. package/dist/_virtual/format-hex.es.js.map +0 -1
  47. package/dist/_virtual/hmac.es.js +0 -5
  48. package/dist/_virtual/hmac.es.js.map +0 -1
  49. package/dist/_virtual/index.es7.js +0 -5
  50. package/dist/_virtual/index.es7.js.map +0 -1
  51. package/dist/_virtual/lib-typedarrays.es.js +0 -5
  52. package/dist/_virtual/lib-typedarrays.es.js.map +0 -1
  53. package/dist/_virtual/md5.es.js +0 -5
  54. package/dist/_virtual/md5.es.js.map +0 -1
  55. package/dist/_virtual/mode-cfb.es.js +0 -5
  56. package/dist/_virtual/mode-cfb.es.js.map +0 -1
  57. package/dist/_virtual/mode-ctr-gladman.es.js +0 -5
  58. package/dist/_virtual/mode-ctr-gladman.es.js.map +0 -1
  59. package/dist/_virtual/mode-ctr.es.js +0 -5
  60. package/dist/_virtual/mode-ctr.es.js.map +0 -1
  61. package/dist/_virtual/mode-ecb.es.js +0 -5
  62. package/dist/_virtual/mode-ecb.es.js.map +0 -1
  63. package/dist/_virtual/mode-ofb.es.js +0 -5
  64. package/dist/_virtual/mode-ofb.es.js.map +0 -1
  65. package/dist/_virtual/pad-ansix923.es.js +0 -5
  66. package/dist/_virtual/pad-ansix923.es.js.map +0 -1
  67. package/dist/_virtual/pad-iso10126.es.js +0 -5
  68. package/dist/_virtual/pad-iso10126.es.js.map +0 -1
  69. package/dist/_virtual/pad-iso97971.es.js +0 -5
  70. package/dist/_virtual/pad-iso97971.es.js.map +0 -1
  71. package/dist/_virtual/pad-nopadding.es.js +0 -5
  72. package/dist/_virtual/pad-nopadding.es.js.map +0 -1
  73. package/dist/_virtual/pad-zeropadding.es.js +0 -5
  74. package/dist/_virtual/pad-zeropadding.es.js.map +0 -1
  75. package/dist/_virtual/pbkdf2.es.js +0 -5
  76. package/dist/_virtual/pbkdf2.es.js.map +0 -1
  77. package/dist/_virtual/rabbit-legacy.es.js +0 -5
  78. package/dist/_virtual/rabbit-legacy.es.js.map +0 -1
  79. package/dist/_virtual/rabbit.es.js +0 -5
  80. package/dist/_virtual/rabbit.es.js.map +0 -1
  81. package/dist/_virtual/rc4.es.js +0 -5
  82. package/dist/_virtual/rc4.es.js.map +0 -1
  83. package/dist/_virtual/ripemd160.es.js +0 -5
  84. package/dist/_virtual/ripemd160.es.js.map +0 -1
  85. package/dist/_virtual/sha1.es.js +0 -5
  86. package/dist/_virtual/sha1.es.js.map +0 -1
  87. package/dist/_virtual/sha224.es.js +0 -5
  88. package/dist/_virtual/sha224.es.js.map +0 -1
  89. package/dist/_virtual/sha256.es.js +0 -5
  90. package/dist/_virtual/sha256.es.js.map +0 -1
  91. package/dist/_virtual/sha3.es.js +0 -5
  92. package/dist/_virtual/sha3.es.js.map +0 -1
  93. package/dist/_virtual/sha384.es.js +0 -5
  94. package/dist/_virtual/sha384.es.js.map +0 -1
  95. package/dist/_virtual/sha512.es.js +0 -5
  96. package/dist/_virtual/sha512.es.js.map +0 -1
  97. package/dist/_virtual/tripledes.es.js +0 -5
  98. package/dist/_virtual/tripledes.es.js.map +0 -1
  99. package/dist/_virtual/x64-core.es.js +0 -5
  100. package/dist/_virtual/x64-core.es.js.map +0 -1
  101. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/aes.es.js +0 -163
  102. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/aes.es.js.map +0 -1
  103. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/blowfish.es.js +0 -1198
  104. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/blowfish.es.js.map +0 -1
  105. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/cipher-core.es.js +0 -650
  106. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/cipher-core.es.js.map +0 -1
  107. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/core.es.js +0 -614
  108. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/core.es.js.map +0 -1
  109. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/enc-base64.es.js +0 -113
  110. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/enc-base64.es.js.map +0 -1
  111. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/enc-base64url.es.js +0 -124
  112. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/enc-base64url.es.js.map +0 -1
  113. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/enc-utf16.es.js +0 -123
  114. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/enc-utf16.es.js.map +0 -1
  115. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/evpkdf.es.js +0 -98
  116. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/evpkdf.es.js.map +0 -1
  117. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/format-hex.es.js +0 -66
  118. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/format-hex.es.js.map +0 -1
  119. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/hmac.es.js +0 -112
  120. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/hmac.es.js.map +0 -1
  121. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/index.es.js +0 -52
  122. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/index.es.js.map +0 -1
  123. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/lib-typedarrays.es.js +0 -50
  124. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/lib-typedarrays.es.js.map +0 -1
  125. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/md5.es.js +0 -184
  126. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/md5.es.js.map +0 -1
  127. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-cfb.es.js +0 -58
  128. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-cfb.es.js.map +0 -1
  129. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ctr-gladman.es.js +0 -86
  130. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ctr-gladman.es.js.map +0 -1
  131. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ctr.es.js +0 -46
  132. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ctr.es.js.map +0 -1
  133. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ecb.es.js +0 -37
  134. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ecb.es.js.map +0 -1
  135. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ofb.es.js +0 -44
  136. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/mode-ofb.es.js.map +0 -1
  137. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-ansix923.es.js +0 -38
  138. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-ansix923.es.js.map +0 -1
  139. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-iso10126.es.js +0 -34
  140. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-iso10126.es.js.map +0 -1
  141. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-iso97971.es.js +0 -33
  142. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-iso97971.es.js.map +0 -1
  143. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-nopadding.es.js +0 -29
  144. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-nopadding.es.js.map +0 -1
  145. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-zeropadding.es.js +0 -40
  146. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pad-zeropadding.es.js.map +0 -1
  147. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pbkdf2.es.js +0 -105
  148. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/pbkdf2.es.js.map +0 -1
  149. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/rabbit-legacy.es.js +0 -135
  150. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/rabbit-legacy.es.js.map +0 -1
  151. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/rabbit.es.js +0 -138
  152. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/rabbit.es.js.map +0 -1
  153. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/rc4.es.js +0 -92
  154. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/rc4.es.js.map +0 -1
  155. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/ripemd160.es.js +0 -486
  156. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/ripemd160.es.js.map +0 -1
  157. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha1.es.js +0 -96
  158. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha1.es.js.map +0 -1
  159. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha224.es.js +0 -51
  160. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha224.es.js.map +0 -1
  161. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha256.es.js +0 -126
  162. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha256.es.js.map +0 -1
  163. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha3.es.js +0 -208
  164. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha3.es.js.map +0 -1
  165. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha384.es.js +0 -53
  166. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha384.es.js.map +0 -1
  167. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha512.es.js +0 -288
  168. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/sha512.es.js.map +0 -1
  169. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/tripledes.es.js +0 -790
  170. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/tripledes.es.js.map +0 -1
  171. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/x64-core.es.js +0 -262
  172. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/x64-core.es.js.map +0 -1
@@ -2,33 +2,8 @@ var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof win
2
2
  function getDefaultExportFromCjs(x) {
3
3
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
4
4
  }
5
- function getAugmentedNamespace(n) {
6
- if (n.__esModule) return n;
7
- var f = n.default;
8
- if (typeof f == "function") {
9
- var a = function a2() {
10
- if (this instanceof a2) {
11
- return Reflect.construct(f, arguments, this.constructor);
12
- }
13
- return f.apply(this, arguments);
14
- };
15
- a.prototype = f.prototype;
16
- } else a = {};
17
- Object.defineProperty(a, "__esModule", { value: true });
18
- Object.keys(n).forEach(function(k) {
19
- var d = Object.getOwnPropertyDescriptor(n, k);
20
- Object.defineProperty(a, k, d.get ? d : {
21
- enumerable: true,
22
- get: function() {
23
- return n[k];
24
- }
25
- });
26
- });
27
- return a;
28
- }
29
5
  export {
30
6
  commonjsGlobal,
31
- getAugmentedNamespace,
32
7
  getDefaultExportFromCjs
33
8
  };
34
9
  //# sourceMappingURL=_commonjsHelpers.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_commonjsHelpers.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"_commonjsHelpers.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -1,5 +1,5 @@
1
- var cryptoJs = { exports: {} };
1
+ var propTypes = { exports: {} };
2
2
  export {
3
- cryptoJs as __module
3
+ propTypes as __module
4
4
  };
5
5
  //# sourceMappingURL=index.es2.js.map
@@ -1,5 +1,5 @@
1
- var coWebWorker = { exports: {} };
1
+ var classnames = { exports: {} };
2
2
  export {
3
- coWebWorker as __module
3
+ classnames as __module
4
4
  };
5
5
  //# sourceMappingURL=index.es3.js.map
@@ -1,5 +1,5 @@
1
- var propTypes = { exports: {} };
1
+ var coWebWorker = { exports: {} };
2
2
  export {
3
- propTypes as __module
3
+ coWebWorker as __module
4
4
  };
5
5
  //# sourceMappingURL=index.es4.js.map
@@ -1,5 +1,5 @@
1
- var classnames = { exports: {} };
1
+ var reactIs = { exports: {} };
2
2
  export {
3
- classnames as __module
3
+ reactIs as __module
4
4
  };
5
5
  //# sourceMappingURL=index.es5.js.map
@@ -1,5 +1,5 @@
1
- var reactIs = { exports: {} };
1
+ var cjs = {};
2
2
  export {
3
- reactIs as __module
3
+ cjs as __exports
4
4
  };
5
5
  //# sourceMappingURL=index.es6.js.map
@@ -1,5 +1,5 @@
1
1
  import { getDefaultExportFromCjs } from "../../../../../_virtual/_commonjsHelpers.es.js";
2
- import { __module as classnames$1 } from "../../../../../_virtual/index.es5.js";
2
+ import { __module as classnames$1 } from "../../../../../_virtual/index.es3.js";
3
3
  /*!
4
4
  Copyright (c) 2018 Jed Watson.
5
5
  Licensed under the MIT License (MIT), see
@@ -1,5 +1,5 @@
1
1
  import { getDefaultExportFromCjs } from "../../../../../_virtual/_commonjsHelpers.es.js";
2
- import { __module as coWebWorker } from "../../../../../_virtual/index.es3.js";
2
+ import { __module as coWebWorker } from "../../../../../_virtual/index.es4.js";
3
3
  class CrossOriginWorker extends Worker {
4
4
  constructor(scriptUrl) {
5
5
  const b = new Blob([`importScripts('${new URL(scriptUrl).toString()}')`], { type: "application/javascript" });
@@ -1,5 +1,5 @@
1
1
  import { getDefaultExportFromCjs } from "../../../../../_virtual/_commonjsHelpers.es.js";
2
- import { __module as propTypes } from "../../../../../_virtual/index.es4.js";
2
+ import { __module as propTypes } from "../../../../../_virtual/index.es2.js";
3
3
  import { __require as requireReactIs } from "../../../react-is@16.13.1/node_modules/react-is/index.es.js";
4
4
  import { __require as requireFactoryWithTypeCheckers } from "./factoryWithTypeCheckers.es.js";
5
5
  import { __require as requireFactoryWithThrowingShims } from "./factoryWithThrowingShims.es.js";
@@ -1,4 +1,4 @@
1
- import { __module as reactIs } from "../../../../../_virtual/index.es6.js";
1
+ import { __module as reactIs } from "../../../../../_virtual/index.es5.js";
2
2
  import { __require as requireReactIs_production_min } from "./cjs/react-is.production.min.es.js";
3
3
  import { __require as requireReactIs_development } from "./cjs/react-is.development.es.js";
4
4
  var hasRequiredReactIs;
@@ -2,7 +2,7 @@ import { getDefaultExportFromCjs, commonjsGlobal } from "../../../../../../_virt
2
2
  import "../../../../style-to-object@1.0.8/node_modules/style-to-object/cjs/index.es.js";
3
3
  import "./utilities.es.js";
4
4
  import { __exports as utilities } from "../../../../../../_virtual/utilities.es.js";
5
- import { __exports as cjs$1 } from "../../../../../../_virtual/index.es7.js";
5
+ import { __exports as cjs$1 } from "../../../../../../_virtual/index.es6.js";
6
6
  var __importDefault = commonjsGlobal && commonjsGlobal.__importDefault || function(mod) {
7
7
  return mod && mod.__esModule ? mod : { "default": mod };
8
8
  };
@@ -1,5 +1,5 @@
1
1
  import { commonjsGlobal } from "../../../../../../_virtual/_commonjsHelpers.es.js";
2
- import { __exports as cjs } from "../../../../../../_virtual/index.es7.js";
2
+ import { __exports as cjs } from "../../../../../../_virtual/index.es6.js";
3
3
  import { i as inlineStyleParser } from "../../../../inline-style-parser@0.2.4/node_modules/inline-style-parser/index.es.js";
4
4
  var __importDefault = commonjsGlobal && commonjsGlobal.__importDefault || function(mod) {
5
5
  return mod && mod.__esModule ? mod : { "default": mod };
@@ -2,25 +2,11 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import CrossOriginWorker from "../../../node_modules/.pnpm/co-web-worker@1.0.1/node_modules/co-web-worker/index.es.js";
5
+ import { requestMicrophonePermission } from "../utils.es.js";
5
6
  import workerJsString from "./processorSource/processor.worker.es.js";
6
7
  import workletJsString from "./processorSource/processor.worklet.es.js";
7
8
  const { AudioWorkletNode } = window;
8
9
  const isAudioWorkletSupported = !!AudioWorkletNode;
9
- async function requestMicrophonePermission() {
10
- const mediaDevices = navigator.mediaDevices ?? {};
11
- if (typeof mediaDevices.getUserMedia === "function") {
12
- console.time("mediaDevices.getUserMedia:");
13
- const stream = await mediaDevices.getUserMedia({ audio: true, video: false });
14
- console.timeEnd("mediaDevices.getUserMedia:");
15
- return stream;
16
- }
17
- if (typeof navigator.getUserMedia === "function") {
18
- return new Promise((resolve, reject) => {
19
- navigator.getUserMedia({ audio: true, video: false }, resolve, reject);
20
- });
21
- }
22
- throw new Error("浏览器不支持录音!");
23
- }
24
10
  const generateWorkletUrl = (type) => {
25
11
  const jsStr = type === "worker" ? workerJsString : workletJsString;
26
12
  const blob = new Blob([jsStr], { type: "application/javascript" });
@@ -1 +1 @@
1
- {"version":3,"file":"RecorderManager.es.js","sources":["../../../../src/utils/xunFeiVoice/RecorderManager/RecorderManager.js"],"sourcesContent":["/**\n * 录音管理器 - 用于处理音频录制及处理\n */\nimport CrossOriginWorker from 'co-web-worker';\nimport workerJsString from './processorSource/processor.worker.js?raw';\nimport workletJsString from './processorSource/processor.worklet.js?raw';\n\nconst { AudioWorkletNode } = window;\nconst isAudioWorkletSupported = !!AudioWorkletNode;\n\n/**\n * 获取用户媒体设备权限\n * @returns {Promise<MediaStream>}\n */\nasync function requestMicrophonePermission() {\n const mediaDevices = navigator.mediaDevices ?? {};\n\n if (typeof mediaDevices.getUserMedia === 'function') {\n console.time('mediaDevices.getUserMedia:');\n const stream = await mediaDevices.getUserMedia({ audio: true, video: false });\n console.timeEnd('mediaDevices.getUserMedia:');\n return stream;\n }\n\n if (typeof navigator.getUserMedia === 'function') {\n return new Promise((resolve, reject) => {\n navigator.getUserMedia({ audio: true, video: false }, resolve, reject);\n });\n }\n\n throw new Error('浏览器不支持录音!');\n}\n\n/** @type {(type: 'worker' | 'worklet') => string} */\nconst generateWorkletUrl = (type) => {\n const jsStr = type === 'worker' ? workerJsString : workletJsString;\n const blob = new Blob([jsStr], { type: 'application/javascript' });\n const scriptUrl = URL.createObjectURL(blob);\n return scriptUrl;\n};\n\n/**\n * 初始化音频处理器\n * @param {AudioContext} audioContext\n * @returns {Promise<AudioWorkletNode|{port: Worker}>}\n */\nasync function initializeAudioProcessor(audioContext) {\n if (isAudioWorkletSupported) {\n if (audioContext.state !== 'closed') {\n await audioContext.audioWorklet.addModule(generateWorkletUrl('worklet'));\n return new AudioWorkletNode(audioContext, 'processor-worklet');\n }\n throw new Error('AudioWorkletNode can not be created, when audioContext is closed.');\n }\n\n const worker = new CrossOriginWorker(generateWorkletUrl('worker'));\n return { port: worker };\n}\n\nconst NOOP = () => {};\n/**\n * 录音管理器\n */\nexport class RecorderManager {\n /** @type {Array<ArrayBuffer>} */\n audioBuffers = [];\n /** @type {(params: RecordFrameInfo) => void} */\n onFrameRecorded = NOOP;\n onStart = NOOP;\n /** @type {(currStatus: 'stop' | 'closed' | 'initialized' | 'initializing' | 'default') => void} */\n onStop = NOOP;\n /** @type {(data: Array<ArrayBuffer>) => void} */\n onLastFrame = NOOP;\n /** @type {'stop' | 'closed' | 'initialized' | 'initializing' | 'default'} */\n status = 'default';\n /** @type {AudioContext} */\n audioContext = null;\n /** @type {AudioWorkletNode} */\n audioWorkletNode = null;\n /** @type {MediaStreamAudioSourceNode} */\n mediaSource = null;\n /** @type {MediaStream} */\n mediaStream = null;\n\n constructor() {}\n\n /**\n * 开始录音\n * @param {{\n * sampleRate?: number,\n * frameSize?: number,\n * arrayBufferType?: string\n * }} config 录音配置\n */\n async start(config) {\n this.status = 'initializing';\n try {\n this.audioBuffers = [];\n // 获取音频流并创建处理节点\n const mediaStream = await requestMicrophonePermission();\n this.mediaStream = mediaStream;\n const audioContext = this.createAudioContext(mediaStream, config.sampleRate);\n this.audioContext = audioContext;\n this.audioWorkletNode = await initializeAudioProcessor(audioContext);\n // 防止开始后立刻结束,资源没有得到释放。\n if (this.status === 'stop' || this.status === 'closed') {\n this.close();\n return;\n }\n\n // 初始化音频处理\n this.setupAudioProcessing(audioContext, mediaStream, this.audioWorkletNode, config);\n\n this.status = 'initialized';\n this.onStart?.();\n } catch (error) {\n console.error('Failed to start recording:', error);\n throw error;\n }\n }\n\n /**\n * 创建音频上下文\n * @private\n */\n createAudioContext(mediaStream, targetSampleRate) {\n try {\n const context = new (window.AudioContext || window.webkitAudioContext)({\n sampleRate: targetSampleRate,\n });\n context.createMediaStreamSource(mediaStream);\n return context;\n } catch (error) {\n // 回退处理:当目标采样率不支持时使用默认采样率\n const fallbackContext = new (window.AudioContext || window.webkitAudioContext)();\n console.warn(`Using default sample rate: ${fallbackContext.sampleRate}`);\n fallbackContext.createMediaStreamSource(mediaStream);\n return fallbackContext;\n }\n }\n\n /**\n * 配置音频处理管线\n * @private\n */\n setupAudioProcessing(audioContext, mediaStream, audioWorkletNode, config) {\n // 创建媒体流源节点\n const mediaSource = audioContext.createMediaStreamSource(mediaStream);\n this.mediaSource = mediaSource;\n\n // 配置处理器通信\n audioWorkletNode.port.postMessage({\n type: 'init',\n data: {\n frameSize: config.frameSize,\n toSampleRate: config.sampleRate || audioContext.sampleRate,\n fromSampleRate: audioContext.sampleRate,\n arrayBufferType: config.arrayBufferType || 'short16',\n },\n });\n\n // 处理音频数据回调\n audioWorkletNode.port.onmessage = (e) => {\n const { data } = e;\n if (config.frameSize && this.onFrameRecorded) {\n this.onFrameRecorded(data);\n }\n\n if (data.frameBuffer) {\n this.audioBuffers.push(data.frameBuffer);\n }\n\n if (data.isLastFrame) {\n if (!isAudioWorkletSupported) {\n audioWorkletNode.port?.terminate();\n }\n this.mediaSource?.disconnect();\n if (this.audioContext?.state !== 'closed') {\n this.audioContext?.close();\n }\n this.onLastFrame?.(this.audioBuffers);\n }\n };\n\n // 连接音频节点\n if (isAudioWorkletSupported) {\n mediaSource.connect(audioWorkletNode);\n } else {\n const scriptProcessor = audioContext.createScriptProcessor(0, 1, 1);\n scriptProcessor.onaudioprocess = ({ inputBuffer }) => {\n audioWorkletNode.port.postMessage({\n type: 'message',\n data: inputBuffer.getChannelData(0),\n });\n };\n mediaSource.connect(scriptProcessor);\n scriptProcessor.connect(audioContext.destination);\n }\n }\n\n /** 停止录音 */\n stop() {\n this.onStop?.(this.status);\n this.status = 'stop';\n /** 暂停音频上下文对象中的进度,并暂时剥离进程对音频设备硬件的访问权限,减少 CPU 和电池的使用 */\n if (this.audioContext?.state === 'running') {\n this.audioContext.suspend();\n }\n if (this.audioWorkletNode) {\n this.audioWorkletNode.port.postMessage({ type: 'stop' });\n }\n }\n\n close() {\n this.audioBuffers = [];\n this.mediaSource?.disconnect();\n this.status = 'closed';\n if (this.audioContext?.state !== 'closed') {\n return this.audioContext?.close();\n }\n }\n\n destroy() {\n this.close();\n this.mediaStream?.getTracks()?.forEach((track) => track.stop());\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RecorderManager.es.js","sources":["../../../../src/utils/xunFeiVoice/RecorderManager/RecorderManager.js"],"sourcesContent":["/**\n * 录音管理器 - 用于处理音频录制及处理\n */\nimport CrossOriginWorker from 'co-web-worker';\nimport { requestMicrophonePermission } from '../utils';\nimport workerJsString from './processorSource/processor.worker.js?raw';\nimport workletJsString from './processorSource/processor.worklet.js?raw';\n\nconst { AudioWorkletNode } = window;\nconst isAudioWorkletSupported = !!AudioWorkletNode;\n\n/** @type {(type: 'worker' | 'worklet') => string} */\nconst generateWorkletUrl = (type) => {\n const jsStr = type === 'worker' ? workerJsString : workletJsString;\n const blob = new Blob([jsStr], { type: 'application/javascript' });\n const scriptUrl = URL.createObjectURL(blob);\n return scriptUrl;\n};\n\n/**\n * 初始化音频处理器\n * @param {AudioContext} audioContext\n * @returns {Promise<AudioWorkletNode|{port: Worker}>}\n */\nasync function initializeAudioProcessor(audioContext) {\n if (isAudioWorkletSupported) {\n if (audioContext.state !== 'closed') {\n await audioContext.audioWorklet.addModule(generateWorkletUrl('worklet'));\n return new AudioWorkletNode(audioContext, 'processor-worklet');\n }\n throw new Error('AudioWorkletNode can not be created, when audioContext is closed.');\n }\n\n const worker = new CrossOriginWorker(generateWorkletUrl('worker'));\n return { port: worker };\n}\n\nconst NOOP = () => {};\n/**\n * 录音管理器\n */\nexport class RecorderManager {\n /** @type {Array<ArrayBuffer>} */\n audioBuffers = [];\n /** @type {(params: RecordFrameInfo) => void} */\n onFrameRecorded = NOOP;\n onStart = NOOP;\n /** @type {(currStatus: 'stop' | 'closed' | 'initialized' | 'initializing' | 'default') => void} */\n onStop = NOOP;\n /** @type {(data: Array<ArrayBuffer>) => void} */\n onLastFrame = NOOP;\n /** @type {'stop' | 'closed' | 'initialized' | 'initializing' | 'default'} */\n status = 'default';\n /** @type {AudioContext} */\n audioContext = null;\n /** @type {AudioWorkletNode} */\n audioWorkletNode = null;\n /** @type {MediaStreamAudioSourceNode} */\n mediaSource = null;\n /** @type {MediaStream} */\n mediaStream = null;\n constructor() {}\n\n /**\n * 开始录音\n * @param {{\n * sampleRate?: number,\n * frameSize?: number,\n * arrayBufferType?: string\n * }} config 录音配置\n */\n async start(config) {\n this.status = 'initializing';\n try {\n this.audioBuffers = [];\n // 获取音频流并创建处理节点\n const mediaStream = await requestMicrophonePermission();\n this.mediaStream = mediaStream;\n const audioContext = this.createAudioContext(mediaStream, config.sampleRate);\n this.audioContext = audioContext;\n this.audioWorkletNode = await initializeAudioProcessor(audioContext);\n // 防止开始后立刻结束,资源没有得到释放。\n if (this.status === 'stop' || this.status === 'closed') {\n this.close();\n return;\n }\n\n // 初始化音频处理\n this.setupAudioProcessing(audioContext, mediaStream, this.audioWorkletNode, config);\n\n this.status = 'initialized';\n this.onStart?.();\n } catch (error) {\n console.error('Failed to start recording:', error);\n throw error;\n }\n }\n\n /**\n * 创建音频上下文\n * @private\n */\n createAudioContext(mediaStream, targetSampleRate) {\n try {\n const context = new (window.AudioContext || window.webkitAudioContext)({\n sampleRate: targetSampleRate,\n });\n context.createMediaStreamSource(mediaStream);\n return context;\n } catch (error) {\n // 回退处理:当目标采样率不支持时使用默认采样率\n const fallbackContext = new (window.AudioContext || window.webkitAudioContext)();\n console.warn(`Using default sample rate: ${fallbackContext.sampleRate}`);\n fallbackContext.createMediaStreamSource(mediaStream);\n return fallbackContext;\n }\n }\n\n /**\n * 配置音频处理管线\n * @private\n */\n setupAudioProcessing(audioContext, mediaStream, audioWorkletNode, config) {\n // 创建媒体流源节点\n const mediaSource = audioContext.createMediaStreamSource(mediaStream);\n this.mediaSource = mediaSource;\n\n // 配置处理器通信\n audioWorkletNode.port.postMessage({\n type: 'init',\n data: {\n frameSize: config.frameSize,\n toSampleRate: config.sampleRate || audioContext.sampleRate,\n fromSampleRate: audioContext.sampleRate,\n arrayBufferType: config.arrayBufferType || 'short16',\n },\n });\n\n // 处理音频数据回调\n audioWorkletNode.port.onmessage = (e) => {\n const { data } = e;\n if (config.frameSize && this.onFrameRecorded) {\n this.onFrameRecorded(data);\n }\n\n if (data.frameBuffer) {\n this.audioBuffers.push(data.frameBuffer);\n }\n\n if (data.isLastFrame) {\n if (!isAudioWorkletSupported) {\n audioWorkletNode.port?.terminate();\n }\n this.mediaSource?.disconnect();\n if (this.audioContext?.state !== 'closed') {\n this.audioContext?.close();\n }\n this.onLastFrame?.(this.audioBuffers);\n }\n };\n\n // 连接音频节点\n if (isAudioWorkletSupported) {\n mediaSource.connect(audioWorkletNode);\n } else {\n const scriptProcessor = audioContext.createScriptProcessor(0, 1, 1);\n scriptProcessor.onaudioprocess = ({ inputBuffer }) => {\n audioWorkletNode.port.postMessage({\n type: 'message',\n data: inputBuffer.getChannelData(0),\n });\n };\n mediaSource.connect(scriptProcessor);\n scriptProcessor.connect(audioContext.destination);\n }\n }\n\n /** 停止录音 */\n stop() {\n this.onStop?.(this.status);\n this.status = 'stop';\n /** 暂停音频上下文对象中的进度,并暂时剥离进程对音频设备硬件的访问权限,减少 CPU 和电池的使用 */\n if (this.audioContext?.state === 'running') {\n this.audioContext.suspend();\n }\n if (this.audioWorkletNode) {\n this.audioWorkletNode.port.postMessage({ type: 'stop' });\n }\n }\n\n close() {\n this.audioBuffers = [];\n this.mediaSource?.disconnect();\n this.status = 'closed';\n if (this.audioContext?.state !== 'closed') {\n return this.audioContext?.close();\n }\n }\n\n destroy() {\n this.close();\n this.mediaStream?.getTracks()?.forEach((track) => track.stop());\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { RecorderManager } from './RecorderManager';
2
2
  import { OnMessageInfo, RecordFrameInfo } from './types';
3
3
 
4
- type errorType = 'socketConnect' | 'message' | 'record';
4
+ type errorType = 'socketConnect' | 'message' | 'record' | 'recordPermission';
5
5
  export interface XFStreamVoiceManagerConfig {
6
6
  onError: (errInfo: {
7
7
  type: errorType;
@@ -13,7 +13,7 @@ export interface XFStreamVoiceManagerConfig {
13
13
  */
14
14
  onMessage: (info: OnMessageInfo) => void;
15
15
  /** webSocket 关闭的回调。
16
- * 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket
16
+ * - 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket
17
17
  */
18
18
  onWsClose: () => void;
19
19
  /**
@@ -33,7 +33,14 @@ export interface XFStreamVoiceManagerConfig {
33
33
  * 获取链接讯飞语音识别地址的 url
34
34
  * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B
35
35
  */
36
- getWebSocketUrl?: () => string;
36
+ getWebSocketUrl: () => string;
37
+ /**
38
+ * 获取录音权限的回调, 用户初次授权或者没有给录音权限时,会调用此回调。
39
+ * @param status 权限状态。
40
+ * - denied: 已拒绝
41
+ * - granted: 已授权
42
+ */
43
+ onGetMicrophonePermission?: (status: 'denied' | 'granted') => void;
37
44
  /**
38
45
  * 讯飞语音的 appid
39
46
  */
@@ -60,6 +67,7 @@ export declare class XFStreamVoiceManager {
60
67
  startTimeStamp: number;
61
68
  recordStatus: 'start' | 'stop';
62
69
  readyToSend: boolean;
70
+ microphonePermissionStatus: 'denied' | 'granted' | 'prompt';
63
71
  private inStopping;
64
72
  private stopResolve;
65
73
  constructor(config: XFStreamVoiceManagerConfig);
@@ -70,7 +78,7 @@ export declare class XFStreamVoiceManager {
70
78
  startNewSocket(): void;
71
79
  onFrameRecorded({ isLastFrame, frameBuffer }: RecordFrameInfo): void;
72
80
  initializeRecorder(): void;
73
- startRecord(): void;
81
+ startRecord(): Promise<void>;
74
82
  stopRecord(): Promise<void>;
75
83
  closeWs(): void;
76
84
  /** 开启持续监听 */
@@ -1,30 +1,9 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import CryptoJS from "../../node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/index.es.js";
5
- import { toBase64, isWsClosed } from "./utils.es.js";
4
+ import { toBase64, checkMicrophonePermission, requestMicrophonePermission, isWsClosed } from "./utils.es.js";
6
5
  import { RecorderManager } from "./RecorderManager/RecorderManager.es.js";
7
6
  let startCount = 0;
8
- const APPID = "93b73e33";
9
- const API_SECRET = "ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0";
10
- const API_KEY = "fe43de7a071e3a32ec03c6f09fe7bedf";
11
- function getWebSocketUrl() {
12
- const url = "wss://iat-api.xfyun.cn/v2/iat";
13
- const host = "iat-api.xfyun.cn";
14
- const apiKey = API_KEY;
15
- const apiSecret = API_SECRET;
16
- const date = (/* @__PURE__ */ new Date()).toUTCString();
17
- const algorithm = "hmac-sha256";
18
- const headers = "host date request-line";
19
- const signatureOrigin = `host: ${host}
20
- date: ${date}
21
- GET /v2/iat HTTP/1.1`;
22
- const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);
23
- const signature = CryptoJS.enc.Base64.stringify(signatureSha);
24
- const authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
25
- const authorization = window.btoa(authorizationOrigin);
26
- return `${url}?authorization=${authorization}&date=${date}&host=${host}`;
27
- }
28
7
  class XFStreamVoiceManager {
29
8
  constructor(config) {
30
9
  __publicField(this, "wsInstance", null);
@@ -36,6 +15,7 @@ class XFStreamVoiceManager {
36
15
  __publicField(this, "startTimeStamp", 0);
37
16
  __publicField(this, "recordStatus", "stop");
38
17
  __publicField(this, "readyToSend", false);
18
+ __publicField(this, "microphonePermissionStatus", "prompt");
39
19
  __publicField(this, "inStopping", false);
40
20
  __publicField(this, "stopResolve", null);
41
21
  this.config = config;
@@ -111,7 +91,7 @@ class XFStreamVoiceManager {
111
91
  }));
112
92
  const params = {
113
93
  common: {
114
- app_id: this.config.xunFeiAppId || APPID
94
+ app_id: this.config.xunFeiAppId
115
95
  },
116
96
  business: {
117
97
  language: "zh_cn",
@@ -130,7 +110,12 @@ class XFStreamVoiceManager {
130
110
  this.sendMessage(JSON.stringify(params));
131
111
  } catch (error) {
132
112
  console.error("record error:", error);
133
- this.config.onError({ type: "record", message: "录音失败, 请检查权限!" });
113
+ if (error.name === "NotAllowedError") {
114
+ this.config.onError({ type: "recordPermission", message: "没有录音权限" });
115
+ this.closeWs();
116
+ return;
117
+ }
118
+ this.config.onError({ type: "record", message: "开始录音失败!" });
134
119
  }
135
120
  }
136
121
  startNewSocket() {
@@ -142,7 +127,11 @@ class XFStreamVoiceManager {
142
127
  (/* @__PURE__ */ new Date()).toLocaleString()
143
128
  );
144
129
  startCount++;
145
- const websocketUrl = ((_b = (_a = this.config).getWebSocketUrl) == null ? void 0 : _b.call(_a)) || getWebSocketUrl();
130
+ const websocketUrl = (_b = (_a = this.config).getWebSocketUrl) == null ? void 0 : _b.call(_a);
131
+ if (!websocketUrl) {
132
+ this.config.onError({ type: "socketConnect", message: "获取 websocket url 失败" });
133
+ return;
134
+ }
146
135
  if ("WebSocket" in window) {
147
136
  this.wsInstance = new WebSocket(websocketUrl);
148
137
  } else {
@@ -218,7 +207,8 @@ class XFStreamVoiceManager {
218
207
  (_b = (_a = this.config).onRecordStart) == null ? void 0 : _b.call(_a);
219
208
  };
220
209
  }
221
- startRecord() {
210
+ async startRecord() {
211
+ var _a, _b;
222
212
  if (this.isDestroy) {
223
213
  return;
224
214
  }
@@ -226,6 +216,29 @@ class XFStreamVoiceManager {
226
216
  this.startTimeStamp = Date.now();
227
217
  this.resultText = "";
228
218
  this.resultTextTemp = "";
219
+ if (this.microphonePermissionStatus !== "granted") {
220
+ const currMicrophoneStatus = await checkMicrophonePermission();
221
+ this.microphonePermissionStatus = currMicrophoneStatus;
222
+ if (currMicrophoneStatus === "prompt") {
223
+ await requestMicrophonePermission().then(() => {
224
+ var _a2, _b2;
225
+ (_b2 = (_a2 = this.config).onGetMicrophonePermission) == null ? void 0 : _b2.call(_a2, "granted");
226
+ }).catch((error) => {
227
+ var _a2, _b2;
228
+ console.error("requestMicrophonePermission error:", error);
229
+ this.config.onError({ type: "recordPermission", message: "没有录音权限" });
230
+ (_b2 = (_a2 = this.config).onGetMicrophonePermission) == null ? void 0 : _b2.call(_a2, "denied");
231
+ });
232
+ this.stopRecord();
233
+ return;
234
+ }
235
+ if (currMicrophoneStatus === "denied") {
236
+ this.config.onError({ type: "recordPermission", message: "没有录音权限" });
237
+ this.stopRecord();
238
+ (_b = (_a = this.config).onGetMicrophonePermission) == null ? void 0 : _b.call(_a, "denied");
239
+ return;
240
+ }
241
+ }
229
242
  this.startNewSocket();
230
243
  }
231
244
  async stopRecord() {
@@ -1 +1 @@
1
- {"version":3,"file":"XFStreamVoiceManager.es.js","sources":["../../../src/utils/xunFeiVoice/XFStreamVoiceManager.ts"],"sourcesContent":["import CryptoJS from 'crypto-js';\nimport { RecorderManager } from './RecorderManager';\nimport type { OnMessageInfo, RecordFrameInfo } from './types';\nimport { isWsClosed, toBase64 } from './utils';\n\ntype errorType = 'socketConnect' | 'message' | 'record';\nlet startCount = 0;\n\nexport interface XFStreamVoiceManagerConfig {\n onError: (errInfo: { type: errorType; message: string }) => void;\n /** ws 收到消息的回调\n * @param info.message 消息内容\n * @param info.isLatest 是否是最后一条消息。 isLatest 为 true 时, message 为完整的语音识别结果。\n */\n onMessage: (info: OnMessageInfo) => void;\n /** webSocket 关闭的回调。\n * 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket\n */\n onWsClose: () => void;\n /**\n * 录音开始的回调\n */\n onRecordStart?: () => void;\n /**\n * 录音开始的回调\n */\n onRecordStop?: () => void;\n /**\n * 录音最后一帧的回调。\n * @param totalData 所有的录音数据, ArrayBuffer[]。\n */\n onLastFrame?: (totalData: ArrayBuffer[]) => void;\n /**\n * 获取链接讯飞语音识别地址的 url\n * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n */\n getWebSocketUrl?: () => string;\n /**\n * 讯飞语音的 appid\n */\n xunFeiAppId: string;\n}\n\n/** 现在前端mock,后续移到接口下发 */\nconst APPID = '93b73e33';\nconst API_SECRET = 'ZGJhMzQ5ZTJlMDgyYmE1ZTFlZDlmYjg0';\nconst API_KEY = 'fe43de7a071e3a32ec03c6f09fe7bedf';\n/**\n * 获取websocket url\n * 该接口需要后端提供,这里为了方便前端处理\n */\nfunction getWebSocketUrl() {\n // 请求地址根据语种不同变化\n const url = 'wss://iat-api.xfyun.cn/v2/iat';\n const host = 'iat-api.xfyun.cn';\n const apiKey = API_KEY;\n const apiSecret = API_SECRET;\n const date = new Date().toUTCString();\n const algorithm = 'hmac-sha256';\n const headers = 'host date request-line';\n const signatureOrigin = `host: ${host}\\ndate: ${date}\\nGET /v2/iat HTTP/1.1`;\n const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret);\n const signature = CryptoJS.enc.Base64.stringify(signatureSha);\n const authorizationOrigin = `api_key=\"${apiKey}\", algorithm=\"${algorithm}\", headers=\"${headers}\", signature=\"${signature}\"`;\n const authorization = window.btoa(authorizationOrigin);\n return `${url}?authorization=${authorization}&date=${date}&host=${host}`;\n}\n\n/**\n * 讯飞流式语音识别\n * 大致流程:\n * 1. recorder start 后开始接受录音,通过 recorder.onFrameRecorded 把数据透出给上层。数据中 isLastFrame 表示是否最后一次数据, frameBuffer 表示真实数据。\n * 2. 上层拿到录音数据后,通过 ws 发送给讯飞。如果 isLastFrame 为 true 时,给讯飞的数据中 status 为 2。如果 isLastFrame 为 false 时,给讯飞的数据中 status 为 1。\n * 3. ws 返回的数据,通过 renderResult 处理。数据中 status 为 2 表示本次录音数据识别结束了,这时候需要关闭 ws,避免消耗资源。且这两个情况服务端会主动断开连接(整个会话时长最多持续60s,或者超过10s未发送数据)。\n * 讯飞流式语音文档:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n * 4. 断开链接后会关闭 recorder ,重置录音等数据,方便下次使用。\n * 备注:使用时,建议在收到 onWsClose 事件后,才让前端恢复到可以再次输入录音的状态\n */\nexport class XFStreamVoiceManager {\n wsInstance: WebSocket | null = null;\n recorder: RecorderManager | null = null;\n isDestroy = false;\n resultText = '';\n resultTextTemp = '';\n continueRecord = false;\n startTimeStamp = 0;\n recordStatus: 'start' | 'stop' = 'stop';\n readyToSend = false;\n\n private inStopping = false;\n private stopResolve: ((value: string) => void) | null = null;\n\n constructor(public config: XFStreamVoiceManagerConfig) {\n this.initializeRecorder();\n }\n renderResult(resultData: string) {\n // 识别结束\n const jsonData = JSON.parse(resultData);\n if (jsonData.data && jsonData.data.result) {\n const data = jsonData.data.result;\n let str = '';\n const ws = data.ws;\n for (let i = 0; i < ws.length; i++) {\n str = str + ws[i].cw[0].w;\n }\n // 开启 wpgs 会有此字段(前提:在控制台开通动态修正功能)\n // 取值为 \"apd\"时表示该片结果是追加到前面的最终结果;取值为\"rpl\" 时表示替换前面的部分结果,替换范围为rg字段\n if (data.pgs) {\n if (data.pgs === 'apd') {\n // 将resultTextTemp同步给resultText\n this.resultText = this.resultTextTemp;\n }\n // 将结果存储在resultTextTemp中\n this.resultTextTemp = this.resultText + str;\n } else {\n this.resultText = this.resultText + str;\n }\n }\n /**\n * 识别结果是否结束标识:\n * 0:识别的第一块结果。\n * 1:识别中间结果。\n * 2:识别最后一块结果\n */\n if (jsonData.code === 0 && jsonData.data.status === 2) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: true,\n tempMessage: this.resultTextTemp,\n });\n this.closeWs();\n return;\n }\n if (jsonData.code === 0 && jsonData.data.status === 1) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: false,\n tempMessage: this.resultTextTemp,\n });\n }\n // code 不为0时表示出错\n if (jsonData.code !== 0) {\n this.closeWs();\n this.config.onError({ type: 'message', message: resultData });\n }\n }\n\n setConfig(newConfig: Partial<XFStreamVoiceManagerConfig>) {\n this.config = {\n ...this.config,\n ...newConfig,\n };\n this.recorder?.destroy();\n this.initializeRecorder();\n }\n sendMessage(message: string) {\n if (this.wsInstance?.readyState === WebSocket.OPEN && this.readyToSend) {\n this.wsInstance.send(message);\n }\n }\n\n async recordConfigSend() {\n if (this.isDestroy) {\n return;\n }\n if (this.recordStatus === 'stop') {\n this.closeWs();\n return;\n }\n try {\n // 开始录音\n await this.recorder?.start({\n sampleRate: 16000,\n frameSize: 1280,\n });\n const params = {\n common: {\n app_id: this.config.xunFeiAppId || APPID,\n },\n business: {\n language: 'zh_cn',\n domain: 'iat',\n accent: 'mandarin',\n vad_eos: 5000,\n dwa: 'wpgs',\n },\n data: {\n status: 0,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n },\n };\n this.readyToSend = true;\n this.sendMessage(JSON.stringify(params));\n } catch (error) {\n console.error('record error:', error);\n this.config.onError({ type: 'record', message: '录音失败, 请检查权限!' });\n }\n }\n\n startNewSocket() {\n console.log(\n '%c ❤️ ==== start new socket:',\n 'color: red; font-size: 16px;',\n startCount,\n new Date().toLocaleString(),\n );\n startCount++;\n const websocketUrl = this.config.getWebSocketUrl?.() || getWebSocketUrl();\n if ('WebSocket' in window) {\n this.wsInstance = new WebSocket(websocketUrl);\n } else {\n this.config.onError({ type: 'socketConnect', message: '浏览器不支持 WebSocket' });\n return;\n }\n this.wsInstance.onmessage = (e) => {\n // 处理返回数据\n this.renderResult(e.data);\n };\n this.wsInstance.onerror = (e) => {\n console.error('socket error:', e);\n if (this.inStopping) {\n this.inStopping = false;\n this.stopResolve?.(this.resultText);\n }\n };\n this.wsInstance.onclose = async () => {\n await this.resetStatus();\n this.config.onWsClose();\n if (this.continueRecord) {\n this.readyToSend = false;\n this.startRecord();\n }\n if (this.inStopping) {\n this.inStopping = false;\n this.stopResolve?.(this.resultText);\n }\n };\n this.wsInstance.onopen = () => {\n this.recordConfigSend();\n };\n }\n\n onFrameRecorded({ isLastFrame, frameBuffer }: RecordFrameInfo) {\n if (this.isDestroy) {\n return;\n }\n if (!this.wsInstance) {\n return;\n }\n if (this.wsInstance.readyState === this.wsInstance.OPEN) {\n this.sendMessage(\n JSON.stringify({\n data: {\n status: isLastFrame ? 2 : 1,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n audio: toBase64(frameBuffer),\n },\n }),\n );\n }\n if (isLastFrame) {\n this.readyToSend = false;\n }\n }\n\n initializeRecorder() {\n this.recorder = new RecorderManager();\n this.recorder.onFrameRecorded = (data: RecordFrameInfo) => {\n this.onFrameRecorded(data);\n };\n this.recorder.onLastFrame = (data: ArrayBuffer[]) => {\n this.config.onLastFrame?.(data);\n };\n this.recorder.onStop = () => {\n this.config.onRecordStop?.();\n };\n this.recorder.onStart = () => {\n this.config.onRecordStart?.();\n };\n }\n\n startRecord() {\n if (this.isDestroy) {\n return;\n }\n this.recordStatus = 'start';\n this.startTimeStamp = Date.now();\n this.resultText = '';\n this.resultTextTemp = '';\n this.startNewSocket();\n }\n\n async stopRecord() {\n this.recordStatus = 'stop';\n this.inStopping = true;\n console.log('stop record ====', Date.now() - this.startTimeStamp, this.recorder?.status);\n this.recorder?.stop();\n // 如果 recorder 还没有初始化完成就关闭了,那么需要手动关闭 ws,否则在收到最后一帧消息时关闭ws\n // if (this.recorder?.status !== 'initialized') {\n // this.closeWs();\n // }\n const stopPromise = new Promise((resolve) => {\n this.stopResolve = resolve;\n });\n await stopPromise;\n if (isWsClosed(this.wsInstance)) {\n this.stopResolve?.(this.resultText);\n }\n this.stopResolve = null;\n }\n\n closeWs() {\n if (this.wsInstance?.readyState !== WebSocket.CLOSED) {\n try {\n this.wsInstance?.close();\n } catch {\n // 有的浏览器在 ws 为 connecting 的时候 close 会报错,这里忽略掉\n }\n }\n }\n /** 开启持续监听 */\n async startContinueRecord() {\n this.continueRecord = true;\n await this.startRecord();\n }\n\n async stopContinueRecord() {\n this.continueRecord = false;\n await this.stopRecord();\n }\n\n async resetStatus() {\n this.recordStatus = 'stop';\n this.resultText = '';\n this.resultTextTemp = '';\n this.readyToSend = false;\n if (this.recorder?.audioContext?.state !== 'closed') {\n await this.recorder?.audioContext?.close();\n }\n }\n\n destroy() {\n this.isDestroy = true;\n this.closeWs();\n this.recorder?.destroy();\n }\n}\n"],"names":["_a"],"mappings":"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"}
1
+ {"version":3,"file":"XFStreamVoiceManager.es.js","sources":["../../../src/utils/xunFeiVoice/XFStreamVoiceManager.ts"],"sourcesContent":["import { RecorderManager } from './RecorderManager';\nimport type { OnMessageInfo, RecordFrameInfo } from './types';\nimport {\n checkMicrophonePermission,\n isWsClosed,\n requestMicrophonePermission,\n toBase64,\n} from './utils';\n\ntype errorType = 'socketConnect' | 'message' | 'record' | 'recordPermission';\nlet startCount = 0;\n\nexport interface XFStreamVoiceManagerConfig {\n onError: (errInfo: { type: errorType; message: string }) => void;\n /** ws 收到消息的回调\n * @param info.message 消息内容\n * @param info.isLatest 是否是最后一条消息。 isLatest 为 true 时, message 为完整的语音识别结果。\n */\n onMessage: (info: OnMessageInfo) => void;\n /** webSocket 关闭的回调。\n * - 每次录音都会新建一个 webSocket 连接。录音结束后关闭 webSocket\n */\n onWsClose: () => void;\n /**\n * 录音开始的回调\n */\n onRecordStart?: () => void;\n /**\n * 录音开始的回调\n */\n onRecordStop?: () => void;\n /**\n * 录音最后一帧的回调。\n * @param totalData 所有的录音数据, ArrayBuffer[]。\n */\n onLastFrame?: (totalData: ArrayBuffer[]) => void;\n /**\n * 获取链接讯飞语音识别地址的 url\n * @returns websocket url。详情查看讯飞文档里的鉴权方法:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n */\n getWebSocketUrl: () => string;\n /**\n * 获取录音权限的回调, 用户初次授权或者没有给录音权限时,会调用此回调。\n * @param status 权限状态。\n * - denied: 已拒绝\n * - granted: 已授权\n */\n onGetMicrophonePermission?: (status: 'denied' | 'granted') => void;\n /**\n * 讯飞语音的 appid\n */\n xunFeiAppId: string;\n}\n\n/**\n * 讯飞流式语音识别\n * 大致流程:\n * 1. recorder start 后开始接受录音,通过 recorder.onFrameRecorded 把数据透出给上层。数据中 isLastFrame 表示是否最后一次数据, frameBuffer 表示真实数据。\n * 2. 上层拿到录音数据后,通过 ws 发送给讯飞。如果 isLastFrame 为 true 时,给讯飞的数据中 status 为 2。如果 isLastFrame 为 false 时,给讯飞的数据中 status 为 1。\n * 3. ws 返回的数据,通过 renderResult 处理。数据中 status 为 2 表示本次录音数据识别结束了,这时候需要关闭 ws,避免消耗资源。且这两个情况服务端会主动断开连接(整个会话时长最多持续60s,或者超过10s未发送数据)。\n * 讯飞流式语音文档:https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B\n * 4. 断开链接后会关闭 recorder ,重置录音等数据,方便下次使用。\n * 备注:使用时,建议在收到 onWsClose 事件后,才让前端恢复到可以再次输入录音的状态\n */\nexport class XFStreamVoiceManager {\n wsInstance: WebSocket | null = null;\n recorder: RecorderManager | null = null;\n isDestroy = false;\n resultText = '';\n resultTextTemp = '';\n continueRecord = false;\n startTimeStamp = 0;\n recordStatus: 'start' | 'stop' = 'stop';\n readyToSend = false;\n microphonePermissionStatus: 'denied' | 'granted' | 'prompt' = 'prompt';\n\n private inStopping = false;\n private stopResolve: ((value: string) => void) | null = null;\n\n constructor(public config: XFStreamVoiceManagerConfig) {\n this.initializeRecorder();\n }\n renderResult(resultData: string) {\n // 识别结束\n const jsonData = JSON.parse(resultData);\n if (jsonData.data && jsonData.data.result) {\n const data = jsonData.data.result;\n let str = '';\n const ws = data.ws;\n for (let i = 0; i < ws.length; i++) {\n str = str + ws[i].cw[0].w;\n }\n // 开启 wpgs 会有此字段(前提:在控制台开通动态修正功能)\n // 取值为 \"apd\"时表示该片结果是追加到前面的最终结果;取值为\"rpl\" 时表示替换前面的部分结果,替换范围为rg字段\n if (data.pgs) {\n if (data.pgs === 'apd') {\n // 将resultTextTemp同步给resultText\n this.resultText = this.resultTextTemp;\n }\n // 将结果存储在resultTextTemp中\n this.resultTextTemp = this.resultText + str;\n } else {\n this.resultText = this.resultText + str;\n }\n }\n /**\n * 识别结果是否结束标识:\n * 0:识别的第一块结果。\n * 1:识别中间结果。\n * 2:识别最后一块结果\n */\n if (jsonData.code === 0 && jsonData.data.status === 2) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: true,\n tempMessage: this.resultTextTemp,\n });\n this.closeWs();\n return;\n }\n if (jsonData.code === 0 && jsonData.data.status === 1) {\n this.config.onMessage({\n message: this.resultText,\n isLatest: false,\n tempMessage: this.resultTextTemp,\n });\n }\n // code 不为0时表示出错\n if (jsonData.code !== 0) {\n this.closeWs();\n this.config.onError({ type: 'message', message: resultData });\n }\n }\n\n setConfig(newConfig: Partial<XFStreamVoiceManagerConfig>) {\n this.config = {\n ...this.config,\n ...newConfig,\n };\n this.recorder?.destroy();\n this.initializeRecorder();\n }\n sendMessage(message: string) {\n if (this.wsInstance?.readyState === WebSocket.OPEN && this.readyToSend) {\n this.wsInstance.send(message);\n }\n }\n\n async recordConfigSend() {\n if (this.isDestroy) {\n return;\n }\n if (this.recordStatus === 'stop') {\n this.closeWs();\n return;\n }\n try {\n // 开始录音\n await this.recorder?.start({\n sampleRate: 16000,\n frameSize: 1280,\n });\n const params = {\n common: {\n app_id: this.config.xunFeiAppId,\n },\n business: {\n language: 'zh_cn',\n domain: 'iat',\n accent: 'mandarin',\n vad_eos: 5000,\n dwa: 'wpgs',\n },\n data: {\n status: 0,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n },\n };\n this.readyToSend = true;\n this.sendMessage(JSON.stringify(params));\n } catch (error) {\n console.error('record error:', error);\n if ((error as Error).name === 'NotAllowedError') {\n this.config.onError({ type: 'recordPermission', message: '没有录音权限' });\n this.closeWs();\n return;\n }\n this.config.onError({ type: 'record', message: '开始录音失败!' });\n }\n }\n\n startNewSocket() {\n console.log(\n '%c ❤️ ==== start new socket:',\n 'color: red; font-size: 16px;',\n startCount,\n new Date().toLocaleString(),\n );\n startCount++;\n const websocketUrl = this.config.getWebSocketUrl?.();\n if (!websocketUrl) {\n this.config.onError({ type: 'socketConnect', message: '获取 websocket url 失败' });\n return;\n }\n if ('WebSocket' in window) {\n this.wsInstance = new WebSocket(websocketUrl);\n } else {\n this.config.onError({ type: 'socketConnect', message: '浏览器不支持 WebSocket' });\n return;\n }\n this.wsInstance.onmessage = (e) => {\n // 处理返回数据\n this.renderResult(e.data);\n };\n this.wsInstance.onerror = (e) => {\n console.error('socket error:', e);\n if (this.inStopping) {\n this.inStopping = false;\n this.stopResolve?.(this.resultText);\n }\n };\n this.wsInstance.onclose = async () => {\n await this.resetStatus();\n this.config.onWsClose();\n if (this.continueRecord) {\n this.readyToSend = false;\n this.startRecord();\n }\n if (this.inStopping) {\n this.inStopping = false;\n this.stopResolve?.(this.resultText);\n }\n };\n this.wsInstance.onopen = () => {\n this.recordConfigSend();\n };\n }\n\n onFrameRecorded({ isLastFrame, frameBuffer }: RecordFrameInfo) {\n if (this.isDestroy) {\n return;\n }\n if (!this.wsInstance) {\n return;\n }\n if (this.wsInstance.readyState === this.wsInstance.OPEN) {\n this.sendMessage(\n JSON.stringify({\n data: {\n status: isLastFrame ? 2 : 1,\n format: 'audio/L16;rate=16000',\n encoding: 'raw',\n audio: toBase64(frameBuffer),\n },\n }),\n );\n }\n if (isLastFrame) {\n this.readyToSend = false;\n }\n }\n\n initializeRecorder() {\n this.recorder = new RecorderManager();\n this.recorder.onFrameRecorded = (data: RecordFrameInfo) => {\n this.onFrameRecorded(data);\n };\n this.recorder.onLastFrame = (data: ArrayBuffer[]) => {\n this.config.onLastFrame?.(data);\n };\n this.recorder.onStop = () => {\n this.config.onRecordStop?.();\n };\n this.recorder.onStart = () => {\n this.config.onRecordStart?.();\n };\n }\n\n async startRecord() {\n if (this.isDestroy) {\n return;\n }\n this.recordStatus = 'start';\n this.startTimeStamp = Date.now();\n this.resultText = '';\n this.resultTextTemp = '';\n if (this.microphonePermissionStatus !== 'granted') {\n const currMicrophoneStatus = await checkMicrophonePermission();\n this.microphonePermissionStatus = currMicrophoneStatus;\n if (currMicrophoneStatus === 'prompt') {\n await requestMicrophonePermission()\n .then(() => {\n this.config.onGetMicrophonePermission?.('granted');\n })\n .catch((error) => {\n console.error('requestMicrophonePermission error:', error);\n this.config.onError({ type: 'recordPermission', message: '没有录音权限' });\n this.config.onGetMicrophonePermission?.('denied');\n });\n this.stopRecord();\n return;\n }\n if (currMicrophoneStatus === 'denied') {\n this.config.onError({ type: 'recordPermission', message: '没有录音权限' });\n this.stopRecord();\n this.config.onGetMicrophonePermission?.('denied');\n return;\n }\n }\n this.startNewSocket();\n }\n\n async stopRecord() {\n this.recordStatus = 'stop';\n this.inStopping = true;\n console.log('stop record ====', Date.now() - this.startTimeStamp, this.recorder?.status);\n this.recorder?.stop();\n // 如果 recorder 还没有初始化完成就关闭了,那么需要手动关闭 ws,否则在收到最后一帧消息时关闭ws\n // if (this.recorder?.status !== 'initialized') {\n // this.closeWs();\n // }\n const stopPromise = new Promise((resolve) => {\n this.stopResolve = resolve;\n });\n await stopPromise;\n if (isWsClosed(this.wsInstance)) {\n this.stopResolve?.(this.resultText);\n }\n this.stopResolve = null;\n }\n\n closeWs() {\n if (this.wsInstance?.readyState !== WebSocket.CLOSED) {\n try {\n this.wsInstance?.close();\n } catch {\n // 有的浏览器在 ws 为 connecting 的时候 close 会报错,这里忽略掉\n }\n }\n }\n /** 开启持续监听 */\n async startContinueRecord() {\n this.continueRecord = true;\n await this.startRecord();\n }\n\n async stopContinueRecord() {\n this.continueRecord = false;\n await this.stopRecord();\n }\n\n async resetStatus() {\n this.recordStatus = 'stop';\n this.resultText = '';\n this.resultTextTemp = '';\n this.readyToSend = false;\n if (this.recorder?.audioContext?.state !== 'closed') {\n await this.recorder?.audioContext?.close();\n }\n }\n\n destroy() {\n this.isDestroy = true;\n this.closeWs();\n this.recorder?.destroy();\n }\n}\n"],"names":["_a","_b"],"mappings":";;;;;AAUA,IAAI,aAAa;AAsDV,MAAM,qBAAqB;AAAA,EAehC,YAAmB,QAAoC;AAdvD,sCAA+B;AAC/B,oCAAmC;AACnC,qCAAY;AACZ,sCAAa;AACb,0CAAiB;AACjB,0CAAiB;AACjB,0CAAiB;AACjB,wCAAiC;AACjC,uCAAc;AACd,sDAA8D;AAEtD,sCAAa;AACb,uCAAgD;AAErC,SAAA,SAAA;AACjB,SAAK,mBAAmB;AAAA,EAAA;AAAA,EAE1B,aAAa,YAAoB;AAEzB,UAAA,WAAW,KAAK,MAAM,UAAU;AACtC,QAAI,SAAS,QAAQ,SAAS,KAAK,QAAQ;AACnC,YAAA,OAAO,SAAS,KAAK;AAC3B,UAAI,MAAM;AACV,YAAM,KAAK,KAAK;AAChB,eAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,cAAM,MAAM,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE;AAAA,MAAA;AAI1B,UAAI,KAAK,KAAK;AACR,YAAA,KAAK,QAAQ,OAAO;AAEtB,eAAK,aAAa,KAAK;AAAA,QAAA;AAGpB,aAAA,iBAAiB,KAAK,aAAa;AAAA,MAAA,OACnC;AACA,aAAA,aAAa,KAAK,aAAa;AAAA,MAAA;AAAA,IACtC;AAQF,QAAI,SAAS,SAAS,KAAK,SAAS,KAAK,WAAW,GAAG;AACrD,WAAK,OAAO,UAAU;AAAA,QACpB,SAAS,KAAK;AAAA,QACd,UAAU;AAAA,QACV,aAAa,KAAK;AAAA,MAAA,CACnB;AACD,WAAK,QAAQ;AACb;AAAA,IAAA;AAEF,QAAI,SAAS,SAAS,KAAK,SAAS,KAAK,WAAW,GAAG;AACrD,WAAK,OAAO,UAAU;AAAA,QACpB,SAAS,KAAK;AAAA,QACd,UAAU;AAAA,QACV,aAAa,KAAK;AAAA,MAAA,CACnB;AAAA,IAAA;AAGC,QAAA,SAAS,SAAS,GAAG;AACvB,WAAK,QAAQ;AACb,WAAK,OAAO,QAAQ,EAAE,MAAM,WAAW,SAAS,YAAY;AAAA,IAAA;AAAA,EAC9D;AAAA,EAGF,UAAU,WAAgD;;AACxD,SAAK,SAAS;AAAA,MACZ,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,IACL;AACA,eAAK,aAAL,mBAAe;AACf,SAAK,mBAAmB;AAAA,EAAA;AAAA,EAE1B,YAAY,SAAiB;;AAC3B,UAAI,UAAK,eAAL,mBAAiB,gBAAe,UAAU,QAAQ,KAAK,aAAa;AACjE,WAAA,WAAW,KAAK,OAAO;AAAA,IAAA;AAAA,EAC9B;AAAA,EAGF,MAAM,mBAAmB;;AACvB,QAAI,KAAK,WAAW;AAClB;AAAA,IAAA;AAEE,QAAA,KAAK,iBAAiB,QAAQ;AAChC,WAAK,QAAQ;AACb;AAAA,IAAA;AAEE,QAAA;AAEI,cAAA,UAAK,aAAL,mBAAe,MAAM;AAAA,QACzB,YAAY;AAAA,QACZ,WAAW;AAAA,MAAA;AAEb,YAAM,SAAS;AAAA,QACb,QAAQ;AAAA,UACN,QAAQ,KAAK,OAAO;AAAA,QACtB;AAAA,QACA,UAAU;AAAA,UACR,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,KAAK;AAAA,QACP;AAAA,QACA,MAAM;AAAA,UACJ,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,UAAU;AAAA,QAAA;AAAA,MAEd;AACA,WAAK,cAAc;AACnB,WAAK,YAAY,KAAK,UAAU,MAAM,CAAC;AAAA,aAChC,OAAO;AACN,cAAA,MAAM,iBAAiB,KAAK;AAC/B,UAAA,MAAgB,SAAS,mBAAmB;AAC/C,aAAK,OAAO,QAAQ,EAAE,MAAM,oBAAoB,SAAS,UAAU;AACnE,aAAK,QAAQ;AACb;AAAA,MAAA;AAEF,WAAK,OAAO,QAAQ,EAAE,MAAM,UAAU,SAAS,WAAW;AAAA,IAAA;AAAA,EAC5D;AAAA,EAGF,iBAAiB;;AACP,YAAA;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,OACA,oBAAI,KAAK,GAAE,eAAe;AAAA,IAC5B;AACA;AACM,UAAA,gBAAe,gBAAK,QAAO,oBAAZ;AACrB,QAAI,CAAC,cAAc;AACjB,WAAK,OAAO,QAAQ,EAAE,MAAM,iBAAiB,SAAS,uBAAuB;AAC7E;AAAA,IAAA;AAEF,QAAI,eAAe,QAAQ;AACpB,WAAA,aAAa,IAAI,UAAU,YAAY;AAAA,IAAA,OACvC;AACL,WAAK,OAAO,QAAQ,EAAE,MAAM,iBAAiB,SAAS,oBAAoB;AAC1E;AAAA,IAAA;AAEG,SAAA,WAAW,YAAY,CAAC,MAAM;AAE5B,WAAA,aAAa,EAAE,IAAI;AAAA,IAC1B;AACK,SAAA,WAAW,UAAU,CAAC,MAAM;;AACvB,cAAA,MAAM,iBAAiB,CAAC;AAChC,UAAI,KAAK,YAAY;AACnB,aAAK,aAAa;AACb,SAAAA,MAAA,KAAA,gBAAA,gBAAAA,IAAA,WAAc,KAAK;AAAA,MAAU;AAAA,IAEtC;AACK,SAAA,WAAW,UAAU,YAAY;;AACpC,YAAM,KAAK,YAAY;AACvB,WAAK,OAAO,UAAU;AACtB,UAAI,KAAK,gBAAgB;AACvB,aAAK,cAAc;AACnB,aAAK,YAAY;AAAA,MAAA;AAEnB,UAAI,KAAK,YAAY;AACnB,aAAK,aAAa;AACb,SAAAA,MAAA,KAAA,gBAAA,gBAAAA,IAAA,WAAc,KAAK;AAAA,MAAU;AAAA,IAEtC;AACK,SAAA,WAAW,SAAS,MAAM;AAC7B,WAAK,iBAAiB;AAAA,IACxB;AAAA,EAAA;AAAA,EAGF,gBAAgB,EAAE,aAAa,eAAgC;AAC7D,QAAI,KAAK,WAAW;AAClB;AAAA,IAAA;AAEE,QAAA,CAAC,KAAK,YAAY;AACpB;AAAA,IAAA;AAEF,QAAI,KAAK,WAAW,eAAe,KAAK,WAAW,MAAM;AAClD,WAAA;AAAA,QACH,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,YACJ,QAAQ,cAAc,IAAI;AAAA,YAC1B,QAAQ;AAAA,YACR,UAAU;AAAA,YACV,OAAO,SAAS,WAAW;AAAA,UAAA;AAAA,QAE9B,CAAA;AAAA,MACH;AAAA,IAAA;AAEF,QAAI,aAAa;AACf,WAAK,cAAc;AAAA,IAAA;AAAA,EACrB;AAAA,EAGF,qBAAqB;AACd,SAAA,WAAW,IAAI,gBAAgB;AAC/B,SAAA,SAAS,kBAAkB,CAAC,SAA0B;AACzD,WAAK,gBAAgB,IAAI;AAAA,IAC3B;AACK,SAAA,SAAS,cAAc,CAAC,SAAwB;;AAC9C,uBAAA,QAAO,gBAAP,4BAAqB;AAAA,IAC5B;AACK,SAAA,SAAS,SAAS,MAAM;;AAC3B,uBAAK,QAAO,iBAAZ;AAAA,IACF;AACK,SAAA,SAAS,UAAU,MAAM;;AAC5B,uBAAK,QAAO,kBAAZ;AAAA,IACF;AAAA,EAAA;AAAA,EAGF,MAAM,cAAc;;AAClB,QAAI,KAAK,WAAW;AAClB;AAAA,IAAA;AAEF,SAAK,eAAe;AACf,SAAA,iBAAiB,KAAK,IAAI;AAC/B,SAAK,aAAa;AAClB,SAAK,iBAAiB;AAClB,QAAA,KAAK,+BAA+B,WAAW;AAC3C,YAAA,uBAAuB,MAAM,0BAA0B;AAC7D,WAAK,6BAA6B;AAClC,UAAI,yBAAyB,UAAU;AAC/B,cAAA,4BAAA,EACH,KAAK,MAAM;;AACL,WAAAC,OAAAD,MAAA,KAAA,QAAO,8BAAP,gBAAAC,IAAA,KAAAD,KAAmC;AAAA,QAAS,CAClD,EACA,MAAM,CAAC,UAAU;;AACR,kBAAA,MAAM,sCAAsC,KAAK;AACzD,eAAK,OAAO,QAAQ,EAAE,MAAM,oBAAoB,SAAS,UAAU;AAC9D,WAAAC,OAAAD,MAAA,KAAA,QAAO,8BAAP,gBAAAC,IAAA,KAAAD,KAAmC;AAAA,QAAQ,CACjD;AACH,aAAK,WAAW;AAChB;AAAA,MAAA;AAEF,UAAI,yBAAyB,UAAU;AACrC,aAAK,OAAO,QAAQ,EAAE,MAAM,oBAAoB,SAAS,UAAU;AACnE,aAAK,WAAW;AACX,yBAAA,QAAO,8BAAP,4BAAmC;AACxC;AAAA,MAAA;AAAA,IACF;AAEF,SAAK,eAAe;AAAA,EAAA;AAAA,EAGtB,MAAM,aAAa;;AACjB,SAAK,eAAe;AACpB,SAAK,aAAa;AACV,YAAA,IAAI,qBAAqB,KAAK,IAAA,IAAQ,KAAK,iBAAgB,UAAK,aAAL,mBAAe,MAAM;AACxF,eAAK,aAAL,mBAAe;AAKf,UAAM,cAAc,IAAI,QAAQ,CAAC,YAAY;AAC3C,WAAK,cAAc;AAAA,IAAA,CACpB;AACK,UAAA;AACF,QAAA,WAAW,KAAK,UAAU,GAAG;AAC1B,iBAAA,gBAAA,8BAAc,KAAK;AAAA,IAAU;AAEpC,SAAK,cAAc;AAAA,EAAA;AAAA,EAGrB,UAAU;;AACR,UAAI,UAAK,eAAL,mBAAiB,gBAAe,UAAU,QAAQ;AAChD,UAAA;AACF,mBAAK,eAAL,mBAAiB;AAAA,MAAM,QACjB;AAAA,MAAA;AAAA,IAER;AAAA,EACF;AAAA;AAAA,EAGF,MAAM,sBAAsB;AAC1B,SAAK,iBAAiB;AACtB,UAAM,KAAK,YAAY;AAAA,EAAA;AAAA,EAGzB,MAAM,qBAAqB;AACzB,SAAK,iBAAiB;AACtB,UAAM,KAAK,WAAW;AAAA,EAAA;AAAA,EAGxB,MAAM,cAAc;;AAClB,SAAK,eAAe;AACpB,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,SAAK,cAAc;AACnB,UAAI,gBAAK,aAAL,mBAAe,iBAAf,mBAA6B,WAAU,UAAU;AAC7C,cAAA,gBAAK,aAAL,mBAAe,iBAAf,mBAA6B;AAAA,IAAM;AAAA,EAC3C;AAAA,EAGF,UAAU;;AACR,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,eAAK,aAAL,mBAAe;AAAA,EAAQ;AAE3B;"}
@@ -1,2 +1,17 @@
1
1
  export declare function toBase64(buffer: ArrayBuffer): string;
2
2
  export declare function isWsClosed(ws?: WebSocket | null): boolean;
3
+ /**
4
+ *
5
+ * @returns
6
+ * granted: 已授权
7
+ * denied: 已拒绝
8
+ * prompt: 未决定
9
+ * error: 错误
10
+ */
11
+ export declare function checkMicrophonePermission(): Promise<PermissionState>;
12
+ /**
13
+ * 获取用户媒体设备权限。
14
+ * - 如果用户拒绝授权,抛出 name 为 NotAllowedError 的 DOMException
15
+ * @returns {Promise<MediaStream>}
16
+ */
17
+ export declare function requestMicrophonePermission(): Promise<MediaStream>;
@@ -13,8 +13,36 @@ function isWsClosed(ws) {
13
13
  }
14
14
  return ws.readyState === WebSocket.CLOSED;
15
15
  }
16
+ async function checkMicrophonePermission() {
17
+ try {
18
+ const permissionStatus = await navigator.permissions.query({
19
+ name: "microphone"
20
+ });
21
+ return permissionStatus.state;
22
+ } catch (error) {
23
+ console.error("麦克风权限查询失败:", error);
24
+ throw error;
25
+ }
26
+ }
27
+ async function requestMicrophonePermission() {
28
+ const mediaDevices = navigator.mediaDevices ?? {};
29
+ try {
30
+ if (typeof mediaDevices.getUserMedia === "function") {
31
+ console.time("mediaDevices.getUserMedia:");
32
+ const stream = await mediaDevices.getUserMedia({ audio: true, video: false });
33
+ console.timeEnd("mediaDevices.getUserMedia:");
34
+ return stream;
35
+ }
36
+ } catch (error) {
37
+ console.error("获取录音权限失败!");
38
+ throw error;
39
+ }
40
+ throw new Error("浏览器不支持录音!");
41
+ }
16
42
  export {
43
+ checkMicrophonePermission,
17
44
  isWsClosed,
45
+ requestMicrophonePermission,
18
46
  toBase64
19
47
  };
20
48
  //# sourceMappingURL=utils.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.es.js","sources":["../../../src/utils/xunFeiVoice/utils.ts"],"sourcesContent":["export function toBase64(buffer: ArrayBuffer) {\n let binary = '';\n const bytes = new Uint8Array(buffer);\n const len = bytes.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return window.btoa(binary);\n}\n\nexport function isWsClosed(ws?: WebSocket | null) {\n if (!ws) {\n return true;\n }\n return ws.readyState === WebSocket.CLOSED;\n}\n"],"names":[],"mappings":"AAAO,SAAS,SAAS,QAAqB;AAC5C,MAAI,SAAS;AACP,QAAA,QAAQ,IAAI,WAAW,MAAM;AACnC,QAAM,MAAM,MAAM;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,cAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,EAAA;AAEjC,SAAA,OAAO,KAAK,MAAM;AAC3B;AAEO,SAAS,WAAW,IAAuB;AAChD,MAAI,CAAC,IAAI;AACA,WAAA;AAAA,EAAA;AAEF,SAAA,GAAG,eAAe,UAAU;AACrC;"}
1
+ {"version":3,"file":"utils.es.js","sources":["../../../src/utils/xunFeiVoice/utils.ts"],"sourcesContent":["export function toBase64(buffer: ArrayBuffer) {\n let binary = '';\n const bytes = new Uint8Array(buffer);\n const len = bytes.byteLength;\n for (let i = 0; i < len; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return window.btoa(binary);\n}\n\nexport function isWsClosed(ws?: WebSocket | null) {\n if (!ws) {\n return true;\n }\n return ws.readyState === WebSocket.CLOSED;\n}\n/**\n *\n * @returns\n * granted: 已授权\n * denied: 已拒绝\n * prompt: 未决定\n * error: 错误\n */\nexport async function checkMicrophonePermission() {\n try {\n const permissionStatus = await navigator.permissions.query({\n name: 'microphone',\n });\n\n // 状态可能是:\n // - 'granted'(已授权)\n // - 'denied'(已拒绝)\n // - 'prompt'(未决定,浏览器会弹窗询问)\n return permissionStatus.state;\n } catch (error) {\n console.error('麦克风权限查询失败:', error);\n throw error;\n }\n}\n\n/**\n * 获取用户媒体设备权限。\n * - 如果用户拒绝授权,抛出 name 为 NotAllowedError 的 DOMException\n * @returns {Promise<MediaStream>}\n */\nexport async function requestMicrophonePermission() {\n const mediaDevices = navigator.mediaDevices ?? {};\n\n try {\n if (typeof mediaDevices.getUserMedia === 'function') {\n console.time('mediaDevices.getUserMedia:');\n const stream = await mediaDevices.getUserMedia({ audio: true, video: false });\n console.timeEnd('mediaDevices.getUserMedia:');\n return stream;\n }\n } catch (error) {\n console.error('获取录音权限失败!');\n throw error;\n }\n\n throw new Error('浏览器不支持录音!');\n}\n"],"names":[],"mappings":"AAAO,SAAS,SAAS,QAAqB;AAC5C,MAAI,SAAS;AACP,QAAA,QAAQ,IAAI,WAAW,MAAM;AACnC,QAAM,MAAM,MAAM;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,cAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,EAAA;AAEjC,SAAA,OAAO,KAAK,MAAM;AAC3B;AAEO,SAAS,WAAW,IAAuB;AAChD,MAAI,CAAC,IAAI;AACA,WAAA;AAAA,EAAA;AAEF,SAAA,GAAG,eAAe,UAAU;AACrC;AASA,eAAsB,4BAA4B;AAC5C,MAAA;AACF,UAAM,mBAAmB,MAAM,UAAU,YAAY,MAAM;AAAA,MACzD,MAAM;AAAA,IAAA,CACP;AAMD,WAAO,iBAAiB;AAAA,WACjB,OAAO;AACN,YAAA,MAAM,cAAc,KAAK;AAC3B,UAAA;AAAA,EAAA;AAEV;AAOA,eAAsB,8BAA8B;AAC5C,QAAA,eAAe,UAAU,gBAAgB,CAAC;AAE5C,MAAA;AACE,QAAA,OAAO,aAAa,iBAAiB,YAAY;AACnD,cAAQ,KAAK,4BAA4B;AACnC,YAAA,SAAS,MAAM,aAAa,aAAa,EAAE,OAAO,MAAM,OAAO,OAAO;AAC5E,cAAQ,QAAQ,4BAA4B;AACrC,aAAA;AAAA,IAAA;AAAA,WAEF,OAAO;AACd,YAAQ,MAAM,WAAW;AACnB,UAAA;AAAA,EAAA;AAGF,QAAA,IAAI,MAAM,WAAW;AAC7B;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easyv/biz-components",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "type": "module",
5
5
  "main": "dist/index.es.js",
6
6
  "module": "dist/index.es.js",
@@ -1,7 +0,0 @@
1
- import * as __viteBrowserExternal from "./__vite-browser-external.es.js";
2
- import { getAugmentedNamespace } from "./_commonjsHelpers.es.js";
3
- const require$$0 = /* @__PURE__ */ getAugmentedNamespace(__viteBrowserExternal);
4
- export {
5
- require$$0 as default
6
- };
7
- //# sourceMappingURL=___vite-browser-external.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"___vite-browser-external.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -1,5 +0,0 @@
1
- const __viteBrowserExternal = {};
2
- export {
3
- __viteBrowserExternal as default
4
- };
5
- //# sourceMappingURL=__vite-browser-external.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"__vite-browser-external.es.js","sources":["../../__vite-browser-external"],"sourcesContent":["export default {}"],"names":[],"mappings":"AAAA,MAAe,wBAAA,CAAA;"}
@@ -1,7 +0,0 @@
1
- function commonjsRequire(path) {
2
- throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
3
- }
4
- export {
5
- commonjsRequire
6
- };
7
- //# sourceMappingURL=_commonjs-dynamic-modules.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_commonjs-dynamic-modules.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
@@ -1,5 +0,0 @@
1
- var aes = { exports: {} };
2
- export {
3
- aes as __module
4
- };
5
- //# sourceMappingURL=aes.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aes.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,5 +0,0 @@
1
- var blowfish = { exports: {} };
2
- export {
3
- blowfish as __module
4
- };
5
- //# sourceMappingURL=blowfish.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blowfish.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,5 +0,0 @@
1
- var cipherCore = { exports: {} };
2
- export {
3
- cipherCore as __module
4
- };
5
- //# sourceMappingURL=cipher-core.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cipher-core.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,5 +0,0 @@
1
- var core = { exports: {} };
2
- export {
3
- core as __module
4
- };
5
- //# sourceMappingURL=core.es.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"core.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}