@f5-sales-demo/pi-utils 19.61.1 → 19.61.3

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/logger.ts +63 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/pi-utils",
4
- "version": "19.61.1",
4
+ "version": "19.61.3",
5
5
  "description": "Shared utilities for pi packages",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/bun": "^1.3",
42
- "@f5-sales-demo/pi-natives": "19.61.1"
42
+ "@f5-sales-demo/pi-natives": "19.61.3"
43
43
  },
44
44
  "engines": {
45
45
  "bun": ">=1.3.7"
package/src/logger.ts CHANGED
@@ -202,3 +202,66 @@ export function time<T, A extends unknown[]>(op: string, fn?: (...args: A) => T,
202
202
  return fn(...args);
203
203
  }
204
204
  }
205
+
206
+ /** True when TTFT attribution emission is enabled (bench sets XCSH_TTFT_ATTRIBUTION=1). */
207
+ function isAttrOn(): boolean {
208
+ return process.env.XCSH_TTFT_ATTRIBUTION === "1";
209
+ }
210
+
211
+ /**
212
+ * Emit one attribution line for a pre-computed duration. Transport is FILE when
213
+ * XCSH_TTFT_ATTR_FILE is set (append), else stderr. Wrapped so a bad path / I/O error can NEVER
214
+ * throw into the caller — routing this into a pipe (stderr:"inherit") was shown to hang the chat
215
+ * turn, so a per-run file is the safe transport and any failure here must stay invisible to the
216
+ * hot path.
217
+ */
218
+ function emitAttrLine(label: string, ms: number): void {
219
+ try {
220
+ const line = `[ttft-attr] ${label} ${ms}`;
221
+ const file = process.env.XCSH_TTFT_ATTR_FILE;
222
+ if (file) {
223
+ fs.appendFileSync(file, `${line}\n`);
224
+ } else {
225
+ console.error(line);
226
+ }
227
+ } catch {
228
+ /* swallow — attribution emission must never throw into the caller */
229
+ }
230
+ }
231
+
232
+ /** Emit an attribution line for the elapsed time since {@link start}. */
233
+ function emitAttr(label: string, start: number): void {
234
+ emitAttrLine(label, performance.now() - start);
235
+ }
236
+
237
+ /**
238
+ * TTFT Phase-4 A1 attribution timer. Runs `fn`, returns its value unchanged.
239
+ * When XCSH_TTFT_ATTRIBUTION=1, emits one `[ttft-attr] <label> <ms>` stderr line on
240
+ * completion (await-accurate for Promises; synchronous otherwise; emits-then-rethrows on throw).
241
+ * Pure pass-through with zero output otherwise — safe on the production hot path.
242
+ */
243
+ export function ttftAttr<T, A extends unknown[]>(label: string, fn: (...args: A) => T, ...args: A): T {
244
+ if (!isAttrOn()) return fn(...args);
245
+ const start = performance.now();
246
+ try {
247
+ const result = fn(...args);
248
+ if (result instanceof Promise) {
249
+ return result.finally(() => emitAttr(label, start)) as T;
250
+ }
251
+ emitAttr(label, start);
252
+ return result;
253
+ } catch (error) {
254
+ emitAttr(label, start);
255
+ throw error;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * TTFT Phase-4 A1 attribution mark for a PRE-COMPUTED duration (not fn-wrapping).
261
+ * When XCSH_TTFT_ATTRIBUTION=1, emits one `[ttft-attr] <label> <ms>` line via the shared
262
+ * transport ({@link emitAttrLine}); a no-op otherwise. Never throws — safe on the hot path.
263
+ */
264
+ export function ttftMark(label: string, ms: number): void {
265
+ if (!isAttrOn()) return;
266
+ emitAttrLine(label, ms);
267
+ }