@webex/contact-center 3.12.0-next.84 → 3.12.0-next.85
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.
- package/.sdd/manifest.json +876 -0
- package/AGENTS.md +65 -409
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +46 -0
- package/ai-docs/GETTING_STARTED.md +168 -0
- package/ai-docs/GLOSSARY.md +43 -0
- package/ai-docs/README.md +25 -18
- package/ai-docs/REVIEW_CHECKLIST.md +41 -0
- package/ai-docs/RULES.md +187 -198
- package/ai-docs/SECURITY.md +52 -0
- package/ai-docs/SERVICE_STATE.md +48 -0
- package/ai-docs/SPEC_INDEX.md +65 -0
- package/ai-docs/adr/0001-spec-source-policy.md +55 -0
- package/ai-docs/adr/README.md +8 -0
- package/ai-docs/adr/_adr-template.md +31 -0
- package/ai-docs/contact-center-spec.md +341 -0
- package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
- package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
- package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
- package/ai-docs/templates/README.md +1 -1
- package/ai-docs/templates/new-method/04-validation.md +1 -1
- package/ai-docs/templates/new-service/05-validation.md +1 -1
- package/dist/webex.js +1 -1
- package/package.json +1 -1
- package/src/metrics/ai-docs/AGENTS.md +2 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +2 -0
- package/src/metrics/ai-docs/metrics-spec.md +854 -0
- package/src/services/agent/ai-docs/AGENTS.md +3 -1
- package/src/services/agent/ai-docs/ARCHITECTURE.md +2 -0
- package/src/services/agent/ai-docs/agent-spec.md +504 -0
- package/src/services/ai-docs/AGENTS.md +4 -2
- package/src/services/ai-docs/services-spec.md +492 -0
- package/src/services/config/ai-docs/AGENTS.md +3 -1
- package/src/services/config/ai-docs/ARCHITECTURE.md +2 -0
- package/src/services/config/ai-docs/config-spec.md +669 -0
- package/src/services/core/ai-docs/AGENTS.md +4 -2
- package/src/services/core/ai-docs/ARCHITECTURE.md +2 -0
- package/src/services/core/ai-docs/core-spec.md +783 -0
- package/src/services/task/ai-docs/AGENTS.md +2 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +11 -2
- package/src/services/task/ai-docs/task-spec.md +1319 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +38 -75
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +6 -4
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2115 -0
- package/src/utils/AGENTS.md +3 -1
- package/src/utils/ai-docs/utils-spec.md +381 -0
- package/umd/contact-center.min.js +2 -2
package/src/utils/AGENTS.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Utils
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Legacy/reference-only.** Canonical SDD: [`ai-docs/utils-spec.md`](ai-docs/utils-spec.md). Use the package [manifest](../../.sdd/manifest.json) and [`SPEC_INDEX.md`](../../ai-docs/SPEC_INDEX.md) for routing; code and tests remain the behavioral referee.
|
|
4
|
+
>
|
|
5
|
+
> **Legacy scope:** This guide covers shared pagination/cache contracts used by data services. For repository rules and cross-service conventions, see the [root orchestrator AGENTS.md](../../AGENTS.md).
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# Utils — SPEC
|
|
2
|
+
|
|
3
|
+
> Start here → root [`AGENTS.md`](../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../ai-docs/ARCHITECTURE.md). This is the module's canonical specification.
|
|
4
|
+
|
|
5
|
+
## Metadata
|
|
6
|
+
|
|
7
|
+
| Field | Value |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Module id | `utils` |
|
|
10
|
+
| Source path(s) | `src/utils` |
|
|
11
|
+
| Doc kind | Module spec |
|
|
12
|
+
| Coverage score | Partial (manifest-authoritative); 15/15 required document fields present |
|
|
13
|
+
| Generated from | `module-spec` @ SDLC template library `0.2.1` |
|
|
14
|
+
| generated_by / approved_by / updated_at | Codex generator / developer-approved follow-up review remediation / 2026-07-21 |
|
|
15
|
+
| Validation status | Follow-up validation passed (independent Claude fallback, 2026-07-21); coverage remains Partial |
|
|
16
|
+
|
|
17
|
+
## Evidence Rules
|
|
18
|
+
Every requirement cites stable source and test file paths. Code/tests are the behavioral referee; routed source text supplies explicit intent and rationale. Missing or contradictory evidence blocks promotion.
|
|
19
|
+
|
|
20
|
+
## Source Material Register
|
|
21
|
+
| Source material | Scope | Decision | Detail location or disposition |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| Reviewed prior module guides and architecture material | overview / architecture / API / tests | used and code-checked | Content is placed by meaning throughout this specification; exact routing remains in the manifest. |
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
Utils is one of nine confirmed Contact Center SDK modules. Own shared pagination contracts and the bounded in-memory page cache used by Contact Center data services. Existing reviewed documentation is migrated by meaning and code/tests remain the behavioral referee.
|
|
27
|
+
|
|
28
|
+
The utils scope currently provides shared pagination and cache behavior for contact-center data services:
|
|
29
|
+
|
|
30
|
+
- **Typed Pagination Contracts**: Reusable interfaces for response metadata and query params
|
|
31
|
+
|
|
32
|
+
- **Generic In-Memory Page Caching**: `PageCache<T>` utility for simple pagination reuse
|
|
33
|
+
|
|
34
|
+
- **Cache Safety Rules**: Explicit bypass behavior for search/filter/sort scenarios
|
|
35
|
+
|
|
36
|
+
- **Spec-Driven Utility Workflow**: Utility-specific implementation and validation flow documented inline in this file
|
|
37
|
+
|
|
38
|
+
| Component | File | Description |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `PageCache` | [`PageCache.ts`](../PageCache.ts) | Generic in-memory cache utility for paginated API responses with TTL expiry and helper methods for key generation and cache eligibility checks. |
|
|
41
|
+
| `Pagination Types` | [`PageCache.ts`](../PageCache.ts) | `PaginationMeta`, `PaginatedResponse<T>`, `BaseSearchParams`, and `PageCacheEntry<T>` shared across data services. |
|
|
42
|
+
| `Pagination Defaults` | [`PageCache.ts`](../PageCache.ts) | `PAGINATION_DEFAULTS` (`PAGE`, `PAGE_SIZE`) used by services for consistent request defaults. |
|
|
43
|
+
| `Specs Workflow` | `ai-docs/utils-spec.md` | Canonical flow for spec-driven utility changes, acceptance criteria, and drift checks. |
|
|
44
|
+
|
|
45
|
+
## Purpose / Responsibility
|
|
46
|
+
Own shared pagination contracts and the bounded in-memory page cache used by Contact Center data services.
|
|
47
|
+
|
|
48
|
+
## Stack
|
|
49
|
+
TypeScript 5.4 generics, in-memory Map/clock, LoggerProxy, Jest consumer tests.
|
|
50
|
+
|
|
51
|
+
## Folder / Package Structure
|
|
52
|
+
```text
|
|
53
|
+
src/utils/
|
|
54
|
+
├── AGENTS.md
|
|
55
|
+
├── PageCache.ts
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
src/utils/
|
|
60
|
+
├── AGENTS.md # Preserved legacy, noncanonical utils guide
|
|
61
|
+
└── PageCache.ts # Generic cache + pagination contracts/defaults
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Key Files (source of truth)
|
|
65
|
+
| File | Holds |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `src/utils/PageCache.ts` | Authoritative PageCache implementation plus pagination/cache contracts and defaults. |
|
|
68
|
+
| `src/types.ts` | Package-wide public response/search types that consume PageCache contracts; not a Utils implementation. |
|
|
69
|
+
| `src/services/AddressBook.ts` | Services-layer PageCache consumer. |
|
|
70
|
+
| `src/services/EntryPoint.ts` | Services-layer PageCache consumer. |
|
|
71
|
+
| `src/services/Queue.ts` | Services-layer PageCache consumer. |
|
|
72
|
+
|
|
73
|
+
## Public Surface
|
|
74
|
+
| Surface | Availability | Source |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `PageCache<T>` | exported from the Utils module file for internal service use; not re-exported by package root | `src/utils/PageCache.ts` |
|
|
77
|
+
| `PaginationMeta`, `PaginatedResponse<T>`, `BaseSearchParams` | exported by PageCache module and consumed by package-wide types | `src/utils/PageCache.ts`, `src/types.ts` |
|
|
78
|
+
| `PAGINATION_DEFAULTS`, `PageCacheEntry<T>`, `CacheValidationParams` | PageCache module exports; not package-root exports | `src/utils/PageCache.ts` |
|
|
79
|
+
| AddressBook/EntryPoint/Queue public response/search aliases | package-root exports whose definitions indirectly use PageCache contracts | `src/types.ts`, `src/index.ts` |
|
|
80
|
+
|
|
81
|
+
No claim is made that `src/types.ts`, AddressBook, EntryPoint, or Queue is implemented by Utils.
|
|
82
|
+
|
|
83
|
+
## Requires (dependencies)
|
|
84
|
+
- LoggerProxy
|
|
85
|
+
- In-memory Map and wall clock
|
|
86
|
+
- AddressBook, EntryPoint, and Queue consumers
|
|
87
|
+
|
|
88
|
+
## Requirements
|
|
89
|
+
| ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence |
|
|
90
|
+
|---|---|---|---|---|---|---|
|
|
91
|
+
| UTILS-R-001 | Use cache only for simple pagination requests without search, filter, attributes, or sortBy. | Parameterized queries cannot safely reuse a page keyed only by scope/page/pageSize. | `src/utils/PageCache.ts` | `test/unit/spec/services/AddressBook.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
|
|
92
|
+
| UTILS-R-002 | Build cache keys from a caller-defined `scopeId` plus `page:pageSize`: `orgId` for EntryPoint/Queue and `bookId` for AddressBook. | Consumer scope and page boundaries prevent cross-scope or cross-page reuse. | `src/utils/PageCache.ts`, `src/services/AddressBook.ts`, `src/services/EntryPoint.ts`, `src/services/Queue.ts` | `test/unit/spec/services/AddressBook.ts`, `test/unit/spec/services/EntryPoint.ts`, `test/unit/spec/services/Queue.ts` | `PageCache.buildCacheKey` names its first implementation parameter `orgId`, but runtime callers establish the broader `scopeId` semantics. | PRESENT |
|
|
93
|
+
| UTILS-R-003 | Expire entries after the configured five-minute TTL and delete them on stale lookup. | Bounded staleness prevents indefinite reuse of remote service data. | `src/utils/PageCache.ts` | None | Direct fake-clock expiration coverage is absent from current consumer tests. | PRESENT |
|
|
94
|
+
| UTILS-R-004 | Cache data with total-page/record metadata and allow owning consumers to clear the cache. | Paginated services need consistent metadata without transferring ownership of remote records to the SDK. | `src/utils/PageCache.ts` | `test/unit/spec/services/AddressBook.ts` | Cache hit/miss and metadata are exercised through consumers; `clearCache()` has no direct assertion. | PRESENT |
|
|
95
|
+
| UTILS-R-005 | Accept already-fetched page values from consuming services and never store or process credentials; use the consumer's scope value only as part of the in-memory key. | Authentication remains in Services/Core and scope-separated keys prevent unrelated pages from colliding. | `src/utils/PageCache.ts`, `src/services/core/WebexRequest.ts`, `src/services/AddressBook.ts`, `src/services/EntryPoint.ts`, `src/services/Queue.ts` | `test/unit/spec/services/AddressBook.ts` | None; security/auth ownership is explicit. | PRESENT |
|
|
96
|
+
| UTILS-R-006 | Keep PageCache free of rollout flags; consuming services decide whether to invoke it and query parameters determine cache eligibility. | Cache correctness must depend on request shape, not hidden deployment state. | `src/utils/PageCache.ts` | `test/unit/spec/services/AddressBook.ts`, `test/unit/spec/services/Queue.ts` | None; rollout applicability is explicitly N/A. | PRESENT |
|
|
97
|
+
|
|
98
|
+
## Design Overview
|
|
99
|
+
Utils separates its stable consumption boundary from collaborators so ownership and failure behavior stay explicit. Caching is intentionally limited to simple page browsing; search/filter/sort requests bypass cache to prevent incorrect reuse.
|
|
100
|
+
|
|
101
|
+
> **This is the authoritative documentation for the `src/utils` scope.** It covers shared pagination/cache contracts used by data services. For task routing and cross-service conventions, see the [root orchestrator AGENTS.md](../../../AGENTS.md).
|
|
102
|
+
|
|
103
|
+
Current consumers of `PageCache` and defaults:
|
|
104
|
+
|
|
105
|
+
| Consumer | File | Usage |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `AddressBook` | [`../../services/AddressBook.ts`](../../services/AddressBook.ts) | Caches paged address-book responses |
|
|
108
|
+
| `EntryPoint` | [`../../services/EntryPoint.ts`](../../services/EntryPoint.ts) | Caches paged entry-point responses |
|
|
109
|
+
| `Queue` | [`../../services/Queue.ts`](../../services/Queue.ts) | Caches paged queue responses |
|
|
110
|
+
| `Public type contracts` | [`../../types.ts`](../../types.ts) | Re-exports pagination/search contracts into SDK-facing types |
|
|
111
|
+
|
|
112
|
+
Cross-scope mention:
|
|
113
|
+
|
|
114
|
+
- Services-layer docs reference utils caching contracts at [`../../services/ai-docs/services-spec.md`](../../services/ai-docs/services-spec.md).
|
|
115
|
+
|
|
116
|
+
## Data Flow
|
|
117
|
+
```mermaid
|
|
118
|
+
flowchart LR
|
|
119
|
+
Service[AddressBook / EntryPoint / Queue] --> Eligible{canUseCache params?}
|
|
120
|
+
Eligible -->|search/filter/attributes/sort present| Backend[Fetch without cache]
|
|
121
|
+
Eligible -->|simple pagination| Key[buildCacheKey scopeId:page:pageSize]
|
|
122
|
+
Key --> Lookup[getCachedPage]
|
|
123
|
+
Lookup -->|fresh| Hit[Return cached page]
|
|
124
|
+
Lookup -->|missing or TTL expired| Backend
|
|
125
|
+
Backend --> Fetched{cache eligible and response has data?}
|
|
126
|
+
Fetched -->|yes| Store[cachePage data + total metadata]
|
|
127
|
+
Fetched -->|no| Result[Return uncached response]
|
|
128
|
+
Store --> Result
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Sequence Diagram(s)
|
|
132
|
+
Sequence coverage:
|
|
133
|
+
|
|
134
|
+
| Operation group | Diagram | Failure / recovery coverage |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| Eligible lookup, fetch, and store | Cache lifecycle | Advanced queries bypass cache; misses and expired entries fetch from the backend; failed backend calls never reach `cachePage`. |
|
|
137
|
+
| Explicit cache clearing | Clear all entries | `clearCache` removes all entries from that consumer-owned PageCache instance and logs the prior size. |
|
|
138
|
+
|
|
139
|
+
### Cache lifecycle
|
|
140
|
+
|
|
141
|
+
```mermaid
|
|
142
|
+
sequenceDiagram
|
|
143
|
+
participant Service as Data service
|
|
144
|
+
participant Cache as PageCache
|
|
145
|
+
participant API as Backend API
|
|
146
|
+
Service->>Cache: canUseCache(params)
|
|
147
|
+
alt simple pagination
|
|
148
|
+
Note over Service,Cache: scopeId = bookId (AddressBook) or orgId (EntryPoint/Queue)
|
|
149
|
+
Service->>Cache: buildCacheKey(scopeId, page, pageSize)
|
|
150
|
+
Service->>Cache: getCachedPage(key)
|
|
151
|
+
alt fresh hit
|
|
152
|
+
Cache-->>Service: PageCacheEntry
|
|
153
|
+
else miss or expired
|
|
154
|
+
Service->>API: fetch page
|
|
155
|
+
alt backend success with response data
|
|
156
|
+
API-->>Service: data + meta
|
|
157
|
+
Service->>Cache: cachePage(key, data, meta)
|
|
158
|
+
else backend rejection
|
|
159
|
+
API--xService: error
|
|
160
|
+
Note over Service,Cache: no cache write
|
|
161
|
+
else success without data
|
|
162
|
+
API-->>Service: response without data
|
|
163
|
+
Note over Service,Cache: return response without a cache write
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
else advanced query
|
|
167
|
+
Service->>API: fetch without cache
|
|
168
|
+
end
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Clear all entries
|
|
172
|
+
|
|
173
|
+
```mermaid
|
|
174
|
+
sequenceDiagram
|
|
175
|
+
participant Owner as Consumer service
|
|
176
|
+
participant Cache as Its PageCache instance
|
|
177
|
+
participant Log as LoggerProxy
|
|
178
|
+
Owner->>Cache: clearCache()
|
|
179
|
+
Cache->>Cache: capture size, then Map.clear()
|
|
180
|
+
Cache->>Log: log cleared entry count
|
|
181
|
+
Cache-->>Owner: void
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Class / Component Relationships
|
|
185
|
+
```mermaid
|
|
186
|
+
classDiagram
|
|
187
|
+
class PageCache~T~ {
|
|
188
|
+
+canUseCache(params) boolean
|
|
189
|
+
+buildCacheKey(scopeId, page, pageSize) string
|
|
190
|
+
+getCachedPage(key) PageCacheEntry
|
|
191
|
+
+cachePage(key, data, meta)
|
|
192
|
+
+clearCache()
|
|
193
|
+
+getCacheSize() number
|
|
194
|
+
}
|
|
195
|
+
class AddressBook
|
|
196
|
+
class EntryPoint
|
|
197
|
+
class Queue
|
|
198
|
+
AddressBook --> PageCache : consumes
|
|
199
|
+
EntryPoint --> PageCache : consumes
|
|
200
|
+
Queue --> PageCache : consumes
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Use Cases
|
|
204
|
+
- **UC-1 Cache eligibility:** AddressBook, EntryPoint, or Queue bypasses cache whenever search, filter, attributes, or sort is supplied. Evidence: `src/utils/PageCache.ts`, `test/unit/spec/services/AddressBook.ts`.
|
|
205
|
+
- **UC-2 Cache-key construction:** a simple page lookup uses `<scopeId>:page:pageSize`; AddressBook supplies `bookId`, while EntryPoint and Queue supply `orgId`. Evidence: `src/utils/PageCache.ts`, `src/services/AddressBook.ts`, `src/services/EntryPoint.ts`, `src/services/Queue.ts`.
|
|
206
|
+
- **UC-3 TTL lookup/expiry:** a fresh cached entry is returned; an expired entry is deleted and treated as a miss. Evidence: `src/utils/PageCache.ts`; no direct expiration test currently exists.
|
|
207
|
+
- **UC-4 Page insertion and clearing:** successful page data and normalized totals are cached, while `clearCache()` removes all entries for that service instance. Evidence: `src/utils/PageCache.ts`; consumer tests cover insertion/hit behavior, but not direct clearing.
|
|
208
|
+
|
|
209
|
+
## State Model
|
|
210
|
+
Utils retains only in-memory runtime state. Durable domain records remain owned by remote Webex services. State changes are driven by explicit calls, events, timers, or actor transitions documented below.
|
|
211
|
+
|
|
212
|
+
`PageCache<T>` provides a consistent caching model for paginated list APIs. It is optimized for simple page browsing and intentionally bypasses cache for parameterized query cases (`search`, `filter`, `attributes`, `sortBy`).
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import PageCache, {PAGINATION_DEFAULTS} from '../utils/PageCache';
|
|
216
|
+
|
|
217
|
+
const cache = new PageCache<MyItem>('MyService');
|
|
218
|
+
|
|
219
|
+
const page = PAGINATION_DEFAULTS.PAGE;
|
|
220
|
+
const pageSize = PAGINATION_DEFAULTS.PAGE_SIZE;
|
|
221
|
+
// AddressBook uses bookId; EntryPoint and Queue use orgId.
|
|
222
|
+
const scopeId = bookIdOrOrgId;
|
|
223
|
+
const cacheKey = cache.buildCacheKey(scopeId, page, pageSize);
|
|
224
|
+
|
|
225
|
+
// Include sortBy only for services that support sorting.
|
|
226
|
+
const canUseCache = cache.canUseCache({search, filter, attributes, sortBy});
|
|
227
|
+
|
|
228
|
+
if (canUseCache) {
|
|
229
|
+
const cachedEntry = cache.getCachedPage(cacheKey);
|
|
230
|
+
if (cachedEntry) {
|
|
231
|
+
return {
|
|
232
|
+
data: cachedEntry.data,
|
|
233
|
+
meta: {
|
|
234
|
+
page,
|
|
235
|
+
pageSize,
|
|
236
|
+
...cachedEntry.totalMeta,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const response = await fetchPageFromApi();
|
|
243
|
+
|
|
244
|
+
if (canUseCache && response.data) {
|
|
245
|
+
cache.cachePage(cacheKey, response.data, response.meta);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return response;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```mermaid
|
|
252
|
+
graph TD
|
|
253
|
+
A[Request arrives with scopeId/page/pageSize] --> B{canUseCache?}
|
|
254
|
+
B -->|No: search/filter/attributes/sortBy provided| C[Bypass cache and call API]
|
|
255
|
+
B -->|Yes| D[buildCacheKey scopeId:page:pageSize]
|
|
256
|
+
D --> E["getCachedPage(cacheKey)"]
|
|
257
|
+
E -->|Miss| C
|
|
258
|
+
E -->|Hit and not expired| F[Return cached data and totalMeta]
|
|
259
|
+
E -->|Hit but expired >= 5 minutes| G[Delete entry and treat as miss]
|
|
260
|
+
G --> C
|
|
261
|
+
C --> H[Receive API response]
|
|
262
|
+
H --> K{canUseCache and response has data?}
|
|
263
|
+
K -->|Yes| I["cachePage(cacheKey, data, meta)"]
|
|
264
|
+
K -->|No| J[Return uncached response]
|
|
265
|
+
I --> J[Return fresh response]
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Creates a typed cache instance and stores `apiName` for `LoggerProxy` context.
|
|
269
|
+
|
|
270
|
+
Returns `true` only when all of these are absent:
|
|
271
|
+
|
|
272
|
+
- `search`
|
|
273
|
+
|
|
274
|
+
- `filter`
|
|
275
|
+
|
|
276
|
+
- `attributes`
|
|
277
|
+
|
|
278
|
+
- `sortBy`
|
|
279
|
+
|
|
280
|
+
`sortOrder` alone does not trigger cache bypass because `CacheValidationParams` currently keys bypass on fields that materially change the query result set in existing consumers.
|
|
281
|
+
|
|
282
|
+
Builds deterministic cache key format:
|
|
283
|
+
|
|
284
|
+
```text
|
|
285
|
+
${scopeId}:${page}:${pageSize}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The implementation parameter is named `orgId`, but its value is the caller-defined `scopeId`: `bookId` in AddressBook and `orgId` in EntryPoint/Queue.
|
|
289
|
+
|
|
290
|
+
Behavior:
|
|
291
|
+
|
|
292
|
+
1. Returns `null` if key not found
|
|
293
|
+
|
|
294
|
+
2. Computes cache age in minutes
|
|
295
|
+
|
|
296
|
+
3. If age is `>= 5`, logs expiry, deletes entry, returns `null`
|
|
297
|
+
|
|
298
|
+
4. Otherwise returns cached entry
|
|
299
|
+
|
|
300
|
+
Stores entry with:
|
|
301
|
+
|
|
302
|
+
- `data`
|
|
303
|
+
|
|
304
|
+
- `timestamp`
|
|
305
|
+
|
|
306
|
+
- `totalMeta.totalPages`
|
|
307
|
+
|
|
308
|
+
- `totalMeta.totalRecords` mapped from `meta.totalRecords || meta.totalItems`
|
|
309
|
+
|
|
310
|
+
Clears all entries and logs cleared entry count.
|
|
311
|
+
|
|
312
|
+
Returns current in-memory entry count.
|
|
313
|
+
|
|
314
|
+
Note: `clearCache()` and `getCacheSize()` are available for future use and are not currently called by existing consumers.
|
|
315
|
+
|
|
316
|
+
## Business Rules & Invariants
|
|
317
|
+
- Utils must preserve its typed public/event contracts and must not invent backend states or responses. Enforced in `src/utils/PageCache.ts`.
|
|
318
|
+
- Security/auth applicability is limited to scope separation: PageCache receives already-fetched values, owns no credentials, and includes its caller-supplied scope value in cache keys.
|
|
319
|
+
- Rollout applicability is N/A: PageCache has no feature flag; consumers choose whether to use it and `canUseCache` decides eligibility from query parameters.
|
|
320
|
+
|
|
321
|
+
## Pitfalls
|
|
322
|
+
- Do not describe the first cache-key field as universally `orgId`: AddressBook passes `bookId`, while EntryPoint and Queue pass `orgId`.
|
|
323
|
+
- Do not cache queries containing `search`, `filter`, `attributes`, or `sortBy`; their result set is not represented in the key.
|
|
324
|
+
- Do not treat an expired entry as usable data. `getCachedPage` deletes it and returns `null`.
|
|
325
|
+
- Do not cache a rejected request or a response without data; only the consuming service owns the backend request and decides when to call `cachePage`.
|
|
326
|
+
|
|
327
|
+
## Module Do's / Don'ts
|
|
328
|
+
- DO create a separate `PageCache` instance per consuming service and pass that consumer's stable scope value into `buildCacheKey`.
|
|
329
|
+
- DO preserve the five-minute `>=` expiry boundary and the `totalRecords || totalItems` compatibility mapping.
|
|
330
|
+
- DO keep credentials, request execution, and backend-error handling in the consuming service/Core layers.
|
|
331
|
+
- DON'T add query dimensions without either extending the cache key or making `canUseCache` bypass them.
|
|
332
|
+
- DON'T claim `clearCache()` or TTL expiry is directly unit-tested until dedicated assertions exist.
|
|
333
|
+
|
|
334
|
+
When changing `src/utils` behavior or contracts:
|
|
335
|
+
|
|
336
|
+
1. Follow the workflow diagram below
|
|
337
|
+
|
|
338
|
+
2. Define acceptance criteria for contract and runtime behavior
|
|
339
|
+
|
|
340
|
+
3. Verify cache TTL, bypass rules, and key schema
|
|
341
|
+
|
|
342
|
+
4. Validate no spec drift before shipping
|
|
343
|
+
|
|
344
|
+
```mermaid
|
|
345
|
+
flowchart TD
|
|
346
|
+
A[Change proposed in src/utils] --> B{Classify change scope}
|
|
347
|
+
B -->|Contract change| C[Document expected API/type behavior]
|
|
348
|
+
B -->|Runtime behavior change| D[Document cache behavior and TTL impact]
|
|
349
|
+
B -->|Both| C
|
|
350
|
+
C --> E[Update canonical utils-spec contracts and consumer map]
|
|
351
|
+
D --> F[Validate cache key schema and bypass conditions]
|
|
352
|
+
E --> G[Run drift check against PageCache.ts and consumer services]
|
|
353
|
+
F --> G
|
|
354
|
+
G --> H{Behavior and docs aligned?}
|
|
355
|
+
H -->|No| I[Revise implementation/docs and re-validate]
|
|
356
|
+
I --> G
|
|
357
|
+
H -->|Yes| J[Prepare PR with acceptance criteria and evidence]
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Key Design Trade-off
|
|
361
|
+
- Caching is intentionally limited to simple page browsing; search/filter/sort requests bypass cache to prevent incorrect reuse.
|
|
362
|
+
|
|
363
|
+
## Test-Case Strategy (module)
|
|
364
|
+
PageCache has no dedicated unit file; current consumer tests in `test/unit/spec/services/AddressBook.ts`, `EntryPoint.ts`, and `Queue.ts` cover request shaping plus cache hit/miss behavior. Add direct PageCache tests for the five-minute expiry boundary, entry deletion, metadata normalization, clearing, and key construction with both `bookId` and `orgId` scope values.
|
|
365
|
+
|
|
366
|
+
| Behavior / Requirement | Existing test evidence | Gap |
|
|
367
|
+
|---|---|---|
|
|
368
|
+
| `UTILS-R-001` | AddressBook, EntryPoint, and Queue consumer tests exercise advanced query parameters and simple pagination. | Add a direct `canUseCache` table test for each bypass field and `sortOrder` alone. |
|
|
369
|
+
| `UTILS-R-002` | AddressBook repeat/miss tests exercise `bookId`-scoped caching; EntryPoint and Queue miss tests exercise `orgId` consumers. | Add direct key-string assertions for both runtime scope kinds. |
|
|
370
|
+
| `UTILS-R-003` | None. | Add fake-clock tests at just below and exactly five minutes, including deletion. |
|
|
371
|
+
| `UTILS-R-004` | AddressBook verifies a repeat-call hit and response metadata; all three consumers verify misses. | Add direct metadata fallback and `clearCache`/`getCacheSize` assertions. |
|
|
372
|
+
| `UTILS-R-005` | Source inspection confirms PageCache has no credential or request dependency. | No runtime credential test is needed; retain the static ownership check. |
|
|
373
|
+
| `UTILS-R-006` | Source inspection confirms no feature-flag dependency; consumer tests cover invocation choices. | No direct rollout test is applicable. |
|
|
374
|
+
|
|
375
|
+
## Traceability
|
|
376
|
+
- Repo architecture: `../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../ai-docs/SPEC_INDEX.md`
|
|
377
|
+
- Coverage state and contracts baseline: `../../../.sdd/manifest.json`
|
|
378
|
+
|
|
379
|
+
- [Root orchestrator AGENTS.md](../../../AGENTS.md) - Task routing and critical package rules
|
|
380
|
+
|
|
381
|
+
- [PageCache implementation](../PageCache.ts)
|