@cspeach/cli 1.1.18 → 1.1.19

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 (224) hide show
  1. package/README.md +3 -4
  2. package/dist/agent/loop.js +143 -25
  3. package/dist/agent/providers/local-provider.js +4 -1
  4. package/dist/agent/repair-partial.js +76 -19
  5. package/dist/agent/tool-dispatch.js +58 -21
  6. package/dist/agent/turn-error-ux.js +77 -26
  7. package/dist/agent/turn-stream.js +25 -3
  8. package/dist/approvals/adt-type.js +27 -0
  9. package/dist/approvals/advisory-prompt.js +48 -0
  10. package/dist/approvals/advisory-render.js +25 -11
  11. package/dist/approvals/approval-prompt.js +74 -24
  12. package/dist/approvals/jwt.js +2 -1
  13. package/dist/approvals/render.js +64 -36
  14. package/dist/approvals/risk-floor.js +53 -2
  15. package/dist/auth/api-key.js +30 -16
  16. package/dist/auth/keychain.js +0 -0
  17. package/dist/auth/me.js +25 -7
  18. package/dist/cli.js +25 -6
  19. package/dist/commands/compact.js +12 -9
  20. package/dist/commands/config-set.js +6 -2
  21. package/dist/commands/config-show.js +7 -5
  22. package/dist/commands/cost.js +41 -34
  23. package/dist/commands/help.js +88 -50
  24. package/dist/commands/login.js +28 -19
  25. package/dist/commands/logout.js +7 -3
  26. package/dist/commands/plan-chain.js +71 -77
  27. package/dist/commands/plan-continue.js +1 -0
  28. package/dist/commands/plan-resume.js +20 -12
  29. package/dist/commands/whoami.js +23 -9
  30. package/dist/config/loader.js +15 -3
  31. package/dist/cost/pricing.js +6 -5
  32. package/dist/doctor/checks/forge-rules.js +13 -3
  33. package/dist/doctor/checks/keychain.js +6 -4
  34. package/dist/doctor/run.js +101 -20
  35. package/dist/index.js +5 -2
  36. package/dist/lib/graceful-exit.js +56 -0
  37. package/dist/lib/piped-prompt.js +65 -0
  38. package/dist/lock-contention.js +2 -2
  39. package/dist/one-shot.js +135 -40
  40. package/dist/projects/answer-blockers.js +6 -1
  41. package/dist/projects/handover-md.js +15 -14
  42. package/dist/projects/plan-run.js +97 -54
  43. package/dist/projects/promote-command.js +11 -2
  44. package/dist/projects/save-command.js +3 -2
  45. package/dist/projects/status.js +2 -1
  46. package/dist/projects/yes-no.js +12 -0
  47. package/dist/renderer/abap-inline.js +18 -14
  48. package/dist/renderer/answer-trim.js +69 -0
  49. package/dist/renderer/banners.js +8 -8
  50. package/dist/renderer/brand-settled.js +19 -0
  51. package/dist/renderer/change-card.js +156 -0
  52. package/dist/renderer/checklist-format.js +130 -0
  53. package/dist/renderer/color-mode.js +45 -0
  54. package/dist/renderer/error-detail.js +104 -0
  55. package/dist/renderer/fence-state.js +46 -0
  56. package/dist/renderer/footer-line.js +96 -0
  57. package/dist/renderer/glyphs.js +25 -0
  58. package/dist/renderer/goodbye.js +38 -0
  59. package/dist/renderer/legacy-palette.js +41 -0
  60. package/dist/renderer/look.js +30 -0
  61. package/dist/renderer/markdown.js +299 -92
  62. package/dist/renderer/notice-log.js +72 -0
  63. package/dist/renderer/notice-shape.js +48 -0
  64. package/dist/renderer/notices.js +40 -8
  65. package/dist/renderer/panel-rows.js +16 -0
  66. package/dist/renderer/pipeline.js +66 -0
  67. package/dist/renderer/progress-chatter.js +23 -17
  68. package/dist/renderer/rendering-mode.js +34 -0
  69. package/dist/renderer/routing-line.js +14 -0
  70. package/dist/renderer/sanitize.js +12 -0
  71. package/dist/renderer/severity.js +2 -7
  72. package/dist/renderer/startup-lines.js +152 -0
  73. package/dist/renderer/status-footer.js +52 -34
  74. package/dist/renderer/steering-echo.js +41 -0
  75. package/dist/renderer/syntax.js +31 -12
  76. package/dist/renderer/tables.js +5 -1
  77. package/dist/renderer/theme.js +44 -0
  78. package/dist/renderer/thinking-heartbeat.js +33 -2
  79. package/dist/renderer/todo-block.js +10 -49
  80. package/dist/renderer/tool-labels.js +238 -0
  81. package/dist/renderer/tool-widget.js +245 -51
  82. package/dist/renderer/trace.js +42 -0
  83. package/dist/renderer/transcript-flow.js +132 -0
  84. package/dist/renderer/tty.js +34 -1
  85. package/dist/renderer/ui-width.js +42 -0
  86. package/dist/renderer/verify-chain.js +4 -2
  87. package/dist/renderer/widget-fallback.js +58 -65
  88. package/dist/repl/bracketed-paste.js +7 -1
  89. package/dist/repl/builtin-commands.js +19 -8
  90. package/dist/repl/credential-handover-gate.js +28 -0
  91. package/dist/repl/early-line-buffer.js +5 -2
  92. package/dist/repl/file-picker.js +54 -12
  93. package/dist/repl/ink-stdin-guard.js +66 -0
  94. package/dist/repl/inquirer-guard.js +59 -16
  95. package/dist/repl/inquirer-theme.js +27 -33
  96. package/dist/repl/paste-marker.js +19 -0
  97. package/dist/repl/plan-turn-end.js +20 -0
  98. package/dist/repl/post-turn-status.js +36 -11
  99. package/dist/repl/reroute-turn.js +21 -0
  100. package/dist/repl/reset-tty-stdin.js +44 -0
  101. package/dist/repl/restore-guard.js +22 -0
  102. package/dist/repl/resume-standalone.js +63 -0
  103. package/dist/repl/rule8-detector.js +11 -3
  104. package/dist/repl/safety-confirm.js +161 -93
  105. package/dist/repl/session-spend-line.js +8 -4
  106. package/dist/repl/themed-prompts.js +19 -0
  107. package/dist/repl/ui-look-command.js +75 -0
  108. package/dist/repl/update-method-preview-hook.js +48 -5
  109. package/dist/repl.js +795 -332
  110. package/dist/rewind/cli.js +3 -2
  111. package/dist/rewind/format.js +14 -9
  112. package/dist/rewind/restore.js +11 -0
  113. package/dist/router/classifier.js +47 -4
  114. package/dist/router/intent-extractor.js +24 -8
  115. package/dist/router/piped-routing.js +23 -0
  116. package/dist/router/resume-routing.js +16 -0
  117. package/dist/router/routing-failure.js +57 -0
  118. package/dist/sap/first-run-choice.js +1 -1
  119. package/dist/sap/onboarding.js +3 -2
  120. package/dist/sap/standalone-onboarding.js +1 -1
  121. package/dist/sap/system-info.js +4 -2
  122. package/dist/sap/unreachable.js +28 -0
  123. package/dist/sap-errors/clean-error-text.js +182 -0
  124. package/dist/session/interrupt-reason.js +39 -0
  125. package/dist/session/recap.js +36 -24
  126. package/dist/session/resume.js +104 -36
  127. package/dist/session/store.js +69 -27
  128. package/dist/session/user-prompt.js +21 -0
  129. package/dist/skill-catalog.js +7 -0
  130. package/dist/skills/bundled-skills.js +76 -69
  131. package/dist/standards/standards-init.js +1 -1
  132. package/dist/test-helpers/answer-any.js +46 -0
  133. package/dist/tools/approval.js +27 -11
  134. package/dist/tools/ask-question.js +47 -5
  135. package/dist/tools/filesystem/file-write.js +19 -2
  136. package/dist/tools/fiori/fe-extend.js +16 -1
  137. package/dist/tools/fiori/fe-scaffold.js +105 -3
  138. package/dist/tools/fiori/html-escapes.js +28 -0
  139. package/dist/tools/fiori/metadata/audit.js +605 -0
  140. package/dist/tools/fiori/metadata/references.js +104 -0
  141. package/dist/tools/fiori/metadata/types.js +8 -0
  142. package/dist/tools/fiori/preview/app-guard.js +215 -0
  143. package/dist/tools/fiori/preview/env.js +193 -0
  144. package/dist/tools/fiori/preview/readiness.js +127 -0
  145. package/dist/tools/fiori/preview/registry.js +412 -0
  146. package/dist/tools/fiori/preview/start.js +469 -0
  147. package/dist/tools/fiori/samples/loader.js +20 -5
  148. package/dist/tools/fiori/scaffold.js +18 -6
  149. package/dist/tools/fiori/smoke/app-driver-page.js +372 -0
  150. package/dist/tools/fiori/smoke/app-driver.js +100 -0
  151. package/dist/tools/fiori/smoke/assertions.js +161 -24
  152. package/dist/tools/fiori/smoke/audit-columns.js +21 -0
  153. package/dist/tools/fiori/smoke/batch.js +273 -0
  154. package/dist/tools/fiori/smoke/draft-safety.js +241 -0
  155. package/dist/tools/fiori/smoke/draft-smoke.js +473 -0
  156. package/dist/tools/fiori/smoke/driver.js +171 -4
  157. package/dist/tools/fiori/smoke/evidence.js +35 -0
  158. package/dist/tools/fiori/smoke/labels-codes.js +207 -0
  159. package/dist/tools/fiori/smoke/nav-error.js +26 -0
  160. package/dist/tools/fiori/smoke/run-smoke.js +155 -12
  161. package/dist/tools/fiori/tools.js +651 -7
  162. package/dist/tools/fiori/ui5-version.js +16 -0
  163. package/dist/tools/index.js +8 -0
  164. package/dist/tools/local-build.js +6 -0
  165. package/dist/tools/sap-read.js +16 -1
  166. package/dist/tools/sap-write.js +122 -10
  167. package/dist/tools/shell/shell_exec.js +30 -12
  168. package/dist/tools/syntax-state.js +67 -0
  169. package/dist/tools/todo.js +23 -18
  170. package/dist/tools/transport-fit.js +462 -0
  171. package/dist/tools/transport-resolution.js +3 -1
  172. package/dist/tools/transport.js +33 -7
  173. package/dist/tools/verify.js +184 -8
  174. package/dist/ui/app.js +284 -146
  175. package/dist/ui/approval-modal.js +41 -15
  176. package/dist/ui/ask-answer-text.js +17 -0
  177. package/dist/ui/body.js +25 -6
  178. package/dist/ui/card-slot.js +98 -0
  179. package/dist/ui/checklist.js +9 -0
  180. package/dist/ui/coaching-picker-classic.js +8 -5
  181. package/dist/ui/command-palette.js +6 -4
  182. package/dist/ui/confirm-request.js +18 -0
  183. package/dist/ui/context-grid.js +2 -1
  184. package/dist/ui/file-palette.js +6 -4
  185. package/dist/ui/files-card-emitter.js +19 -0
  186. package/dist/ui/footer-line.js +16 -0
  187. package/dist/ui/footer.js +105 -104
  188. package/dist/ui/input-wrap.js +122 -0
  189. package/dist/ui/interrupt.js +103 -0
  190. package/dist/ui/key-burst.js +117 -0
  191. package/dist/ui/line-resolution.js +4 -3
  192. package/dist/ui/live-area.js +71 -0
  193. package/dist/ui/login-banner.js +54 -43
  194. package/dist/ui/rewind-panel.js +76 -24
  195. package/dist/ui/role-props.js +47 -0
  196. package/dist/ui/sap-state-store.js +1 -0
  197. package/dist/ui/session-timeline.js +49 -3
  198. package/dist/ui/skill-picker.js +5 -47
  199. package/dist/ui/text-input.js +128 -24
  200. package/dist/ui/todo-emitter.js +19 -0
  201. package/dist/ui/turn-status-emitter.js +28 -2
  202. package/dist/ui/turn-status.js +16 -19
  203. package/dist/ui/use-card-request.js +44 -0
  204. package/dist/ui/widgets/ask-card.js +521 -0
  205. package/dist/ui/widgets/bar-chart.js +11 -5
  206. package/dist/ui/widgets/coaching-picker.js +50 -55
  207. package/dist/ui/widgets/confirm-card.js +193 -0
  208. package/dist/ui/widgets/dep-graph.js +3 -2
  209. package/dist/ui/widgets/diff-viewer.js +6 -5
  210. package/dist/ui/widgets/files-card.js +97 -0
  211. package/dist/ui/widgets/question-card.js +2 -1
  212. package/dist/ui/widgets/reclaim-raw-stdin.js +24 -0
  213. package/dist/ui/widgets/shortcuts-sheet.js +25 -0
  214. package/dist/ui/widgets/stack-frames.js +2 -1
  215. package/dist/upgrade-check.js +39 -8
  216. package/package.json +11 -2
  217. package/test-harness/fe-draft/app.ts +188 -0
  218. package/test-harness/fe-draft/fe-draft.e2e.test.ts +461 -0
  219. package/test-harness/fe-draft/manifest-enhancer.unit.test.ts +51 -0
  220. package/test-harness/fe-draft/routes.ts +224 -0
  221. package/test-harness/fe-draft/routes.unit.test.ts +45 -0
  222. package/test-harness/fe-draft/server.ts +225 -0
  223. package/test-harness/fe-draft/variants.ts +196 -0
  224. package/vitest.smoke-e2e.config.ts +20 -0
