# gbpipes — In-Depth Analysis

**Date:** 2026-02-25
**Scope:** `libs/gbpipes/` (8 pipes) + inline pipe in `features/gbfolderview/`
**Active usages:** ~2,059 across templates
**Test coverage:** 0%
**Overall health:** F (security), D (standards), C+ (performance)

---

## Pipes Inventory

| Pipe Class | File | Standalone | Pure | Usages |
|---|---|---|---|---|
| `DateFormatterPipe` | `libs/gbpipes/dateformat.pipe.ts` | YES | implicit true ❌ | ~300 |
| `NumberFormatPipe` | `libs/gbpipes/numberformatter.pipe.ts` | NO | implicit true ❌ | ~400 |
| `NullSuppressionPipe` | `libs/gbpipes/nullsupression.pipe.ts` | NO | implicit true ✅ | ~500 |
| `ShortNumberPipe` | `libs/gbpipes/short-number.pipe.ts` | NO | implicit true ✅ | ~200 |
| `SpecialCharacterFormatterPipe` | `libs/gbpipes/specialcharacterformater.pipe.ts` | NO | implicit true ⚠️ | ~50 |
| `TimeFormatterPipe` | `libs/gbpipes/timeformatter.pipe.ts` | NO | implicit true ✅ | ~100 |
| `TruncatePipe` | `libs/gbpipes/truncatetext.pipe.ts` | NO declared | implicit true ✅ | ~150 |
| `SafePipe` | `libs/gbpipes/safepipe.pipe.ts` | NO | implicit true ✅ | ~50 |
| `TitleCaseProperPipe` | `features/gbfolderview/gbfolderview.component.ts:45` | YES (inline) | implicit true ✅ | 1 |

---

## Pipe 1 — DateFormatterPipe

**File:** [libs/gbpipes/dateformat.pipe.ts](libs/gbpipes/dateformat.pipe.ts)

### Issues

#### P0 — Security: sessionStorage LoginDTO read
**Line 20:** `this.loginDTOs = JSON.parse(sessionStorage.getItem('LoginDTO') as any);`

Called inside `transform()` — fires on **every change detection cycle** for every bound value. Same P0 pattern found in gbslickgrid, gbdashboard, gbattachment. Must be replaced with an injected auth service signal. SessionStorage is also a P0 security issue per CLAUDE.md.

#### P0 — Standards: Not marked `pure: false` despite reading external state
Pipe reads `sessionStorage` inside `transform()`. Angular treats it as `pure: true` (default). This means Angular **caches** the result and will not re-run the pipe when the sessionStorage value changes (e.g. locale/format update after login). Results can be permanently stale. Must add `pure: false` or, preferably, eliminate the sessionStorage dependency entirely.

#### P0 — TypeScript: Pervasive `any`
`ReceivedDate: any`, `time: boolean`, `format: string`, `loginDTOs: any` — no interface for `LoginDTO` or `DateFormatInput`. Violates strict TS requirement in CLAUDE.md.

#### P1 — Functional: Fragile `/Date(...)` detection
**Line ~36:** Uses `substring(1,5) === 'Date'` to detect Microsoft JSON date format. This is brittle — it matches `/Datexxx(...)` or `/Date**/`. Should use full regex: `/^\/Date\(-?\d+\)\/$/`.

#### P1 — Functional: Hardcoded `'en-US'` locale in DatePipe
**Line ~33:** `new DatePipe('en-US')` — locale is hardcoded. Should use the locale from Angular's `LOCALE_ID` token or the user's login profile.

#### P1 — Performance: `new DatePipe('en-US')` instantiated per transform call
A new `DatePipe` instance is constructed on **every transform invocation**. For ~300 bindings per page, this creates 300 instances per CD cycle. Should be injected at the class level: `private datePipe = inject(DatePipe)`.

#### P1 — Functional: Format param overwrite ambiguity
If `format` param is provided, it overwrites both `dateformat` AND `timeformat`. If `time=true` but `format` is also passed, the intent is unclear. Document behavior or remove dual-param overlap.

