@kirill.konshin/utils 0.0.18 → 0.0.20

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 (352) hide show
  1. package/.ctirc +19 -10
  2. package/.storybook/main.ts +1 -20
  3. package/.storybook/preview.ts +1 -16
  4. package/.turbo/turbo-build.log +9 -66
  5. package/CHANGELOG.md +27 -0
  6. package/README.md +6 -0
  7. package/dist/bootstrap/index.d.ts +1 -12
  8. package/dist/bootstrap/index.d.ts.map +1 -1
  9. package/dist/browser/index.d.ts +2 -0
  10. package/dist/browser/index.d.ts.map +1 -0
  11. package/dist/core/index.d.ts +1 -9
  12. package/dist/core/index.d.ts.map +1 -1
  13. package/dist/electron/index.d.ts +1 -2
  14. package/dist/electron/index.d.ts.map +1 -1
  15. package/dist/electron-builder/index.d.ts +1 -2
  16. package/dist/electron-builder/index.d.ts.map +1 -1
  17. package/dist/index.d.ts +13 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +27 -0
  20. package/dist/mui/index.d.ts +1 -4
  21. package/dist/mui/index.d.ts.map +1 -1
  22. package/dist/next/index.d.ts +1 -5
  23. package/dist/next/index.d.ts.map +1 -1
  24. package/dist/node/index.d.ts +2 -0
  25. package/dist/node/index.d.ts.map +1 -0
  26. package/dist/react/index.d.ts +1 -5
  27. package/dist/react/index.d.ts.map +1 -1
  28. package/dist/react-native/index.d.ts +1 -3
  29. package/dist/react-native/index.d.ts.map +1 -1
  30. package/dist/tailwind/index.d.ts +1 -2
  31. package/dist/tailwind/index.d.ts.map +1 -1
  32. package/dist/worker/index.d.ts +2 -0
  33. package/dist/worker/index.d.ts.map +1 -0
  34. package/package.json +41 -99
  35. package/src/bootstrap/index.ts +2 -12
  36. package/src/browser/index.ts +2 -0
  37. package/src/core/index.ts +2 -9
  38. package/src/electron/index.ts +2 -2
  39. package/src/electron-builder/index.ts +2 -2
  40. package/src/index.ts +12 -0
  41. package/src/mui/index.ts +2 -4
  42. package/src/next/index.ts +2 -5
  43. package/src/node/index.ts +2 -0
  44. package/src/react/index.ts +2 -5
  45. package/src/react-native/index.ts +2 -3
  46. package/src/tailwind/index.ts +2 -2
  47. package/src/worker/index.ts +2 -0
  48. package/tsconfig.json +2 -14
  49. package/vite.config.ts +2 -56
  50. package/.turbo/turbo-test.log +0 -171
  51. package/builders/.swcrc +0 -18
  52. package/builders/build.config.ts +0 -11
  53. package/builders/bun.build.js +0 -23
  54. package/builders/package.json +0 -209
  55. package/builders/perf.mjs +0 -63
  56. package/builders/rollup.config.mjs +0 -50
  57. package/builders/rslib.config.ts +0 -26
  58. package/builders/tsconfig.json +0 -21
  59. package/builders/tsup.config.ts +0 -20
  60. package/builders/turbo.json +0 -24
  61. package/builders/vite.config.ts +0 -37
  62. package/demo/worker-demo.ts +0 -73
  63. package/dist/bootstrap/adaptiveContainer.d.ts +0 -3
  64. package/dist/bootstrap/adaptiveContainer.d.ts.map +0 -1
  65. package/dist/bootstrap/adaptiveContainer.js +0 -11
  66. package/dist/bootstrap/adaptiveContainer.js.map +0 -1
  67. package/dist/bootstrap/controls.d.ts +0 -38
  68. package/dist/bootstrap/controls.d.ts.map +0 -1
  69. package/dist/bootstrap/controls.js +0 -78
  70. package/dist/bootstrap/controls.js.map +0 -1
  71. package/dist/bootstrap/controls.stories.d.ts +0 -9
  72. package/dist/bootstrap/controls.stories.d.ts.map +0 -1
  73. package/dist/bootstrap/error.d.ts +0 -7
  74. package/dist/bootstrap/error.d.ts.map +0 -1
  75. package/dist/bootstrap/error.js +0 -32
  76. package/dist/bootstrap/error.js.map +0 -1
  77. package/dist/bootstrap/error.stories.d.ts +0 -8
  78. package/dist/bootstrap/error.stories.d.ts.map +0 -1
  79. package/dist/bootstrap/field.d.ts +0 -9
  80. package/dist/bootstrap/field.d.ts.map +0 -1
  81. package/dist/bootstrap/field.js +0 -29
  82. package/dist/bootstrap/field.js.map +0 -1
  83. package/dist/bootstrap/field.stories.d.ts +0 -8
  84. package/dist/bootstrap/field.stories.d.ts.map +0 -1
  85. package/dist/bootstrap/footer.d.ts +0 -10
  86. package/dist/bootstrap/footer.d.ts.map +0 -1
  87. package/dist/bootstrap/footer.js +0 -40
  88. package/dist/bootstrap/footer.js.map +0 -1
  89. package/dist/bootstrap/globalLoading.d.ts +0 -4
  90. package/dist/bootstrap/globalLoading.d.ts.map +0 -1
  91. package/dist/bootstrap/globalLoading.js +0 -10
  92. package/dist/bootstrap/globalLoading.js.map +0 -1
  93. package/dist/bootstrap/globalLoading.stories.d.ts +0 -7
  94. package/dist/bootstrap/globalLoading.stories.d.ts.map +0 -1
  95. package/dist/bootstrap/index.js +0 -35
  96. package/dist/bootstrap/loading.d.ts +0 -8
  97. package/dist/bootstrap/loading.d.ts.map +0 -1
  98. package/dist/bootstrap/loading.js +0 -18
  99. package/dist/bootstrap/loading.js.map +0 -1
  100. package/dist/bootstrap/loading.stories.d.ts +0 -9
  101. package/dist/bootstrap/loading.stories.d.ts.map +0 -1
  102. package/dist/bootstrap/responsiveHelper.d.ts +0 -7
  103. package/dist/bootstrap/responsiveHelper.d.ts.map +0 -1
  104. package/dist/bootstrap/responsiveHelper.js +0 -46
  105. package/dist/bootstrap/responsiveHelper.js.map +0 -1
  106. package/dist/bootstrap/screen.d.ts +0 -20
  107. package/dist/bootstrap/screen.d.ts.map +0 -1
  108. package/dist/bootstrap/screen.js +0 -79
  109. package/dist/bootstrap/screen.js.map +0 -1
  110. package/dist/bootstrap/screen.stories.d.ts +0 -14
  111. package/dist/bootstrap/screen.stories.d.ts.map +0 -1
  112. package/dist/bootstrap/toaster.d.ts +0 -9
  113. package/dist/bootstrap/toaster.d.ts.map +0 -1
  114. package/dist/bootstrap/toaster.js +0 -37
  115. package/dist/bootstrap/toaster.js.map +0 -1
  116. package/dist/bootstrap/toaster.stories.d.ts +0 -8
  117. package/dist/bootstrap/toaster.stories.d.ts.map +0 -1
  118. package/dist/bootstrap/useModal.d.ts +0 -22
  119. package/dist/bootstrap/useModal.d.ts.map +0 -1
  120. package/dist/bootstrap/useModal.js +0 -50
  121. package/dist/bootstrap/useModal.js.map +0 -1
  122. package/dist/bootstrap/useModal.stories.d.ts +0 -9
  123. package/dist/bootstrap/useModal.stories.d.ts.map +0 -1
  124. package/dist/bootstrap/useWrappedForm.d.ts +0 -14
  125. package/dist/bootstrap/useWrappedForm.d.ts.map +0 -1
  126. package/dist/bootstrap/useWrappedForm.js +0 -32
  127. package/dist/bootstrap/useWrappedForm.js.map +0 -1
  128. package/dist/core/cache.d.ts +0 -120
  129. package/dist/core/cache.d.ts.map +0 -1
  130. package/dist/core/cache.js +0 -107
  131. package/dist/core/cache.js.map +0 -1
  132. package/dist/core/cache.test.d.ts +0 -2
  133. package/dist/core/cache.test.d.ts.map +0 -1
  134. package/dist/core/errors.d.ts +0 -2
  135. package/dist/core/errors.d.ts.map +0 -1
  136. package/dist/core/errors.js +0 -15
  137. package/dist/core/errors.js.map +0 -1
  138. package/dist/core/files.d.ts +0 -3
  139. package/dist/core/files.d.ts.map +0 -1
  140. package/dist/core/files.js +0 -27
  141. package/dist/core/files.js.map +0 -1
  142. package/dist/core/index.js +0 -34
  143. package/dist/core/index.js.map +0 -1
  144. package/dist/core/ls.d.ts +0 -6
  145. package/dist/core/ls.d.ts.map +0 -1
  146. package/dist/core/ls.js +0 -12
  147. package/dist/core/ls.js.map +0 -1
  148. package/dist/core/measure.d.ts +0 -35
  149. package/dist/core/measure.d.ts.map +0 -1
  150. package/dist/core/measure.js +0 -56
  151. package/dist/core/measure.js.map +0 -1
  152. package/dist/core/mutex.d.ts +0 -6
  153. package/dist/core/mutex.d.ts.map +0 -1
  154. package/dist/core/mutex.js +0 -26
  155. package/dist/core/mutex.js.map +0 -1
  156. package/dist/core/worker.d.ts +0 -98
  157. package/dist/core/worker.d.ts.map +0 -1
  158. package/dist/core/worker.js +0 -240
  159. package/dist/core/worker.js.map +0 -1
  160. package/dist/core/worker.test.d.ts +0 -2
  161. package/dist/core/worker.test.d.ts.map +0 -1
  162. package/dist/core/workerUtils.d.ts +0 -3
  163. package/dist/core/workerUtils.d.ts.map +0 -1
  164. package/dist/core/workerUtils.js +0 -31
  165. package/dist/core/workerUtils.js.map +0 -1
  166. package/dist/core/wrpc.d.ts +0 -45
  167. package/dist/core/wrpc.d.ts.map +0 -1
  168. package/dist/core/wrpc.fixture.d.ts +0 -29
  169. package/dist/core/wrpc.fixture.d.ts.map +0 -1
  170. package/dist/core/wrpc.js +0 -242
  171. package/dist/core/wrpc.js.map +0 -1
  172. package/dist/core/wrpc.test.d.ts +0 -1
  173. package/dist/core/wrpc.test.d.ts.map +0 -1
  174. package/dist/electron/createWindow.d.ts +0 -23
  175. package/dist/electron/createWindow.d.ts.map +0 -1
  176. package/dist/electron/createWindow.js +0 -160
  177. package/dist/electron/createWindow.js.map +0 -1
  178. package/dist/electron/index.js +0 -9
  179. package/dist/electron/index.js.map +0 -1
  180. package/dist/electron/updater.d.ts +0 -2
  181. package/dist/electron/updater.d.ts.map +0 -1
  182. package/dist/electron/updater.js +0 -47
  183. package/dist/electron/updater.js.map +0 -1
  184. package/dist/electron-builder/builder.d.ts +0 -8
  185. package/dist/electron-builder/builder.d.ts.map +0 -1
  186. package/dist/electron-builder/builder.js +0 -130
  187. package/dist/electron-builder/builder.js.map +0 -1
  188. package/dist/electron-builder/images.d.ts +0 -7
  189. package/dist/electron-builder/images.d.ts.map +0 -1
  190. package/dist/electron-builder/images.js +0 -25
  191. package/dist/electron-builder/images.js.map +0 -1
  192. package/dist/electron-builder/index.js +0 -7
  193. package/dist/electron-builder/index.js.map +0 -1
  194. package/dist/mui/formControlFieldset.d.ts +0 -5
  195. package/dist/mui/formControlFieldset.d.ts.map +0 -1
  196. package/dist/mui/formControlFieldset.js +0 -28
  197. package/dist/mui/formControlFieldset.js.map +0 -1
  198. package/dist/mui/formControlFieldset.stories.d.ts +0 -7
  199. package/dist/mui/formControlFieldset.stories.d.ts.map +0 -1
  200. package/dist/mui/formLabelLegend.d.ts +0 -4
  201. package/dist/mui/formLabelLegend.d.ts.map +0 -1
  202. package/dist/mui/formLabelLegend.js +0 -12
  203. package/dist/mui/formLabelLegend.js.map +0 -1
  204. package/dist/mui/genericControl.d.ts +0 -7
  205. package/dist/mui/genericControl.d.ts.map +0 -1
  206. package/dist/mui/genericControl.js +0 -21
  207. package/dist/mui/genericControl.js.map +0 -1
  208. package/dist/mui/index.js +0 -11
  209. package/dist/mui/index.js.map +0 -1
  210. package/dist/mui/readOnly.d.ts +0 -6
  211. package/dist/mui/readOnly.d.ts.map +0 -1
  212. package/dist/mui/readOnly.js +0 -7
  213. package/dist/mui/readOnly.js.map +0 -1
  214. package/dist/next/appLink.d.ts +0 -8
  215. package/dist/next/appLink.d.ts.map +0 -1
  216. package/dist/next/appLink.js +0 -34
  217. package/dist/next/appLink.js.map +0 -1
  218. package/dist/next/index.js +0 -15
  219. package/dist/next/index.js.map +0 -1
  220. package/dist/next/measure.d.ts +0 -15
  221. package/dist/next/measure.d.ts.map +0 -1
  222. package/dist/next/measure.js +0 -9
  223. package/dist/next/measure.js.map +0 -1
  224. package/dist/next/noSSR.d.ts +0 -5
  225. package/dist/next/noSSR.d.ts.map +0 -1
  226. package/dist/next/noSSR.js +0 -15
  227. package/dist/next/noSSR.js.map +0 -1
  228. package/dist/next/redirect.d.ts +0 -6
  229. package/dist/next/redirect.d.ts.map +0 -1
  230. package/dist/next/useIsInner.d.ts +0 -3
  231. package/dist/next/useIsInner.d.ts.map +0 -1
  232. package/dist/next/useIsInner.js +0 -15
  233. package/dist/next/useIsInner.js.map +0 -1
  234. package/dist/react/apiCall.d.ts +0 -6
  235. package/dist/react/apiCall.d.ts.map +0 -1
  236. package/dist/react/apiCall.js +0 -24
  237. package/dist/react/apiCall.js.map +0 -1
  238. package/dist/react/form/client.d.ts +0 -7
  239. package/dist/react/form/client.d.ts.map +0 -1
  240. package/dist/react/form/client.js +0 -46
  241. package/dist/react/form/client.js.map +0 -1
  242. package/dist/react/form/form.d.ts +0 -45
  243. package/dist/react/form/form.d.ts.map +0 -1
  244. package/dist/react/form/form.js +0 -69
  245. package/dist/react/form/form.js.map +0 -1
  246. package/dist/react/form/index.d.ts +0 -3
  247. package/dist/react/form/index.d.ts.map +0 -1
  248. package/dist/react/index.js +0 -25
  249. package/dist/react/index.js.map +0 -1
  250. package/dist/react/keyboard.d.ts +0 -11
  251. package/dist/react/keyboard.d.ts.map +0 -1
  252. package/dist/react/keyboard.js +0 -38
  253. package/dist/react/keyboard.js.map +0 -1
  254. package/dist/react/useFetch.d.ts +0 -2
  255. package/dist/react/useFetch.d.ts.map +0 -1
  256. package/dist/react/useFetch.js +0 -20
  257. package/dist/react/useFetch.js.map +0 -1
  258. package/dist/react/useFetcher.d.ts +0 -10
  259. package/dist/react/useFetcher.d.ts.map +0 -1
  260. package/dist/react/useFetcher.js +0 -43
  261. package/dist/react/useFetcher.js.map +0 -1
  262. package/dist/react-native/index.js +0 -9
  263. package/dist/react-native/index.js.map +0 -1
  264. package/dist/react-native/share.d.ts +0 -9
  265. package/dist/react-native/share.d.ts.map +0 -1
  266. package/dist/react-native/share.js +0 -23
  267. package/dist/react-native/share.js.map +0 -1
  268. package/dist/react-native/update.d.ts +0 -4
  269. package/dist/react-native/update.d.ts.map +0 -1
  270. package/dist/react-native/update.js +0 -40
  271. package/dist/react-native/update.js.map +0 -1
  272. package/dist/react-native/useAppState.d.ts +0 -2
  273. package/dist/react-native/useAppState.d.ts.map +0 -1
  274. package/dist/react-native/useAppState.js +0 -18
  275. package/dist/react-native/useAppState.js.map +0 -1
  276. package/dist/tailwind/fullpage.d.ts +0 -7
  277. package/dist/tailwind/fullpage.d.ts.map +0 -1
  278. package/dist/tailwind/fullpage.js +0 -14
  279. package/dist/tailwind/fullpage.js.map +0 -1
  280. package/dist/tailwind/index.js +0 -7
  281. package/dist/tailwind/index.js.map +0 -1
  282. package/dist/tailwind/responsiveHelper.d.ts +0 -2
  283. package/dist/tailwind/responsiveHelper.d.ts.map +0 -1
  284. package/dist/tailwind/responsiveHelper.js +0 -16
  285. package/dist/tailwind/responsiveHelper.js.map +0 -1
  286. package/src/bootstrap/adaptiveContainer.tsx +0 -12
  287. package/src/bootstrap/controls.stories.tsx +0 -62
  288. package/src/bootstrap/controls.tsx +0 -119
  289. package/src/bootstrap/error.stories.tsx +0 -34
  290. package/src/bootstrap/error.tsx +0 -40
  291. package/src/bootstrap/field.stories.tsx +0 -42
  292. package/src/bootstrap/field.tsx +0 -35
  293. package/src/bootstrap/footer.tsx +0 -49
  294. package/src/bootstrap/globalLoading.stories.tsx +0 -25
  295. package/src/bootstrap/globalLoading.tsx +0 -17
  296. package/src/bootstrap/loading.stories.tsx +0 -35
  297. package/src/bootstrap/loading.tsx +0 -22
  298. package/src/bootstrap/main.scss +0 -86
  299. package/src/bootstrap/responsiveHelper.tsx +0 -50
  300. package/src/bootstrap/screen.stories.tsx +0 -160
  301. package/src/bootstrap/screen.tsx +0 -144
  302. package/src/bootstrap/toaster.stories.tsx +0 -32
  303. package/src/bootstrap/toaster.tsx +0 -38
  304. package/src/bootstrap/useModal.stories.tsx +0 -51
  305. package/src/bootstrap/useModal.tsx +0 -88
  306. package/src/bootstrap/useWrappedForm.ts +0 -40
  307. package/src/core/README.md +0 -91
  308. package/src/core/cache.test.ts +0 -121
  309. package/src/core/cache.ts +0 -239
  310. package/src/core/errors.ts +0 -14
  311. package/src/core/files.ts +0 -25
  312. package/src/core/ls.ts +0 -11
  313. package/src/core/measure.ts +0 -61
  314. package/src/core/mutex.ts +0 -22
  315. package/src/core/worker.test.ts +0 -197
  316. package/src/core/worker.ts +0 -340
  317. package/src/core/workerUtils.ts +0 -32
  318. package/src/core/wrpc.fixture.ts +0 -72
  319. package/src/core/wrpc.test.ts +0 -225
  320. package/src/core/wrpc.ts +0 -359
  321. package/src/electron/README.md +0 -13
  322. package/src/electron/createWindow.ts +0 -234
  323. package/src/electron/updater.ts +0 -54
  324. package/src/electron-builder/builder.ts +0 -150
  325. package/src/electron-builder/images.ts +0 -33
  326. package/src/mui/README.md +0 -3
  327. package/src/mui/formControlFieldset.stories.tsx +0 -71
  328. package/src/mui/formControlFieldset.tsx +0 -26
  329. package/src/mui/formLabelLegend.tsx +0 -11
  330. package/src/mui/genericControl.tsx +0 -20
  331. package/src/mui/readOnly.tsx +0 -6
  332. package/src/next/appLink.tsx +0 -40
  333. package/src/next/measure.ts +0 -7
  334. package/src/next/noSSR.tsx +0 -17
  335. package/src/next/redirect.tsx +0 -13
  336. package/src/next/useIsInner.ts +0 -13
  337. package/src/react/apiCall.ts +0 -25
  338. package/src/react/form/client.tsx +0 -62
  339. package/src/react/form/form.tsx +0 -129
  340. package/src/react/form/index.ts +0 -2
  341. package/src/react/keyboard.tsx +0 -49
  342. package/src/react/useFetch.ts +0 -29
  343. package/src/react/useFetcher.ts +0 -49
  344. package/src/react-native/share.ts +0 -31
  345. package/src/react-native/update.tsx +0 -40
  346. package/src/react-native/useAppState.ts +0 -18
  347. package/src/tailwind/README.md +0 -8
  348. package/src/tailwind/fullpage.tsx +0 -11
  349. package/src/tailwind/responsiveHelper.tsx +0 -16
  350. package/src-todo/auth0.tsx +0 -177
  351. package/vite.exports.ts +0 -121
  352. /package/dist/{bootstrap/index.js.map → index.js.map} +0 -0
