@robinpath/cli 1.73.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.
- package/README.md +111 -0
- package/dist/cli.mjs +28256 -0
- package/modules/_helpers.js +33 -0
- package/modules/assert.js +270 -0
- package/modules/buffer.js +245 -0
- package/modules/child.js +176 -0
- package/modules/crypto.js +352 -0
- package/modules/dns.js +146 -0
- package/modules/events.js +174 -0
- package/modules/file.js +361 -0
- package/modules/http.js +268 -0
- package/modules/index.js +76 -0
- package/modules/net.js +189 -0
- package/modules/os.js +219 -0
- package/modules/path.js +162 -0
- package/modules/process.js +214 -0
- package/modules/stream.js +322 -0
- package/modules/string_decoder.js +106 -0
- package/modules/timer.js +167 -0
- package/modules/tls.js +264 -0
- package/modules/tty.js +169 -0
- package/modules/url.js +189 -0
- package/modules/util.js +275 -0
- package/modules/zlib.js +126 -0
- package/package.json +57 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native stream module for RobinPath.
|
|
3
|
+
* Readable, Writable, Transform, Duplex, PassThrough, and pipeline.
|
|
4
|
+
*/
|
|
5
|
+
import { Readable, Writable, Transform, Duplex, PassThrough, pipeline as _pipeline } from 'node:stream';
|
|
6
|
+
import { toStr, toNum, requireArgs } from './_helpers.js';
|
|
7
|
+
|
|
8
|
+
const _streams = new Map();
|
|
9
|
+
let _nextId = 1;
|
|
10
|
+
|
|
11
|
+
export const StreamFunctions = {
|
|
12
|
+
|
|
13
|
+
readable: (args) => {
|
|
14
|
+
const data = args[0];
|
|
15
|
+
const id = `readable_${_nextId++}`;
|
|
16
|
+
let chunks;
|
|
17
|
+
if (typeof data === 'string') {
|
|
18
|
+
chunks = [data];
|
|
19
|
+
} else if (Array.isArray(data)) {
|
|
20
|
+
chunks = data.map(c => toStr(c));
|
|
21
|
+
} else {
|
|
22
|
+
chunks = [toStr(data)];
|
|
23
|
+
}
|
|
24
|
+
const stream = new Readable({
|
|
25
|
+
read() {
|
|
26
|
+
if (chunks.length > 0) this.push(chunks.shift());
|
|
27
|
+
else this.push(null);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
_streams.set(id, { stream, data: '' });
|
|
31
|
+
stream.on('data', (chunk) => {
|
|
32
|
+
const entry = _streams.get(id);
|
|
33
|
+
if (entry) entry.data += chunk.toString();
|
|
34
|
+
});
|
|
35
|
+
return id;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
writable: (args) => {
|
|
39
|
+
const id = `writable_${_nextId++}`;
|
|
40
|
+
let collected = '';
|
|
41
|
+
const stream = new Writable({
|
|
42
|
+
write(chunk, encoding, callback) {
|
|
43
|
+
collected += chunk.toString();
|
|
44
|
+
const entry = _streams.get(id);
|
|
45
|
+
if (entry) entry.data = collected;
|
|
46
|
+
callback();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
_streams.set(id, { stream, data: '' });
|
|
50
|
+
return id;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
transform: (args) => {
|
|
54
|
+
const id = `transform_${_nextId++}`;
|
|
55
|
+
const stream = new Transform({
|
|
56
|
+
transform(chunk, encoding, callback) {
|
|
57
|
+
callback(null, chunk);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
_streams.set(id, { stream, data: '' });
|
|
61
|
+
stream.on('data', (chunk) => {
|
|
62
|
+
const entry = _streams.get(id);
|
|
63
|
+
if (entry) entry.data += chunk.toString();
|
|
64
|
+
});
|
|
65
|
+
return id;
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
duplex: (args) => {
|
|
69
|
+
const id = `duplex_${_nextId++}`;
|
|
70
|
+
const stream = new Duplex({
|
|
71
|
+
read() {},
|
|
72
|
+
write(chunk, encoding, callback) {
|
|
73
|
+
this.push(chunk);
|
|
74
|
+
callback();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
_streams.set(id, { stream, data: '' });
|
|
78
|
+
stream.on('data', (chunk) => {
|
|
79
|
+
const entry = _streams.get(id);
|
|
80
|
+
if (entry) entry.data += chunk.toString();
|
|
81
|
+
});
|
|
82
|
+
return id;
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
passThrough: (args) => {
|
|
86
|
+
const id = `passthrough_${_nextId++}`;
|
|
87
|
+
const stream = new PassThrough();
|
|
88
|
+
_streams.set(id, { stream, data: '' });
|
|
89
|
+
stream.on('data', (chunk) => {
|
|
90
|
+
const entry = _streams.get(id);
|
|
91
|
+
if (entry) entry.data += chunk.toString();
|
|
92
|
+
});
|
|
93
|
+
return id;
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
write: (args) => {
|
|
97
|
+
requireArgs('stream.write', args, 2);
|
|
98
|
+
const id = toStr(args[0]);
|
|
99
|
+
const data = toStr(args[1]);
|
|
100
|
+
const entry = _streams.get(id);
|
|
101
|
+
if (!entry) throw new Error(`stream.write: stream ${id} not found`);
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
entry.stream.write(data, (err) => {
|
|
104
|
+
if (err) reject(err);
|
|
105
|
+
else resolve(true);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
read: (args) => {
|
|
111
|
+
requireArgs('stream.read', args, 1);
|
|
112
|
+
const id = toStr(args[0]);
|
|
113
|
+
const entry = _streams.get(id);
|
|
114
|
+
if (!entry) throw new Error(`stream.read: stream ${id} not found`);
|
|
115
|
+
const data = entry.data;
|
|
116
|
+
entry.data = '';
|
|
117
|
+
return data;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
end: (args) => {
|
|
121
|
+
requireArgs('stream.end', args, 1);
|
|
122
|
+
const id = toStr(args[0]);
|
|
123
|
+
const entry = _streams.get(id);
|
|
124
|
+
if (!entry) throw new Error(`stream.end: stream ${id} not found`);
|
|
125
|
+
entry.stream.end();
|
|
126
|
+
return true;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
destroy: (args) => {
|
|
130
|
+
requireArgs('stream.destroy', args, 1);
|
|
131
|
+
const id = toStr(args[0]);
|
|
132
|
+
const entry = _streams.get(id);
|
|
133
|
+
if (entry) {
|
|
134
|
+
entry.stream.destroy();
|
|
135
|
+
_streams.delete(id);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
pipe: (args) => {
|
|
142
|
+
requireArgs('stream.pipe', args, 2);
|
|
143
|
+
const srcId = toStr(args[0]);
|
|
144
|
+
const destId = toStr(args[1]);
|
|
145
|
+
const src = _streams.get(srcId);
|
|
146
|
+
const dest = _streams.get(destId);
|
|
147
|
+
if (!src) throw new Error(`stream.pipe: source ${srcId} not found`);
|
|
148
|
+
if (!dest) throw new Error(`stream.pipe: destination ${destId} not found`);
|
|
149
|
+
src.stream.pipe(dest.stream);
|
|
150
|
+
return destId;
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
pipeline: (args) => {
|
|
154
|
+
if (!Array.isArray(args[0]) && args.length < 2) {
|
|
155
|
+
throw new Error('stream.pipeline requires at least 2 stream IDs');
|
|
156
|
+
}
|
|
157
|
+
const ids = Array.isArray(args[0]) ? args[0] : args;
|
|
158
|
+
const streams = ids.map(id => {
|
|
159
|
+
const entry = _streams.get(toStr(id));
|
|
160
|
+
if (!entry) throw new Error(`stream.pipeline: stream ${id} not found`);
|
|
161
|
+
return entry.stream;
|
|
162
|
+
});
|
|
163
|
+
return new Promise((resolve, reject) => {
|
|
164
|
+
_pipeline(...streams, (err) => {
|
|
165
|
+
if (err) reject(err);
|
|
166
|
+
else resolve(true);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
toBuffer: (args) => {
|
|
172
|
+
requireArgs('stream.toBuffer', args, 1);
|
|
173
|
+
const id = toStr(args[0]);
|
|
174
|
+
const entry = _streams.get(id);
|
|
175
|
+
if (!entry) throw new Error(`stream.toBuffer: stream ${id} not found`);
|
|
176
|
+
return new Promise((resolve) => {
|
|
177
|
+
const chunks = [];
|
|
178
|
+
entry.stream.on('data', (chunk) => chunks.push(chunk));
|
|
179
|
+
entry.stream.on('end', () => {
|
|
180
|
+
resolve(Buffer.concat(chunks).toString('base64'));
|
|
181
|
+
});
|
|
182
|
+
// If already ended
|
|
183
|
+
if (entry.stream.readableEnded) {
|
|
184
|
+
resolve(Buffer.from(entry.data).toString('base64'));
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
toString: (args) => {
|
|
190
|
+
requireArgs('stream.toString', args, 1);
|
|
191
|
+
const id = toStr(args[0]);
|
|
192
|
+
const entry = _streams.get(id);
|
|
193
|
+
if (!entry) throw new Error(`stream.toString: stream ${id} not found`);
|
|
194
|
+
return new Promise((resolve) => {
|
|
195
|
+
let data = '';
|
|
196
|
+
entry.stream.on('data', (chunk) => { data += chunk.toString(); });
|
|
197
|
+
entry.stream.on('end', () => resolve(data));
|
|
198
|
+
if (entry.stream.readableEnded) resolve(entry.data);
|
|
199
|
+
});
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
fromString: (args) => {
|
|
203
|
+
requireArgs('stream.fromString', args, 1);
|
|
204
|
+
const data = toStr(args[0]);
|
|
205
|
+
const id = `readable_${_nextId++}`;
|
|
206
|
+
const stream = Readable.from([data]);
|
|
207
|
+
_streams.set(id, { stream, data });
|
|
208
|
+
return id;
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
fromArray: (args) => {
|
|
212
|
+
requireArgs('stream.fromArray', args, 1);
|
|
213
|
+
const arr = Array.isArray(args[0]) ? args[0] : [args[0]];
|
|
214
|
+
const id = `readable_${_nextId++}`;
|
|
215
|
+
const stream = Readable.from(arr.map(a => toStr(a)));
|
|
216
|
+
_streams.set(id, { stream, data: arr.join('') });
|
|
217
|
+
return id;
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
active: () => Array.from(_streams.keys()),
|
|
221
|
+
|
|
222
|
+
count: () => _streams.size
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const StreamFunctionMetadata = {
|
|
226
|
+
readable: {
|
|
227
|
+
description: 'Create a readable stream from data',
|
|
228
|
+
parameters: [{ name: 'data', dataType: 'any', description: 'String or array of chunks', formInputType: 'textarea', required: true }],
|
|
229
|
+
returnType: 'string', returnDescription: 'Stream handle ID', example: 'stream.readable "hello world"'
|
|
230
|
+
},
|
|
231
|
+
writable: {
|
|
232
|
+
description: 'Create a writable stream that collects data',
|
|
233
|
+
parameters: [],
|
|
234
|
+
returnType: 'string', returnDescription: 'Stream handle ID', example: 'stream.writable'
|
|
235
|
+
},
|
|
236
|
+
transform: {
|
|
237
|
+
description: 'Create a transform (pass-through) stream',
|
|
238
|
+
parameters: [],
|
|
239
|
+
returnType: 'string', returnDescription: 'Stream handle ID', example: 'stream.transform'
|
|
240
|
+
},
|
|
241
|
+
duplex: {
|
|
242
|
+
description: 'Create a duplex (read/write) stream',
|
|
243
|
+
parameters: [],
|
|
244
|
+
returnType: 'string', returnDescription: 'Stream handle ID', example: 'stream.duplex'
|
|
245
|
+
},
|
|
246
|
+
passThrough: {
|
|
247
|
+
description: 'Create a passthrough stream',
|
|
248
|
+
parameters: [],
|
|
249
|
+
returnType: 'string', returnDescription: 'Stream handle ID', example: 'stream.passThrough'
|
|
250
|
+
},
|
|
251
|
+
write: {
|
|
252
|
+
description: 'Write data to a stream',
|
|
253
|
+
parameters: [
|
|
254
|
+
{ name: 'streamId', dataType: 'string', description: 'Stream handle ID', formInputType: 'text', required: true },
|
|
255
|
+
{ name: 'data', dataType: 'string', description: 'Data to write', formInputType: 'textarea', required: true }
|
|
256
|
+
],
|
|
257
|
+
returnType: 'boolean', returnDescription: 'true on success', example: 'stream.write $s "data"'
|
|
258
|
+
},
|
|
259
|
+
read: {
|
|
260
|
+
description: 'Read buffered data from a stream',
|
|
261
|
+
parameters: [{ name: 'streamId', dataType: 'string', description: 'Stream handle ID', formInputType: 'text', required: true }],
|
|
262
|
+
returnType: 'string', returnDescription: 'Buffered data', example: 'stream.read $s'
|
|
263
|
+
},
|
|
264
|
+
end: {
|
|
265
|
+
description: 'Signal end of stream',
|
|
266
|
+
parameters: [{ name: 'streamId', dataType: 'string', description: 'Stream handle ID', formInputType: 'text', required: true }],
|
|
267
|
+
returnType: 'boolean', returnDescription: 'true', example: 'stream.end $s'
|
|
268
|
+
},
|
|
269
|
+
destroy: {
|
|
270
|
+
description: 'Destroy a stream and free resources',
|
|
271
|
+
parameters: [{ name: 'streamId', dataType: 'string', description: 'Stream handle ID', formInputType: 'text', required: true }],
|
|
272
|
+
returnType: 'boolean', returnDescription: 'true if destroyed', example: 'stream.destroy $s'
|
|
273
|
+
},
|
|
274
|
+
pipe: {
|
|
275
|
+
description: 'Pipe one stream into another',
|
|
276
|
+
parameters: [
|
|
277
|
+
{ name: 'sourceId', dataType: 'string', description: 'Source stream', formInputType: 'text', required: true },
|
|
278
|
+
{ name: 'destId', dataType: 'string', description: 'Destination stream', formInputType: 'text', required: true }
|
|
279
|
+
],
|
|
280
|
+
returnType: 'string', returnDescription: 'Destination stream ID', example: 'stream.pipe $src $dest'
|
|
281
|
+
},
|
|
282
|
+
pipeline: {
|
|
283
|
+
description: 'Chain multiple streams together with error propagation',
|
|
284
|
+
parameters: [{ name: 'streamIds', dataType: 'array', description: 'Array of stream IDs to chain', formInputType: 'json', required: true }],
|
|
285
|
+
returnType: 'boolean', returnDescription: 'true on completion', example: 'stream.pipeline [$s1, $s2, $s3]'
|
|
286
|
+
},
|
|
287
|
+
toBuffer: {
|
|
288
|
+
description: 'Collect stream data as base64 buffer',
|
|
289
|
+
parameters: [{ name: 'streamId', dataType: 'string', description: 'Stream handle ID', formInputType: 'text', required: true }],
|
|
290
|
+
returnType: 'string', returnDescription: 'Base64-encoded buffer', example: 'stream.toBuffer $s'
|
|
291
|
+
},
|
|
292
|
+
toString: {
|
|
293
|
+
description: 'Collect stream data as string',
|
|
294
|
+
parameters: [{ name: 'streamId', dataType: 'string', description: 'Stream handle ID', formInputType: 'text', required: true }],
|
|
295
|
+
returnType: 'string', returnDescription: 'Collected string', example: 'stream.toString $s'
|
|
296
|
+
},
|
|
297
|
+
fromString: {
|
|
298
|
+
description: 'Create a readable stream from a string',
|
|
299
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Input string', formInputType: 'textarea', required: true }],
|
|
300
|
+
returnType: 'string', returnDescription: 'Stream handle ID', example: 'stream.fromString "hello"'
|
|
301
|
+
},
|
|
302
|
+
fromArray: {
|
|
303
|
+
description: 'Create a readable stream from an array',
|
|
304
|
+
parameters: [{ name: 'data', dataType: 'array', description: 'Array of chunks', formInputType: 'json', required: true }],
|
|
305
|
+
returnType: 'string', returnDescription: 'Stream handle ID', example: 'stream.fromArray ["chunk1", "chunk2"]'
|
|
306
|
+
},
|
|
307
|
+
active: { description: 'List all active stream handles', parameters: [], returnType: 'array', returnDescription: 'Array of stream IDs', example: 'stream.active' },
|
|
308
|
+
count: { description: 'Count active streams', parameters: [], returnType: 'number', returnDescription: 'Number of active streams', example: 'stream.count' }
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export const StreamModuleMetadata = {
|
|
312
|
+
description: 'Stream operations: Readable, Writable, Transform, Duplex, PassThrough, pipe, pipeline',
|
|
313
|
+
methods: Object.keys(StreamFunctions)
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
export default {
|
|
317
|
+
name: 'stream',
|
|
318
|
+
functions: StreamFunctions,
|
|
319
|
+
functionMetadata: StreamFunctionMetadata,
|
|
320
|
+
moduleMetadata: StreamModuleMetadata,
|
|
321
|
+
global: false
|
|
322
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native string_decoder module for RobinPath.
|
|
3
|
+
* Decode Buffer sequences to strings, handling multi-byte characters.
|
|
4
|
+
*/
|
|
5
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
6
|
+
import { toStr, requireArgs } from './_helpers.js';
|
|
7
|
+
|
|
8
|
+
const _decoders = new Map();
|
|
9
|
+
let _nextId = 1;
|
|
10
|
+
|
|
11
|
+
export const StringDecoderFunctions = {
|
|
12
|
+
|
|
13
|
+
create: (args) => {
|
|
14
|
+
const encoding = toStr(args[0], 'utf-8');
|
|
15
|
+
const id = `decoder_${_nextId++}`;
|
|
16
|
+
_decoders.set(id, new StringDecoder(encoding));
|
|
17
|
+
return id;
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
write: (args) => {
|
|
21
|
+
requireArgs('stringDecoder.write', args, 2);
|
|
22
|
+
const id = toStr(args[0]);
|
|
23
|
+
const decoder = _decoders.get(id);
|
|
24
|
+
if (!decoder) throw new Error(`stringDecoder.write: decoder ${id} not found`);
|
|
25
|
+
const buf = Buffer.from(toStr(args[1]), 'base64');
|
|
26
|
+
return decoder.write(buf);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
end: (args) => {
|
|
30
|
+
requireArgs('stringDecoder.end', args, 1);
|
|
31
|
+
const id = toStr(args[0]);
|
|
32
|
+
const decoder = _decoders.get(id);
|
|
33
|
+
if (!decoder) throw new Error(`stringDecoder.end: decoder ${id} not found`);
|
|
34
|
+
const result = decoder.end();
|
|
35
|
+
_decoders.delete(id);
|
|
36
|
+
return result;
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
decode: (args) => {
|
|
40
|
+
requireArgs('stringDecoder.decode', args, 1);
|
|
41
|
+
const encoding = toStr(args[1], 'utf-8');
|
|
42
|
+
const buf = Buffer.from(toStr(args[0]), 'base64');
|
|
43
|
+
const decoder = new StringDecoder(encoding);
|
|
44
|
+
return decoder.write(buf) + decoder.end();
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
destroy: (args) => {
|
|
48
|
+
requireArgs('stringDecoder.destroy', args, 1);
|
|
49
|
+
const id = toStr(args[0]);
|
|
50
|
+
if (_decoders.has(id)) {
|
|
51
|
+
_decoders.delete(id);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
active: () => Array.from(_decoders.keys())
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const StringDecoderFunctionMetadata = {
|
|
61
|
+
create: {
|
|
62
|
+
description: 'Create a string decoder for an encoding',
|
|
63
|
+
parameters: [{ name: 'encoding', dataType: 'string', description: 'Encoding (utf-8, ascii, base64, hex, etc.)', formInputType: 'text', required: false, defaultValue: 'utf-8' }],
|
|
64
|
+
returnType: 'string', returnDescription: 'Decoder handle ID', example: 'stringDecoder.create "utf-8"'
|
|
65
|
+
},
|
|
66
|
+
write: {
|
|
67
|
+
description: 'Write buffer data through decoder',
|
|
68
|
+
parameters: [
|
|
69
|
+
{ name: 'decoderId', dataType: 'string', description: 'Decoder handle', formInputType: 'text', required: true },
|
|
70
|
+
{ name: 'buffer', dataType: 'string', description: 'Base64-encoded buffer data', formInputType: 'text', required: true }
|
|
71
|
+
],
|
|
72
|
+
returnType: 'string', returnDescription: 'Decoded string', example: 'stringDecoder.write $dec $buf'
|
|
73
|
+
},
|
|
74
|
+
end: {
|
|
75
|
+
description: 'Flush remaining bytes and close decoder',
|
|
76
|
+
parameters: [{ name: 'decoderId', dataType: 'string', description: 'Decoder handle', formInputType: 'text', required: true }],
|
|
77
|
+
returnType: 'string', returnDescription: 'Any remaining decoded bytes', example: 'stringDecoder.end $dec'
|
|
78
|
+
},
|
|
79
|
+
decode: {
|
|
80
|
+
description: 'One-shot decode: buffer to string',
|
|
81
|
+
parameters: [
|
|
82
|
+
{ name: 'buffer', dataType: 'string', description: 'Base64-encoded buffer', formInputType: 'text', required: true },
|
|
83
|
+
{ name: 'encoding', dataType: 'string', description: 'Encoding (default: utf-8)', formInputType: 'text', required: false, defaultValue: 'utf-8' }
|
|
84
|
+
],
|
|
85
|
+
returnType: 'string', returnDescription: 'Decoded string', example: 'stringDecoder.decode $buf "utf-8"'
|
|
86
|
+
},
|
|
87
|
+
destroy: {
|
|
88
|
+
description: 'Destroy a decoder',
|
|
89
|
+
parameters: [{ name: 'decoderId', dataType: 'string', description: 'Decoder handle', formInputType: 'text', required: true }],
|
|
90
|
+
returnType: 'boolean', returnDescription: 'true if destroyed', example: 'stringDecoder.destroy $dec'
|
|
91
|
+
},
|
|
92
|
+
active: { description: 'List active decoders', parameters: [], returnType: 'array', returnDescription: 'Array of decoder IDs', example: 'stringDecoder.active' }
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const StringDecoderModuleMetadata = {
|
|
96
|
+
description: 'String decoder: convert Buffer sequences to strings with multi-byte character handling',
|
|
97
|
+
methods: Object.keys(StringDecoderFunctions)
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default {
|
|
101
|
+
name: 'stringDecoder',
|
|
102
|
+
functions: StringDecoderFunctions,
|
|
103
|
+
functionMetadata: StringDecoderFunctionMetadata,
|
|
104
|
+
moduleMetadata: StringDecoderModuleMetadata,
|
|
105
|
+
global: false
|
|
106
|
+
};
|
package/modules/timer.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native timer module for RobinPath.
|
|
3
|
+
* Sleep, delay, and interval utilities.
|
|
4
|
+
*/
|
|
5
|
+
import { toNum, requireArgs } from './_helpers.js';
|
|
6
|
+
|
|
7
|
+
const _intervals = new Map();
|
|
8
|
+
const _timeouts = new Map();
|
|
9
|
+
let _nextId = 1;
|
|
10
|
+
|
|
11
|
+
export const TimerFunctions = {
|
|
12
|
+
|
|
13
|
+
sleep: (args) => {
|
|
14
|
+
const ms = toNum(args[0], 1000);
|
|
15
|
+
return new Promise(resolve => setTimeout(() => resolve(true), ms));
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
delay: (args) => {
|
|
19
|
+
const ms = toNum(args[0], 1000);
|
|
20
|
+
return new Promise(resolve => setTimeout(() => resolve(true), ms));
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
setTimeout: (args, callback) => {
|
|
24
|
+
requireArgs('timer.setTimeout', args, 1);
|
|
25
|
+
const ms = toNum(args[0], 1000);
|
|
26
|
+
const id = `timeout_${_nextId++}`;
|
|
27
|
+
const handle = setTimeout(() => {
|
|
28
|
+
_timeouts.delete(id);
|
|
29
|
+
if (callback) callback([id]);
|
|
30
|
+
}, ms);
|
|
31
|
+
_timeouts.set(id, handle);
|
|
32
|
+
return id;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
setInterval: (args, callback) => {
|
|
36
|
+
requireArgs('timer.setInterval', args, 1);
|
|
37
|
+
const ms = toNum(args[0], 1000);
|
|
38
|
+
const id = `interval_${_nextId++}`;
|
|
39
|
+
const handle = setInterval(() => {
|
|
40
|
+
if (callback) callback([id]);
|
|
41
|
+
}, ms);
|
|
42
|
+
_intervals.set(id, handle);
|
|
43
|
+
return id;
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
clearTimeout: (args) => {
|
|
47
|
+
requireArgs('timer.clearTimeout', args, 1);
|
|
48
|
+
const id = String(args[0]);
|
|
49
|
+
const handle = _timeouts.get(id);
|
|
50
|
+
if (handle) {
|
|
51
|
+
clearTimeout(handle);
|
|
52
|
+
_timeouts.delete(id);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
clearInterval: (args) => {
|
|
59
|
+
requireArgs('timer.clearInterval', args, 1);
|
|
60
|
+
const id = String(args[0]);
|
|
61
|
+
const handle = _intervals.get(id);
|
|
62
|
+
if (handle) {
|
|
63
|
+
clearInterval(handle);
|
|
64
|
+
_intervals.delete(id);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
clearAll: () => {
|
|
71
|
+
for (const handle of _timeouts.values()) clearTimeout(handle);
|
|
72
|
+
for (const handle of _intervals.values()) clearInterval(handle);
|
|
73
|
+
_timeouts.clear();
|
|
74
|
+
_intervals.clear();
|
|
75
|
+
return true;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
active: () => {
|
|
79
|
+
return {
|
|
80
|
+
timeouts: Array.from(_timeouts.keys()),
|
|
81
|
+
intervals: Array.from(_intervals.keys())
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
measure: async (args, callback) => {
|
|
86
|
+
const start = process.hrtime.bigint();
|
|
87
|
+
if (callback) await callback([]);
|
|
88
|
+
const end = process.hrtime.bigint();
|
|
89
|
+
const ms = Number(end - start) / 1_000_000;
|
|
90
|
+
return ms;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
timestamp: () => Date.now(),
|
|
94
|
+
|
|
95
|
+
now: () => performance.now()
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const TimerFunctionMetadata = {
|
|
99
|
+
sleep: {
|
|
100
|
+
description: 'Pause execution for a duration',
|
|
101
|
+
parameters: [{ name: 'milliseconds', dataType: 'number', description: 'Duration in ms (default: 1000)', formInputType: 'number', required: false, defaultValue: 1000 }],
|
|
102
|
+
returnType: 'boolean', returnDescription: 'true when done', example: 'timer.sleep 2000'
|
|
103
|
+
},
|
|
104
|
+
delay: {
|
|
105
|
+
description: 'Alias for sleep',
|
|
106
|
+
parameters: [{ name: 'milliseconds', dataType: 'number', description: 'Duration in ms', formInputType: 'number', required: false, defaultValue: 1000 }],
|
|
107
|
+
returnType: 'boolean', returnDescription: 'true when done', example: 'timer.delay 500'
|
|
108
|
+
},
|
|
109
|
+
setTimeout: {
|
|
110
|
+
description: 'Execute callback after a delay',
|
|
111
|
+
parameters: [{ name: 'milliseconds', dataType: 'number', description: 'Delay in ms', formInputType: 'number', required: true }],
|
|
112
|
+
returnType: 'string', returnDescription: 'Timeout handle ID', example: 'timer.setTimeout 1000'
|
|
113
|
+
},
|
|
114
|
+
setInterval: {
|
|
115
|
+
description: 'Execute callback repeatedly at an interval',
|
|
116
|
+
parameters: [{ name: 'milliseconds', dataType: 'number', description: 'Interval in ms', formInputType: 'number', required: true }],
|
|
117
|
+
returnType: 'string', returnDescription: 'Interval handle ID', example: 'timer.setInterval 5000'
|
|
118
|
+
},
|
|
119
|
+
clearTimeout: {
|
|
120
|
+
description: 'Cancel a pending timeout',
|
|
121
|
+
parameters: [{ name: 'id', dataType: 'string', description: 'Timeout handle ID', formInputType: 'text', required: true }],
|
|
122
|
+
returnType: 'boolean', returnDescription: 'true if cleared', example: 'timer.clearTimeout $id'
|
|
123
|
+
},
|
|
124
|
+
clearInterval: {
|
|
125
|
+
description: 'Cancel a repeating interval',
|
|
126
|
+
parameters: [{ name: 'id', dataType: 'string', description: 'Interval handle ID', formInputType: 'text', required: true }],
|
|
127
|
+
returnType: 'boolean', returnDescription: 'true if cleared', example: 'timer.clearInterval $id'
|
|
128
|
+
},
|
|
129
|
+
clearAll: {
|
|
130
|
+
description: 'Cancel all active timeouts and intervals',
|
|
131
|
+
parameters: [],
|
|
132
|
+
returnType: 'boolean', returnDescription: 'true', example: 'timer.clearAll'
|
|
133
|
+
},
|
|
134
|
+
active: {
|
|
135
|
+
description: 'List all active timers',
|
|
136
|
+
parameters: [],
|
|
137
|
+
returnType: 'object', returnDescription: 'Object with timeouts and intervals arrays', example: 'timer.active'
|
|
138
|
+
},
|
|
139
|
+
measure: {
|
|
140
|
+
description: 'Measure execution time of a callback in milliseconds',
|
|
141
|
+
parameters: [],
|
|
142
|
+
returnType: 'number', returnDescription: 'Execution time in ms', example: 'timer.measure'
|
|
143
|
+
},
|
|
144
|
+
timestamp: {
|
|
145
|
+
description: 'Get current Unix timestamp in milliseconds',
|
|
146
|
+
parameters: [],
|
|
147
|
+
returnType: 'number', returnDescription: 'Unix timestamp (ms)', example: 'timer.timestamp'
|
|
148
|
+
},
|
|
149
|
+
now: {
|
|
150
|
+
description: 'Get high-resolution monotonic time (performance.now)',
|
|
151
|
+
parameters: [],
|
|
152
|
+
returnType: 'number', returnDescription: 'Time in ms', example: 'timer.now'
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const TimerModuleMetadata = {
|
|
157
|
+
description: 'Timer operations: sleep, delay, setTimeout, setInterval, measure, and timestamp',
|
|
158
|
+
methods: Object.keys(TimerFunctions)
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default {
|
|
162
|
+
name: 'timer',
|
|
163
|
+
functions: TimerFunctions,
|
|
164
|
+
functionMetadata: TimerFunctionMetadata,
|
|
165
|
+
moduleMetadata: TimerModuleMetadata,
|
|
166
|
+
global: false
|
|
167
|
+
};
|