### Recommendations
1. Remove `sessionStorage` read — inject `GbAuthService` signal for date format config
2. Add `pure: false` if any external state dependency remains
3. Inject `DatePipe` at class level rather than instantiating per call
4. Replace `substring(1,5) === 'Date'` with `/^\/Date\(-?\d+\)\/$/` regex
5. Define `ILoginDTO` interface and `DateFormatConfig` interface
6. Extract `/Date(...)` parsing into shared `parseJsonDate(s: string): Date | null` utility (shared with TimeFormatter)

---

## Pipe 2 — NumberFormatPipe

**File:** [libs/gbpipes/numberformatter.pipe.ts](libs/gbpipes/numberformatter.pipe.ts)

### Issues

#### P0 — Security: sessionStorage LoginDTO read
**Line 7:** `loginDTO = JSON.parse(sessionStorage.getItem('LoginDTO') as any);`

This is a **class field initializer** — runs once at pipe instantiation (not per transform). However, it reads sensitive auth data directly from sessionStorage (P0 security violation per CLAUDE.md). If the user's locale/currency format changes between sessions without page reload, the pipe retains the stale value. Replace with `GbAuthService` signal.

#### P0 — Standards: Not marked `pure: false`
Pipe reads sessionStorage at instantiation; session data can change. Current `pure: true` default means Angular caches transform results and will not re-run when format config changes. Must be `pure: false` or eliminate the sessionStorage dependency.

#### P0 — TypeScript: `any` everywhere
`value: any`, `loginDTO: any`, return type implicit. No `ICurrencyFormatConfig` interface.

#### P1 — Functional: Hardcoded `'en-IN'` locale
**Line ~19:** `toLocaleString('en-IN', ...)` — permanently Indian locale formatting regardless of user's country. Violates i18n requirement. Use `navigator.language` or a config service.

#### P1 — Functional: Fragile CurrencyFormat parsing
**Line ~17:** `.split('.')` assumes the format string uses `.` as separator (e.g. `"XXX.00"`). Breaks for non-en locales that use `,` (e.g. `"XXX,00"`).

#### P1 — Functional: NaN returns `'0.00'`
**Lines ~12–13:** When input is NaN, returns the string `'0.00'`. This silently masks missing or corrupt data as zero — misleading in financial UI. Should return `null` or the original value.

### Recommendations
1. Remove `sessionStorage` read — inject `GbAuthService` signal for currency format config
2. Add `pure: false`
3. Define `ICurrencyFormatConfig` and `ILoginDTO` interfaces
4. Use Angular's `CurrencyPipe` with locale from `LOCALE_ID` token
5. Return `null` (or `''`) for NaN instead of `'0.00'`
6. Do not hardcode `'en-IN'`

---

## Pipe 3 — NullSuppressionPipe

**File:** [libs/gbpipes/nullsupression.pipe.ts](libs/gbpipes/nullsupression.pipe.ts)

### Issues

#### P0 — Functional: Loose `==` comparison for string `"null"`
**Line ~8:** `value == "null"` uses loose equality — type-coercive and unpredictable. Use strict `===`. More importantly, this only handles the literal string `"null"` (from JSON serialization) and actual `null`/`undefined`, but misses: empty string `""`, whitespace-only `"   "`, `NaN`, `0`, `false`. The "suppression" contract is underspecified.

#### P1 — TypeScript: Return type `any`
Return type should be `string` not `any`.

#### P1 — Naming: Class name typo
Class is `NullSupressionPipe` (one `s`). Pipe `name` is `'NullSuppression'` (two `s`). The file is `nullsupression.pipe.ts`. All three should be `NullSuppression`.

#### P1 — Functional: Incomplete null/empty contract
Callers may expect suppression of `""`, `"undefined"`, `"NaN"`, or whitespace. Current implementation only handles `null`, `undefined`, and `"null"`. Unclear whether that is intentional. Needs documentation or extension.

### Recommendations
1. Use strict `===` equality
2. Rename class and file to `NullSuppressionPipe` / `nullsuppression.pipe.ts`
3. Return type: `string`
4. Document and test full contract: what values get suppressed, what is returned?
5. Consider handling `""`, `"undefined"`, `"NaN"`, whitespace per caller expectations
6. Add unit tests: `null`, `undefined`, `"null"`, `0`, `false`, `""`, `"   "`, `NaN`