@@ -1,14 +0,0 @@
1
- // @see https://www.youtube.com/watch?v=AdmGHwvgaVs
2
- export function catchErrors<T, E extends Error = Error>(promise: Promise<T>, errors?: E[]) {
3
- return promise
4
- .then((data) => [undefined, data])
5
- .catch((error) => {
6
- if (!errors?.length) {
7
- return [error];
8
- }
9
- if (errors.some((e) => e instanceof error)) {
10
- return [error];
11
- }
12
- throw error;
13
- });
14
- }
package/src/core/files.ts DELETED
@@ -1,25 +0,0 @@
1
- export const downloadFile = async (filename, text, type = 'application/json') => {
2
- //FIXME https://github.com/eligrey/FileSaver.js/issues/471
3
- const { saveAs } = require('file-saver');
4
-
5
- //FIXME https://github.com/eligrey/FileSaver.js/issues/731
6
- saveAs(new Blob([text], { type }), filename);
7
- };
8
-
9
- export const openFile = (): Promise<string> =>
10
- new Promise((res, rej) => {
11
- const input = document.createElement('input');
12
- input.type = 'file';
13
- input.addEventListener('change', function readFile(e) {
14
- input.removeEventListener('change', readFile);
15
- // @ts-expect-error file is always there
16
- const [file] = e.target.files;
17
- if (!file) return;
18
- const reader = new FileReader();
19
- reader.onload = (e) => res(e.target?.result as string);
20
- reader.onerror = rej;
21
- reader.onabort = rej;
22
- reader.readAsText(file);
23
- });
24
- input.click();
25
- });
package/src/core/ls.ts DELETED
@@ -1,11 +0,0 @@
1
- export function getStorage(pref: string) {
2
- const prefix = (key) => `${pref}-${key}`;
3
-
4
- const ls = (typeof window !== 'undefined' ? localStorage : null) as Storage;
5
-
6
- const read = (key: string) => JSON.parse(ls?.getItem(prefix(key)) || 'null');
7
- const write = (key: string, value: any) => ls?.setItem(prefix(key), JSON.stringify(value));
8
- const remove = (key: string) => ls?.removeItem(prefix(key));
9
-
10
- return { read, write, remove };
11
- }
@@ -1,61 +0,0 @@
1
- import { bold, magenta, cyan, yellow, red, green } from 'colors/safe';
2
-
3
- export const colored = {
4
- important: bold,
5
- subject: magenta,
6
- arg: cyan,
7
- sup: yellow,
8
- err: red,
9
- ok: green,
10
- };
11
-
12
- const noColor = (str) => str;
13
-
14
- export const uncolored = {
15
- important: noColor,
16
- subject: noColor,
17
- arg: noColor,
18
- sup: noColor,
19
- err: noColor,
20
- ok: noColor,
21
- };
22
-
23
- export function createMeasurer({ colors = true, prepend = 'LOG', padding = 10 }) {
24
- const { important, subject, arg, sup, ok, err } = colors ? colored : uncolored;
25
-
26
- function measure(what: string, step: string, supplemental: string = '', ...args) {
27
- const time = performance.now();
28
-
29
- const prefix = [
30
- prepend,
31
- important(subject(what.substring(0, padding).padStart(padding, ' '))),
32
- arg(step),
33
- sup(supplemental),
34
- ...args,
35
- ];
36
-
37
- const done = (result: string = ok('DONE'), ...args2) => {
38
- console.log(
39
- ...[
40
- ...prefix,
41
- important(result),
42
- ...args2,
43
- 'in',
44
- important((performance.now() - time).toFixed(0)),
45
- 'ms',
46
- ].filter(Boolean),
47
- );
48
- console.groupEnd();
49
- };
50
-
51
- const fail = (result: string = err('FAIL'), ...args2) => done(result, ...args2);
52
-
53
- const log = (...args2) => {
54
- console.log(...prefix, ...args2);
55
- };
56
-
57
- return { done, fail, log };
58
- }
59
-
60
- return { measure, important, subject, arg, sup, ok, err };
61
- }
package/src/core/mutex.ts DELETED
@@ -1,22 +0,0 @@
1
- //TODO Implement queue with ranking & sorting
2
- //TODO https://github.com/sindresorhus/p-queue
3
- export class Mutex {
4
- private promise = Promise.resolve();
5
-
6
- public exec(fn: (...args: any) => any): ReturnType<typeof fn> {
7
- return new Promise((resolve, reject) => {
8
- // wrap in always successful function
9
- this.promise = this.promise.then(async () => {
10
- try {
11
- resolve(await fn());
12
- } catch (e) {
13
- reject(e);
14
- }
15
- });
16
- });
17
- }
18
-
19
- public wrap(fn: (...args: any) => any): ReturnType<typeof fn> {
20
- return (...args: any) => this.exec(() => fn(...args));
21
- }
22
- }
@@ -1,197 +0,0 @@
1
- import { expect, describe, test, vi } from 'vitest';
2
- import { RespondersBase, WorkerDialog } from './worker';
3
- import EventEmitter from 'node:events';
4
-
5
- // Prepare
6
-
7
- //TODO @vitest/web-worker can ve used, coverage is collected, but this gives more direct access to worker internals
8
- // class WorkerPeer extends EventEmitter implements WorkerLike {
9
- class WorkerPeer extends EventEmitter {
10
- peer: WorkerPeer;
11
-
12
- setPeer(peer: WorkerPeer) {
13
- this.peer = peer;
14
- }
15
-
16
- postMessage(data: any) {
17
- this.peer?.emit('message', { data });
18
- }
19
-
20
- addEventListener(event: string, listener: (e: any) => void, options) {
21
- this[options['once'] ? 'once' : 'on'](event, listener);
22
- options?.signal?.addEventListener('abort', () => this.off(event, listener));
23
- }
24
-
25
- removeEventListener(event: string, listener: (e: any) => void) {
26
- this.off(event, listener);
27
- }
28
- }
29
-
30
- class Responders extends RespondersBase<Responders> {
31
- lastContext: any; // for testing purposes
32
-
33
- test = this.create(
34
- 'test',
35
- async (
36
- { n, abort = false, buf }: { n: number; abort?: boolean; buf?: ArrayBuffer }, // should have all possible combinations for strong typing
37
- ctx,
38
- ) => {
39
- this.lastContext = ctx;
40
-
41
- const res: number[] = [];
42
-
43
- let aborted = false;
44
- let progress = 0;
45
-
46
- ctx.listen(({ abort }) => {
47
- if (abort) aborted = true;
48
- });
49
-
50
- for (const i of Array(n).keys()) {
51
- if (aborted) break;
52
- progress = (i + 1) / n;
53
- ctx.send({ progress });
54
- res.push(i);
55
- }
56
-
57
- return { res, progress, aborted };
58
- },
59
- );
60
-
61
- error = this.create(
62
- 'error',
63
- async (
64
- _, // should have all possible combinations for strong typing
65
- ctx,
66
- ) => {
67
- this.lastContext = ctx;
68
- throw new Error('Test');
69
- },
70
- );
71
- }
72
-
73
- const createWorker = () => {
74
- const mainWorker = new WorkerPeer();
75
- const mainDialog = new WorkerDialog(
76
- mainWorker as never as Worker,
77
- {} as WorkerDialog<Responders>['responders'],
78
- 'Main',
79
- );
80
-
81
- const worker = new WorkerPeer();
82
- const workerDialog = new WorkerDialog(worker as never as Worker, new Responders(), 'Worker');
83
-
84
- mainWorker.setPeer(worker);
85
- worker.setPeer(mainWorker);
86
-
87
- return { mainWorker, mainDialog, worker, workerDialog };
88
- };
89
-
90
- // Tests
91
-
92
- describe('Worker', async () => {
93
- test('simple', async () => {
94
- const { mainDialog, workerDialog } = createWorker();
95
-
96
- const l = vi.fn();
97
- let mctx;
98
-
99
- const result = await mainDialog.withMessage('test').fetch({ n: 3, buf: new ArrayBuffer() }, (ctx) => {
100
- mctx = ctx;
101
- ctx.listen(l);
102
- });
103
-
104
- expect(l).toBeCalledTimes(4); // 3x progress, last
105
- expect(result).toStrictEqual({ res: [0, 1, 2], progress: 1, aborted: false });
106
-
107
- expect(mainDialog.contexts.size).toBe(0);
108
- expect(mctx.debugInfo.closed).toBe(true);
109
-
110
- expect(workerDialog.contexts.size).toBe(2);
111
- expect(workerDialog.contexts.values().next().value.debugInfo).toStrictEqual({
112
- id: '*',
113
- parentId: undefined,
114
- message: 'test',
115
- closed: false,
116
- type: 'response',
117
- listeners: 1,
118
- });
119
- expect(workerDialog.responders.lastContext.debugInfo).toStrictEqual({
120
- id: '1',
121
- parentId: '*',
122
- message: 'test',
123
- closed: true,
124
- type: 'response',
125
- listeners: 0,
126
- });
127
- });
128
-
129
- test('abort', async () => {
130
- const { mainDialog } = createWorker();
131
-
132
- const result = await mainDialog.withMessage('test').fetch({ n: 3 }, (ctx) => {
133
- ctx.listen(() => {
134
- ctx.send({ abort: true });
135
- });
136
- });
137
-
138
- expect(result).toStrictEqual({ res: [0], progress: 0.3333333333333333, aborted: true });
139
- });
140
-
141
- test('faulty listener', async () => {
142
- const { mainDialog } = createWorker();
143
-
144
- await mainDialog.withMessage('test').fetch({ n: 3 }, (ctx) => {
145
- ctx.listen(() => {
146
- throw new Error('Listener');
147
- });
148
- });
149
-
150
- expect.assertions(0); // nothing happened
151
- });
152
-
153
- test('error', async () => {
154
- const { mainDialog } = createWorker();
155
- await expect(async () => mainDialog.withMessage('error').fetch()).rejects.toThrowError('Test');
156
- });
157
-
158
- test('close', async () => {
159
- const { mainDialog, workerDialog } = createWorker();
160
-
161
- mainDialog.close();
162
- workerDialog.close();
163
-
164
- expect(mainDialog.contexts.size).toBe(0);
165
- expect(workerDialog.contexts.size).toBe(0);
166
- });
167
-
168
- test('empty message', async () => {
169
- expect(() => createWorker().mainDialog.withMessage(undefined as any)).toThrowError(
170
- 'New context must have message',
171
- );
172
- });
173
-
174
- test('empty ID', async () => {
175
- const { mainDialog } = createWorker();
176
- const first = mainDialog.withMessage('test');
177
- expect(() => first.withMessage('error', '')).toThrowError('ID is required');
178
- });
179
-
180
- test('nesting', async () => {
181
- const { mainDialog } = createWorker();
182
- const first = mainDialog.withMessage('test');
183
- const second = first.withMessage('error', 'x');
184
- const third = second.withMessage('error');
185
- expect(mainDialog.contexts.size).toBe(3);
186
- expect(first.info).toBe('Main:*/1:test[open,request]');
187
- first.close();
188
- expect(mainDialog.contexts.size).toBe(0);
189
- expect(first.debugInfo.closed).toBe(true);
190
- expect(second.debugInfo.closed).toBe(true);
191
- expect(third.debugInfo.closed).toBe(true);
192
- expect(first.info).toBe('Main:*/1:test[closed,request]'); // id bumped from 0
193
- expect(second.info).toBe('Main:1/x:error[closed,request]'); // new id
194
- expect(third.info).toBe('Main:x/x:error[closed,request]'); // id kept
195
- await expect(() => first.fetch({ n: 1 })).rejects.toThrowError(/Context 1 is closed/);
196
- });
197
- });
@@ -1,340 +0,0 @@
1
- import { getTransferrable } from './workerUtils';
2
-
3
- // Types
4
-
5
- type WebWorker = Worker | (Window & typeof globalThis) | (WorkerGlobalScope & typeof globalThis);
6
-
7
- type MethodReturn<R, M extends keyof R> = R[M] extends (...args: any[]) => any
8
- ? Partial<Awaited<ReturnType<R[M]>>>
9
- : never;
10
-
11
- type MethodArg<R, M extends keyof R> = R[M] extends (arg: infer A, ...args: any[]) => any ? Partial<A> : never;
12
-
13
- export type Ctx<R, K extends keyof R> = {
14
- id: string;
15
- parentId?: string;
16
- message: K;
17
- done: boolean;
18
- };
19
-
20
- type Data<R, M extends keyof R> = MethodReturn<R, M> | MethodArg<R, M> | Error;
21
-
22
- type Event<R, M extends keyof R> = MessageEvent<{ data: Data<R, M>; ctx: Ctx<R, M> }>;
23
-
24
- type Listener<R, M extends keyof R, D extends Data<R, M> = Data<R, M>, S extends Context<R, M> = Context<R, M>> = (
25
- data: D,
26
- self: S,
27
- event: Event<R, M>,
28
- ) => void | Promise<void>;
29
-
30
- // Constants
31
-
32
- const ALL = '*';
33
-
34
- const RESPONDER = Symbol('responder');
35
-
36
- function checkClosed(obj: any, label: string = 'external') {
37
- if (obj?.closed) throw new Error(`Context ${obj.id} is closed (${label})`, { cause: obj });
38
- }
39
-
40
- function checkMessage(obj: any, message: any) {
41
- if (!message) throw new Error('New context must have message', { cause: obj });
42
- }
43
-
44
- // Classes
45
-
46
- export class WorkerDialog<R extends RespondersBase<any>> {
47
- readonly contexts = new Set<Context<R, keyof R>>();
48
- protected closed = false;
49
- protected currentId = 0; // IDs are intentionally not global
50
-
51
- constructor(
52
- public worker: WebWorker,
53
- public responders: R,
54
- public name: string,
55
- public debug: number = 0, // 0 - none, 1 - general messages, 2 - sub/unsub, 3 - transport TODO Object?
56
- ) {
57
- if (!this.worker) return; // Next.js SSR
58
-
59
- for (const message in this.responders) {
60
- if (!this.responders?.[message]?.[RESPONDER]) continue;
61
-
62
- // root context with id=all
63
- const rootContext = new ResponseContext<R, typeof message>(this, message, ALL);
64
-
65
- //TODO: unsub(); ? Probably not needed, root must be always listening until closed by WorkerDialog
66
- rootContext.listen(async (data, context, event) => {
67
- // create sub context with ID
68
- const responseContext = context.withMessage(message, event.data.ctx.id);
69
-
70
- /* v8 ignore start */
71
- if (this.debug)
72
- console.log('Root Listener', rootContext.info, {
73
- rootContext: context,
74
- data,
75
- responseContext,
76
- event,
77
- responder: this.responders[message],
78
- });
79
- /* v8 ignore stop */
80
-
81
- try {
82
- responseContext.send(await (this.responders[message] as any)(data, responseContext), true);
83
- responseContext.close('root.listener');
84
- } catch (e) {
85
- /* v8 ignore next */
86
- if (this.debug) console.error(`Error in responder ${responseContext.info}`, { e });
87
- responseContext.send(e, true);
88
- }
89
- }, true);
90
- }
91
- }
92
-
93
- getID() {
94
- return (++this.currentId).toString();
95
- }
96
-
97
- close() {
98
- for (const context of this.contexts) context.close('root');
99
- this.contexts.clear();
100
- }
101
-
102
- withMessage<M extends keyof R>(message: M) {
103
- return new RequestContext<R, M>(this, message, this.getID()); // always new context
104
- }
105
- }
106
-
107
- export class RespondersBase<R> {
108
- create<M extends keyof R, F extends (input: any, context: ResponseContext<R, M>) => any>(
109
- message: M,
110
- responder: F,
111
- ): F {
112
- responder[RESPONDER] = true;
113
- return responder as any;
114
- }
115
- }
116
-
117
- // can only be used to create actual contexts, carries the context data
118
- abstract class Context<R, M extends keyof R> {
119
- protected readonly unsub = new Set<() => void>();
120
- protected closed = false;
121
-
122
- constructor(
123
- protected readonly dialog: WorkerDialog<any>,
124
- public message: M,
125
- public id: string | undefined,
126
- public parent: Context<R, keyof R> | undefined | null = null,
127
- ) {
128
- checkClosed(dialog, 'newContext');
129
- checkClosed(parent, 'newContext');
130
- checkMessage(this, message);
131
- this.id = id ?? parent?.id;
132
- if (!this.id) throw new Error('ID is required', { cause: this });
133
- this.dialog.contexts.add(this);
134
- }
135
-
136
- abstract withMessage<M2 extends keyof R>(message: M2, id?: string): Context<R, M2>;
137
-
138
- close(reason: string = 'external') {
139
- /* v8 ignore next */
140
- if (this.dialog.debug >= 2) console.log('Closing', this.info, reason, this);
141
-
142
- this.dialog.contexts.delete(this);
143
-
144
- // Unsubscribe from all listeners
145
- for (const unsub of this.unsub) unsub();
146
- this.unsub.clear();
147
-
148
- // Remove all child contexts
149
- this.dialog.contexts.forEach((context) => {
150
- if (context.parent?.id === this.id) context.close('parent');
151
- });
152
-
153
- this.closed = true;
154
- }
155
-
156
- get info() {
157
- return `${this.dialog.name}:${this.parent?.id ?? ALL}/${this.id}:${this.message.toString()}[${this.closed ? 'closed' : 'open'},${this.type}]`;
158
- }
159
-
160
- get debugInfo() {
161
- return {
162
- id: this.id,
163
- parentId: this.parent?.id,
164
- message: this.message,
165
- closed: this.closed,
166
- type: this.type,
167
- listeners: this.unsub.size,
168
- };
169
- }
170
-
171
- abstract type: 'request' | 'response' | string;
172
-
173
- protected postMessage(data?: Data<R, M>, done = false) {
174
- checkClosed(this, 'postMessage');
175
-
176
- const transfer = getTransferrable(data);
177
-
178
- const ctx = {
179
- done,
180
- id: this.id,
181
- parentId: this.parent?.id,
182
- message: this.message,
183
- };
184
-
185
- /* v8 ignore next */
186
- if (this.dialog.debug >= 3) console.log('postMessage', this.info, { ctx, data, transfer }, this);
187
-
188
- this.dialog.worker.postMessage({ data, ctx }, transfer);
189
-
190
- return data;
191
- }
192
-
193
- send(data?: Data<R, M>, done = false) {
194
- return this.postMessage(data, done);
195
- }
196
-
197
- /**
198
- * Does not automatically close, assumes consumer will close
199
- *
200
- * ```ts
201
- * dialog.request('x').fetch((c) => {
202
- * c.response('x').listen(() => { ... }); // listens to "x", will be closed by upper level fetch
203
- * c.request('x').fetcH(y, (c) => { ... }); // closes
204
- * }) // closes
205
- *
206
- * const un = dialog.context('x').listen((e) => { ... }); // listens to 'x'
207
- * un(); // closes
208
- *
209
- * const un = dialog.context('x').listen((e) => { ... }); // listens to 'x'
210
- * un(); // closes
211
- * ```
212
- */
213
- listen(callback: Listener<R, M>, done = false): () => void {
214
- checkClosed(this, 'listen');
215
-
216
- const controller = new AbortController();
217
-
218
- this.dialog.worker.addEventListener(
219
- 'message',
220
- async (event: Event<R, M>) => {
221
- try {
222
- const {
223
- data: { ctx, data },
224
- } = event;
225
-
226
- if (
227
- !ctx ||
228
- (this.id !== ALL && this.id !== ctx.id) ||
229
- this.message !== ctx.message ||
230
- (done && !ctx.done)
231
- )
232
- return; // unknown message from devtools etc., or different context
233
-
234
- /* v8 ignore next */
235
- if (this.dialog.debug) console.log('Listener event', this.info, event, this);
236
-
237
- await callback(data, this, event); // Requests can come here, but they're typed in responders
238
- } catch (e) {
239
- /* v8 ignore next */
240
- if (this.dialog.debug) console.error(`Error in listener ${this.info}`, this, { event, e });
241
- //TODO Respond with error?
242
- }
243
- },
244
- { signal: controller.signal },
245
- );
246
-
247
- const unsub = () => {
248
- /* v8 ignore next */
249
- if (this.dialog.debug >= 2) console.log('Stop listening', this.info, this);
250
- controller.abort();
251
- this.unsub.delete(unsub);
252
- };
253
-
254
- this.unsub.add(unsub);
255
-
256
- return unsub;
257
- }
258
- }
259
-
260
- class RequestContext<R, M extends keyof R> extends Context<R, M> {
261
- type = 'request';
262
-
263
- withMessage<M2 extends keyof R>(message: M2, id?: string): RequestContext<R, M2> {
264
- return new RequestContext<R, M2>(this.dialog, message, id, this);
265
- }
266
-
267
- send(data?: MethodArg<R, M>, done = false): MethodArg<R, M> {
268
- return super.send(data, done) as any;
269
- }
270
-
271
- listen(callback: Listener<R, M, MethodReturn<R, M>, RequestContext<R, M>>, done = false): () => void {
272
- return super.listen(callback, done);
273
- }
274
-
275
- /**
276
- * Returns response
277
- * If you need to work with ctx, use callback
278
- * Closed automatically on response
279
- *
280
- * dialog.fetch('x', (c) => {
281
- * c.listen(() => { ... }); // will be closed by upper level fetch
282
- * c.fetcH(y, (c) => { ... }); // closes c
283
- * c.context().fetcH(y, (c) => { ... }); // closes sub, not closes c, will be closed by upper level fetch
284
- * })
285
- */
286
- async fetch(
287
- data?: MethodArg<R, M>,
288
- callback?: (context: RequestContext<R, M>) => void,
289
- ): Promise<MethodReturn<R, M>> {
290
- checkClosed(this, 'fetch');
291
-
292
- const res = await Promise.all([
293
- callback?.(this),
294
- this.expect(), // will be closed in expect -> listener -> unsub
295
- this.send(data, true),
296
- ]);
297
-
298
- /* v8 ignore next */
299
- if (this.dialog.debug >= 2) console.log('Fetch', this.info, this);
300
-
301
- this.close('fetch');
302
-
303
- return res[1];
304
- }
305
-
306
- protected async expect() {
307
- checkClosed(this, 'expect');
308
-
309
- //FIXME Manual promise type
310
- return new Promise<MethodReturn<R, M>>((resolve, reject) => {
311
- const responseListener = this.listen(async (data) => {
312
- try {
313
- if ((data as any) instanceof Error) throw data;
314
- responseListener();
315
- /* v8 ignore next */
316
- if (this.dialog.debug >= 3) console.log('Expect', this.info, data, this);
317
- resolve(data as any);
318
- } catch (e) {
319
- reject(e);
320
- }
321
- }, true);
322
- });
323
- }
324
- }
325
-
326
- class ResponseContext<R, M extends keyof R> extends Context<R, M> {
327
- type = 'response';
328
-
329
- withMessage<M2 extends keyof R>(message: M2, id?: string): ResponseContext<R, M2> {
330
- return new ResponseContext<R, M2>(this.dialog, message, id, this);
331
- }
332
-
333
- listen(callback: Listener<R, M, MethodArg<R, M>, ResponseContext<R, M>>, done = false): () => void {
334
- return super.listen(callback, done);
335
- }
336
-
337
- send(data?: MethodReturn<R, M>, done = false): MethodReturn<R, M> {
338
- return super.send(data, done) as any;
339
- }
340
- }