@softerist/heuristic-mcp 3.0.15 → 3.0.16

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 (49) hide show
  1. package/README.md +104 -104
  2. package/config.jsonc +173 -173
  3. package/features/ann-config.js +131 -0
  4. package/features/clear-cache.js +84 -0
  5. package/features/find-similar-code.js +291 -0
  6. package/features/hybrid-search.js +544 -0
  7. package/features/index-codebase.js +3268 -0
  8. package/features/lifecycle.js +1189 -0
  9. package/features/package-version.js +302 -0
  10. package/features/register.js +408 -0
  11. package/features/resources.js +156 -0
  12. package/features/set-workspace.js +265 -0
  13. package/index.js +96 -96
  14. package/lib/cache-ops.js +22 -22
  15. package/lib/cache-utils.js +565 -565
  16. package/lib/cache.js +1870 -1870
  17. package/lib/call-graph.js +396 -396
  18. package/lib/cli.js +1 -1
  19. package/lib/config.js +517 -517
  20. package/lib/constants.js +39 -39
  21. package/lib/embed-query-process.js +7 -7
  22. package/lib/embedding-process.js +7 -7
  23. package/lib/embedding-worker.js +299 -299
  24. package/lib/ignore-patterns.js +316 -316
  25. package/lib/json-worker.js +14 -14
  26. package/lib/json-writer.js +337 -337
  27. package/lib/logging.js +164 -164
  28. package/lib/memory-logger.js +13 -13
  29. package/lib/onnx-backend.js +193 -193
  30. package/lib/project-detector.js +84 -84
  31. package/lib/server-lifecycle.js +165 -165
  32. package/lib/settings-editor.js +754 -754
  33. package/lib/tokenizer.js +256 -256
  34. package/lib/utils.js +428 -428
  35. package/lib/vector-store-binary.js +627 -627
  36. package/lib/vector-store-sqlite.js +95 -95
  37. package/lib/workspace-env.js +28 -28
  38. package/mcp_config.json +9 -9
  39. package/package.json +86 -75
  40. package/scripts/clear-cache.js +20 -0
  41. package/scripts/download-model.js +43 -0
  42. package/scripts/mcp-launcher.js +49 -0
  43. package/scripts/postinstall.js +12 -0
  44. package/search-configs.js +36 -36
  45. package/.prettierrc +0 -7
  46. package/debug-pids.js +0 -30
  47. package/eslint.config.js +0 -36
  48. package/specs/plan.md +0 -23
  49. package/vitest.config.js +0 -39