@@ -0,0 +1,196 @@
1
+ // cspeach-cli/test-harness/fe-draft/variants.ts
2
+ //
3
+ // L1-3 — backend VARIANTS of the captured draft `$metadata`, built IN MEMORY.
4
+ //
5
+ // The captured ZUI_LAS_INSPECTIONLOG_O4 service carries Severity and Status as
6
+ // raw one-letter codes with no text (that is what the real system emitted), and
7
+ // every LineItem column is labelled. The `labels` / `no-raw-codes` checks need
8
+ // a positive case and negatives, so these pure transforms derive them from the
9
+ // capture. The result is written only to the harness's temp folder; the
10
+ // scrubbed capture in fixtures/metadata/ is never written, copied or edited.
11
+ //
12
+ // Said plainly: the coded-with-text shape is HARNESS-BUILT, not captured. A
13
+ // real capture of it is owed once a stack built with the current RAP rules
14
+ // exists (CAPTURE.md, "Not present anywhere on the system").
15
+
16
+ const ENTITY_TYPE = 'InspectionLogType';
17
+ const CODED = [
18
+ { property: 'Severity', text: 'SeverityText' },
19
+ { property: 'Status', text: 'StatusText' },
20
+ ] as const;
21
+
22
+ function mustReplace(xml: string, find: RegExp, replace: (m: string) => string, what: string): string {
23
+ if (!find.test(xml)) throw new Error(`variant anchor not found in the captured metadata: ${what}`);
24
+ return xml.replace(find, replace);
25
+ }
26
+
27
+ /**
28
+ * Give Severity and Status a text: a `…Text` property on the entity type and
29
+ * `SAP__common.Text` on the code, with `SAP__UI.TextArrangement` TextOnly when
30
+ * asked. Without an arrangement the OData default applies (TextFirst →
31
+ * "Open (O)"), which is the negative case for `no-raw-codes`.
32
+ */
33
+ export function withCodeTexts(xml: string, arrangement?: 'TextOnly'): string {
34
+ let out = mustReplace(
35
+ xml,
36
+ new RegExp(`(<EntityType Name="${ENTITY_TYPE}"[^>]*>(?:(?!</EntityType>)[\\s\\S])*?<Property Name="Status"[^>]*/>)`),
37
+ (m) => m + CODED.map((c) => `<Property Name="${c.text}" Type="Edm.String" MaxLength="20"/>`).join(''),
38
+ `${ENTITY_TYPE}/Status property`,
39
+ );
40
+ const arr = arrangement
41
+ ? `<Annotation Term="SAP__UI.TextArrangement" EnumMember="SAP__UI.TextArrangementType/${arrangement}"/>`
42
+ : '';
43
+ for (const c of CODED) {
44
+ out = mustReplace(
45
+ out,
46
+ new RegExp(`<Annotations Target="SAP__self\\.${ENTITY_TYPE}/${c.property}">`),
47
+ (m) => `${m}<Annotation Term="SAP__common.Text" Path="${c.text}">${arr}</Annotation>`,
48
+ `${ENTITY_TYPE}/${c.property} annotations`,
49
+ );
50
+ }
51
+ return out;
52
+ }
53
+
54
+ /** Strip every label a Fiori header could use: Common.Label/Heading/QuickInfo and DataField Label. */
55
+ export function withoutLabels(xml: string): string {
56
+ const out = xml
57
+ .replace(/<Annotation Term="SAP__common\.(?:Label|Heading|QuickInfo)" String="[^"]*"\/>/g, '')
58
+ .replace(/<PropertyValue Property="Label" String="[^"]*"\/>/g, '');
59
+ if (out === xml) throw new Error('variant anchor not found in the captured metadata: no labels to strip');
60
+ return out;
61
+ }
62
+
63
+ const SEVERITY_TEXT: Record<string, string> = { H: 'High', M: 'Medium', L: 'Low' };
64
+ const STATUS_TEXT: Record<string, string> = { O: 'Open', C: 'Closed' };
65
+
66
+ /**
67
+ * L1-5b — value helps on the two coded fields, built in memory (the capture has
68
+ * value helps only on the draft-admin user fields, CAPTURE.md). Status gets a
69
+ * FIXED-VALUES value help (a dropdown popover at runtime), Severity a plain
70
+ * value list (a dialog). Each reads a small code-list entity set added to the
71
+ * service (`StatusVH`, `SeverityVH`: Code + Text). Apply AFTER withCodeTexts.
72
+ * Harness-built, not captured — said plainly, like withCodeTexts.
73
+ */
74
+ export function withValueHelps(xml: string): string {
75
+ const vhTypes = ['StatusVH', 'SeverityVH']
76
+ .map(
77
+ (n) =>
78
+ `<EntityType Name="${n}Type"><Key><PropertyRef Name="Code"/></Key>` +
79
+ `<Property Name="Code" Type="Edm.String" Nullable="false" MaxLength="1"/>` +
80
+ `<Property Name="Text" Type="Edm.String" MaxLength="20"/></EntityType>`,
81
+ )
82
+ .join('');
83
+ let out = mustReplace(
84
+ xml,
85
+ new RegExp(`<EntityType Name="${ENTITY_TYPE}"`),
86
+ (m) => vhTypes + m,
87
+ `${ENTITY_TYPE} entity type`,
88
+ );
89
+ out = mustReplace(
90
+ out,
91
+ /<EntityContainer Name="Container">/,
92
+ (m) =>
93
+ m +
94
+ '<EntitySet Name="StatusVH" EntityType="SAP__self.StatusVHType"/>' +
95
+ '<EntitySet Name="SeverityVH" EntityType="SAP__self.SeverityVHType"/>',
96
+ 'entity container',
97
+ );
98
+ const valueList = (local: string, set: string, label: string) =>
99
+ `<Annotation Term="SAP__common.ValueList"><Record>` +
100
+ `<PropertyValue Property="Label" String="${label}"/>` +
101
+ `<PropertyValue Property="CollectionPath" String="${set}"/>` +
102
+ `<PropertyValue Property="Parameters"><Collection>` +
103
+ `<Record Type="SAP__common.ValueListParameterInOut"><PropertyValue Property="LocalDataProperty" PropertyPath="${local}"/><PropertyValue Property="ValueListProperty" String="Code"/></Record>` +
104
+ `<Record Type="SAP__common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="Text"/></Record>` +
105
+ `</Collection></PropertyValue></Record></Annotation>`;
106
+ out = mustReplace(
107
+ out,
108
+ new RegExp(`<Annotations Target="SAP__self\\.${ENTITY_TYPE}/Status">`),
109
+ (m) => m + valueList('Status', 'StatusVH', 'Status') + '<Annotation Term="SAP__common.ValueListWithFixedValues"/>',
110
+ `${ENTITY_TYPE}/Status annotations`,
111
+ );
112
+ out = mustReplace(
113
+ out,
114
+ new RegExp(`<Annotations Target="SAP__self\\.${ENTITY_TYPE}/Severity">`),
115
+ (m) => m + valueList('Severity', 'SeverityVH', 'Severity'),
116
+ `${ENTITY_TYPE}/Severity annotations`,
117
+ );
118
+ // The code lists' own labels + the text of each code (so the dialog columns are labelled).
119
+ const vhAnn = (n: string) =>
120
+ `<Annotations Target="SAP__self.${n}Type/Code"><Annotation Term="SAP__common.Label" String="Code"/>` +
121
+ `<Annotation Term="SAP__common.Text" Path="Text"/></Annotations>` +
122
+ `<Annotations Target="SAP__self.${n}Type/Text"><Annotation Term="SAP__common.Label" String="Description"/></Annotations>`;
123
+ out = mustReplace(
124
+ out,
125
+ new RegExp(`<Annotations Target="SAP__self\\.${ENTITY_TYPE}/Status">`),
126
+ (m) => vhAnn('StatusVH') + vhAnn('SeverityVH') + m,
127
+ 'value-help annotations anchor',
128
+ );
129
+ return out;
130
+ }
131
+
132
+ /** Rows of the two code lists withValueHelps adds. */
133
+ export const VALUE_HELP_ROWS = {
134
+ StatusVH: Object.entries(STATUS_TEXT).map(([Code, Text]) => ({ Code, Text })),
135
+ SeverityVH: Object.entries(SEVERITY_TEXT).map(([Code, Text]) => ({ Code, Text })),
136
+ };
137
+
138
+ /**
139
+ * The mock backend's behaviour for the draft root, as a fe-mockserver
140
+ * `<EntitySet>.js` module (CommonJS source, written into the harness temp dir):
141
+ * the text of each code follows the code (a RAP service reads it through an
142
+ * association; the mock stores it), and — for the negative test — activation is
143
+ * REJECTED with a 400 and an SAP message while Status is `rejectStatus`.
144
+ */
145
+ export function inspectionLogHooks(opts: { rejectStatus?: string } = {}): string {
146
+ return `'use strict';
147
+ const STATUS_TEXT = ${JSON.stringify(STATUS_TEXT)};
148
+ const SEVERITY_TEXT = ${JSON.stringify(SEVERITY_TEXT)};
149
+ const REJECT = ${JSON.stringify(opts.rejectStatus ?? null)};
150
+ function texts(d) {
151
+ if (d && typeof d === 'object') {
152
+ if ('Status' in d) d.StatusText = STATUS_TEXT[d.Status] || '';
153
+ if ('Severity' in d) d.SeverityText = SEVERITY_TEXT[d.Severity] || '';
154
+ }
155
+ return d;
156
+ }
157
+ // The mock answers the dynamic feature-control fields (__EntityControl /
158
+ // __OperationControl) with empty strings, which FE reads as "not updatable":
159
+ // the draft opens read-only. A RAP backend computes them; the mock says yes.
160
+ function control(d) {
161
+ if (d && typeof d === 'object') {
162
+ if ('__EntityControl' in d) d.__EntityControl = { Deletable: true, Updatable: true };
163
+ if ('__OperationControl' in d) d.__OperationControl = { Edit: true };
164
+ }
165
+ return d;
166
+ }
167
+ module.exports = {
168
+ onAfterRead: async function (data) { if (Array.isArray(data)) data.forEach(control); else control(data); return data; },
169
+ addEntry: function (entry, req) { return this.base.addEntry(texts(entry), req); },
170
+ updateEntry: function (keys, updated, patch, req) { return this.base.updateEntry(keys, texts(Object.assign({}, patch)), req); },
171
+ onBeforeAction: async function (def, data, keys, req) {
172
+ if (REJECT !== null && /Activate$/.test(String(def && def.name))) {
173
+ const rows = await this.base.fetchEntries(keys, req);
174
+ const row = rows && rows[0];
175
+ if (row && row.Status === REJECT) {
176
+ this.throwError('CSP-HARNESS-REJECT: status ' + REJECT + ' cannot be saved', 400, {
177
+ code: 'CSP/001', message: 'CSP-HARNESS-REJECT: status ' + REJECT + ' cannot be saved', numericSeverity: 4, target: 'Status',
178
+ });
179
+ }
180
+ }
181
+ return data;
182
+ },
183
+ };
184
+ `;
185
+ }
186
+
187
+ /** The seeded rows plus the text for each code (for the withCodeTexts variants). */
188
+ export function rowsWithTexts<T extends { Severity: string; Status: string }>(
189
+ rows: readonly T[],
190
+ ): Array<T & { SeverityText: string; StatusText: string }> {
191
+ return rows.map((r) => ({
192
+ ...r,
193
+ SeverityText: SEVERITY_TEXT[r.Severity] ?? '',
194
+ StatusText: STATUS_TEXT[r.Status] ?? '',
195
+ }));
196
+ }
@@ -0,0 +1,20 @@
1
+ // The `smoke-e2e` vitest project (L1-2b): the lane-1 smoke checks against the
2
+ // offline Fiori Elements harness in test-harness/. NOT part of `pnpm test` — it
3
+ // needs a local Edge/Chrome and the UI5 CDN. Run: `pnpm test:smoke-e2e`.
4
+ import { defineConfig } from 'vitest/config';
5
+
6
+ export default defineConfig({
7
+ test: {
8
+ name: 'smoke-e2e',
9
+ include: ['test-harness/**/*.e2e.test.ts'],
10
+ environment: 'node',
11
+ pool: 'forks',
12
+ // One browser at a time; the files share the CDN cache per process anyway.
13
+ fileParallelism: false,
14
+ testTimeout: 240_000,
15
+ hookTimeout: 600_000,
16
+ },
17
+ resolve: {
18
+ conditions: ['node'],
19
+ },
20
+ });