@inhuman.tools/policy 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE.md +7 -0
  2. package/dist/check.d.ts +1 -2
  3. package/dist/check.d.ts.map +1 -1
  4. package/dist/check.js +0 -3
  5. package/dist/check.js.map +1 -1
  6. package/package.json +8 -8
  7. package/src/check.ts +0 -10
  8. package/dist/decide.d.ts +0 -64
  9. package/dist/decide.d.ts.map +0 -1
  10. package/dist/decide.js +0 -482
  11. package/dist/decide.js.map +0 -1
  12. package/dist/error-with-invariant.d.ts +0 -9
  13. package/dist/error-with-invariant.d.ts.map +0 -1
  14. package/dist/error-with-invariant.js +0 -12
  15. package/dist/error-with-invariant.js.map +0 -1
  16. package/dist/license-law.d.ts +0 -43
  17. package/dist/license-law.d.ts.map +0 -1
  18. package/dist/license-law.js +0 -129
  19. package/dist/license-law.js.map +0 -1
  20. package/dist/license.d.ts +0 -3
  21. package/dist/license.d.ts.map +0 -1
  22. package/dist/license.js +0 -2
  23. package/dist/license.js.map +0 -1
  24. package/dist/peel-cwd.d.ts +0 -8
  25. package/dist/peel-cwd.d.ts.map +0 -1
  26. package/dist/peel-cwd.js +0 -120
  27. package/dist/peel-cwd.js.map +0 -1
  28. package/dist/prep.d.ts +0 -58
  29. package/dist/prep.d.ts.map +0 -1
  30. package/dist/prep.js +0 -690
  31. package/dist/prep.js.map +0 -1
  32. package/dist/schemas/tirith.d.ts +0 -58
  33. package/dist/schemas/tirith.d.ts.map +0 -1
  34. package/dist/schemas/tirith.js +0 -23
  35. package/dist/schemas/tirith.js.map +0 -1
  36. package/dist/tirith-units.d.ts +0 -2
  37. package/dist/tirith-units.d.ts.map +0 -1
  38. package/dist/tirith-units.js +0 -227
  39. package/dist/tirith-units.js.map +0 -1
  40. package/dist/tirith.d.ts +0 -24
  41. package/dist/tirith.d.ts.map +0 -1
  42. package/dist/tirith.js +0 -311
  43. package/dist/tirith.js.map +0 -1