---

## Pipe 4 — ShortNumberPipe

**File:** [libs/gbpipes/short-number.pipe.ts](libs/gbpipes/short-number.pipe.ts)

### Issues

#### P0 — Functional: M (Million) scale is wrong by 10×
The scale array defines `{ key: 'M', value: Math.pow(10, 7) }` = 10,000,000. One million is `10^6 = 1,000,000`. So `1,500,000` will NOT display as `1.5M` — it will only show `M` for values ≥ 10M. This is a **factual bug** affecting all "millions" display.

#### P1 — Functional: Locale-specific `'L'` abbreviation
`{ key: 'L', value: Math.pow(10, 5) }` represents Indian Lakh (1,00,000). This is not a standard international abbreviation. For non-Indian users, `'L'` is meaningless. Either document and restrict usage to Indian locale, or replace with `'K'` (100K) in international context.

#### P1 — Functional: Inconsistent return type
- **Line ~11:** Returns bare number `0` when input is `<= 0`
- **Line ~34:** Returns string like `"1.5M"`
- Return type is `any` — callers cannot rely on consistent type

#### P1 — TypeScript: Unused param
`args?: any` declared in `transform()` signature but never used.

#### P1 — Performance: `Math.pow()` recalculated every transform
Scale values (`Math.pow(10, 12)` etc.) computed on every call. Should be a static `const` array defined once.

#### P1 — Functional: Hardcoded 1 decimal place
Rounding factor hardcoded as `Math.pow(10, 1)` — always 1 decimal. Should be a configurable pipe parameter with default.

### Recommendations
1. Fix M: `Math.pow(10, 7)` → `Math.pow(10, 6)`
2. Standardize to `{ K: 1e3, M: 1e6, B: 1e9, T: 1e12 }` — document L as India-specific variant
3. Always return `string` — return `'0'` not `0`
4. Add `decimals = 1` param: `transform(number: number, decimals = 1): string`
5. Extract scale as `static readonly SCALES` class const
6. Remove unused `args` param
7. Add unit tests: 0, 999, 1000, 999999, 1000000, 1500000, negative numbers

---

## Pipe 5 — SpecialCharacterFormatterPipe

**File:** [libs/gbpipes/specialcharacterformater.pipe.ts](libs/gbpipes/specialcharacterformater.pipe.ts)

### Issues

