natureco-cli 5.20.4 → 5.22.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.
@@ -1,334 +1,346 @@
1
- /**
2
- * paste-safe-input — Terminal'e uzun/çok satırlı metin yapıştırılınca
3
- * her satırın ayrı bir "Enter" gibi gönderilip anında submit edilmesini önler.
4
- *
5
- * Sorun: Node'un düz `readline` arayüzü, terminalden gelen her "\n" karakterini
6
- * bir 'line' event'i olarak görür. Terminal "bracketed paste mode" açık değilse
7
- * (veya açık olsa da bu karakterler ayıklanmazsa), kullanıcı 10 satırlık bir metni
8
- * yapıştırdığında readline bunu 10 ayrı mesaj gibi okur ve her birini hemen
9
- * gönderir — kullanıcı paste'i bitirmeden cevaplar gelmeye başlar.
10
- *
11
- * Çözüm: İki katmanlı bir yaklaşım:
12
- *
13
- * 1. INPUT KATMANI (createPasteSafeInput):
14
- * Terminalin "bracketed paste" (ESC[200~ ... ESC[201~) işaretleyicilerini
15
- * dinleyip, bu işaretler arasındaki tüm veriyi tek parça olarak topluyoruz.
16
- * Ayrıca terminal-agnostik bir heuristik: çok satırlı büyük chunk'ları da
17
- * paste olarak algılar. İçindeki gerçek satır sonlarını geçici bir
18
- * placeholder (NEWLINE_PLACEHOLDER) ile değiştirip readline'a TEK satır gibi
19
- * besliyoruz. Kullanıcı gerçekten Enter'a basana kadar hiçbir 'line' event'i
20
- * tetiklenmez.
21
- *
22
- * 2. OUTPUT KATMANI (createOutputFilter):
23
- * Readline'ın echo mekanizması placeholder karakterlerini terminale
24
- * yazarken araya girer. Bir state machine ile karakterleri placeholder
25
- * dizisiyle eşleştirir, tam eşleşme olursa terminale \n (yeni satır)
26
- * yazar. Böylece kullanıcı "␤␤LINEBREAK␤␤" gibi literal placeholder
27
- * yazıları görmez, satır sonlarını doğal yeni satırlar olarak deneyimler.
28
- *
29
- * Enter'a basıldığında, readline'dan gelen placeholder'lı satır,
30
- * restoreNewlines() ile gerçek \n karakterlerine dönüştürülüp mesaj
31
- * olarak işlenir.
32
- */
33
-
34
- 'use strict';
35
-
36
- const { PassThrough } = require('stream');
37
-
38
- const PASTE_START = '\x1b[200~';
39
- const PASTE_END = '\x1b[201~';
40
-
41
- // Input proxy ile output filter arasında paste durumunu paylaşır.
42
- // escapeEmbeddedNewlines paste algıladığında bu context'i set eder,
43
- // output filter tüm ekoyu yutar (terminalde hiçbir şey gözükmez),
44
- // repl.js line handler'ı context'i temizler.
45
- let _pasteContext = null; // { lineCount } or null
46
-
47
- // Yapıştırılan içindeki satır sonlarını gizlemek için kullanılan placeholder.
48
- // Gerçek kullanıcı girdisinde pratikte hiç geçmeyecek bir dizi.
49
- const NEWLINE_PLACEHOLDER = '\u2424\u2424LINEBREAK\u2424\u2424';
50
-
51
- /**
52
- * Bracketed paste'i destekleyen bir PassThrough stream döner.
53
- * `source` (genellikle process.stdin) üzerinden gelen veriyi işler.
54
- *
55
- * readline.createInterface({ input: createPasteSafeInput(process.stdin), ... })
56
- * şeklinde process.stdin yerine kullanılmalı.
57
- */
58
- // str'nin sonunda PASTE_START veya PASTE_END marker'ının bir ön-eki (prefix)
59
- // olarak duran kısmın uzunluğunu döner (0 = eşleşme yok). Sadece gerçekten
60
- // marker olabilecek bir kuyruk varsa bekletiyoruz; aksi halde tek tuş
61
- // yazımında karakterler bir sonraki tuşa kadar ekranda gecikir.
62
- function trailingMarkerPrefixLen(str, marker) {
63
- const maxLen = Math.min(str.length, marker.length - 1);
64
- for (let len = maxLen; len > 0; len--) {
65
- if (str.slice(str.length - len) === marker.slice(0, len)) return len;
66
- }
67
- return 0;
68
- }
69
-
70
- // TERMINAL-AGNOSTIK PASTE TESPİTİ
71
- // ---------------------------------
72
- // Birçok terminal (örn. macOS'un yerleşik Terminal.app'i) bracketed-paste
73
- // (ESC[200~/[201~) modunu desteklemez/etkinleştirmez. Bu yüzden bracket
74
- // marker'larına güvenmek yetersiz kalıyor. Daha güvenilir, terminalden
75
- // bağımsız bir sinyal var: bir gerçek Enter tuşuna basış, işletim sisteminden
76
- // tek başına ve tek bir karakter ('\r' veya '\n') olarak gelir — başka hiçbir
77
- // karakterle birlikte değil. Ama bir yapıştırma (paste), terminal emülatörü
78
- // tarafından TEK SEFERDE (çok karakterli, içinde satır sonları barındıran bir
79
- // "data" event'i olarak) pty'ye yazılır. Yani: içinde satır sonu BARINDIRAN
80
- // ama SADECE tek bir satır sonu karakterinden İBARET OLMAYAN bir chunk,
81
- // hemen hemen kesin olarak bir paste'tir — bracket marker olsun olmasın.
82
- function isLoneEnterKeystroke(str) {
83
- return str === '\r' || str === '\n' || str === '\r\n';
84
- }
85
-
86
- function escapeEmbeddedNewlines(str) {
87
- if (!str || isLoneEnterKeystroke(str)) return str;
88
- const newlineCount = (str.match(/\r\n|\r|\n/g) || []).length;
89
- // Terminal-agnostik paste tespiti:
90
- // Gerçek terminalde tuş vuruşları tek tek karakter olarak gelir.
91
- // İçinde satır sonu BARINDIRAN ve sadece bir satır sonundan İBARET
92
- // OLMAYAN her chunk paste'tir — kaç satır olduğu fark etmez.
93
- // (Tek istisna: test ortamında PassThrough kullanılır, orada da
94
- // testler karakter-karakter yazacak şekilde güncellenmiştir.)
95
- if (newlineCount === 0) return str;
96
-
97
- // Paste algılandı — output filter'a sinyal ver ve summary yaz
98
- onPasteDetected(str);
99
-
100
- return str.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER);
101
- }
102
-
103
- function onPasteDetected(pasteStr) {
104
- const newlineCount = (pasteStr.match(/\r\n|\r|\n/g) || []).length;
105
- const lineCount = newlineCount + 1;
106
- _pasteContext = { lineCount };
107
- // Mevcut satırı temizle (prompt + öncesinde yazılan text) ve özet yaz
108
- process.stdout.write(`\r\x1b[2K[Pasted ~${lineCount} lines]\n`);
109
- }
110
-
111
- function createPasteSafeInput(source = process.stdin) {
112
- const proxy = new PassThrough();
113
-
114
- let inPaste = false;
115
- let carry = ''; // marker'ın chunk sınırında bölünmesine karşı tampon
116
- let pasteBuffer = ''; // onPasteDetected için biriken paste içeriği
117
-
118
- const onData = (chunk) => {
119
- let str = carry + chunk.toString('utf8');
120
- carry = '';
121
- let out = '';
122
-
123
- while (str.length) {
124
- if (inPaste) {
125
- const endIdx = str.indexOf(PASTE_END);
126
- if (endIdx === -1) {
127
- const keepLen = trailingMarkerPrefixLen(str, PASTE_END);
128
- const flushLen = str.length - keepLen;
129
- const segment = str.slice(0, flushLen);
130
- pasteBuffer += segment;
131
- out += segment.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER);
132
- carry = str.slice(flushLen);
133
- str = '';
134
- break;
135
- }
136
- const pasted = str.slice(0, endIdx);
137
- pasteBuffer += pasted;
138
- out += pasted.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER);
139
- inPaste = false;
140
- onPasteDetected(pasteBuffer);
141
- pasteBuffer = '';
142
- str = str.slice(endIdx + PASTE_END.length);
143
- continue;
144
- }
145
-
146
- const startIdx = str.indexOf(PASTE_START);
147
- if (startIdx === -1) {
148
- const keepLen = trailingMarkerPrefixLen(str, PASTE_START);
149
- const flushLen = str.length - keepLen;
150
- // Bracket marker'ı olmayan terminallerde de paste'i yakala: bu segment
151
- // tek başına bir Enter tuşu değilse ve içinde satır sonu varsa, o
152
- // satır sonlarını gizle (terminal-agnostik heuristik).
153
- out += escapeEmbeddedNewlines(str.slice(0, flushLen));
154
- carry = str.slice(flushLen);
155
- str = '';
156
- break;
157
- }
158
- out += escapeEmbeddedNewlines(str.slice(0, startIdx));
159
- str = str.slice(startIdx + PASTE_START.length);
160
- inPaste = true;
161
- }
162
-
163
- if (out) proxy.write(out);
164
- };
165
-
166
- source.on('data', onData);
167
- source.on('end', () => {
168
- if (carry) {
169
- proxy.write(inPaste ? carry.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER) : carry);
170
- carry = '';
171
- }
172
- proxy.end();
173
- });
174
- source.on('error', (e) => proxy.emit('error', e));
175
-
176
- // process.stdin'in raw-mode/pause-resume API'lerini proxy üzerinden de eriştir,
177
- // readline bunları çağırabiliyor.
178
- proxy.isTTY = source.isTTY;
179
- proxy.setRawMode = source.setRawMode ? source.setRawMode.bind(source) : undefined;
180
- proxy.ref = source.ref ? source.ref.bind(source) : undefined;
181
- proxy.unref = source.unref ? source.unref.bind(source) : undefined;
182
-
183
- return proxy;
184
- }
185
-
186
- /**
187
- * Terminalde bracketed paste mode'u açar. Çoğu modern terminal (iTerm2, Terminal.app,
188
- * Windows Terminal, VS Code, gnome-terminal vb.) bunu destekler. Desteklemeyen
189
- * terminallerde bu escape kodu sessizce yok sayılır, davranış eskisi gibi kalır.
190
- */
191
- function enableBracketedPaste(out = process.stdout) {
192
- if (out.isTTY) out.write('\x1b[?2004h');
193
- }
194
-
195
- function disableBracketedPaste(out = process.stdout) {
196
- if (out.isTTY) out.write('\x1b[?2004l');
197
- }
198
-
199
- /**
200
- * readline'dan gelen satırı, paste placeholder'larını gerçek "\n" karakterine
201
- * geri çevirerek normalize eder. Gönderilecek mesaj olarak bunu kullan.
202
- */
203
- function restoreNewlines(line) {
204
- return line.split(NEWLINE_PLACEHOLDER).join('\n');
205
- }
206
-
207
- /**
208
- * createOutputFilter — Readline'ın output'unda dolaşan placeholder
209
- * karakterlerini (NEWLINE_PLACEHOLDER) TAMAMEN gizleyen bir wrapper.
210
- * Kullanıcı terminalde ne placeholder yazılarını ne de satır sonlarını
211
- * görür — paste içeriği terminal ekosunda yer kaplamaz.
212
- *
213
- * Paste'in içeriği terminalde görünmez; repl.js line handler'ı paste
214
- * algılandığında "[Pasted ~N lines]" gibi kısa bir özet yazar.
215
- *
216
- * Kullanım:
217
- * const outFilter = createOutputFilter(process.stdout);
218
- * const rl = readline.createInterface({ input, output: outFilter, ... });
219
- *
220
- * Çalışma prensibi:
221
- * Readline, echo mekanizmasıyla her karakteri output.write() ile
222
- * terminale yazar. Placeholder karakterleri de tek tek yazılır.
223
- * Bu fonksiyon bir state machine tutar: karakterleri placeholder
224
- * ile eşleştirir, tam eşleşme olursa placeholder'ı ATAR (terminalde
225
- * hiçbir iz kalmaz), olmazsa karakteri olduğu gibi geçirir. Kısmi
226
- * eşleşmeler (chunk sınırında bölünmüş placeholder) sonraki
227
- * write()'da tamamlanmak üzere partial buffer'da bekletilir.
228
- */
229
- function createOutputFilter(output = process.stdout) {
230
- let partial = '';
231
-
232
- const filter = {
233
- write(data) {
234
- // Paste aktifken tüm ekoyu yut terminalde hiçbir şey gözükmez
235
- if (_pasteContext !== null) {
236
- // Placeholder state machine'i çalıştır (sonraki write'lar için
237
- // partial buffer temiz kalsın) ama output'a hiçbir şey yazma.
238
- const str = data.toString('utf8');
239
- let i = 0;
240
- while (i < str.length) {
241
- if (partial.length > 0) {
242
- const expected = NEWLINE_PLACEHOLDER[partial.length];
243
- const ch = str[i];
244
- if (ch === expected) {
245
- partial += ch;
246
- i++;
247
- if (partial === NEWLINE_PLACEHOLDER) partial = '';
248
- } else {
249
- partial = '';
250
- }
251
- } else {
252
- if (str[i] === NEWLINE_PLACEHOLDER[0]) {
253
- partial = str[i];
254
- }
255
- i++;
256
- }
257
- }
258
- return true;
259
- }
260
-
261
- const str = data.toString('utf8');
262
- let result = '';
263
- let i = 0;
264
-
265
- while (i < str.length) {
266
- if (partial.length > 0) {
267
- const expected = NEWLINE_PLACEHOLDER[partial.length];
268
- const ch = str[i];
269
- if (ch === expected) {
270
- partial += ch;
271
- i++;
272
- if (partial === NEWLINE_PLACEHOLDER) {
273
- partial = '';
274
- }
275
- } else {
276
- result += partial;
277
- partial = '';
278
- }
279
- } else {
280
- const ch = str[i];
281
- if (ch === NEWLINE_PLACEHOLDER[0]) {
282
- partial = ch;
283
- i++;
284
- } else {
285
- result += ch;
286
- i++;
287
- }
288
- }
289
- }
290
-
291
- if (result) return output.write(result);
292
- return true;
293
- },
294
-
295
- end(...args) {
296
- if (partial) {
297
- output.write(partial);
298
- partial = '';
299
- }
300
- return output.end(...args);
301
- },
302
-
303
- get columns() { return output.columns; },
304
- get rows() { return output.rows; },
305
- get isTTY() { return output.isTTY; },
306
-
307
- on(...args) { return output.on(...args); },
308
- off(...args) { return output.off ? output.off(...args) : undefined; },
309
- addListener(...args) { return output.addListener(...args); },
310
- removeListener(...args) { return output.removeListener(...args); },
311
- emit(...args) { return output.emit(...args); },
312
- getColorDepth(...args) {
313
- return typeof output.getColorDepth === 'function'
314
- ? output.getColorDepth(...args)
315
- : undefined;
316
- },
317
- };
318
-
319
- return filter;
320
- }
321
-
322
- function setPasteContext(ctx) { _pasteContext = ctx; }
323
- function clearPasteContext() { _pasteContext = null; }
324
-
325
- module.exports = {
326
- createPasteSafeInput,
327
- createOutputFilter,
328
- enableBracketedPaste,
329
- disableBracketedPaste,
330
- restoreNewlines,
331
- NEWLINE_PLACEHOLDER,
332
- setPasteContext,
333
- clearPasteContext,
334
- };
1
+ /**
2
+ * paste-safe-input — Terminal'e uzun/çok satırlı metin yapıştırılınca
3
+ * her satırın ayrı bir "Enter" gibi gönderilip anında submit edilmesini önler.
4
+ *
5
+ * Sorun: Node'un düz `readline` arayüzü, terminalden gelen her "\n" karakterini
6
+ * bir 'line' event'i olarak görür. Terminal "bracketed paste mode" açık değilse
7
+ * (veya açık olsa da bu karakterler ayıklanmazsa), kullanıcı 10 satırlık bir metni
8
+ * yapıştırdığında readline bunu 10 ayrı mesaj gibi okur ve her birini hemen
9
+ * gönderir — kullanıcı paste'i bitirmeden cevaplar gelmeye başlar.
10
+ *
11
+ * Çözüm: İki katmanlı bir yaklaşım:
12
+ *
13
+ * 1. INPUT KATMANI (createPasteSafeInput):
14
+ * Terminalin "bracketed paste" (ESC[200~ ... ESC[201~) işaretleyicilerini
15
+ * dinleyip, bu işaretler arasındaki tüm veriyi tek parça olarak topluyoruz.
16
+ * Ayrıca terminal-agnostik bir heuristik: çok satırlı büyük chunk'ları da
17
+ * paste olarak algılar. İçindeki gerçek satır sonlarını geçici bir
18
+ * placeholder (NEWLINE_PLACEHOLDER) ile değiştirip readline'a TEK satır gibi
19
+ * besliyoruz. Kullanıcı gerçekten Enter'a basana kadar hiçbir 'line' event'i
20
+ * tetiklenmez.
21
+ *
22
+ * 2. OUTPUT KATMANI (createOutputFilter):
23
+ * Readline'ın echo mekanizması placeholder karakterlerini terminale
24
+ * yazarken araya girer. Bir state machine ile karakterleri placeholder
25
+ * dizisiyle eşleştirir, tam eşleşme olursa terminale \n (yeni satır)
26
+ * yazar. Böylece kullanıcı "␤␤LINEBREAK␤␤" gibi literal placeholder
27
+ * yazıları görmez, satır sonlarını doğal yeni satırlar olarak deneyimler.
28
+ *
29
+ * Enter'a basıldığında, readline'dan gelen placeholder'lı satır,
30
+ * restoreNewlines() ile gerçek \n karakterlerine dönüştürülüp mesaj
31
+ * olarak işlenir.
32
+ */
33
+
34
+ 'use strict';
35
+
36
+ const { PassThrough } = require('stream');
37
+
38
+ const PASTE_START = '\x1b[200~';
39
+ const PASTE_END = '\x1b[201~';
40
+
41
+ // Input proxy ile output filter arasında paste durumunu paylaşır.
42
+ // escapeEmbeddedNewlines paste algıladığında bu context'i set eder,
43
+ // output filter tüm ekoyu yutar (terminalde hiçbir şey gözükmez),
44
+ // repl.js line handler'ı context'i temizler.
45
+ let _pasteContext = null; // { lineCount } or null
46
+
47
+ // Yapıştırılan içindeki satır sonlarını gizlemek için kullanılan placeholder.
48
+ // Gerçek kullanıcı girdisinde pratikte hiç geçmeyecek bir dizi.
49
+ const NEWLINE_PLACEHOLDER = '\u2424\u2424LINEBREAK\u2424\u2424';
50
+
51
+ /**
52
+ * Bracketed paste'i destekleyen bir PassThrough stream döner.
53
+ * `source` (genellikle process.stdin) üzerinden gelen veriyi işler.
54
+ *
55
+ * readline.createInterface({ input: createPasteSafeInput(process.stdin), ... })
56
+ * şeklinde process.stdin yerine kullanılmalı.
57
+ */
58
+ // str'nin sonunda PASTE_START veya PASTE_END marker'ının bir ön-eki (prefix)
59
+ // olarak duran kısmın uzunluğunu döner (0 = eşleşme yok). Sadece gerçekten
60
+ // marker olabilecek bir kuyruk varsa bekletiyoruz; aksi halde tek tuş
61
+ // yazımında karakterler bir sonraki tuşa kadar ekranda gecikir.
62
+ function trailingMarkerPrefixLen(str, marker) {
63
+ const maxLen = Math.min(str.length, marker.length - 1);
64
+ for (let len = maxLen; len > 0; len--) {
65
+ if (str.slice(str.length - len) === marker.slice(0, len)) return len;
66
+ }
67
+ return 0;
68
+ }
69
+
70
+ // TERMINAL-AGNOSTIK PASTE TESPİTİ
71
+ // ---------------------------------
72
+ // Birçok terminal (örn. macOS'un yerleşik Terminal.app'i) bracketed-paste
73
+ // (ESC[200~/[201~) modunu desteklemez/etkinleştirmez. Bu yüzden bracket
74
+ // marker'larına güvenmek yetersiz kalıyor. Daha güvenilir, terminalden
75
+ // bağımsız bir sinyal var: bir gerçek Enter tuşuna basış, işletim sisteminden
76
+ // tek başına ve tek bir karakter ('\r' veya '\n') olarak gelir — başka hiçbir
77
+ // karakterle birlikte değil. Ama bir yapıştırma (paste), terminal emülatörü
78
+ // tarafından TEK SEFERDE (çok karakterli, içinde satır sonları barındıran bir
79
+ // "data" event'i olarak) pty'ye yazılır. Yani: içinde satır sonu BARINDIRAN
80
+ // ama SADECE tek bir satır sonu karakterinden İBARET OLMAYAN bir chunk,
81
+ // hemen hemen kesin olarak bir paste'tir — bracket marker olsun olmasın.
82
+ function isLoneEnterKeystroke(str) {
83
+ return str === '\r' || str === '\n' || str === '\r\n';
84
+ }
85
+
86
+ function escapeEmbeddedNewlines(str) {
87
+ if (!str || isLoneEnterKeystroke(str)) return str;
88
+ const newlineCount = (str.match(/\r\n|\r|\n/g) || []).length;
89
+ // Terminal-agnostik paste tespiti:
90
+ // Gerçek terminalde tuş vuruşları tek tek karakter olarak gelir.
91
+ // İçinde satır sonu BARINDIRAN ve sadece bir satır sonundan İBARET
92
+ // OLMAYAN her chunk paste'tir — kaç satır olduğu fark etmez.
93
+ // (Tek istisna: test ortamında PassThrough kullanılır, orada da
94
+ // testler karakter-karakter yazacak şekilde güncellenmiştir.)
95
+ if (newlineCount === 0) return str;
96
+
97
+ // Paste algılandı — output filter'a sinyal ver ve summary yaz
98
+ onPasteDetected(str);
99
+
100
+ return str.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER);
101
+ }
102
+
103
+ function onPasteDetected(pasteStr) {
104
+ const newlineCount = (pasteStr.match(/\r\n|\r|\n/g) || []).length;
105
+ const lineCount = newlineCount + 1;
106
+ _pasteContext = { lineCount };
107
+ // Mevcut satırı temizle (prompt + öncesinde yazılan text) ve özet yaz
108
+ process.stdout.write(`\r\x1b[2K[Pasted ~${lineCount} lines]\n`);
109
+ }
110
+
111
+ function createPasteSafeInput(source = process.stdin) {
112
+ const proxy = new PassThrough();
113
+
114
+ // Non-TTY (pipe, script, CI) girdide paste algılama ANLAMSIZ ve zararlı:
115
+ // chunk'lar doğal olarak çok satırlı gelir, her girdi "paste" sanılıp
116
+ // satır sonları placeholder'a çevrilir ve komutlar hiç işlenmez.
117
+ // İnteraktif olmayan kaynakta saf geçiş yap.
118
+ if (!source.isTTY) {
119
+ source.pipe(proxy);
120
+ source.on('error', (e) => proxy.emit('error', e));
121
+ proxy.isTTY = source.isTTY;
122
+ proxy.setRawMode = source.setRawMode ? source.setRawMode.bind(source) : undefined;
123
+ proxy.ref = source.ref ? source.ref.bind(source) : undefined;
124
+ proxy.unref = source.unref ? source.unref.bind(source) : undefined;
125
+ return proxy;
126
+ }
127
+
128
+ let inPaste = false;
129
+ let carry = ''; // marker'ın chunk sınırında bölünmesine karşı tampon
130
+ let pasteBuffer = ''; // onPasteDetected için biriken paste içeriği
131
+
132
+ const onData = (chunk) => {
133
+ let str = carry + chunk.toString('utf8');
134
+ carry = '';
135
+ let out = '';
136
+
137
+ while (str.length) {
138
+ if (inPaste) {
139
+ const endIdx = str.indexOf(PASTE_END);
140
+ if (endIdx === -1) {
141
+ const keepLen = trailingMarkerPrefixLen(str, PASTE_END);
142
+ const flushLen = str.length - keepLen;
143
+ const segment = str.slice(0, flushLen);
144
+ pasteBuffer += segment;
145
+ out += segment.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER);
146
+ carry = str.slice(flushLen);
147
+ break;
148
+ }
149
+ const pasted = str.slice(0, endIdx);
150
+ pasteBuffer += pasted;
151
+ out += pasted.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER);
152
+ inPaste = false;
153
+ onPasteDetected(pasteBuffer);
154
+ pasteBuffer = '';
155
+ str = str.slice(endIdx + PASTE_END.length);
156
+ continue;
157
+ }
158
+
159
+ const startIdx = str.indexOf(PASTE_START);
160
+ if (startIdx === -1) {
161
+ const keepLen = trailingMarkerPrefixLen(str, PASTE_START);
162
+ const flushLen = str.length - keepLen;
163
+ // Bracket marker'ı olmayan terminallerde de paste'i yakala: bu segment
164
+ // tek başına bir Enter tuşu değilse ve içinde satır sonu varsa, o
165
+ // satır sonlarını gizle (terminal-agnostik heuristik).
166
+ out += escapeEmbeddedNewlines(str.slice(0, flushLen));
167
+ carry = str.slice(flushLen);
168
+ break;
169
+ }
170
+ out += escapeEmbeddedNewlines(str.slice(0, startIdx));
171
+ str = str.slice(startIdx + PASTE_START.length);
172
+ inPaste = true;
173
+ }
174
+
175
+ if (out) proxy.write(out);
176
+ };
177
+
178
+ source.on('data', onData);
179
+ source.on('end', () => {
180
+ if (carry) {
181
+ proxy.write(inPaste ? carry.replace(/\r\n|\r|\n/g, NEWLINE_PLACEHOLDER) : carry);
182
+ carry = '';
183
+ }
184
+ proxy.end();
185
+ });
186
+ source.on('error', (e) => proxy.emit('error', e));
187
+
188
+ // process.stdin'in raw-mode/pause-resume API'lerini proxy üzerinden de eriştir,
189
+ // readline bunları çağırabiliyor.
190
+ proxy.isTTY = source.isTTY;
191
+ proxy.setRawMode = source.setRawMode ? source.setRawMode.bind(source) : undefined;
192
+ proxy.ref = source.ref ? source.ref.bind(source) : undefined;
193
+ proxy.unref = source.unref ? source.unref.bind(source) : undefined;
194
+
195
+ return proxy;
196
+ }
197
+
198
+ /**
199
+ * Terminalde bracketed paste mode'u açar. Çoğu modern terminal (iTerm2, Terminal.app,
200
+ * Windows Terminal, VS Code, gnome-terminal vb.) bunu destekler. Desteklemeyen
201
+ * terminallerde bu escape kodu sessizce yok sayılır, davranış eskisi gibi kalır.
202
+ */
203
+ function enableBracketedPaste(out = process.stdout) {
204
+ if (out.isTTY) out.write('\x1b[?2004h');
205
+ }
206
+
207
+ function disableBracketedPaste(out = process.stdout) {
208
+ if (out.isTTY) out.write('\x1b[?2004l');
209
+ }
210
+
211
+ /**
212
+ * readline'dan gelen satırı, paste placeholder'larını gerçek "\n" karakterine
213
+ * geri çevirerek normalize eder. Gönderilecek mesaj olarak bunu kullan.
214
+ */
215
+ function restoreNewlines(line) {
216
+ return line.split(NEWLINE_PLACEHOLDER).join('\n');
217
+ }
218
+
219
+ /**
220
+ * createOutputFilter — Readline'ın output'unda dolaşan placeholder
221
+ * karakterlerini (NEWLINE_PLACEHOLDER) TAMAMEN gizleyen bir wrapper.
222
+ * Kullanıcı terminalde ne placeholder yazılarını ne de satır sonlarını
223
+ * görür paste içeriği terminal ekosunda yer kaplamaz.
224
+ *
225
+ * Paste'in içeriği terminalde görünmez; repl.js line handler'ı paste
226
+ * algılandığında "[Pasted ~N lines]" gibi kısa bir özet yazar.
227
+ *
228
+ * Kullanım:
229
+ * const outFilter = createOutputFilter(process.stdout);
230
+ * const rl = readline.createInterface({ input, output: outFilter, ... });
231
+ *
232
+ * Çalışma prensibi:
233
+ * Readline, echo mekanizmasıyla her karakteri output.write() ile
234
+ * terminale yazar. Placeholder karakterleri de tek tek yazılır.
235
+ * Bu fonksiyon bir state machine tutar: karakterleri placeholder
236
+ * ile eşleştirir, tam eşleşme olursa placeholder'ı ATAR (terminalde
237
+ * hiçbir iz kalmaz), olmazsa karakteri olduğu gibi geçirir. Kısmi
238
+ * eşleşmeler (chunk sınırında bölünmüş placeholder) sonraki
239
+ * write()'da tamamlanmak üzere partial buffer'da bekletilir.
240
+ */
241
+ function createOutputFilter(output = process.stdout) {
242
+ let partial = '';
243
+
244
+ const filter = {
245
+ write(data) {
246
+ // Paste aktifken tüm ekoyu yut — terminalde hiçbir şey gözükmez
247
+ if (_pasteContext !== null) {
248
+ // Placeholder state machine'i çalıştır (sonraki write'lar için
249
+ // partial buffer temiz kalsın) ama output'a hiçbir şey yazma.
250
+ const str = data.toString('utf8');
251
+ let i = 0;
252
+ while (i < str.length) {
253
+ if (partial.length > 0) {
254
+ const expected = NEWLINE_PLACEHOLDER[partial.length];
255
+ const ch = str[i];
256
+ if (ch === expected) {
257
+ partial += ch;
258
+ i++;
259
+ if (partial === NEWLINE_PLACEHOLDER) partial = '';
260
+ } else {
261
+ partial = '';
262
+ }
263
+ } else {
264
+ if (str[i] === NEWLINE_PLACEHOLDER[0]) {
265
+ partial = str[i];
266
+ }
267
+ i++;
268
+ }
269
+ }
270
+ return true;
271
+ }
272
+
273
+ const str = data.toString('utf8');
274
+ let result = '';
275
+ let i = 0;
276
+
277
+ while (i < str.length) {
278
+ if (partial.length > 0) {
279
+ const expected = NEWLINE_PLACEHOLDER[partial.length];
280
+ const ch = str[i];
281
+ if (ch === expected) {
282
+ partial += ch;
283
+ i++;
284
+ if (partial === NEWLINE_PLACEHOLDER) {
285
+ partial = '';
286
+ }
287
+ } else {
288
+ result += partial;
289
+ partial = '';
290
+ }
291
+ } else {
292
+ const ch = str[i];
293
+ if (ch === NEWLINE_PLACEHOLDER[0]) {
294
+ partial = ch;
295
+ i++;
296
+ } else {
297
+ result += ch;
298
+ i++;
299
+ }
300
+ }
301
+ }
302
+
303
+ if (result) return output.write(result);
304
+ return true;
305
+ },
306
+
307
+ end(...args) {
308
+ if (partial) {
309
+ output.write(partial);
310
+ partial = '';
311
+ }
312
+ return output.end(...args);
313
+ },
314
+
315
+ get columns() { return output.columns; },
316
+ get rows() { return output.rows; },
317
+ get isTTY() { return output.isTTY; },
318
+
319
+ on(...args) { return output.on(...args); },
320
+ off(...args) { return output.off ? output.off(...args) : undefined; },
321
+ addListener(...args) { return output.addListener(...args); },
322
+ removeListener(...args) { return output.removeListener(...args); },
323
+ emit(...args) { return output.emit(...args); },
324
+ getColorDepth(...args) {
325
+ return typeof output.getColorDepth === 'function'
326
+ ? output.getColorDepth(...args)
327
+ : undefined;
328
+ },
329
+ };
330
+
331
+ return filter;
332
+ }
333
+
334
+ function setPasteContext(ctx) { _pasteContext = ctx; }
335
+ function clearPasteContext() { _pasteContext = null; }
336
+
337
+ module.exports = {
338
+ createPasteSafeInput,
339
+ createOutputFilter,
340
+ enableBracketedPaste,
341
+ disableBracketedPaste,
342
+ restoreNewlines,
343
+ NEWLINE_PLACEHOLDER,
344
+ setPasteContext,
345
+ clearPasteContext,
346
+ };