@far-world-labs/verblets 0.2.0 → 0.4.0

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 (330) hide show
  1. package/README.md +98 -213
  2. package/dist/index.browser.js +221 -0
  3. package/dist/index.js +696 -0
  4. package/dist/shared-CMgpfDG4.js +10714 -0
  5. package/package.json +38 -15
  6. package/.cursor/launch.json +0 -30
  7. package/.cursor/settings.json +0 -20
  8. package/.github/workflows/branch-protection.yml +0 -22
  9. package/.github/workflows/ci.yml +0 -165
  10. package/.husky/pre-commit +0 -4
  11. package/.prettierrc +0 -6
  12. package/.release-it.json +0 -12
  13. package/.vitest.config.examples.js +0 -12
  14. package/.vitest.config.js +0 -8
  15. package/.vscode/launch.json +0 -31
  16. package/AGENTS.md +0 -220
  17. package/DEVELOPING.md +0 -105
  18. package/docker-compose.yml +0 -7
  19. package/eslint.config.js +0 -80
  20. package/scripts/clear-redis.js +0 -74
  21. package/scripts/generate-chain/index.js +0 -111
  22. package/scripts/generate-lib/index.js +0 -68
  23. package/scripts/generate-test/index.js +0 -137
  24. package/scripts/generate-verblet/README.md +0 -17
  25. package/scripts/generate-verblet/index.js +0 -110
  26. package/scripts/run.sh +0 -15
  27. package/scripts/runner/index.js +0 -56
  28. package/scripts/simple-editor/README.md +0 -34
  29. package/scripts/simple-editor/index.js +0 -79
  30. package/scripts/summarize-files/index.js +0 -70
  31. package/src/chains/README.md +0 -30
  32. package/src/chains/anonymize/README.md +0 -21
  33. package/src/chains/anonymize/index.examples.js +0 -75
  34. package/src/chains/anonymize/index.js +0 -121
  35. package/src/chains/anonymize/index.spec.js +0 -78
  36. package/src/chains/bulk-central-tendency/index.examples.js +0 -138
  37. package/src/chains/bulk-central-tendency/index.js +0 -91
  38. package/src/chains/bulk-filter/README.md +0 -21
  39. package/src/chains/bulk-filter/index.examples.js +0 -22
  40. package/src/chains/bulk-filter/index.js +0 -58
  41. package/src/chains/bulk-filter/index.spec.js +0 -38
  42. package/src/chains/bulk-find/README.md +0 -16
  43. package/src/chains/bulk-find/index.examples.js +0 -20
  44. package/src/chains/bulk-find/index.js +0 -30
  45. package/src/chains/bulk-find/index.spec.js +0 -26
  46. package/src/chains/bulk-group/README.md +0 -23
  47. package/src/chains/bulk-group/index.examples.js +0 -18
  48. package/src/chains/bulk-group/index.js +0 -34
  49. package/src/chains/bulk-group/index.spec.js +0 -41
  50. package/src/chains/bulk-map/README.md +0 -43
  51. package/src/chains/bulk-map/index.examples.js +0 -17
  52. package/src/chains/bulk-map/index.js +0 -86
  53. package/src/chains/bulk-map/index.spec.js +0 -44
  54. package/src/chains/bulk-reduce/README.md +0 -12
  55. package/src/chains/bulk-reduce/index.examples.js +0 -15
  56. package/src/chains/bulk-reduce/index.js +0 -13
  57. package/src/chains/bulk-reduce/index.spec.js +0 -25
  58. package/src/chains/bulk-score/README.md +0 -16
  59. package/src/chains/bulk-score/bulk-score-result.json +0 -18
  60. package/src/chains/bulk-score/index.examples.js +0 -22
  61. package/src/chains/bulk-score/index.js +0 -133
  62. package/src/chains/bulk-score/index.spec.js +0 -30
  63. package/src/chains/category-samples/README.md +0 -61
  64. package/src/chains/category-samples/index.examples.js +0 -103
  65. package/src/chains/category-samples/index.js +0 -134
  66. package/src/chains/collect-terms/README.md +0 -12
  67. package/src/chains/collect-terms/index.examples.js +0 -16
  68. package/src/chains/collect-terms/index.js +0 -44
  69. package/src/chains/collect-terms/index.spec.js +0 -25
  70. package/src/chains/conversation/README.md +0 -26
  71. package/src/chains/conversation/index.examples.js +0 -398
  72. package/src/chains/conversation/index.js +0 -126
  73. package/src/chains/conversation/index.spec.js +0 -148
  74. package/src/chains/conversation/turn-policies.js +0 -93
  75. package/src/chains/conversation/turn-policies.md +0 -123
  76. package/src/chains/conversation/turn-policies.spec.js +0 -135
  77. package/src/chains/date/README.md +0 -12
  78. package/src/chains/date/index.examples.js +0 -47
  79. package/src/chains/date/index.js +0 -74
  80. package/src/chains/date/index.spec.js +0 -62
  81. package/src/chains/disambiguate/README.md +0 -22
  82. package/src/chains/disambiguate/disambiguate-meanings-result.json +0 -16
  83. package/src/chains/disambiguate/index.examples.js +0 -18
  84. package/src/chains/disambiguate/index.js +0 -92
  85. package/src/chains/disambiguate/index.spec.js +0 -25
  86. package/src/chains/dismantle/README.md +0 -67
  87. package/src/chains/dismantle/dismantle.examples.js +0 -27
  88. package/src/chains/dismantle/index.examples.js +0 -30
  89. package/src/chains/dismantle/index.js +0 -303
  90. package/src/chains/dismantle/index.spec.js +0 -32
  91. package/src/chains/expect/README.md +0 -171
  92. package/src/chains/expect/index.examples.js +0 -146
  93. package/src/chains/expect/index.js +0 -207
  94. package/src/chains/expect/index.spec.js +0 -324
  95. package/src/chains/filter-ambiguous/README.md +0 -11
  96. package/src/chains/filter-ambiguous/index.examples.js +0 -20
  97. package/src/chains/filter-ambiguous/index.js +0 -49
  98. package/src/chains/filter-ambiguous/index.spec.js +0 -31
  99. package/src/chains/glossary/README.md +0 -19
  100. package/src/chains/glossary/index.examples.js +0 -386
  101. package/src/chains/glossary/index.js +0 -75
  102. package/src/chains/glossary/index.spec.js +0 -19
  103. package/src/chains/intersections/README.md +0 -166
  104. package/src/chains/intersections/index.examples.js +0 -280
  105. package/src/chains/intersections/index.js +0 -218
  106. package/src/chains/intersections/intersection-result.json +0 -38
  107. package/src/chains/list/index.examples.js +0 -68
  108. package/src/chains/list/index.js +0 -214
  109. package/src/chains/list/index.spec.js +0 -67
  110. package/src/chains/list/list-result.json +0 -16
  111. package/src/chains/list/schema.json +0 -24
  112. package/src/chains/llm-logger/README.md +0 -366
  113. package/src/chains/llm-logger/index.js +0 -591
  114. package/src/chains/llm-logger/index.spec.js +0 -391
  115. package/src/chains/llm-logger/schema.json +0 -105
  116. package/src/chains/questions/index.examples.js +0 -69
  117. package/src/chains/questions/index.js +0 -135
  118. package/src/chains/questions/index.spec.js +0 -29
  119. package/src/chains/scan-js/index.js +0 -116
  120. package/src/chains/set-interval/README.md +0 -81
  121. package/src/chains/set-interval/index.examples.js +0 -64
  122. package/src/chains/set-interval/index.js +0 -152
  123. package/src/chains/set-interval/index.spec.js +0 -70
  124. package/src/chains/socratic/README.md +0 -17
  125. package/src/chains/socratic/index.js +0 -64
  126. package/src/chains/socratic/index.spec.js +0 -24
  127. package/src/chains/sort/index.examples.js +0 -36
  128. package/src/chains/sort/index.js +0 -163
  129. package/src/chains/sort/index.spec.js +0 -112
  130. package/src/chains/sort/sort-result.json +0 -16
  131. package/src/chains/summary-map/README.md +0 -41
  132. package/src/chains/summary-map/index.examples.js +0 -64
  133. package/src/chains/summary-map/index.js +0 -226
  134. package/src/chains/summary-map/index.spec.js +0 -153
  135. package/src/chains/test/index.js +0 -114
  136. package/src/chains/test-advice/index.js +0 -35
  137. package/src/chains/themes/README.md +0 -20
  138. package/src/chains/themes/index.examples.js +0 -17
  139. package/src/chains/themes/index.js +0 -28
  140. package/src/chains/themes/index.spec.js +0 -19
  141. package/src/chains/veiled-variants/index.examples.js +0 -18
  142. package/src/chains/veiled-variants/index.js +0 -107
  143. package/src/chains/veiled-variants/index.spec.js +0 -40
  144. package/src/constants/common.js +0 -13
  145. package/src/constants/messages.js +0 -3
  146. package/src/constants/models.js +0 -184
  147. package/src/index.js +0 -203
  148. package/src/json-schemas/README.md +0 -13
  149. package/src/json-schemas/cars-test.json +0 -11
  150. package/src/json-schemas/index.js +0 -12
  151. package/src/json-schemas/intent.json +0 -38
  152. package/src/json-schemas/schema-dot-org-photograph.json +0 -133
  153. package/src/json-schemas/schema-dot-org-place.json +0 -129
  154. package/src/lib/README.md +0 -26
  155. package/src/lib/any-signal/index.js +0 -28
  156. package/src/lib/assert/README.md +0 -84
  157. package/src/lib/assert/index.js +0 -50
  158. package/src/lib/bulk-filter/README.md +0 -22
  159. package/src/lib/bulk-filter/index.examples.js +0 -27
  160. package/src/lib/bulk-filter/index.js +0 -63
  161. package/src/lib/bulk-filter/index.spec.js +0 -38
  162. package/src/lib/bulk-find/README.md +0 -18
  163. package/src/lib/bulk-find/index.examples.js +0 -19
  164. package/src/lib/bulk-find/index.js +0 -30
  165. package/src/lib/bulk-find/index.spec.js +0 -41
  166. package/src/lib/chatgpt/index.js +0 -163
  167. package/src/lib/combinations/index.js +0 -30
  168. package/src/lib/combinations/index.spec.js +0 -23
  169. package/src/lib/editor/index.js +0 -31
  170. package/src/lib/functional/index.js +0 -28
  171. package/src/lib/logger-service/index.js +0 -32
  172. package/src/lib/parse-js-parts/index.js +0 -321
  173. package/src/lib/parse-js-parts/index.spec.js +0 -156
  174. package/src/lib/parse-llm-list/README.md +0 -39
  175. package/src/lib/parse-llm-list/index.js +0 -54
  176. package/src/lib/parse-llm-list/index.spec.js +0 -59
  177. package/src/lib/path-aliases/index.js +0 -37
  178. package/src/lib/path-aliases/index.spec.js +0 -64
  179. package/src/lib/pave/index.js +0 -34
  180. package/src/lib/pave/index.spec.js +0 -76
  181. package/src/lib/prompt-cache/index.js +0 -50
  182. package/src/lib/retry/index.js +0 -66
  183. package/src/lib/retry/index.spec.js +0 -86
  184. package/src/lib/ring-buffer/README.md +0 -82
  185. package/src/lib/ring-buffer/index.js +0 -235
  186. package/src/lib/ring-buffer/index.spec.js +0 -388
  187. package/src/lib/search-best-first/city-walk.spec.js +0 -37
  188. package/src/lib/search-best-first/index.js +0 -97
  189. package/src/lib/search-best-first/index.spec.js +0 -35
  190. package/src/lib/search-js-files/code-features-property-definitions.json +0 -123
  191. package/src/lib/search-js-files/index.examples.js +0 -22
  192. package/src/lib/search-js-files/index.js +0 -155
  193. package/src/lib/search-js-files/index.spec.js +0 -34
  194. package/src/lib/search-js-files/scan-file.js +0 -242
  195. package/src/lib/shorten-text/index.js +0 -25
  196. package/src/lib/shorten-text/index.spec.js +0 -68
  197. package/src/lib/strip-numeric/index.js +0 -5
  198. package/src/lib/strip-response/index.js +0 -30
  199. package/src/lib/template-replace/index.js +0 -23
  200. package/src/lib/template-replace/index.spec.js +0 -60
  201. package/src/lib/timed-abort-controller/index.js +0 -41
  202. package/src/lib/to-bool/index.js +0 -8
  203. package/src/lib/to-date/index.js +0 -11
  204. package/src/lib/to-enum/index.js +0 -14
  205. package/src/lib/to-number/index.js +0 -12
  206. package/src/lib/to-number-with-units/index.js +0 -51
  207. package/src/lib/transcribe/index.js +0 -78
  208. package/src/prompts/README.md +0 -17
  209. package/src/prompts/as-enum.js +0 -5
  210. package/src/prompts/as-json-schema.js +0 -9
  211. package/src/prompts/as-object-with-schema.js +0 -26
  212. package/src/prompts/as-schema-org-text.js +0 -25
  213. package/src/prompts/as-schema-org-type.js +0 -1
  214. package/src/prompts/blog-post.js +0 -7
  215. package/src/prompts/code-features.js +0 -24
  216. package/src/prompts/constants.js +0 -101
  217. package/src/prompts/features-json-schema.js +0 -27
  218. package/src/prompts/generate-collection.js +0 -26
  219. package/src/prompts/generate-list.js +0 -48
  220. package/src/prompts/generate-questions.js +0 -19
  221. package/src/prompts/index.js +0 -20
  222. package/src/prompts/intent.js +0 -60
  223. package/src/prompts/output-succinct-names.js +0 -3
  224. package/src/prompts/select-from-threshold.js +0 -17
  225. package/src/prompts/sort.js +0 -31
  226. package/src/prompts/style.js +0 -38
  227. package/src/prompts/summarize.js +0 -13
  228. package/src/prompts/token-budget.js +0 -3
  229. package/src/prompts/wrap-list.js +0 -11
  230. package/src/prompts/wrap-variable.js +0 -36
  231. package/src/services/llm-model/global-overrides.spec.js +0 -432
  232. package/src/services/llm-model/index.js +0 -308
  233. package/src/services/llm-model/model.js +0 -21
  234. package/src/services/llm-model/negotiate.spec.js +0 -447
  235. package/src/services/redis/index.js +0 -147
  236. package/src/test/setup.js +0 -20
  237. package/src/verblets/README.md +0 -26
  238. package/src/verblets/auto/index.examples.js +0 -31
  239. package/src/verblets/auto/index.js +0 -28
  240. package/src/verblets/auto/index.spec.js +0 -32
  241. package/src/verblets/bool/README.md +0 -36
  242. package/src/verblets/bool/index.examples.js +0 -80
  243. package/src/verblets/bool/index.js +0 -25
  244. package/src/verblets/bool/index.schema.json +0 -14
  245. package/src/verblets/bool/index.spec.js +0 -33
  246. package/src/verblets/central-tendency/README.md +0 -166
  247. package/src/verblets/central-tendency/central-tendency-result.json +0 -24
  248. package/src/verblets/central-tendency/index.examples.js +0 -196
  249. package/src/verblets/central-tendency/index.js +0 -171
  250. package/src/verblets/central-tendency/index.spec.js +0 -148
  251. package/src/verblets/conversation-turn/README.md +0 -33
  252. package/src/verblets/conversation-turn/index.examples.js +0 -218
  253. package/src/verblets/conversation-turn/index.js +0 -68
  254. package/src/verblets/conversation-turn/index.spec.js +0 -77
  255. package/src/verblets/conversation-turn-multi/README.md +0 -31
  256. package/src/verblets/conversation-turn-multi/index.examples.js +0 -160
  257. package/src/verblets/conversation-turn-multi/index.js +0 -104
  258. package/src/verblets/conversation-turn-multi/index.spec.js +0 -63
  259. package/src/verblets/enum/index.examples.js +0 -30
  260. package/src/verblets/enum/index.js +0 -18
  261. package/src/verblets/enum/index.spec.js +0 -35
  262. package/src/verblets/expect/README.md +0 -64
  263. package/src/verblets/expect/index.examples.js +0 -109
  264. package/src/verblets/expect/index.js +0 -75
  265. package/src/verblets/expect/index.spec.js +0 -127
  266. package/src/verblets/intent/index.examples.js +0 -139
  267. package/src/verblets/intent/index.js +0 -60
  268. package/src/verblets/intent/index.spec.js +0 -31
  269. package/src/verblets/intersection/README.md +0 -16
  270. package/src/verblets/intersection/index.examples.js +0 -89
  271. package/src/verblets/intersection/index.js +0 -125
  272. package/src/verblets/intersection/index.spec.js +0 -60
  273. package/src/verblets/intersection/intersection-result.json +0 -16
  274. package/src/verblets/list-expand/README.md +0 -10
  275. package/src/verblets/list-expand/index.examples.js +0 -14
  276. package/src/verblets/list-expand/index.js +0 -104
  277. package/src/verblets/list-expand/index.spec.js +0 -18
  278. package/src/verblets/list-expand/list-expand-result.json +0 -16
  279. package/src/verblets/list-filter/README.md +0 -22
  280. package/src/verblets/list-filter/index.examples.js +0 -26
  281. package/src/verblets/list-filter/index.js +0 -18
  282. package/src/verblets/list-filter/index.spec.js +0 -19
  283. package/src/verblets/list-find/README.md +0 -11
  284. package/src/verblets/list-find/index.examples.js +0 -15
  285. package/src/verblets/list-find/index.js +0 -17
  286. package/src/verblets/list-find/index.spec.js +0 -19
  287. package/src/verblets/list-group/README.md +0 -16
  288. package/src/verblets/list-group/index.examples.js +0 -16
  289. package/src/verblets/list-group/index.js +0 -112
  290. package/src/verblets/list-group/index.spec.js +0 -35
  291. package/src/verblets/list-group/list-group-result.json +0 -16
  292. package/src/verblets/list-map/README.md +0 -11
  293. package/src/verblets/list-map/index.examples.js +0 -15
  294. package/src/verblets/list-map/index.js +0 -26
  295. package/src/verblets/list-map/index.spec.js +0 -17
  296. package/src/verblets/list-reduce/README.md +0 -10
  297. package/src/verblets/list-reduce/index.examples.js +0 -14
  298. package/src/verblets/list-reduce/index.js +0 -21
  299. package/src/verblets/list-reduce/index.spec.js +0 -27
  300. package/src/verblets/list-reduce/index.spec.jsx +0 -27
  301. package/src/verblets/name/README.md +0 -15
  302. package/src/verblets/name/index.examples.js +0 -28
  303. package/src/verblets/name/index.js +0 -19
  304. package/src/verblets/name/index.spec.js +0 -33
  305. package/src/verblets/name-similar-to/README.md +0 -26
  306. package/src/verblets/name-similar-to/index.examples.js +0 -18
  307. package/src/verblets/name-similar-to/index.js +0 -20
  308. package/src/verblets/name-similar-to/index.spec.js +0 -13
  309. package/src/verblets/number/index.examples.js +0 -199
  310. package/src/verblets/number/index.js +0 -25
  311. package/src/verblets/number/index.spec.js +0 -33
  312. package/src/verblets/number-with-units/index.examples.js +0 -38
  313. package/src/verblets/number-with-units/index.js +0 -84
  314. package/src/verblets/number-with-units/index.spec.js +0 -46
  315. package/src/verblets/number-with-units/number-with-units-result.json +0 -23
  316. package/src/verblets/people-list/README.md +0 -28
  317. package/src/verblets/people-list/index.examples.js +0 -184
  318. package/src/verblets/people-list/index.js +0 -44
  319. package/src/verblets/people-list/index.spec.js +0 -49
  320. package/src/verblets/schema-org/index.examples.js +0 -51
  321. package/src/verblets/schema-org/index.js +0 -37
  322. package/src/verblets/schema-org/index.spec.js +0 -39
  323. package/src/verblets/sentiment/README.md +0 -10
  324. package/src/verblets/sentiment/index.examples.js +0 -20
  325. package/src/verblets/sentiment/index.js +0 -9
  326. package/src/verblets/sentiment/index.spec.js +0 -20
  327. package/src/verblets/to-object/README.md +0 -38
  328. package/src/verblets/to-object/index.examples.js +0 -29
  329. package/src/verblets/to-object/index.js +0 -131
  330. package/src/verblets/to-object/index.spec.js +0 -71