package/dist/prep.js DELETED
@@ -1,690 +0,0 @@
1
- /**
2
- * Question prep: unbash AST → prepared questions + unparsed acts.
3
- * Hermetic — no PATH, no process.env, no execution. Word.parts is a lazy
4
- * getter; this file always reads it. Nested parse errors live on the nested
5
- * script, not the root.
6
- *
7
- * Partition builds I: a known value-flag attaches; an unknown flag followed
8
- * by a non-dash, non-`--` token forks attached and boolean readings.
9
- * `valueFlagsFor(argv0, suffix)` sees the leading positional spine — not
10
- * every descendant under argv0. `=value` and glued shorts stay one reading.
11
- * `--` never attaches as a value. Cap 16 is open ruling 5; overflow is ⊤I.
12
- * A nameless command still emits its redirects. `xargs -a FILE` is a read
13
- * of FILE as well as the inner command.
14
- */
15
- import { parse, } from "unbash";
16
- import { resolveKnown } from "./identity.js";
17
- const TRANSPARENT_FLAG_ARGS = {
18
- xargs: ["-a", "-d", "-E", "-e", "-I", "-i", "-L", "-l", "-n", "-P", "-s"],
19
- env: ["-C", "-S", "-u"],
20
- timeout: ["-k", "--kill-after", "-s", "--signal"],
21
- nice: ["-n"],
22
- watch: ["-n", "-d", "-t", "-e"],
23
- time: ["-p"],
24
- stdbuf: ["-i", "-o", "-e"],
25
- exec: ["-a"],
26
- sudo: ["-C", "-D", "-g", "-l", "-p", "-r", "-U", "-u"],
27
- doas: ["-C", "-u"],
28
- };
29
- /** Duration is the first positional; COMMAND follows. */
30
- const HOF_SKIP_POSITIONALS = { timeout: 1 };
31
- const PARTLESS_FORBIDDEN = /[*?[\\$`]/;
32
- export function prep(source, opts) {
33
- const script = parse(source);
34
- const errors = [...(script.errors ?? [])];
35
- if (errors.length > 0) {
36
- return { questions: [], unparsed: [{ via: "parse-error", source }], errors, redirects: [] };
37
- }
38
- const ctx = {
39
- questions: [],
40
- unparsed: [],
41
- wrapChain: [],
42
- elevated: false,
43
- envDelta: {},
44
- source,
45
- valueFlags: opts?.valueFlags,
46
- valueFlagsFor: opts?.valueFlagsFor,
47
- which: opts?.which,
48
- accessReads: [],
49
- orphans: [],
50
- };
51
- walkScript(script, ctx, { background: false, stdinIsPipe: false, redirects: [] });
52
- return { questions: ctx.questions, unparsed: ctx.unparsed, errors, redirects: ctx.orphans };
53
- }
54
- function walkScript(script, ctx, frame) {
55
- if (script.errors !== undefined && script.errors.length > 0) {
56
- ctx.unparsed.push({ via: "parse-error", source: ctx.source.slice(script.pos, script.end) });
57
- return;
58
- }
59
- for (const statement of script.commands)
60
- walkStatement(statement, ctx, frame);
61
- }
62
- function walkStatement(statement, ctx, frame) {
63
- walkNode(statement.command, ctx, {
64
- background: statement.background === true || frame.background,
65
- stdinIsPipe: frame.stdinIsPipe,
66
- redirects: [...frame.redirects, ...statement.redirects],
67
- });
68
- }
69
- function walkNode(node, ctx, frame) {
70
- switch (node.type) {
71
- case "Statement":
72
- walkStatement(node, ctx, frame);
73
- return;
74
- case "CompoundList":
75
- for (const statement of node.commands)
76
- walkStatement(statement, ctx, frame);
77
- return;
78
- case "Pipeline": {
79
- const wrap = node.time === true ? withWrap(ctx, "time") : ctx;
80
- for (const [i, child] of node.commands.entries()) {
81
- walkNode(child, wrap, { ...frame, stdinIsPipe: frame.stdinIsPipe || i > 0 });
82
- }
83
- return;
84
- }
85
- case "AndOr":
86
- for (const child of node.commands)
87
- walkNode(child, ctx, frame);
88
- return;
89
- case "Subshell":
90
- case "BraceGroup":
91
- walkNode(node.body, ctx, frame);
92
- return;
93
- case "If":
94
- case "For":
95
- case "ArithmeticFor":
96
- case "While":
97
- case "Select":
98
- case "Case":
99
- ctx.unparsed.push({ via: "control-flow", source: slice(ctx, node) });
100
- walkControlInner(node, ctx, frame);
101
- return;
102
- case "Function":
103
- ctx.unparsed.push({ via: "function", source: slice(ctx, node) });
104
- return;
105
- case "Coproc":
106
- ctx.unparsed.push({ via: "coproc", source: slice(ctx, node) });
107
- walkNode(node.body, ctx, frame);
108
- return;
109
- case "TestCommand":
110
- ctx.unparsed.push({ via: "test", source: slice(ctx, node) });
111
- walkTestWords(node.expression, ctx, frame);
112
- return;
113
- case "ArithmeticCommand":
114
- ctx.unparsed.push({ via: "arithmetic", source: slice(ctx, node) });
115
- walkArith(node.expression, ctx, frame);
116
- return;
117
- case "Command":
118
- walkCommand(node, ctx, frame);
119
- return;
120
- }
121
- }
122
- function walkControlInner(node, ctx, frame) {
123
- switch (node.type) {
124
- case "If":
125
- walkNode(node.clause, ctx, frame);
126
- walkNode(node.then, ctx, frame);
127
- if (node.else !== undefined)
128
- walkNode(node.else, ctx, frame);
129
- return;
130
- case "For":
131
- case "Select":
132
- walkWord(node.name, ctx, frame);
133
- for (const word of node.wordlist)
134
- walkWord(word, ctx, frame);
135
- walkNode(node.body, ctx, frame);
136
- return;
137
- case "ArithmeticFor":
138
- walkArith(node.initialize, ctx, frame);
139
- walkArith(node.test, ctx, frame);
140
- walkArith(node.update, ctx, frame);
141
- walkNode(node.body, ctx, frame);
142
- return;
143
- case "While":
144
- walkNode(node.clause, ctx, frame);
145
- walkNode(node.body, ctx, frame);
146
- return;
147
- case "Case":
148
- walkWord(node.word, ctx, frame);
149
- for (const item of node.items) {
150
- for (const pattern of item.pattern)
151
- walkWord(pattern, ctx, frame);
152
- walkNode(item.body, ctx, frame);
153
- }
154
- return;
155
- default:
156
- return;
157
- }
158
- }
159
- function walkCommand(command, ctx, frame) {
160
- const envDelta = { ...ctx.envDelta };
161
- for (const prefix of command.prefix) {
162
- takeAssignment(prefix, envDelta);
163
- walkAssignment(prefix, ctx, frame);
164
- }
165
- const redirects = [...frame.redirects, ...command.redirects];
166
- for (const redirect of redirects)
167
- walkRedirect(redirect, ctx, frame);
168
- if (command.name === undefined) {
169
- ctx.orphans.push(...redirects.flatMap(emitRedirect));
170
- return;
171
- }
172
- const argv0Word = command.name;
173
- walkWord(argv0Word, ctx, frame);
174
- if (!isLiteralWord(argv0Word)) {
175
- for (const word of command.suffix)
176
- walkWord(word, ctx, frame);
177
- ctx.unparsed.push({ via: "non-literal-argv0", source: slice(ctx, command) });
178
- return;
179
- }
180
- const raw = argv0Word.value;
181
- const argv0 = peelName(raw, ctx.which);
182
- const suffix = command.suffix.map((word) => word.value);
183
- const innerCtx = { ...ctx, envDelta };
184
- switch (argv0) {
185
- case undefined:
186
- break;
187
- case "sudo":
188
- case "doas":
189
- emitCommand(innerCtx, raw, suffix, { ...frame, redirects }, true);
190
- peelInner(command, argv0, suffix, { ...innerCtx, elevated: true }, frame, redirects, true);
191
- return;
192
- case "source":
193
- case ".":
194
- for (const word of command.suffix)
195
- walkWord(word, ctx, frame);
196
- ctx.unparsed.push({ via: "source", source: slice(ctx, command) });
197
- return;
198
- case "find":
199
- for (const word of command.suffix)
200
- walkWord(word, ctx, frame);
201
- ctx.unparsed.push({ via: "find-expression", source: slice(ctx, command) });
202
- return;
203
- case "python":
204
- case "python3":
205
- case "node":
206
- if (hasScriptFlag(suffix)) {
207
- for (const word of command.suffix)
208
- walkWord(word, ctx, frame);
209
- ctx.unparsed.push({ via: "script-string", source: slice(ctx, command) });
210
- emitCommand(innerCtx, raw, suffix, { ...frame, redirects }, false);
211
- return;
212
- }
213
- break;
214
- case "eval":
215
- peelEval(command, innerCtx, frame, redirects);
216
- return;
217
- case "bash":
218
- case "sh":
219
- case "zsh":
220
- case "dash":
221
- case "ksh":
222
- peelShellC(command, argv0, suffix, innerCtx, frame, redirects);
223
- return;
224
- case "env":
225
- if (envHasDashFlags(suffix)) {
226
- for (const word of command.suffix)
227
- walkWord(word, ctx, frame);
228
- emitCommand(innerCtx, raw, suffix, { ...frame, redirects }, false);
229
- return;
230
- }
231
- peelInner(command, argv0, suffix, innerCtx, frame, redirects, false);
232
- return;
233
- case "xargs":
234
- case "timeout":
235
- case "nice":
236
- case "nohup":
237
- case "stdbuf":
238
- case "watch":
239
- case "time":
240
- case "command":
241
- case "exec":
242
- case "builtin":
243
- peelInner(command, argv0, suffix, innerCtx, frame, redirects, false);
244
- return;
245
- }
246
- for (const word of command.suffix)
247
- walkWord(word, ctx, frame);
248
- emitCommand(innerCtx, raw, suffix, { ...frame, redirects }, false);
249
- }
250
- function peelInner(command, argv0, suffix, ctx, frame, redirects, keepWrapper) {
251
- const flagArgs = TRANSPARENT_FLAG_ARGS[argv0];
252
- const skipAssign = argv0 === "env";
253
- const boundary = scanPassthroughBoundary(suffix, flagArgs, skipAssign) + (HOF_SKIP_POSITIONALS[argv0] ?? 0);
254
- const reads = wrapperFileReads(argv0, suffix);
255
- const innerCtx = reads.length === 0 ? ctx : { ...ctx, accessReads: [...ctx.accessReads, ...reads] };
256
- if (boundary >= suffix.length) {
257
- if (keepWrapper) {
258
- for (const word of command.suffix)
259
- walkWord(word, innerCtx, frame);
260
- }
261
- else {
262
- emitCommand(innerCtx, argv0, suffix, { ...frame, redirects }, false);
263
- }
264
- return;
265
- }
266
- for (const word of command.suffix.slice(0, boundary))
267
- walkWord(word, innerCtx, frame);
268
- const envDelta = { ...innerCtx.envDelta };
269
- if (skipAssign) {
270
- for (const word of suffix.slice(0, boundary)) {
271
- if (isVarAssignment(word))
272
- takeEnvText(word, envDelta);
273
- }
274
- }
275
- const nameWord = command.suffix[boundary];
276
- const wrapped = withWrap({ ...innerCtx, envDelta }, argv0);
277
- const inner = {
278
- type: "Command",
279
- pos: nameWord.pos,
280
- end: command.end,
281
- name: nameWord,
282
- prefix: [],
283
- suffix: command.suffix.slice(boundary + 1),
284
- redirects: [],
285
- };
286
- walkCommand(inner, wrapped, { ...frame, redirects });
287
- }
288
- function peelShellC(command, argv0, suffix, ctx, frame, redirects) {
289
- for (const word of command.suffix)
290
- walkWord(word, ctx, frame);
291
- const body = scriptFlagBody(suffix, "-c");
292
- if (body === undefined) {
293
- emitCommand(ctx, argv0, suffix, { ...frame, redirects }, false);
294
- return;
295
- }
296
- const inner = parse(body);
297
- if (inner.errors !== undefined && inner.errors.length > 0) {
298
- ctx.unparsed.push({ via: "script-string", source: body });
299
- return;
300
- }
301
- walkScript(inner, withWrap(ctx, argv0), {
302
- background: frame.background,
303
- stdinIsPipe: frame.stdinIsPipe,
304
- redirects,
305
- });
306
- }
307
- function peelEval(command, ctx, frame, redirects) {
308
- const suffix = command.suffix.map((word) => word.value);
309
- emitCommand(ctx, "eval", suffix, { ...frame, redirects }, false);
310
- if (suffix.length === 0)
311
- return;
312
- const body = suffix.join(" ");
313
- const inner = parse(body);
314
- if (inner.errors !== undefined && inner.errors.length > 0) {
315
- ctx.unparsed.push({ via: "script-string", source: body });
316
- return;
317
- }
318
- walkScript(inner, withWrap(ctx, "eval"), {
319
- background: frame.background,
320
- stdinIsPipe: frame.stdinIsPipe,
321
- redirects,
322
- });
323
- }
324
- function emitCommand(ctx, argv0, suffix, frame, elevated) {
325
- const { readings, overflow } = partition(suffix, ctx.valueFlagsFor?.(argv0, suffix) ?? ctx.valueFlags);
326
- ctx.questions.push({
327
- argv0,
328
- readings,
329
- ...(overflow ? { overflow: true } : {}),
330
- wrapChain: ctx.wrapChain,
331
- elevated: elevated || ctx.elevated,
332
- envDelta: ctx.envDelta,
333
- redirects: [
334
- ...frame.redirects.flatMap(emitRedirect),
335
- ...ctx.accessReads.map((target) => ({ operator: "<", target })),
336
- ],
337
- background: frame.background,
338
- stdinIsPipe: frame.stdinIsPipe,
339
- });
340
- }
341
- function emitRedirect(redirect) {
342
- if (redirect.target === undefined)
343
- return [];
344
- return [{ operator: redirect.operator, target: redirect.target.value }];
345
- }
346
- const READING_CAP = 16; // open ruling 5 (policy-algebra.md); overflow cannot prove deny/allow
347
- /**
348
- * Tokenize argv into positionals + flag bag.
349
- * `closed`: only those names are flags; other dash tokens are positionals.
350
- * Used when a `"--"` field reopens the suffix — undeclared `-file` stays a path.
351
- */
352
- export function partition(words, valueFlags, closed) {
353
- const readings = [];
354
- let overflow = false;
355
- const rec = (i, cut, tokens, flags) => {
356
- if (overflow)
357
- return;
358
- if (i >= words.length) {
359
- if (readings.length >= READING_CAP) {
360
- overflow = true;
361
- return;
362
- }
363
- readings.push({ tokens, flags });
364
- return;
365
- }
366
- const word = words[i];
367
- if (cut || word === "--" || !word.startsWith("-") || word === "-" || !isDeclaredFlag(word, closed)) {
368
- rec(i + 1, cut || word === "--", [...tokens, word], flags);
369
- return;
370
- }
371
- if (word.startsWith("--")) {
372
- const eq = word.indexOf("=");
373
- if (eq !== -1) {
374
- rec(i + 1, cut, tokens, withFlag(flags, word.slice(0, eq), word.slice(eq + 1)));
375
- return;
376
- }
377
- takeFlag(words, i, cut, tokens, flags, word, true, valueFlags, rec);
378
- return;
379
- }
380
- const eq = word.indexOf("=");
381
- if (eq >= 2) {
382
- rec(i + 1, cut, tokens, withFlag(flags, word.slice(0, eq), word.slice(eq + 1)));
383
- return;
384
- }
385
- if (/^-[a-z]+$/i.test(word) && word.length > 2) {
386
- let nextFlags = flags;
387
- for (const ch of word.slice(1))
388
- nextFlags = withFlag(nextFlags, `-${ch}`, "");
389
- rec(i + 1, cut, tokens, nextFlags);
390
- return;
391
- }
392
- takeFlag(words, i, cut, tokens, flags, word, word.length === 2, valueFlags, rec);
393
- };
394
- rec(0, false, [], {});
395
- return { readings, overflow };
396
- }
397
- function takeFlag(words, i, cut, tokens, flags, name, mayAttach, valueFlags, rec) {
398
- const next = words[i + 1];
399
- const canAttach = mayAttach && i + 1 < words.length && next !== "--" && !next.startsWith("-");
400
- if (canAttach && valueFlags?.has(name) === true) {
401
- rec(i + 2, cut, tokens, withFlag(flags, name, next));
402
- return;
403
- }
404
- if (canAttach) {
405
- rec(i + 2, cut, tokens, withFlag(flags, name, next));
406
- rec(i + 1, cut, tokens, withFlag(flags, name, ""));
407
- return;
408
- }
409
- rec(i + 1, cut, tokens, withFlag(flags, name, ""));
410
- }
411
- function isDeclaredFlag(word, closed) {
412
- if (closed === undefined)
413
- return true;
414
- if (word.startsWith("--")) {
415
- const eq = word.indexOf("=");
416
- return closed.has(eq === -1 ? word : word.slice(0, eq));
417
- }
418
- const eq = word.indexOf("=");
419
- if (eq >= 2)
420
- return closed.has(word.slice(0, eq));
421
- if (/^-[a-z]+$/i.test(word) && word.length > 2) {
422
- for (const ch of word.slice(1)) {
423
- if (!closed.has(`-${ch}`))
424
- return false;
425
- }
426
- return true;
427
- }
428
- return closed.has(word);
429
- }
430
- function withFlag(flags, name, val) {
431
- return { ...flags, [name]: [...(flags[name] ?? []), val] };
432
- }
433
- function scanPassthroughBoundary(args, flagArgs, skipVarAssignments) {
434
- let i = 0;
435
- while (i < args.length) {
436
- const arg = args[i];
437
- if (skipVarAssignments && isVarAssignment(arg)) {
438
- i++;
439
- continue;
440
- }
441
- if (arg === "--")
442
- return i + 1;
443
- if (!arg.startsWith("-"))
444
- break;
445
- i += flagSpan(arg, i, args, flagArgs);
446
- }
447
- return i;
448
- }
449
- function flagSpan(arg, i, args, flagArgs) {
450
- if (arg.includes("="))
451
- return 1;
452
- if (arg.length > 2 && !arg.startsWith("--") && takesValue(arg.slice(0, 2), flagArgs))
453
- return 1;
454
- if (takesValue(arg, flagArgs) && i + 1 < args.length && !args[i + 1].startsWith("-")) {
455
- return 2;
456
- }
457
- return 1;
458
- }
459
- /** Wrapper-owned file reads that peeling must not drop. `xargs -a FILE` reads FILE. */
460
- function wrapperFileReads(argv0, suffix) {
461
- if (argv0 !== "xargs")
462
- return [];
463
- const reads = [];
464
- for (let i = 0; i < suffix.length; i++) {
465
- const arg = suffix[i];
466
- if (arg === "--")
467
- break;
468
- if (arg === "-a" || arg === "--arg-file") {
469
- const next = suffix[i + 1];
470
- if (next !== undefined && next !== "--" && !next.startsWith("-")) {
471
- reads.push(next);
472
- i += 1;
473
- }
474
- continue;
475
- }
476
- if (arg.startsWith("--arg-file=") && arg.length > 11)
477
- reads.push(arg.slice(11));
478
- if (arg.startsWith("-a=") && arg.length > 3)
479
- reads.push(arg.slice(3));
480
- }
481
- return reads;
482
- }
483
- function takesValue(flag, flagArgs) {
484
- if (flagArgs === undefined)
485
- return false;
486
- const stripped = flag.replace(/^-+/, "");
487
- return flagArgs.some((name) => name.replace(/^-+/, "") === stripped);
488
- }
489
- function scriptFlagBody(suffix, flag) {
490
- for (let i = 0; i < suffix.length; i++) {
491
- const arg = suffix[i];
492
- if (arg === flag)
493
- return suffix[i + 1];
494
- if (arg.startsWith(`${flag}=`) && arg.length > flag.length + 1)
495
- return arg.slice(flag.length + 1);
496
- }
497
- return undefined;
498
- }
499
- function hasScriptFlag(suffix) {
500
- return suffix.some((arg) => arg === "-c" || arg === "-e" || arg.startsWith("-c=") || arg.startsWith("-e="));
501
- }
502
- function isVarAssignment(arg) {
503
- return /^[a-z_]\w*=/i.test(arg);
504
- }
505
- function takeAssignment(prefix, envDelta) {
506
- if (prefix.name === undefined)
507
- return;
508
- envDelta[prefix.name] = prefix.value?.value ?? "";
509
- }
510
- function takeEnvText(text, envDelta) {
511
- const eq = text.indexOf("=");
512
- if (eq <= 0)
513
- return;
514
- envDelta[text.slice(0, eq)] = text.slice(eq + 1);
515
- }
516
- function isLiteralWord(word) {
517
- if (word.parts === undefined) {
518
- return !PARTLESS_FORBIDDEN.test(word.text) && !word.text.startsWith("~");
519
- }
520
- return word.parts.every(partIsLiteral);
521
- }
522
- function partIsLiteral(part) {
523
- switch (part.type) {
524
- case "Literal":
525
- case "SingleQuoted":
526
- return true;
527
- case "AnsiCQuoted":
528
- return !part.value.includes("\0");
529
- case "DoubleQuoted":
530
- return part.parts.every((child) => child.type === "Literal");
531
- default:
532
- return false;
533
- }
534
- }
535
- const PEEL_NAMES = new Set([
536
- "sudo",
537
- "doas",
538
- "source",
539
- ".",
540
- "find",
541
- "python",
542
- "python3",
543
- "node",
544
- "eval",
545
- "bash",
546
- "sh",
547
- "zsh",
548
- "dash",
549
- "ksh",
550
- "env",
551
- "xargs",
552
- "timeout",
553
- "nice",
554
- "nohup",
555
- "stdbuf",
556
- "watch",
557
- "time",
558
- "command",
559
- "exec",
560
- "builtin",
561
- ]);
562
- /** Named peeler is itself. Path-form peels only when `which` agrees. */
563
- function peelName(raw, which) {
564
- return resolveKnown(raw, PEEL_NAMES, which);
565
- }
566
- /** `env -C` / `-S` / `-u` change context this matcher does not apply. Do not peel. */
567
- function envHasDashFlags(suffix) {
568
- for (const arg of suffix) {
569
- if (arg === "--")
570
- return false;
571
- if (isVarAssignment(arg))
572
- continue;
573
- if (arg.startsWith("-"))
574
- return true;
575
- }
576
- return false;
577
- }
578
- function withWrap(ctx, name) {
579
- return { ...ctx, wrapChain: [...ctx.wrapChain, name] };
580
- }
581
- function slice(ctx, node) {
582
- return ctx.source.slice(node.pos, node.end);
583
- }
584
- function walkWord(word, ctx, frame) {
585
- if (word === undefined)
586
- return;
587
- walkParts(word.parts, ctx, frame);
588
- }
589
- function walkAssignment(prefix, ctx, frame) {
590
- walkWord(prefix.value, ctx, frame);
591
- if (prefix.array !== undefined) {
592
- for (const word of prefix.array)
593
- walkWord(word, ctx, frame);
594
- }
595
- walkParts(prefix.indexParts, ctx, frame);
596
- }
597
- function walkRedirect(redirect, ctx, frame) {
598
- walkWord(redirect.target, ctx, frame);
599
- walkWord(redirect.body, ctx, frame);
600
- }
601
- function walkParts(parts, ctx, frame) {
602
- if (parts === undefined)
603
- return;
604
- for (const part of parts) {
605
- switch (part.type) {
606
- case "DoubleQuoted":
607
- case "LocaleString":
608
- walkParts(part.parts, ctx, frame);
609
- break;
610
- case "CommandExpansion":
611
- case "ProcessSubstitution":
612
- if (part.script !== undefined) {
613
- walkScript(part.script, ctx, { background: false, stdinIsPipe: false, redirects: [] });
614
- }
615
- break;
616
- case "ParameterExpansion":
617
- walkWord(part.operand, ctx, frame);
618
- walkParts(part.indexParts, ctx, frame);
619
- if (part.slice !== undefined) {
620
- walkWord(part.slice.offset, ctx, frame);
621
- walkWord(part.slice.length, ctx, frame);
622
- }
623
- if (part.replace !== undefined) {
624
- walkWord(part.replace.pattern, ctx, frame);
625
- walkWord(part.replace.replacement, ctx, frame);
626
- }
627
- break;
628
- case "ArithmeticExpansion":
629
- walkArith(part.expression, ctx, frame);
630
- break;
631
- case "BraceExpansion":
632
- case "ExtendedGlob":
633
- walkParts(part.parts, ctx, frame);
634
- break;
635
- default:
636
- break;
637
- }
638
- }
639
- }
640
- function walkArith(expr, ctx, frame) {
641
- if (expr === undefined)
642
- return;
643
- switch (expr.type) {
644
- case "ArithmeticBinary":
645
- walkArith(expr.left, ctx, frame);
646
- walkArith(expr.right, ctx, frame);
647
- return;
648
- case "ArithmeticUnary":
649
- walkArith(expr.operand, ctx, frame);
650
- return;
651
- case "ArithmeticTernary":
652
- walkArith(expr.test, ctx, frame);
653
- walkArith(expr.consequent, ctx, frame);
654
- walkArith(expr.alternate, ctx, frame);
655
- return;
656
- case "ArithmeticGroup":
657
- walkArith(expr.expression, ctx, frame);
658
- return;
659
- case "ArithmeticWord":
660
- walkParts(expr.parts, ctx, frame);
661
- return;
662
- case "ArithmeticCommandExpansion":
663
- if (expr.script !== undefined) {
664
- walkScript(expr.script, ctx, { background: false, stdinIsPipe: false, redirects: [] });
665
- }
666
- return;
667
- }
668
- }
669
- function walkTestWords(expr, ctx, frame) {
670
- switch (expr.type) {
671
- case "TestUnary":
672
- walkWord(expr.operand, ctx, frame);
673
- return;
674
- case "TestBinary":
675
- walkWord(expr.left, ctx, frame);
676
- walkWord(expr.right, ctx, frame);
677
- return;
678
- case "TestLogical":
679
- walkTestWords(expr.left, ctx, frame);
680
- walkTestWords(expr.right, ctx, frame);
681
- return;
682
- case "TestNot":
683
- walkTestWords(expr.operand, ctx, frame);
684
- return;
685
- case "TestGroup":
686
- walkTestWords(expr.expression, ctx, frame);
687
- return;
688
- }
689
- }
690
- //# sourceMappingURL=prep.js.map