#### P0 — Security: `JSON.parse()` injection risk
**Line ~10:** `JSON.parse('"' + escapedString + '"')` — if `escapedString` contains an unescaped `"` character (possible if the replace on line 9 doesn't catch all cases), the resulting string breaks JSON syntax and either throws or produces unexpected output. No try/catch. A throw propagates out of the pipe and crashes the template. This is an XSS-adjacent input handling risk.

#### P0 — Functional: No error handling for `JSON.parse`
`JSON.parse` throws `SyntaxError` on malformed input. Pipe has no try/catch. Any bad input (empty string, partial escape sequences) will **throw uncaught into the Angular CD cycle**, breaking the entire component.

#### P1 — Functional: Ambiguous intent
The pipe unescapes JSON string escape sequences (`\n`, `\"`, `\\`). This is a very narrow use case (displaying escaped JSON field values). The pipe name `SpecialCharacterFormatter` suggests broader use. No JSDoc explaining why or when to use it. Many callers may be using it incorrectly.

#### P1 — Performance: Regex instances per transform
`/\\\\/g` and `/\"/g` instantiated on every `transform()` call. Should be `static readonly` class fields.

#### P1 — Naming: Typo in file and class
File: `specialcharacterformater.pipe.ts` (missing `t`). Class: same typo. Pipe name is `SpecialCharacterFormatter` (correct). All three should match.

#### P1 — TypeScript: No return type
Return type should be `string`.

### Recommendations
1. Wrap `JSON.parse` in try/catch, return original value on error
2. Document the use case precisely in JSDoc (when to use vs not)
3. Validate input before parse: `if (!value || typeof value !== 'string') return '';`
4. Extract static regexes: `static readonly BACKSLASH_RE = /\\\\/g;`
5. Fix typos: `SpecialCharacterFormatter` everywhere
6. Return type: `string`
7. Consider replacing with a well-tested utility: `JSON.parse(JSON.stringify('"'+s+'"'))` is not the right tool — use a proper unescape function

---

## Pipe 6 — TimeFormatterPipe

**File:** [libs/gbpipes/timeformatter.pipe.ts](libs/gbpipes/timeformatter.pipe.ts)

### Issues

#### P1 — i18n: Hardcoded English strings
**Lines ~31, 38:** Returns `'Invalid Time'`, `'AM'`, `'PM'` as hardcoded English. Per CLAUDE.md all display strings must use Transloco keys. In Arabic (RTL) locale, AM/PM rendering also differs.

#### P1 — Functional: 12-hour format not configurable
Always outputs 12-hour AM/PM format. Users in 24-hour locales (most of Europe, Middle East) will get wrong format. Should accept a `format` param: `transform(epochString, format: '12h' | '24h' = '12h')` or read from user locale config.

#### P1 — Functional: No timezone handling
`new Date(epoch)` uses the browser's local timezone. If epoch values are stored as UTC timestamps, the displayed time will be offset for users in non-UTC timezones. This should be documented or configurable.

#### P1 — TypeScript: Missing null guard
`epochString: string` — if `null` or `undefined` is passed, string operations throw. Accept `string | null | undefined` with early return.

#### P0 — Architecture: Duplicate `/Date(...)` parsing with DateFormatterPipe
Both `DateFormatterPipe` and `TimeFormatterPipe` parse Microsoft JSON `/Date(\d+)/` format independently with slightly different implementations. This is a maintenance hazard — bugs in one parser will not be fixed in the other. Extract to shared `parseJsonDate(s: string): number | null` utility.

### Recommendations
1. Replace `'Invalid Time'`, `'AM'`, `'PM'` with Transloco keys
2. Add `format: '12h' | '24h' = '12h'` param
3. Document timezone assumption or add `timezone` param
4. Type: `epochString: string | null | undefined`
5. Extract `/Date(...)` parsing shared utility
6. Consider delegating entirely to Angular's `DatePipe` with `'shortTime'` format

---

## Pipe 7 — TruncatePipe

**File:** [libs/gbpipes/truncatetext.pipe.ts](libs/gbpipes/truncatetext.pipe.ts)

### Issues

#### P0 — TypeScript: Return type violation
**Line ~13:** Returns `value` when null/undefined, but return type is `string`. TypeScript may allow this if `value` types are loosely defined but at runtime callers expecting `string` receive `null`. Should be: `if (!value) return '';`

#### P1 — Standards: Missing `standalone: true`
The module uses this pipe but it lacks `standalone: true` in `@Pipe`. All new pipes should declare `standalone: true` per Angular 20 best practices.

#### P1 — Functional: Trim-after-slice shortens below requested length
**Line ~17:** `value.slice(0, length).trim()` — trailing whitespace is trimmed AFTER slicing. Example: `"hello world "` with `length=12` → `slice(0,12)` = `"hello world "` → `.trim()` = `"hello world"` (11 chars, suffix not appended). But `"hello   "` with `length=8` → `slice(0,8)` = `"hello   "` → `.trim()` = `"hello"` (5 chars — significantly less than 8). Should trim first, then slice.

#### P1 — Functional: Negative length not validated
`length ?? 40` uses nullish coalesce but passes through negative values. `slice(-5)` returns the last 5 chars — not the intended behavior.

#### P1 — Functional: Suffix hardcoded as `'...'` default
Suffix `'...'` is the default but isn't a Transloco key. If used in RTL (Arabic) layout, `'...'` position semantics change. Should document or make the suffix locale-aware.

### Recommendations
1. Fix return: `if (!value) return '';`
2. Add `standalone: true` to `@Pipe` decorator
3. Fix order: trim first, then slice: `value.trim().slice(0, length)`
4. Validate negative length: `const safeLength = Math.max(0, length ?? 40);`
5. Type: `value: string | null | undefined`, return `string`
6. Add unit tests: null, empty string, length=0, length > value, trailing whitespace

---

## Pipe 8 — SafePipe (CRITICAL)

**File:** [libs/gbpipes/safepipe.pipe.ts](libs/gbpipes/safepipe.pipe.ts)

### Issues

#### P0 CRITICAL — Security: `bypassSecurityTrustResourceUrl`
**Line ~10:** `sanitizer.bypassSecurityTrustResourceUrl(url)`

**This is an explicit P0 violation listed in CLAUDE.md§Security:** "No `bypassSecurityTrustHtml` or `bypassSecurityTrustResourceUrl`."

This pipe disables Angular's entire XSS protection for resource URLs. Any `javascript:`, `data:`, or `vbscript:` URI passed through this pipe is injected unguarded into `src`, `href`, or other attributes. Attackers who control any URL field (from API responses, form inputs, query params) can execute arbitrary JavaScript.

The pipe has **~50 active usages** across the codebase — each is a potential XSS attack surface.

#### P0 — Security: No protocol whitelist
No check for allowed URL protocols. Accepts `javascript:alert(1)`, `data:text/html,...`, `vbscript:` etc.

#### P0 — Standards: Constructor injection
**Line ~8:** `constructor(private sanitizer: DomSanitizer) { }` — must use `inject(DomSanitizer)` per CLAUDE.md.

#### P1 — TypeScript: `any` types
`url: any`, `args?: any`, return type implicit. Should be `url: string`, return `SafeResourceUrl`.

#### P1 — Standards: Unused `args` param
`args?: any` is declared but never used.

### Deprecation Plan
1. **Immediately:** Add `@deprecated` JSDoc and lint rule blocking new usages
2. **Audit all ~50 usages:** For each, determine the legitimate URL source
3. **Replace with service-based validation:**
   ```typescript
   // In GbSafeUrlService
   getSafeUrl(url: string): SafeResourceUrl | null {
     const allowed = /^https?:\/\//i;
     if (!allowed.test(url)) return null;
     return this.sanitizer.bypassSecurityTrustResourceUrl(url);
   }
   ```
4. **Template pattern:**
   ```html
   <!-- Before (unsafe) -->
   <iframe [src]="url | safe"></iframe>

   <!-- After -->
   <iframe [src]="safeUrlService.getSafeUrl(url)"></iframe>
   ```
5. **Delete the pipe** once all usages are migrated

---

## Pipe 9 — TitleCaseProperPipe (Inline)

**File:** [features/gbfolderview/gbfolderview.component.ts:45](features/gbfolderview/gbfolderview.component.ts#L45)

### Issues

#### P1 — Architecture: Inline pipe not in shared library
Defined inline inside a component file. Cannot be reused. If other components need title-case formatting, they will duplicate this pipe. Should be extracted to `libs/gbpipes/`.

#### P1 — Functional: Redundant vs Angular's built-in `TitleCasePipe`
Angular's `@angular/common` provides `TitleCasePipe` which does the same thing. Unless there is a specific behavioral difference (e.g. handling of apostrophes), this is unnecessary duplication.

#### P1 — Functional: Apostrophe handling
`/\b\w/g` capitalizes after word boundaries. `"it's a test"` → `"It'S A Test"` (the `s` after apostrophe is incorrectly capitalized). Angular's built-in `TitleCasePipe` has the same limitation — document or fix.

#### P1 — TypeScript: No null guard in signature
`value: string` — but `if (!value)` early return handles it at runtime. Should type as `value: string | null | undefined`.

#### P0 — Context: Parent component has multiple P0 violations
`MenuTreeComponent` (the host component) has:
- 9× `console.log` calls (lines 115, 122, 128, 132, 143, 149, 156, 157, 183)
- Constructor injection (lines 100–102) instead of `inject()`
- `AfterViewChecked` + `detectChanges()` (line 166) — anti-pattern causing infinite CD loops
- Missing `ChangeDetectionStrategy.OnPush`
- `TreeData: any`, `navItems: any`, `filteredDataSource: any` — all untyped
- `private formSubscription` declared (line 93) but never subscribed or cleaned up

### Recommendations
1. Extract to `libs/gbpipes/titlecaseproper.pipe.ts` or delete and use Angular's `TitleCasePipe`
2. Type: `value: string | null | undefined`, return `string`
3. Fix apostrophe behavior or document
4. Address host component P0 violations (console.log, constructor injection, detectChanges anti-pattern)

---

## Cross-Cutting Issues

### Security Summary (P0)
| Issue | Pipes Affected | Impact |
|---|---|---|
| `sessionStorage.getItem('LoginDTO')` in transform | DateFormatter, NumberFormat | Every CD cycle reads sensitive auth data; violates CLAUDE.md |
| `bypassSecurityTrustResourceUrl` | SafePipe | XSS attack surface on all 50 usages |
| `JSON.parse()` with no error handling | SpecialCharacterFormatter | Template crash on malformed input |
| No protocol whitelist | SafePipe | Allows `javascript:`, `data:` URIs |

### Standards Violations (P0)
| Violation | Pipes Affected |
|---|---|
| `any` types throughout | All 8 pipes |
| Not marked `pure: false` despite reading external state | DateFormatter, NumberFormat |
| Constructor injection instead of `inject()` | SafePipe |
| No `standalone: true` | NumberFormat, NullSuppression, ShortNumber, SpecialCharacterFormatter, TimeFormatter, TruncatePipe, SafePipe |
| Hardcoded English display strings | TimeFormatter (`'AM'`, `'PM'`, `'Invalid Time'`), TruncatePipe (`'...'`) |

### Performance Issues (P1)
| Issue | Pipe | Detail |
|---|---|---|
| `new DatePipe('en-US')` per transform | DateFormatter | Creates N instances per CD cycle |
| Regex literals inside `transform()` | SpecialCharacterFormatter, TimeFormatter | New regex objects per call; use `static readonly` |
| `Math.pow()` inside `transform()` | ShortNumber | Recalculated per call; use `static readonly SCALES` |
| sessionStorage read per transform call | DateFormatter | I/O operation on every CD cycle |

### Functional Bugs (P1)
| Bug | Pipe | Line | Impact |
|---|---|---|---|
| M scale = 10^7 instead of 10^6 | ShortNumber | ~20 | All million-range numbers display incorrectly |
| Loose `==` for null check | NullSuppression | ~8 | Type coercion edge cases |
| Trim after slice | TruncatePipe | ~17 | Actual truncation shorter than requested |
| Null return as `string` | TruncatePipe | ~13 | Runtime null returned as string type |
| Fragile `/Date(...)` regex | DateFormatter | ~36 | False positives on similar strings |
| Duplicate `/Date(...)` parsers | DateFormatter + TimeFormatter | — | Bugs fixed in one won't apply to other |
| 12-hour AM/PM hardcoded | TimeFormatter | ~29-38 | Wrong format for 24-hour locales |

### Maintainability (P1)
| Issue | Detail |
|---|---|
| 0% test coverage | No spec files for any pipe |
| Class name typos | `NullSupressionPipe` (missing `s`), `SpecialCharacterFormaterPipe` (missing `t`) |
| File name typos | `nullsupression.pipe.ts`, `specialcharacterformater.pipe.ts` |
| Inline pipe in component | `TitleCaseProperPipe` in gbfolderview — not reusable |
| Shared parsing logic not extracted | `/Date(\d+)` parsed differently in DateFormatter and TimeFormatter |
| No JSDoc on any pipe | Purpose, params, return values, and edge cases undocumented |

---

## Actionable Recommendations

### IMMEDIATE — P0 (Block deployment)

1. **Deprecate and remove `SafePipe`**
   - Add `@deprecated` JSDoc
   - Audit all ~50 template usages
   - Replace with `GbSafeUrlService.getSafeUrl(url)` that validates protocols

2. **Remove `sessionStorage` reads from `DateFormatterPipe` and `NumberFormatPipe`**
   - Inject `GbAuthService` (or equivalent) signal for locale/format config
   - `private authService = inject(GbAuthService);`
   - `private dateFormat = computed(() => this.authService.currentUser()?.dateFormat ?? 'dd/MM/yyyy');`

3. **Mark `DateFormatterPipe` and `NumberFormatPipe` as `pure: false`**
   - Until sessionStorage dependency is removed, stale values are a bug
   - `@Pipe({ name: 'DateFormatter', standalone: true, pure: false })`

4. **Add try/catch to `SpecialCharacterFormatterPipe`**
   - Wrap `JSON.parse()` — return original value on error

5. **Fix `NullSuppressionPipe` loose equality**
   - `value == "null"` → `value === "null"`

### HIGH — P1 (Fix before release)

6. **Fix `ShortNumberPipe` M scale**
   - `Math.pow(10, 7)` → `Math.pow(10, 6)`

7. **Replace hardcoded strings with Transloco keys**
   - `TimeFormatterPipe`: `'AM'` → `translocoService.translate('time.am')`, etc.
   - `TruncatePipe`: document that `'...'` suffix is not i18n'd

8. **Fix constructor injection in `SafePipe`**
   - `constructor(private sanitizer: DomSanitizer)` → `private sanitizer = inject(DomSanitizer)`
   - (Before deletion)

9. **Add `standalone: true` to all pipes**
   - Enables tree-shaking and lazy loading compatibility

10. **Inject `DatePipe` in `DateFormatterPipe`** instead of `new DatePipe('en-US')` per call

11. **Extract shared `/Date(...)` parser**
    - `libs/gbpipes/utils/parse-json-date.ts`
    - Used by both `DateFormatterPipe` and `TimeFormatterPipe`

12. **Fix `TruncatePipe` type safety**
    - Return `''` on null/undefined, not the `null` itself

### MEDIUM — P2 (Technical debt)

13. Add unit tests for all pipes (especially date/number edge cases)
14. Fix class and file name typos: `NullSuppression`, `SpecialCharacterFormatter`
15. Extract static regex and scale consts (SpecialCharacterFormatter, ShortNumber, TimeFormatter)
16. Add `format` param to `TimeFormatterPipe` for 12h/24h
17. Extract `TitleCaseProperPipe` to `libs/gbpipes/` or replace with Angular's `TitleCasePipe`
18. Add JSDoc to all pipes documenting params, return values, and edge cases

---

## Code Health Scorecard

| Metric | Score | Notes |
|---|---|---|
| Test coverage | F (0%) | No spec files; all pipes untested |
| Type safety | F | All pipes use `any`; no interfaces for I/O |
| Security | F | `bypassSecurityTrust*`, sessionStorage reads, no input validation |
| i18n compliance | D | Hardcoded English in TimeFormatter; no Transloco keys |
| Performance | C+ | SessionStorage per transform, DatePipe instantiation per call |
| Standards adherence | D | Constructor injection, no `pure: false`, no `standalone` |
| Functional correctness | C | ShortNumber M off by 10×, TruncatePipe trim order, NullSuppression loose equality |

---

## Files to Fix

| File | Priority | Key Actions |
|---|---|---|
| [libs/gbpipes/safepipe.pipe.ts](libs/gbpipes/safepipe.pipe.ts) | P0 | Deprecate and delete; audit 50 usages |
| [libs/gbpipes/dateformat.pipe.ts](libs/gbpipes/dateformat.pipe.ts) | P0 | Remove sessionStorage, inject DatePipe, add `pure: false` |
| [libs/gbpipes/numberformatter.pipe.ts](libs/gbpipes/numberformatter.pipe.ts) | P0 | Remove sessionStorage, add `pure: false`, fix locale |
| [libs/gbpipes/nullsupression.pipe.ts](libs/gbpipes/nullsupression.pipe.ts) | P0 | Fix `==`, rename file/class, strict return type |
| [libs/gbpipes/specialcharacterformater.pipe.ts](libs/gbpipes/specialcharacterformater.pipe.ts) | P0 | Add try/catch, rename, static regex |
| [libs/gbpipes/short-number.pipe.ts](libs/gbpipes/short-number.pipe.ts) | P1 | Fix M scale (10^7→10^6), static consts, return type |
| [libs/gbpipes/timeformatter.pipe.ts](libs/gbpipes/timeformatter.pipe.ts) | P1 | Transloco keys, extract parser, add format param |
| [libs/gbpipes/truncatetext.pipe.ts](libs/gbpipes/truncatetext.pipe.ts) | P1 | Fix null return, trim order, add `standalone: true` |
| [features/gbfolderview/gbfolderview.component.ts](features/gbfolderview/gbfolderview.component.ts) | P1 | Extract pipe to shared lib, fix host component violations |
