@swipt/cli 0.1.0-nightly.42 → 0.1.0-nightly.44

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/swipt.mjs CHANGED
@@ -12,9 +12,24 @@ const globalOptions = {
12
12
  help: { type: "boolean", short: "h", help: "help" },
13
13
  };
14
14
 
15
- const deviceOption = { device: { type: "string", short: "d", value: "NAME|URL", help: "the device to use (SWIPT_DEVICE)" } };
15
+ const deviceOption = {
16
+ device: {
17
+ type: "string",
18
+ short: "d",
19
+ value: "NAME|URL",
20
+ help: "the device to use (SWIPT_DEVICE)",
21
+ },
22
+ };
16
23
 
17
- const durationUnits = { ns: 1e-6, us: 1e-3, "µs": 1e-3, ms: 1, s: 1000, m: 60_000, h: 3_600_000 };
24
+ const durationUnits = {
25
+ ns: 1e-6,
26
+ us: 1e-3,
27
+ µs: 1e-3,
28
+ ms: 1,
29
+ s: 1000,
30
+ m: 60_000,
31
+ h: 3_600_000,
32
+ };
18
33
 
19
34
  function parseDuration(text, option) {
20
35
  if (text === undefined) {
@@ -28,7 +43,9 @@ function parseDuration(text, option) {
28
43
  while (rest !== "") {
29
44
  const match = /^(\d+(?:\.\d*)?|\.\d+)(ns|us|µs|ms|s|m|h)/.exec(rest);
30
45
  if (!match) {
31
- throw new UsageError(`invalid argument ${JSON.stringify(text)} for ${option}: it is a duration such as 30s or 1m30s`);
46
+ throw new UsageError(
47
+ `invalid argument ${JSON.stringify(text)} for ${option}: it is a duration such as 30s or 1m30s`,
48
+ );
32
49
  }
33
50
  total += Number(match[1]) * durationUnits[match[2]];
34
51
  rest = rest.slice(match[0].length);
@@ -41,7 +58,9 @@ function parseCount(text, option) {
41
58
  return undefined;
42
59
  }
43
60
  if (!/^\d+$/.test(text) || Number(text) > 0xffffffff) {
44
- throw new UsageError(`invalid argument ${JSON.stringify(text)} for ${option}: it is a number`);
61
+ throw new UsageError(
62
+ `invalid argument ${JSON.stringify(text)} for ${option}: it is a number`,
63
+ );
45
64
  }
46
65
  return Number(text);
47
66
  }
@@ -52,11 +71,50 @@ const dataCommands = (operand, files) => {
52
71
  name: "data",
53
72
  short: `Manage the files in the data directory of ${operand === "" ? "the REPL" : "a swiplet"}`,
54
73
  commands: [
55
- { name: "list", use: `list ${operand}[PATH]`, short: "List data files", args: [before, before + 1], options: { recursive: { type: "boolean", help: "include subdirectories" } }, run: files.list },
56
- { name: "read", use: `read ${operand}PATH`, short: "Read a data file or directory", args: [before + 1, before + 1], options: { output: { type: "string", value: "PATH", help: "destination" } }, run: files.read },
57
- { name: "write", use: `write ${operand}PATH [FILE | -]`, short: "Write a data file", args: [before + 1, before + 2], run: files.write },
58
- { name: "remove", use: `remove ${operand}PATH`, short: "Remove a data file", args: [before + 1, before + 1], options: { recursive: { type: "boolean", help: "include directory contents" } }, run: files.remove },
59
- { name: "clear", use: `clear ${operand}`.trimEnd(), short: "Remove every data file, keeping the data directory", args: [before, before], run: files.clear },
74
+ {
75
+ name: "list",
76
+ use: `list ${operand}[PATH]`,
77
+ short: "List data files",
78
+ args: [before, before + 1],
79
+ options: {
80
+ recursive: { type: "boolean", help: "include subdirectories" },
81
+ },
82
+ run: files.list,
83
+ },
84
+ {
85
+ name: "read",
86
+ use: `read ${operand}PATH`,
87
+ short: "Read a data file or directory",
88
+ args: [before + 1, before + 1],
89
+ options: {
90
+ output: { type: "string", value: "PATH", help: "destination" },
91
+ },
92
+ run: files.read,
93
+ },
94
+ {
95
+ name: "write",
96
+ use: `write ${operand}PATH [FILE | -]`,
97
+ short: "Write a data file",
98
+ args: [before + 1, before + 2],
99
+ run: files.write,
100
+ },
101
+ {
102
+ name: "remove",
103
+ use: `remove ${operand}PATH`,
104
+ short: "Remove a data file",
105
+ args: [before + 1, before + 1],
106
+ options: {
107
+ recursive: { type: "boolean", help: "include directory contents" },
108
+ },
109
+ run: files.remove,
110
+ },
111
+ {
112
+ name: "clear",
113
+ use: `clear ${operand}`.trimEnd(),
114
+ short: "Remove every data file, keeping the data directory",
115
+ args: [before, before],
116
+ run: files.clear,
117
+ },
60
118
  ],
61
119
  };
62
120
  };
@@ -86,17 +144,28 @@ const commands = [
86
144
  {
87
145
  name: "source",
88
146
  use: "source [SWIPLET...]",
89
- short: 'Gather the built swiplets of this directory\'s workspaces, or the given ones, into dist/ with a swiplets.json described by "source" in package.json',
147
+ short:
148
+ 'Gather the built swiplets of this directory\'s workspaces, or the given ones, into dist/ with a swiplets.json described by "source" in package.json',
90
149
  args: [0, Infinity],
91
150
  run: ({ args }) => sourceCommand(process.cwd(), args),
92
151
  },
93
- { name: "deploy", use: "deploy [PROJECT]", short: "Build the project and install it on the device", args: [0, 1], options: deviceOption, run: device.deployProject },
152
+ {
153
+ name: "deploy",
154
+ use: "deploy [PROJECT]",
155
+ short: "Build the project and install it on the device",
156
+ args: [0, 1],
157
+ options: deviceOption,
158
+ run: device.deployProject,
159
+ },
94
160
  {
95
161
  name: "develop",
96
162
  use: "develop [PROJECT]",
97
163
  short: "Build, install and follow a swiplet project as it changes",
98
164
  args: [0, 1],
99
- options: { ...deviceOption, "no-logs": { type: "boolean", help: "without logs" } },
165
+ options: {
166
+ ...deviceOption,
167
+ "no-logs": { type: "boolean", help: "without logs" },
168
+ },
100
169
  run: device.develop,
101
170
  },
102
171
  ],
@@ -105,9 +174,27 @@ const commands = [
105
174
  name: "device",
106
175
  short: "Manage the devices and read their logs",
107
176
  commands: [
108
- { name: "list", short: "List the devices, adding the ones found on the local network", args: [0, 0], run: connection.list },
109
- { name: "add", use: "add URL", short: "Add a device by its URL; needed when it is remote or requires a token", args: [1, 1], run: connection.add },
110
- { name: "remove", use: "remove NAME", short: "Forget a device", args: [1, 1], run: connection.remove },
177
+ {
178
+ name: "list",
179
+ short: "List the devices, adding the ones found on the local network",
180
+ args: [0, 0],
181
+ run: connection.list,
182
+ },
183
+ {
184
+ name: "add",
185
+ use: "add URL",
186
+ short:
187
+ "Add a device by its URL; needed when it is remote or requires a token",
188
+ args: [1, 1],
189
+ run: connection.add,
190
+ },
191
+ {
192
+ name: "remove",
193
+ use: "remove NAME",
194
+ short: "Forget a device",
195
+ args: [1, 1],
196
+ run: connection.remove,
197
+ },
111
198
  {
112
199
  name: "logs",
113
200
  short: "Print the logs",
@@ -115,12 +202,25 @@ const commands = [
115
202
  options: {
116
203
  ...deviceOption,
117
204
  follow: { type: "boolean", help: "follow new entries" },
118
- tail: { type: "string", value: "COUNT", help: "number of past entries (default 100)" },
205
+ tail: {
206
+ type: "string",
207
+ value: "COUNT",
208
+ help: "number of past entries (default 100)",
209
+ },
119
210
  since: { type: "string", value: "DURATION", help: "maximum age" },
120
- level: { type: "string", value: "LEVEL", help: "minimum level (default info)" },
211
+ level: {
212
+ type: "string",
213
+ value: "LEVEL",
214
+ help: "minimum level (default info)",
215
+ },
121
216
  swiplet: { type: "string", value: "ID", help: "swiplet ID" },
122
217
  },
123
- parse: options => ({ ...options, tail: parseCount(options.tail, "--tail"), since: parseDuration(options.since, "--since"), level: options.level ?? "info" }),
218
+ parse: (options) => ({
219
+ ...options,
220
+ tail: parseCount(options.tail, "--tail"),
221
+ since: parseDuration(options.since, "--since"),
222
+ level: options.level ?? "info",
223
+ }),
124
224
  run: device.logs,
125
225
  },
126
226
  ],
@@ -130,25 +230,74 @@ const commands = [
130
230
  short: "Manage the swiplets on the device",
131
231
  options: deviceOption,
132
232
  commands: [
133
- { name: "list", short: "List the swiplets", args: [0, 0], run: device.list },
134
- { name: "show", use: "show ID", short: "Show a swiplet", args: [1, 1], run: device.show },
135
- { name: "install", use: "install ARCHIVE", short: "Install a swiplet archive", args: [1, 1], run: device.install },
136
- { name: "remove", use: "remove ID", short: "Remove a swiplet", args: [1, 1], run: device.remove },
233
+ {
234
+ name: "list",
235
+ short: "List the swiplets",
236
+ args: [0, 0],
237
+ run: device.list,
238
+ },
239
+ {
240
+ name: "show",
241
+ use: "show ID",
242
+ short: "Show a swiplet",
243
+ args: [1, 1],
244
+ run: device.show,
245
+ },
246
+ {
247
+ name: "install",
248
+ use: "install ARCHIVE",
249
+ short: "Install a swiplet archive",
250
+ args: [1, 1],
251
+ run: device.install,
252
+ },
253
+ {
254
+ name: "remove",
255
+ use: "remove ID",
256
+ short: "Remove a swiplet",
257
+ args: [1, 1],
258
+ run: device.remove,
259
+ },
137
260
  {
138
261
  name: "call",
139
262
  use: "call ID ACTION",
140
263
  short: "Call a swiplet action",
141
264
  args: [2, 2],
142
- options: { input: { type: "string", value: "JSON", help: "input JSON, @FILE or -" } },
265
+ options: {
266
+ input: {
267
+ type: "string",
268
+ value: "JSON",
269
+ help: "input JSON, @FILE or -",
270
+ },
271
+ },
143
272
  run: device.call,
144
273
  },
145
274
  {
146
275
  name: "service",
147
276
  short: "Manage swiplet services",
148
277
  commands: [
149
- { name: "enable", use: "enable ID", short: "Turn on a swiplet service", args: [1, 1], options: { watch: { type: "boolean", help: "watch the state" } }, run: device.setService(true) },
150
- { name: "disable", use: "disable ID", short: "Turn off a swiplet service", args: [1, 1], options: { watch: { type: "boolean", help: "watch the state" } }, run: device.setService(false) },
151
- { name: "watch", use: "watch [ID]", short: "Watch swiplet services", args: [0, 1], run: device.serviceWatch },
278
+ {
279
+ name: "enable",
280
+ use: "enable ID",
281
+ short: "Turn on a swiplet service",
282
+ args: [1, 1],
283
+ options: { watch: { type: "boolean", help: "watch the state" } },
284
+ run: device.setService(true),
285
+ },
286
+ {
287
+ name: "disable",
288
+ use: "disable ID",
289
+ short: "Turn off a swiplet service",
290
+ args: [1, 1],
291
+ options: { watch: { type: "boolean", help: "watch the state" } },
292
+ run: device.setService(false),
293
+ },
294
+ {
295
+ name: "watch",
296
+ use: "watch [ID]",
297
+ short: "Watch swiplet services",
298
+ args: [0, 1],
299
+ run: device.serviceWatch,
300
+ },
152
301
  ],
153
302
  },
154
303
  dataCommands("ID ", device.swipletData),
@@ -169,25 +318,56 @@ const commands = [
169
318
  "FILE, or standard input given as - or no argument, is a program; only its console output and thrown error are printed, and the global args holds its ARGs.",
170
319
  'Top-level await is available, and top-level bindings persist across runs until "swipt repl reset".',
171
320
  "An image given to show(), or as the completion value of CODE, is saved as a PNG file and printed as its path.",
172
- 'There is no module system, so import is unavailable; the globals are accessibility, input, device, screen, image, dialog, dataDirectory, show and sleep, declared by "swipt repl api".',
173
- 'dataDirectory is the directory that "swipt repl data" manages.',
321
+ 'The globals are accessibility, input, device, screen, image, dialog, dataDirectory, show and sleep; import declarations and import() load the modules that swiplets get from Swipt; "swipt repl api" declares both.',
322
+ 'dataDirectory, which also holds the expo-sqlite databases, is the directory that "swipt repl data" manages.',
174
323
  ],
175
324
  args: [0, Infinity],
176
325
  passthrough: device.isProgram,
177
- options: { output: { type: "string", value: "DIRECTORY", help: "image directory (default: a new temporary directory)" } },
326
+ options: {
327
+ output: {
328
+ type: "string",
329
+ value: "DIRECTORY",
330
+ help: "image directory (default: a new temporary directory)",
331
+ },
332
+ },
178
333
  run: device.run,
179
334
  },
180
- { name: "reset", short: "Reset the session", args: [0, 0], run: device.reset },
181
- { name: "api", short: "Print the TypeScript declarations of the globals", args: [0, 0], run: device.api },
335
+ {
336
+ name: "reset",
337
+ short: "Reset the session",
338
+ args: [0, 0],
339
+ run: device.reset,
340
+ },
341
+ {
342
+ name: "api",
343
+ short: "Print the TypeScript declarations of the globals and modules",
344
+ args: [0, 0],
345
+ run: device.api,
346
+ },
182
347
  dataCommands("", device.replData),
183
348
  ],
184
349
  },
185
- { name: "version", short: "Print the version", args: [0, 0], run: device.printVersion },
350
+ {
351
+ name: "version",
352
+ short: "Print the version",
353
+ args: [0, 0],
354
+ run: device.printVersion,
355
+ },
186
356
  ];
187
357
 
188
- const root = { name: "swipt", short: "Operate a device through Swipt", commands };
358
+ const root = {
359
+ name: "swipt",
360
+ short: "Operate a device through Swipt",
361
+ commands,
362
+ };
189
363
 
190
- const parseOptions = options => Object.fromEntries(Object.entries(options ?? {}).map(([name, option]) => [name, { type: option.type, ...(option.short ? { short: option.short } : {}) }]));
364
+ const parseOptions = (options) =>
365
+ Object.fromEntries(
366
+ Object.entries(options ?? {}).map(([name, option]) => [
367
+ name,
368
+ { type: option.type, ...(option.short ? { short: option.short } : {}) },
369
+ ]),
370
+ );
191
371
 
192
372
  function allOptions(command, collected = {}) {
193
373
  Object.assign(collected, parseOptions(command.options));
@@ -199,31 +379,61 @@ function allOptions(command, collected = {}) {
199
379
 
200
380
  function usage(trail) {
201
381
  const command = trail.at(-1);
202
- const name = trail.map(it => it.name).join(" ");
382
+ const name = trail.map((it) => it.name).join(" ");
203
383
  const lines = [command.short, ""];
204
384
  if (command.commands) {
205
385
  lines.push(`usage: ${name} COMMAND`, "", "commands:");
206
- lines.push(...columns(command.commands.map(child => [` ${child.use ?? child.name}`, child.short])).split("\n"));
386
+ lines.push(
387
+ ...columns(
388
+ command.commands.map((child) => [
389
+ ` ${child.use ?? child.name}`,
390
+ child.short,
391
+ ]),
392
+ ).split("\n"),
393
+ );
207
394
  } else {
208
- lines.push(`usage: ${trail.slice(0, -1).map(it => it.name).join(" ")} ${command.use ?? command.name}`.replace(/ +/g, " "));
395
+ lines.push(
396
+ `usage: ${trail
397
+ .slice(0, -1)
398
+ .map((it) => it.name)
399
+ .join(" ")} ${command.use ?? command.name}`.replace(/ +/g, " "),
400
+ );
209
401
  }
210
402
  if (command.description) {
211
403
  lines.push("", ...command.description);
212
404
  }
213
- const options = { ...trail.reduce((found, it) => ({ ...found, ...it.options }), {}), ...globalOptions };
405
+ const options = {
406
+ ...trail.reduce((found, it) => ({ ...found, ...it.options }), {}),
407
+ ...globalOptions,
408
+ };
214
409
  lines.push("", "options:");
215
- lines.push(...columns(Object.entries(options).map(([option, it]) => [` ${it.short ? `-${it.short}, ` : ""}--${option}${it.value ? ` ${it.value}` : ""}`, it.help])).split("\n"));
410
+ lines.push(
411
+ ...columns(
412
+ Object.entries(options).map(([option, it]) => [
413
+ ` ${it.short ? `-${it.short}, ` : ""}--${option}${it.value ? ` ${it.value}` : ""}`,
414
+ it.help,
415
+ ]),
416
+ ).split("\n"),
417
+ );
216
418
  return lines.join("\n");
217
419
  }
218
420
 
219
421
  function columns(rows) {
220
- const width = Math.max(...rows.map(row => row[0].length));
221
- return rows.map(row => `${row[0].padEnd(width + 2)}${row[1]}`).join("\n");
422
+ const width = Math.max(...rows.map((row) => row[0].length));
423
+ return rows.map((row) => `${row[0].padEnd(width + 2)}${row[1]}`).join("\n");
222
424
  }
223
425
 
224
426
  function resolve(argv) {
225
- const loose = parseArgs({ args: argv, options: { ...parseOptions(globalOptions), ...allOptions(root) }, allowPositionals: true, strict: false, tokens: true });
226
- const positionals = loose.tokens.filter(token => token.kind === "positional");
427
+ const loose = parseArgs({
428
+ args: argv,
429
+ options: { ...parseOptions(globalOptions), ...allOptions(root) },
430
+ allowPositionals: true,
431
+ strict: false,
432
+ tokens: true,
433
+ });
434
+ const positionals = loose.tokens.filter(
435
+ (token) => token.kind === "positional",
436
+ );
227
437
  const trail = [root];
228
438
  let consumed = 0;
229
439
  let help = positionals[0]?.value === "help";
@@ -239,31 +449,56 @@ function resolve(argv) {
239
449
  }
240
450
  throw new UsageError("a command is missing");
241
451
  }
242
- const found = current.commands.find(it => it.name === word);
452
+ const found = current.commands.find((it) => it.name === word);
243
453
  if (!found) {
244
- throw new UsageError(`unknown command ${JSON.stringify(word)} for ${JSON.stringify(trail.map(it => it.name).join(" "))}`);
454
+ throw new UsageError(
455
+ `unknown command ${JSON.stringify(word)} for ${JSON.stringify(trail.map((it) => it.name).join(" "))}`,
456
+ );
245
457
  }
246
458
  trail.push(found);
247
459
  consumed++;
248
460
  }
249
461
  const command = trail.at(-1);
250
- const options = trail.reduce((found, it) => ({ ...found, ...it.options }), {});
462
+ const options = trail.reduce(
463
+ (found, it) => ({ ...found, ...it.options }),
464
+ {},
465
+ );
251
466
  const first = positionals[consumed];
252
- const verbatim = first !== undefined && command.passthrough?.(first.value) ? first.index + 1 : argv.length;
467
+ const verbatim =
468
+ first !== undefined && command.passthrough?.(first.value)
469
+ ? first.index + 1
470
+ : argv.length;
253
471
  let parsed;
254
472
  try {
255
- parsed = parseArgs({ args: argv.slice(0, verbatim), options: { ...parseOptions(globalOptions), ...parseOptions(options) }, allowPositionals: true, strict: true });
473
+ parsed = parseArgs({
474
+ args: argv.slice(0, verbatim),
475
+ options: { ...parseOptions(globalOptions), ...parseOptions(options) },
476
+ allowPositionals: true,
477
+ strict: true,
478
+ });
256
479
  } catch (error) {
257
480
  const option = error.message.match(/^Unknown option '(-[^']*)'/)?.[1];
258
- throw new UsageError(option === undefined ? error.message : `${option} is not an option of ${JSON.stringify(trail.map(it => it.name).join(" "))}`);
481
+ throw new UsageError(
482
+ option === undefined
483
+ ? error.message
484
+ : `${option} is not an option of ${JSON.stringify(trail.map((it) => it.name).join(" "))}`,
485
+ );
259
486
  }
260
487
  if (parsed.values.help) {
261
488
  help = true;
262
489
  }
263
490
  const args = [...parsed.positionals.slice(consumed), ...argv.slice(verbatim)];
264
- if (!help && (args.length < command.args[0] || args.length > command.args[1])) {
491
+ if (
492
+ !help &&
493
+ (args.length < command.args[0] || args.length > command.args[1])
494
+ ) {
265
495
  const [least, most] = command.args;
266
- const expected = least === most ? `${least} arg(s)` : most === Infinity ? `at least ${least} arg(s)` : `between ${least} and ${most} arg(s)`;
496
+ const expected =
497
+ least === most
498
+ ? `${least} arg(s)`
499
+ : most === Infinity
500
+ ? `at least ${least} arg(s)`
501
+ : `between ${least} and ${most} arg(s)`;
267
502
  throw new UsageError(`accepts ${expected}, received ${args.length}`);
268
503
  }
269
504
  return { trail, help, args, values: parsed.values };
@@ -288,7 +523,10 @@ function printError(error, target, operation) {
288
523
  break;
289
524
  }
290
525
  }
291
- console.error("error:", operation === undefined ? message : `${operation}: ${message}`);
526
+ console.error(
527
+ "error:",
528
+ operation === undefined ? message : `${operation}: ${message}`,
529
+ );
292
530
  if (exit === 2) {
293
531
  console.error('run "swipt help" for usage');
294
532
  }
@@ -308,8 +546,16 @@ async function main() {
308
546
  return;
309
547
  }
310
548
  const command = resolved.trail.at(-1);
311
- const values = command.parse ? command.parse(resolved.values) : resolved.values;
312
- await command.run({ args: resolved.args, options: values, target, signal: interrupt.signal, printError: (error, operation) => printError(error, target, operation) });
549
+ const values = command.parse
550
+ ? command.parse(resolved.values)
551
+ : resolved.values;
552
+ await command.run({
553
+ args: resolved.args,
554
+ options: values,
555
+ target,
556
+ signal: interrupt.signal,
557
+ printError: (error, operation) => printError(error, target, operation),
558
+ });
313
559
  } catch (error) {
314
560
  if (error instanceof FalseResult) {
315
561
  process.exitCode = 1;