@pikku/core 0.12.100 → 0.12.101

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.
@@ -0,0 +1,96 @@
1
+ import { isExpectedError } from '../../errors/error-handler.js'
2
+
3
+ /**
4
+ * A `PikkuFetchError` — recognised by shape, not by `instanceof`, because
5
+ * `@pikku/client-fetch` depends on core and core cannot depend back on it.
6
+ */
7
+ type FetchFailure = {
8
+ status: number
9
+ statusText: string
10
+ response: { url?: string }
11
+ message?: string
12
+ }
13
+
14
+ const isFetchFailure = (error: unknown): error is FetchFailure => {
15
+ const candidate = error as Partial<FetchFailure> | null
16
+ return (
17
+ typeof candidate?.status === 'number' &&
18
+ typeof candidate?.statusText === 'string' &&
19
+ typeof candidate?.response === 'object' &&
20
+ candidate.response !== null
21
+ )
22
+ }
23
+
24
+ /**
25
+ * The user asked to see the machinery. `--verbose`/`-v` is the flag the CLI
26
+ * already documents; `PIKKU_DEBUG` is for the times the flag cannot be typed —
27
+ * a command that does not declare the option, or a CLI run from a script.
28
+ */
29
+ export const wantsStackTrace = (
30
+ args: string[],
31
+ env: Record<string, string | undefined> = process.env
32
+ ): boolean =>
33
+ args.includes('--verbose') ||
34
+ args.includes('-v') ||
35
+ (env.PIKKU_DEBUG !== undefined &&
36
+ env.PIKKU_DEBUG !== '' &&
37
+ env.PIKKU_DEBUG !== '0')
38
+
39
+ /**
40
+ * What the CLI prints when a command throws.
41
+ *
42
+ * A stack trace is an answer to "which line of pikku broke", and almost every
43
+ * failure a user actually hits is not that question: a missing role, an expired
44
+ * token, a gateway that is down. Those errors are written to be read, so the
45
+ * message alone is the whole output — an expected error is one deliberately
46
+ * raised as `PikkuError` (or carrying `expected: true`), and everything else
47
+ * keeps its stack, because an unexpected `TypeError` with its frames removed is
48
+ * a bug nobody can diagnose.
49
+ *
50
+ * A stack already begins with `Name: message`, so it is returned as-is: the
51
+ * `console.error('Error:', error)` this replaced produced the doubled
52
+ * `Error: Error: …` prefix that made even real traces look broken.
53
+ */
54
+ export const formatCLIError = (
55
+ error: unknown,
56
+ { verbose = false }: { verbose?: boolean } = {}
57
+ ): string => {
58
+ if (isFetchFailure(error)) {
59
+ return formatFetchFailure(error, verbose)
60
+ }
61
+
62
+ const stack = (error as { stack?: unknown } | null)?.stack
63
+ const message = (error as { message?: unknown } | null)?.message
64
+
65
+ if (!isExpectedError(error)) {
66
+ return typeof stack === 'string' && stack ? stack : String(error)
67
+ }
68
+
69
+ const text = typeof message === 'string' && message ? message : String(error)
70
+ return verbose && typeof stack === 'string' && stack
71
+ ? `${text}\n${stack}`
72
+ : text
73
+ }
74
+
75
+ /**
76
+ * A failed HTTP call, as the line the user needs: which status, from which URL.
77
+ *
78
+ * Never the `Response` itself. Node inspects an error's own properties when it
79
+ * prints one, so a thrown fetch error used to dump the headers, the body stream
80
+ * and the redirect flags — pages of output whose only real content was the
81
+ * status code.
82
+ */
83
+ const formatFetchFailure = (error: FetchFailure, verbose: boolean): string => {
84
+ const url = error.response?.url
85
+ const where = url ? ` from ${url}` : ''
86
+ const summary = `${error.status} ${error.statusText}${where}`
87
+ const message = error.message
88
+ const text =
89
+ message && message !== error.statusText
90
+ ? `${message}\n ${summary}`
91
+ : summary
92
+ const stack = (error as { stack?: unknown }).stack
93
+ return verbose && typeof stack === 'string' && stack
94
+ ? `${text}\n${stack}`
95
+ : text
96
+ }
@@ -77,7 +77,6 @@ export const VIRTUAL_USER_VARIABLES = {
77
77
  * fallback for a run nobody handed a token to, which is what a schedule is.
78
78
  */
79
79
  operatorToken: 'FABRIC_OPERATOR_TOKEN',
80
- createMissing: 'PIKKU_PERSONA_CREATE_MISSING',
81
80
  } as const
82
81
 
83
82
  /**
@@ -560,9 +559,6 @@ export const executeVirtualUserRun = async ({
560
559
  )
561
560
  }
562
561
 
563
- const createMissing =
564
- String(await variables.get(VIRTUAL_USER_VARIABLES.createMissing)) ===
565
- 'true'
566
562
  const model = await variables.get(VIRTUAL_USER_VARIABLES.model)
567
563
  if (!model) {
568
564
  throw new Error(
@@ -606,7 +602,6 @@ export const executeVirtualUserRun = async ({
606
602
  ? {
607
603
  operator: {
608
604
  token,
609
- createMissing,
610
605
  signInPath: signInPathFor(configuredSignInPath, 'fabric'),
611
606
  },
612
607
  }