@@ -1,337 +1,337 @@
1
- import fs from 'fs';
2
-
3
- function isTypedArray(x) {
4
- return x && ArrayBuffer.isView(x) && !(x instanceof DataView);
5
- }
6
-
7
- function onceDrainOrError(stream) {
8
- return new Promise((resolve, reject) => {
9
- const onDrain = () => cleanup(resolve);
10
- const onError = (err) => cleanup(() => reject(err));
11
-
12
- const cleanup = (fn) => {
13
- stream.off('drain', onDrain);
14
- stream.off('error', onError);
15
- fn();
16
- };
17
-
18
- stream.once('drain', onDrain);
19
- stream.once('error', onError);
20
- });
21
- }
22
-
23
- /**
24
- * Streaming JSON array writer optimized for:
25
- * - TypedArray vectors streamed (no per-item vector allocation)
26
- * - backpressure safety
27
- * - configurable float rounding + flush threshold
28
- * - compact mode when indent === '' (no forced newlines)
29
- * - safe cleanup on failure (abort)
30
- * - optional native TypedArray.join(',') fast-path when rounding is disabled
31
- */
32
- export class StreamingJsonWriter {
33
- /**
34
- * @param {string} filePath
35
- * @param {object} [opts]
36
- * @param {number} [opts.highWaterMark] Stream internal buffer size.
37
- * @param {number|null} [opts.floatDigits] Round floats to N digits. null disables rounding.
38
- * @param {number} [opts.flushChars] Flush threshold for the internal string buffer.
39
- * @param {string} [opts.indent] Indent prefix per item ("" for compact, " " for pretty).
40
- * @param {boolean} [opts.assumeFinite] Skip NaN/Infinity checks (unsafe if false data).
41
- * @param {boolean} [opts.checkFinite] If set, overrides assumeFinite (true = check, false = skip).
42
- * @param {boolean} [opts.noMutation] Avoid temporary mutation when stripping vector.
43
- * @param {number} [opts.joinThreshold] Max elements to use single join() string.
44
- * @param {number} [opts.joinChunkSize] Elements per join() chunk when chunking.
45
- */
46
- constructor(
47
- filePath,
48
- {
49
- highWaterMark = 256 * 1024,
50
- floatDigits = 6,
51
- flushChars = 256 * 1024,
52
- indent = '',
53
- assumeFinite,
54
- checkFinite,
55
- noMutation = false,
56
- joinThreshold = 8192,
57
- joinChunkSize = 2048,
58
- } = {}
59
- ) {
60
- this.filePath = filePath;
61
- this.highWaterMark =
62
- Number.isInteger(highWaterMark) && highWaterMark > 8 * 1024 ? highWaterMark : 256 * 1024;
63
- this.flushChars =
64
- Number.isInteger(flushChars) && flushChars > 8 * 1024 ? flushChars : 256 * 1024;
65
- this.indent = typeof indent === 'string' ? indent : '';
66
- this.pretty = this.indent.length > 0;
67
- this.assumeFinite = typeof checkFinite === 'boolean' ? !checkFinite : !!assumeFinite;
68
- this.noMutation = !!noMutation;
69
- this.joinThreshold =
70
- Number.isInteger(joinThreshold) && joinThreshold > 0 ? joinThreshold : 8192;
71
- this.joinChunkSize =
72
- Number.isInteger(joinChunkSize) && joinChunkSize > 0 ? joinChunkSize : 2048;
73
-
74
- this._prefixFirst = this.pretty ? this.indent : '';
75
- this._prefixNext = this.pretty ? ',\n' + this.indent : ',';
76
-
77
- this.stream = null;
78
- this.first = true;
79
- this._streamError = null;
80
-
81
- // Formatter + fast-path flag
82
- this._useJoinFastPath = floatDigits === null;
83
-
84
- if (!this._useJoinFastPath) {
85
- const digitsOk = Number.isInteger(floatDigits) && floatDigits >= 0 && floatDigits <= 12;
86
- const d = digitsOk ? floatDigits : 6;
87
- const scale = 10 ** d;
88
- if (this.assumeFinite) {
89
- this._formatFn = (x) => String(Math.round(x * scale) / scale);
90
- } else {
91
- this._formatFn = (x) => {
92
- if (!Number.isFinite(x)) return '0';
93
- return String(Math.round(x * scale) / scale);
94
- };
95
- }
96
- } else {
97
- if (this.assumeFinite) {
98
- this._formatFn = (x) => String(x);
99
- } else {
100
- this._formatFn = (x) => {
101
- if (!Number.isFinite(x)) return '0';
102
- return String(x);
103
- };
104
- }
105
- }
106
- }
107
-
108
- async writeStart() {
109
- if (this.stream) return;
110
-
111
- this.stream = fs.createWriteStream(this.filePath, {
112
- flags: 'w',
113
- encoding: 'utf8',
114
- highWaterMark: this.highWaterMark,
115
- });
116
-
117
- this.stream.on('error', (err) => {
118
- this._streamError = err;
119
- });
120
-
121
- await new Promise((resolve, reject) => {
122
- if (this.stream.fd !== null) return resolve();
123
- this.stream.once('open', resolve);
124
- this.stream.once('error', reject);
125
- });
126
-
127
- const p = this._writeRaw(this.pretty ? '[\n' : '[');
128
- if (p) await p;
129
- this.first = true;
130
- }
131
-
132
- /**
133
- * Best-effort early shutdown (use in catch/finally blocks).
134
- * Destroys the stream to avoid fd leaks when writeEnd() is not reached.
135
- */
136
- abort(err) {
137
- if (!this.stream) return;
138
- try {
139
- this._streamError = err || this._streamError || new Error('StreamingJsonWriter aborted');
140
- this.stream.destroy(this._streamError);
141
- } catch {
142
- // ignore
143
- } finally {
144
- this.stream = null;
145
- }
146
- }
147
-
148
- async drain() {
149
- if (!this.stream || !this.stream.writableNeedDrain) return;
150
- await onceDrainOrError(this.stream);
151
- }
152
-
153
- writeItem(item) {
154
- if (!this.stream) throw new Error('StreamingJsonWriter not started. Call writeStart() first.');
155
- if (this._streamError) throw this._streamError;
156
-
157
- const prefix = this.first ? this._prefixFirst : this._prefixNext;
158
- this.first = false;
159
-
160
- const vec = item?.vector;
161
-
162
- if (isTypedArray(vec)) {
163
- const base = this.noMutation
164
- ? this._stringifyWithoutMutation(item, vec)
165
- : this._stringifyWithoutVector(item, vec);
166
- const hasBase = typeof base === 'string' && base.length > 0 && base !== '{}';
167
- const header = hasBase ? `${prefix}${base.slice(0, -1)},"vector":` : `${prefix}{"vector":`;
168
-
169
- return this._chain(this._writeRaw(header), () =>
170
- this._chain(this._writeTypedArray(vec), () => this._writeRaw('}'))
171
- );
172
- }
173
-
174
- return this._writeRaw(prefix + JSON.stringify(item));
175
- }
176
-
177
- async writeEnd() {
178
- if (!this.stream) return;
179
- if (this._streamError) throw this._streamError;
180
-
181
- const p = this._writeRaw(this.pretty ? '\n]\n' : ']\n');
182
- if (p) await p;
183
-
184
- await new Promise((resolve, reject) => {
185
- this.stream.once('error', reject);
186
- this.stream.end(resolve);
187
- });
188
-
189
- this.stream = null;
190
- this._streamError = null;
191
- }
192
-
193
- _chain(promise, next) {
194
- if (promise) return promise.then(() => next());
195
- return next();
196
- }
197
-
198
- _stringifyWithoutVector(item, vec) {
199
- let base;
200
- let restored = false;
201
-
202
- try {
203
- const prev = item.vector;
204
- item.vector = undefined;
205
- base = JSON.stringify(item);
206
- item.vector = prev;
207
- restored = true;
208
- } catch {
209
- base = JSON.stringify(item, (key, val) =>
210
- key === 'vector' && val === vec ? undefined : val
211
- );
212
- } finally {
213
- if (!restored) {
214
- try {
215
- item.vector = vec;
216
- } catch {
217
- // ignore
218
- }
219
- }
220
- }
221
-
222
- return base;
223
- }
224
-
225
- _stringifyWithoutMutation(item, vec) {
226
- try {
227
- const rest = { ...item };
228
- delete rest.vector;
229
- return JSON.stringify(rest);
230
- } catch {
231
- return JSON.stringify(item, (key, val) =>
232
- key === 'vector' && val === vec ? undefined : val
233
- );
234
- }
235
- }
236
-
237
- /**
238
- * Core write method.
239
- * Returns null on synchronous success (fast path).
240
- * Returns a Promise only when backpressure is hit (slow path).
241
- */
242
- _writeRaw(str) {
243
- if (this._streamError) throw this._streamError;
244
-
245
- const ok = this.stream.write(str);
246
- if (ok) return null;
247
-
248
- return onceDrainOrError(this.stream).then(() => {
249
- if (this._streamError) throw this._streamError;
250
- });
251
- }
252
-
253
- _writeTypedArray(vec) {
254
- return this._chain(this._writeRaw('['), () => this._writeTypedArrayBody(vec));
255
- }
256
-
257
- _writeTypedArrayBody(vec) {
258
- if (this._useJoinFastPath) {
259
- if (!this.assumeFinite && !this._allFinite(vec)) {
260
- return this._writeFormatted(vec);
261
- }
262
-
263
- if (vec.length <= this.joinThreshold) {
264
- return this._chain(this._writeRaw(vec.join(',')), () => this._writeRaw(']'));
265
- }
266
-
267
- return this._writeJoinChunks(vec);
268
- }
269
-
270
- return this._writeFormatted(vec);
271
- }
272
-
273
- _allFinite(vec) {
274
- for (let i = 0; i < vec.length; i++) {
275
- if (!Number.isFinite(vec[i])) return false;
276
- }
277
- return true;
278
- }
279
-
280
- _writeJoinChunks(vec) {
281
- const len = vec.length;
282
- if (len === 0) return this._writeRaw(']');
283
-
284
- let i = 0;
285
- const chunkSize = this.joinChunkSize;
286
-
287
- const writeNext = () => {
288
- while (i < len) {
289
- const end = Math.min(len, i + chunkSize);
290
- let chunk = vec.subarray(i, end).join(',');
291
- if (i !== 0) chunk = ',' + chunk;
292
- i = end;
293
-
294
- const pending = this._writeRaw(chunk);
295
- if (pending) return pending.then(writeNext);
296
- }
297
-
298
- return this._writeRaw(']');
299
- };
300
-
301
- return writeNext();
302
- }
303
-
304
- _writeFormatted(vec) {
305
- const len = vec.length;
306
- if (len === 0) return this._writeRaw(']');
307
-
308
- let i = 0;
309
- let buf = '';
310
- const FLUSH_AT = this.flushChars;
311
- const format = this._formatFn;
312
-
313
- const writeNext = () => {
314
- while (i < len) {
315
- if (i) buf += ',';
316
- buf += format(vec[i]);
317
- i += 1;
318
-
319
- if (buf.length >= FLUSH_AT) {
320
- const pending = this._writeRaw(buf);
321
- buf = '';
322
- if (pending) return pending.then(writeNext);
323
- }
324
- }
325
-
326
- if (buf) {
327
- const pending = this._writeRaw(buf);
328
- buf = '';
329
- if (pending) return pending.then(() => this._writeRaw(']'));
330
- }
331
-
332
- return this._writeRaw(']');
333
- };
334
-
335
- return writeNext();
336
- }
337
- }
1
+ import fs from 'fs';
2
+
3
+ function isTypedArray(x) {
4
+ return x && ArrayBuffer.isView(x) && !(x instanceof DataView);
5
+ }
6
+
7
+ function onceDrainOrError(stream) {
8
+ return new Promise((resolve, reject) => {
9
+ const onDrain = () => cleanup(resolve);
10
+ const onError = (err) => cleanup(() => reject(err));
11
+
12
+ const cleanup = (fn) => {
13
+ stream.off('drain', onDrain);
14
+ stream.off('error', onError);
15
+ fn();
16
+ };
17
+
18
+ stream.once('drain', onDrain);
19
+ stream.once('error', onError);
20
+ });
21
+ }
22
+
23
+ /**
24
+ * Streaming JSON array writer optimized for:
25
+ * - TypedArray vectors streamed (no per-item vector allocation)
26
+ * - backpressure safety
27
+ * - configurable float rounding + flush threshold
28
+ * - compact mode when indent === '' (no forced newlines)
29
+ * - safe cleanup on failure (abort)
30
+ * - optional native TypedArray.join(',') fast-path when rounding is disabled
31
+ */
32
+ export class StreamingJsonWriter {
33
+ /**
34
+ * @param {string} filePath
35
+ * @param {object} [opts]
36
+ * @param {number} [opts.highWaterMark] Stream internal buffer size.
37
+ * @param {number|null} [opts.floatDigits] Round floats to N digits. null disables rounding.
38
+ * @param {number} [opts.flushChars] Flush threshold for the internal string buffer.
39
+ * @param {string} [opts.indent] Indent prefix per item ("" for compact, " " for pretty).
40
+ * @param {boolean} [opts.assumeFinite] Skip NaN/Infinity checks (unsafe if false data).
41
+ * @param {boolean} [opts.checkFinite] If set, overrides assumeFinite (true = check, false = skip).
42
+ * @param {boolean} [opts.noMutation] Avoid temporary mutation when stripping vector.
43
+ * @param {number} [opts.joinThreshold] Max elements to use single join() string.
44
+ * @param {number} [opts.joinChunkSize] Elements per join() chunk when chunking.
45
+ */
46
+ constructor(
47
+ filePath,
48
+ {
49
+ highWaterMark = 256 * 1024,
50
+ floatDigits = 6,
51
+ flushChars = 256 * 1024,
52
+ indent = '',
53
+ assumeFinite,
54
+ checkFinite,
55
+ noMutation = false,
56
+ joinThreshold = 8192,
57
+ joinChunkSize = 2048,
58
+ } = {}
59
+ ) {
60
+ this.filePath = filePath;
61
+ this.highWaterMark =
62
+ Number.isInteger(highWaterMark) && highWaterMark > 8 * 1024 ? highWaterMark : 256 * 1024;
63
+ this.flushChars =
64
+ Number.isInteger(flushChars) && flushChars > 8 * 1024 ? flushChars : 256 * 1024;
65
+ this.indent = typeof indent === 'string' ? indent : '';
66
+ this.pretty = this.indent.length > 0;
67
+ this.assumeFinite = typeof checkFinite === 'boolean' ? !checkFinite : !!assumeFinite;
68
+ this.noMutation = !!noMutation;
69
+ this.joinThreshold =
70
+ Number.isInteger(joinThreshold) && joinThreshold > 0 ? joinThreshold : 8192;
71
+ this.joinChunkSize =
72
+ Number.isInteger(joinChunkSize) && joinChunkSize > 0 ? joinChunkSize : 2048;
73
+
74
+ this._prefixFirst = this.pretty ? this.indent : '';
75
+ this._prefixNext = this.pretty ? ',\n' + this.indent : ',';
76
+
77
+ this.stream = null;
78
+ this.first = true;
79
+ this._streamError = null;
80
+
81
+ // Formatter + fast-path flag
82
+ this._useJoinFastPath = floatDigits === null;
83
+
84
+ if (!this._useJoinFastPath) {
85
+ const digitsOk = Number.isInteger(floatDigits) && floatDigits >= 0 && floatDigits <= 12;
86
+ const d = digitsOk ? floatDigits : 6;
87
+ const scale = 10 ** d;
88
+ if (this.assumeFinite) {
89
+ this._formatFn = (x) => String(Math.round(x * scale) / scale);
90
+ } else {
91
+ this._formatFn = (x) => {
92
+ if (!Number.isFinite(x)) return '0';
93
+ return String(Math.round(x * scale) / scale);
94
+ };
95
+ }
96
+ } else {
97
+ if (this.assumeFinite) {
98
+ this._formatFn = (x) => String(x);
99
+ } else {
100
+ this._formatFn = (x) => {
101
+ if (!Number.isFinite(x)) return '0';
102
+ return String(x);
103
+ };
104
+ }
105
+ }
106
+ }
107
+
108
+ async writeStart() {
109
+ if (this.stream) return;
110
+
111
+ this.stream = fs.createWriteStream(this.filePath, {
112
+ flags: 'w',
113
+ encoding: 'utf8',
114
+ highWaterMark: this.highWaterMark,
115
+ });
116
+
117
+ this.stream.on('error', (err) => {
118
+ this._streamError = err;
119
+ });
120
+
121
+ await new Promise((resolve, reject) => {
122
+ if (this.stream.fd !== null) return resolve();
123
+ this.stream.once('open', resolve);
124
+ this.stream.once('error', reject);
125
+ });
126
+
127
+ const p = this._writeRaw(this.pretty ? '[\n' : '[');
128
+ if (p) await p;
129
+ this.first = true;
130
+ }
131
+
132
+ /**
133
+ * Best-effort early shutdown (use in catch/finally blocks).
134
+ * Destroys the stream to avoid fd leaks when writeEnd() is not reached.
135
+ */
136
+ abort(err) {
137
+ if (!this.stream) return;
138
+ try {
139
+ this._streamError = err || this._streamError || new Error('StreamingJsonWriter aborted');
140
+ this.stream.destroy(this._streamError);
141
+ } catch {
142
+ // ignore
143
+ } finally {
144
+ this.stream = null;
145
+ }
146
+ }
147
+
148
+ async drain() {
149
+ if (!this.stream || !this.stream.writableNeedDrain) return;
150
+ await onceDrainOrError(this.stream);
151
+ }
152
+
153
+ writeItem(item) {
154
+ if (!this.stream) throw new Error('StreamingJsonWriter not started. Call writeStart() first.');
155
+ if (this._streamError) throw this._streamError;
156
+
157
+ const prefix = this.first ? this._prefixFirst : this._prefixNext;
158
+ this.first = false;
159
+
160
+ const vec = item?.vector;
161
+
162
+ if (isTypedArray(vec)) {
163
+ const base = this.noMutation
164
+ ? this._stringifyWithoutMutation(item, vec)
165
+ : this._stringifyWithoutVector(item, vec);
166
+ const hasBase = typeof base === 'string' && base.length > 0 && base !== '{}';
167
+ const header = hasBase ? `${prefix}${base.slice(0, -1)},"vector":` : `${prefix}{"vector":`;
168
+
169
+ return this._chain(this._writeRaw(header), () =>
170
+ this._chain(this._writeTypedArray(vec), () => this._writeRaw('}'))
171
+ );
172
+ }
173
+
174
+ return this._writeRaw(prefix + JSON.stringify(item));
175
+ }
176
+
177
+ async writeEnd() {
178
+ if (!this.stream) return;
179
+ if (this._streamError) throw this._streamError;
180
+
181
+ const p = this._writeRaw(this.pretty ? '\n]\n' : ']\n');
182
+ if (p) await p;
183
+
184
+ await new Promise((resolve, reject) => {
185
+ this.stream.once('error', reject);
186
+ this.stream.end(resolve);
187
+ });
188
+
189
+ this.stream = null;
190
+ this._streamError = null;
191
+ }
192
+
193
+ _chain(promise, next) {
194
+ if (promise) return promise.then(() => next());
195
+ return next();
196
+ }
197
+
198
+ _stringifyWithoutVector(item, vec) {
199
+ let base;
200
+ let restored = false;
201
+
202
+ try {
203
+ const prev = item.vector;
204
+ item.vector = undefined;
205
+ base = JSON.stringify(item);
206
+ item.vector = prev;
207
+ restored = true;
208
+ } catch {
209
+ base = JSON.stringify(item, (key, val) =>
210
+ key === 'vector' && val === vec ? undefined : val
211
+ );
212
+ } finally {
213
+ if (!restored) {
214
+ try {
215
+ item.vector = vec;
216
+ } catch {
217
+ // ignore
218
+ }
219
+ }
220
+ }
221
+
222
+ return base;
223
+ }
224
+
225
+ _stringifyWithoutMutation(item, vec) {
226
+ try {
227
+ const rest = { ...item };
228
+ delete rest.vector;
229
+ return JSON.stringify(rest);
230
+ } catch {
231
+ return JSON.stringify(item, (key, val) =>
232
+ key === 'vector' && val === vec ? undefined : val
233
+ );
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Core write method.
239
+ * Returns null on synchronous success (fast path).
240
+ * Returns a Promise only when backpressure is hit (slow path).
241
+ */
242
+ _writeRaw(str) {
243
+ if (this._streamError) throw this._streamError;
244
+
245
+ const ok = this.stream.write(str);
246
+ if (ok) return null;
247
+
248
+ return onceDrainOrError(this.stream).then(() => {
249
+ if (this._streamError) throw this._streamError;
250
+ });
251
+ }
252
+
253
+ _writeTypedArray(vec) {
254
+ return this._chain(this._writeRaw('['), () => this._writeTypedArrayBody(vec));
255
+ }
256
+
257
+ _writeTypedArrayBody(vec) {
258
+ if (this._useJoinFastPath) {
259
+ if (!this.assumeFinite && !this._allFinite(vec)) {
260
+ return this._writeFormatted(vec);
261
+ }
262
+
263
+ if (vec.length <= this.joinThreshold) {
264
+ return this._chain(this._writeRaw(vec.join(',')), () => this._writeRaw(']'));
265
+ }
266
+
267
+ return this._writeJoinChunks(vec);
268
+ }
269
+
270
+ return this._writeFormatted(vec);
271
+ }
272
+
273
+ _allFinite(vec) {
274
+ for (let i = 0; i < vec.length; i++) {
275
+ if (!Number.isFinite(vec[i])) return false;
276
+ }
277
+ return true;
278
+ }
279
+
280
+ _writeJoinChunks(vec) {
281
+ const len = vec.length;
282
+ if (len === 0) return this._writeRaw(']');
283
+
284
+ let i = 0;
285
+ const chunkSize = this.joinChunkSize;
286
+
287
+ const writeNext = () => {
288
+ while (i < len) {
289
+ const end = Math.min(len, i + chunkSize);
290
+ let chunk = vec.subarray(i, end).join(',');
291
+ if (i !== 0) chunk = ',' + chunk;
292
+ i = end;
293
+
294
+ const pending = this._writeRaw(chunk);
295
+ if (pending) return pending.then(writeNext);
296
+ }
297
+
298
+ return this._writeRaw(']');
299
+ };
300
+
301
+ return writeNext();
302
+ }
303
+
304
+ _writeFormatted(vec) {
305
+ const len = vec.length;
306
+ if (len === 0) return this._writeRaw(']');
307
+
308
+ let i = 0;
309
+ let buf = '';
310
+ const FLUSH_AT = this.flushChars;
311
+ const format = this._formatFn;
312
+
313
+ const writeNext = () => {
314
+ while (i < len) {
315
+ if (i) buf += ',';
316
+ buf += format(vec[i]);
317
+ i += 1;
318
+
319
+ if (buf.length >= FLUSH_AT) {
320
+ const pending = this._writeRaw(buf);
321
+ buf = '';
322
+ if (pending) return pending.then(writeNext);
323
+ }
324
+ }
325
+
326
+ if (buf) {
327
+ const pending = this._writeRaw(buf);
328
+ buf = '';
329
+ if (pending) return pending.then(() => this._writeRaw(']'));
330
+ }
331
+
332
+ return this._writeRaw(']');
333
+ };
334
+
335
+ return writeNext();
336
+ }
337
+ }