@@ -1,86 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import retry from './index.js';
4
-
5
- const retryDelayGlobal = 10;
6
-
7
- const mockFn = () => {
8
- return 'Success';
9
- };
10
-
11
- describe('Retry', () => {
12
- it('Succeeds on first attempt', async () => {
13
- const result = await retry(mockFn, { retryDelay: retryDelayGlobal });
14
- expect(result).toStrictEqual('Success');
15
- });
16
-
17
- it('Succeeds after retrying', async () => {
18
- let callCount = 0;
19
- const fn = async () => {
20
- callCount += 1;
21
- if (callCount === 1) {
22
- const error = new Error('Error 429');
23
- error.response = { status: 429 };
24
- throw error;
25
- }
26
- return 'Success';
27
- };
28
-
29
- const result = await retry(fn, { retryDelay: retryDelayGlobal });
30
- expect(result).toStrictEqual('Success');
31
- expect(callCount).toStrictEqual(2);
32
- });
33
-
34
- it('Fails after maxRetries', async () => {
35
- const maxRetries = 2;
36
- let callCount = 0;
37
-
38
- const fn = async () => {
39
- callCount += 1;
40
- const error = new Error('Error 429');
41
- error.response = { status: 429 };
42
- throw error;
43
- };
44
-
45
- try {
46
- await retry(fn, { maxRetries, retryDelay: retryDelayGlobal });
47
- } catch (error) {
48
- expect(error.message).toStrictEqual('Error 429');
49
- expect(callCount).toStrictEqual(maxRetries + 1);
50
- }
51
- });
52
-
53
- it('Throws non-retryable error', async () => {
54
- const mockFnWithOtherError = () => {
55
- throw new Error('Error 500');
56
- };
57
-
58
- try {
59
- await retry(mockFnWithOtherError, { retryDelay: retryDelayGlobal });
60
- } catch (error) {
61
- expect(error.message).toStrictEqual('Error 500');
62
- }
63
- });
64
-
65
- it('Retries on all errors when retryOnAll is true', async () => {
66
- let callCount = 0;
67
- const maxRetries = 2;
68
- const fn = async () => {
69
- callCount += 1;
70
- const error = new Error('Error 500');
71
- error.response = { status: 500 };
72
- throw error;
73
- };
74
-
75
- try {
76
- await retry(fn, {
77
- maxRetries,
78
- retryDelay: retryDelayGlobal,
79
- retryOnAll: true,
80
- });
81
- } catch (error) {
82
- expect(error.message).toStrictEqual('Error 500');
83
- expect(callCount).toStrictEqual(maxRetries + 1);
84
- }
85
- });
86
- });
@@ -1,82 +0,0 @@
1
- # High-Performance Ring Buffer
2
-
3
- A high-performance ring buffer for single writer, multiple async readers with blocking semantics and offset tracking for coordination.
4
-
5
- ## Key Features
6
-
7
- - **Double Buffering**: Eliminates wraparound logic for efficient batch reads
8
- - **Blocking Reads**: Readers block until data is available
9
- - **Multiple Independent Readers**: Each reader maintains its own position
10
- - **Offset Tracking**: Returns offset information for external coordination
11
- - **Zero-Copy Slicing**: Efficient batch operations using array slicing
12
-
13
- ## Example Usage
14
-
15
- ```javascript
16
- import RingBuffer from './index.js';
17
-
18
- const buffer = new RingBuffer(100);
19
-
20
- // Register readers
21
- const reader1 = buffer.registerReader();
22
- const reader2 = buffer.registerReader();
23
-
24
- // Write data
25
- buffer.write('message1');
26
- buffer.write('message2');
27
- buffer.write('message3');
28
-
29
- // Read single items with offset tracking
30
- const result1 = await buffer.read(reader1);
31
- // { data: 'message1', offset: 0 }
32
-
33
- // Read batches with offset tracking
34
- const batch = await buffer.readBatch(reader2, 2);
35
- // { data: ['message1', 'message2'], startOffset: 0, lastOffset: 1 }
36
-
37
- // Coordinate multiple readers using offsets
38
- const processedOffsets = new Map();
39
- processedOffsets.set(reader1, result1.offset);
40
- processedOffsets.set(reader2, batch.lastOffset);
41
-
42
- const minOffset = Math.min(...processedOffsets.values());
43
- console.log(`Safe to cleanup data up to offset ${minOffset}`);
44
- ```
45
-
46
- ## API
47
-
48
- ### Constructor
49
- ```javascript
50
- const buffer = new RingBuffer(maxSize);
51
- ```
52
-
53
- ### Reader Management
54
- ```javascript
55
- const readerId = buffer.registerReader();
56
- buffer.unregisterReader(readerId);
57
- ```
58
-
59
- ### Writing
60
- ```javascript
61
- const sequence = buffer.write(data);
62
- ```
63
-
64
- ### Reading
65
- ```javascript
66
- // Single item - blocks until available
67
- const result = await buffer.read(readerId);
68
- // Returns: { data: any, offset: number }
69
-
70
- // Batch - blocks until full batch available
71
- const batch = await buffer.readBatch(readerId, batchSize);
72
- // Returns: { data: any[], startOffset: number, lastOffset: number }
73
- ```
74
-
75
- ### Utilities
76
- ```javascript
77
- const stats = buffer.getStats();
78
- console.log(`Buffer usage: ${stats.sequence} items written`);
79
- console.log(`Active readers: ${stats.registeredReaders}`);
80
- console.log(`Waiting readers: ${stats.waitingReaders}`);
81
- ```
82
-
@@ -1,235 +0,0 @@
1
- /**
2
- * High-performance ring buffer with single writer, multiple async readers.
3
- * Uses double-buffer technique to eliminate wraparound logic.
4
- * Optimized for performance over safety - assumes buffer is sized appropriately.
5
- */
6
- export default class RingBuffer {
7
- constructor(maxSize = 1000) {
8
- this.maxSize = maxSize;
9
- this.writeIndex = 0;
10
- this.sequence = 0;
11
- this.nextReaderId = 0;
12
-
13
- // Double buffer: second half mirrors first half for wraparound-free reads
14
- this.buffer = new Array(maxSize * 2);
15
-
16
- // Reader tracking - Map for O(1) lookups
17
- this.readers = new Map(); // readerId -> lastReadSequence
18
-
19
- // Notification system for blocking reads
20
- this.waitingReaders = new Set(); // Set of { resolve, readerId, batchSize? }
21
- }
22
-
23
- /**
24
- * Register a new reader.
25
- * @returns {string} Reader ID
26
- */
27
- registerReader() {
28
- const readerId = `r${this.nextReaderId++}`;
29
- this.readers.set(readerId, -1);
30
- return readerId;
31
- }
32
-
33
- /**
34
- * Unregister a reader.
35
- * @param {string} readerId
36
- */
37
- unregisterReader(readerId) {
38
- this.readers.delete(readerId);
39
- // Remove any waiting operations for this reader
40
- for (const waiter of this.waitingReaders) {
41
- if (waiter.readerId === readerId) {
42
- if (waiter.batchSize) {
43
- waiter.resolve({ data: [], startOffset: 0, lastOffset: -1 });
44
- } else {
45
- waiter.resolve({ data: null, offset: -1 });
46
- }
47
- this.waitingReaders.delete(waiter);
48
- }
49
- }
50
- }
51
-
52
- /**
53
- * Write data to buffer (single writer only).
54
- * @param {any} data
55
- * @returns {number} Sequence number
56
- */
57
- write(data) {
58
- const seq = this.sequence++;
59
- const idx = this.writeIndex;
60
-
61
- // Write to both positions (main and mirror)
62
- this.buffer[idx] = data;
63
- this.buffer[idx + this.maxSize] = data;
64
-
65
- this.writeIndex = (this.writeIndex + 1) % this.maxSize;
66
-
67
- // Notify waiting readers
68
- this._notifyWaiters(seq);
69
-
70
- return seq;
71
- }
72
-
73
- /**
74
- * Read latest data for a reader (blocks until available).
75
- * @param {string} readerId
76
- * @returns {Promise<{data: any, offset: number}>}
77
- */
78
- read(readerId) {
79
- const lastSeq = this.readers.get(readerId);
80
- if (lastSeq === undefined) {
81
- throw new Error(`Reader ${readerId} not registered`);
82
- }
83
-
84
- // Check if new data is available
85
- if (this.sequence > lastSeq + 1) {
86
- const nextSeq = lastSeq + 1;
87
- const data = this._getDataAtSequence(nextSeq);
88
- this.readers.set(readerId, nextSeq);
89
- return Promise.resolve({ data, offset: nextSeq });
90
- }
91
-
92
- // No data available, wait for it
93
- return new Promise((resolve) => {
94
- this.waitingReaders.add({ resolve, readerId });
95
- });
96
- }
97
-
98
- /**
99
- * Read batch of data (blocks until batch is full).
100
- * @param {string} readerId
101
- * @param {number} batchSize
102
- * @returns {Promise<{data: any[], startOffset: number, lastOffset: number}>}
103
- */
104
- readBatch(readerId, batchSize) {
105
- const lastSeq = this.readers.get(readerId);
106
- if (lastSeq === undefined) {
107
- throw new Error(`Reader ${readerId} not registered`);
108
- }
109
-
110
- const availableCount = this.sequence - (lastSeq + 1);
111
-
112
- if (availableCount >= batchSize) {
113
- // Batch is ready
114
- const result = this._readBatchSync(readerId, batchSize);
115
- return Promise.resolve(result);
116
- }
117
-
118
- // Wait for full batch
119
- return new Promise((resolve) => {
120
- this.waitingReaders.add({ resolve, readerId, batchSize });
121
- });
122
- }
123
-
124
- /**
125
- * Get data at specific sequence (uses double buffer for wraparound-free access).
126
- * @private
127
- */
128
- _getDataAtSequence(seq) {
129
- const bufferPos = seq % this.maxSize;
130
- return this.buffer[bufferPos];
131
- }
132
-
133
- /**
134
- * Read batch synchronously when data is available.
135
- * @private
136
- */
137
- _readBatchSync(readerId, batchSize) {
138
- const lastSeq = this.readers.get(readerId);
139
- const startSeq = lastSeq + 1;
140
- const startPos = startSeq % this.maxSize;
141
-
142
- // Use double buffer to avoid wraparound logic
143
- const batch = this.buffer.slice(startPos, startPos + batchSize);
144
-
145
- this.readers.set(readerId, lastSeq + batchSize);
146
- return { data: batch, startOffset: startSeq, lastOffset: lastSeq + batchSize };
147
- }
148
-
149
- /**
150
- * Notify waiting readers when new data arrives.
151
- * @private
152
- */
153
- _notifyWaiters(newSeq) {
154
- const toRemove = [];
155
-
156
- for (const waiter of this.waitingReaders) {
157
- const { resolve, readerId, batchSize } = waiter;
158
- const lastSeq = this.readers.get(readerId);
159
-
160
- if (lastSeq === undefined) continue; // Reader was unregistered
161
-
162
- if (batchSize) {
163
- // Batch reader
164
- const availableCount = newSeq - lastSeq;
165
- if (availableCount >= batchSize) {
166
- const result = this._readBatchSync(readerId, batchSize);
167
- resolve(result);
168
- toRemove.push(waiter);
169
- }
170
- } else {
171
- // Single reader
172
- if (newSeq > lastSeq) {
173
- const nextSeq = lastSeq + 1;
174
- const data = this._getDataAtSequence(nextSeq);
175
- this.readers.set(readerId, nextSeq);
176
- resolve({ data, offset: nextSeq });
177
- toRemove.push(waiter);
178
- }
179
- }
180
- }
181
-
182
- // Remove resolved waiters
183
- for (const waiter of toRemove) {
184
- this.waitingReaders.delete(waiter);
185
- }
186
- }
187
-
188
- /**
189
- * Get available data count for a reader.
190
- * @param {string} readerId
191
- * @returns {number}
192
- */
193
- getAvailableCount(readerId) {
194
- const lastSeq = this.readers.get(readerId);
195
- return lastSeq === undefined ? 0 : Math.max(0, this.sequence - (lastSeq + 1));
196
- }
197
-
198
- /**
199
- * Get buffer statistics.
200
- * @returns {object}
201
- */
202
- getStats() {
203
- return {
204
- maxSize: this.maxSize,
205
- writeIndex: this.writeIndex,
206
- sequence: this.sequence,
207
- registeredReaders: this.readers.size,
208
- waitingReaders: this.waitingReaders.size,
209
- };
210
- }
211
-
212
- /**
213
- * Clear buffer and reset state.
214
- */
215
- clear() {
216
- this.writeIndex = 0;
217
- this.sequence = 0;
218
- this.buffer.fill(undefined);
219
-
220
- // Reset reader sequences
221
- for (const readerId of this.readers.keys()) {
222
- this.readers.set(readerId, -1);
223
- }
224
-
225
- // Resolve all waiting readers with empty results
226
- for (const waiter of this.waitingReaders) {
227
- if (waiter.batchSize) {
228
- waiter.resolve({ data: [], startOffset: 0, lastOffset: -1 });
229
- } else {
230
- waiter.resolve({ data: null, offset: -1 });
231
- }
232
- }
233
- this.waitingReaders.clear();
234
- }
235
- }