@brownandroot/api 0.13.0 → 0.15.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @brownandroot/api
2
2
 
3
- TypeScript client for the Brown & Root APIHub data service. Provides read-only access to company data (employees, business units, cost codes, pay types, work orders, job type/job steps) and an LLM chat endpoint powered by Azure AI Foundry.
3
+ TypeScript client for the Brown & Root APIHub data service. Provides access to employee, business unit, cost code, pay type, work order, job type/job step, LLM chat, and document search data.
4
4
 
5
5
  ## Installation
6
6
 
@@ -16,25 +16,156 @@ import { ApiHubClient } from '@brownandroot/api'
16
16
  const client = new ApiHubClient({
17
17
  baseUrl: 'https://your-apihub-url.com',
18
18
  apiKey: 'your-api-key',
19
+ cacheTtl: 5 * 60 * 1000, // optional, default 5 minutes (0 to disable)
19
20
  })
20
21
  ```
21
22
 
22
- ## Usage
23
+ ---
23
24
 
24
- ### Employees
25
+ ## Employees
26
+
27
+ ### Fetching
25
28
 
26
29
  ```typescript
30
+ // All employees
27
31
  const employees = await client.getEmployees()
28
- const dropdown = await client.getEmployeesDropdown() // { value, label }[]
32
+
33
+ // Dropdown format: { value: employeeId, label: name }[]
34
+ const dropdown = await client.getEmployeesDropdown()
35
+
36
+ // Single employee by ID — throws if not found
29
37
  const employee = await client.getEmployee('12345')
38
+ ```
39
+
40
+ ### Search
41
+
42
+ ```typescript
43
+ // By name (case-insensitive partial match)
30
44
  const results = await client.searchByName('John')
45
+
46
+ // By email (case-insensitive partial match)
31
47
  const results = await client.searchByEmail('john@example.com')
48
+
49
+ // By home business unit (case-insensitive partial match)
50
+ const results = await client.searchByHbu('TX01')
51
+ ```
52
+
53
+ ### Org hierarchy
54
+
55
+ ```typescript
56
+ // All employees reporting directly to a supervisor
32
57
  const reports = await client.getBySupervisor('12345')
58
+
59
+ // Full supervisor chain above an employee (excludes the employee themselves)
33
60
  const chain = await client.getSupervisorChain('12345')
61
+ ```
62
+
63
+ ### Email → JDE lookup
64
+
65
+ ```typescript
34
66
  const { jde, employee } = await client.getJdeFromEmail('john@example.com')
67
+ // jde: string | null
68
+ // employee: Employee | null
69
+ ```
70
+
71
+ ### Identity verification
72
+
73
+ Verifies an employee's identity from name, date of birth, and last 4 of SSN alone — no employee ID needed. Returns the full employee record on a match.
74
+
75
+ ```typescript
76
+ const employee = await client.verifyIdentity({
77
+ first3FirstName: 'joh', // first 3 chars of first name, case-insensitive
78
+ first3LastName: 'doe', // first 3 chars of last name, case-insensitive
79
+ dob: '1985-03-15', // YYYY-MM-DD
80
+ ssn4: '4321', // last 4 digits of SSN
81
+ })
82
+
83
+ if (employee) {
84
+ // identity confirmed — full Employee object returned
85
+ } else {
86
+ // no employee matched these inputs
87
+ }
88
+ // Throws on 400 (missing fields) or 503 (not configured server-side)
89
+ ```
90
+
91
+ ### Employee fields
92
+
93
+ ```typescript
94
+ interface Employee {
95
+ // Identity
96
+ employeeId: string
97
+ name: string | null
98
+ email: string | null
99
+ badgeNumber: string | null
100
+ nccerNumber: string | null
101
+
102
+ // Organization
103
+ company: string | null // company code
104
+ businessUnitId: string | null
105
+ hbu: string | null // home business unit
106
+ departmentCode: string | null
107
+ division: string | null
108
+ sector: string | null
109
+ subsector: string | null
110
+
111
+ // Contact
112
+ phone: string | null
113
+
114
+ // Employment
115
+ employementStatus: string | null
116
+ employeePayStatus: string | null
117
+ recordType: string | null
118
+
119
+ // Job
120
+ jobType: string | null
121
+ jobStep: string | null
122
+ jobDescription: string | null
123
+ workSchedule: string | null
124
+ shift: string | null
125
+
126
+ // Pay & compensation
127
+ payClass: string | null
128
+ payFrequency: string | null
129
+ payCycleCode: string | null
130
+ checkRouteCode: string | null
131
+ hourlyRate: string | null // string — format as needed
132
+ annualSalary: string | null // string — format as needed
133
+
134
+ // Tax
135
+ residentTaxArea: string | null
136
+ workTaxArea: string | null
137
+
138
+ // Benefits
139
+ benefitGroup: string | null
140
+
141
+ // PTO
142
+ topFlexPtoDate: string | null // ISO timestamp
143
+ clientPtoDate: string | null // ISO timestamp
144
+
145
+ // Security & reporting
146
+ securityLevel: string | null
147
+ reportingLevel: string | null
148
+
149
+ // Relationships
150
+ supervisor: string | null // supervisor employee ID
151
+ mentor: string | null
152
+
153
+ // Dates
154
+ hireDate: string | null
155
+ termDate: string | null
156
+ adjustedServiceDate: string | null
157
+
158
+ // Metadata
159
+ source: string | null
160
+ createdAt: string | null
161
+ updatedAtJulian: number | null
162
+ updatedAt: string | null
163
+ }
35
164
  ```
36
165
 
37
- ### Business Units
166
+ ---
167
+
168
+ ## Business Units
38
169
 
39
170
  ```typescript
40
171
  const units = await client.getBusinessUnits()
@@ -42,15 +173,25 @@ const dropdown = await client.getBusinessUnitsDropdown() // { value, label }[]
42
173
  const unit = await client.getBusinessUnit('BU001')
43
174
  ```
44
175
 
45
- ### Cost Codes
176
+ ---
177
+
178
+ ## Cost Codes
46
179
 
47
180
  ```typescript
48
181
  const codes = await client.getCostcodes()
49
182
  const dropdown = await client.getCostcodesDropdown() // { value, label }[]
50
183
  const code = await client.getCostcode('CC001')
184
+
185
+ // Filtered by business unit
186
+ const dropdown = await client.getCostcodesDropdownByBu('BU001')
187
+
188
+ // Filtered by business unit and pay type
189
+ const dropdown = await client.getCostcodesDropdownByBuAndPayType('BU001', 'PT01')
51
190
  ```
52
191
 
53
- ### Pay Types
192
+ ---
193
+
194
+ ## Pay Types
54
195
 
55
196
  ```typescript
56
197
  const types = await client.getPaytypes()
@@ -58,15 +199,22 @@ const dropdown = await client.getPaytypesDropdown() // { value, label, payClass
58
199
  const type = await client.getPaytype('PT001')
59
200
  ```
60
201
 
61
- ### Work Orders
202
+ ---
203
+
204
+ ## Work Orders
62
205
 
63
206
  ```typescript
64
207
  const orders = await client.getWorkorders()
65
208
  const dropdown = await client.getWorkordersDropdown() // { value, label }[]
66
209
  const order = await client.getWorkorder('WO001')
210
+
211
+ // Filtered by business unit
212
+ const dropdown = await client.getWorkordersDropdownByBu('BU001')
67
213
  ```
68
214
 
69
- ### Job Type / Job Steps
215
+ ---
216
+
217
+ ## Job Type / Job Steps
70
218
 
71
219
  ```typescript
72
220
  const items = await client.getJobtypejobsteps()
@@ -74,26 +222,83 @@ const dropdown = await client.getJobtypejobstepsDropdown() // { value, label }[]
74
222
  const item = await client.getJobtypejobstep('JTJS001')
75
223
  ```
76
224
 
77
- ### LLM
225
+ ---
78
226
 
79
- ```typescript
80
- // List logs
81
- const logs = await client.getLlmLogs()
227
+ ## LLM
228
+
229
+ ### Chat completion
82
230
 
83
- // Chat completion
231
+ ```typescript
84
232
  const result = await client.chat({
85
233
  messages: [{ role: 'user', content: 'Summarize this document...' }],
86
- source: 'my-app',
87
- user: 'jane.doe',
88
- function: 'summarize',
234
+ source: 'my-app', // your application name
235
+ user: 'jane.doe', // user identifier for logging
236
+ function: 'summarize', // optional label for logging
237
+ temperature: 0.7, // optional, default 0.7
238
+ maxTokens: 1000, // optional
89
239
  })
240
+
90
241
  console.log(result.message.content)
91
242
  console.log(result.usage) // { tokensIn, tokensOut, totalTokens }
92
243
  ```
93
244
 
94
- ## Types
245
+ ### Streaming chat
95
246
 
96
- The package exports interfaces for all resource types:
247
+ Returns a raw SSE `Response` for you to proxy or consume directly.
248
+
249
+ ```typescript
250
+ const response = await client.chatStream({
251
+ messages: [{ role: 'user', content: 'Hello' }],
252
+ userContext: { name: 'Jane Doe', department: 'Engineering', roles: ['admin'] },
253
+ useRag: true, // optional, search document knowledge base
254
+ tools: ['...'], // optional
255
+ source: 'my-app',
256
+ })
257
+
258
+ // Proxy to the browser, or read the SSE stream directly
259
+ ```
260
+
261
+ ### LLM logs
262
+
263
+ ```typescript
264
+ const logs = await client.getLlmLogs() // newest first
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Documents (RAG)
270
+
271
+ Upload and search documents in the knowledge base.
272
+
273
+ ```typescript
274
+ // Upload a document (PDF or CSV)
275
+ const doc = await client.uploadDocument('report.pdf', base64Content, 'jane.doe')
276
+
277
+ // List all documents
278
+ const docs = await client.listDocuments()
279
+
280
+ // Search with a natural language query
281
+ const results = await client.searchDocuments('overtime policy', 5)
282
+ // results: { chunkId, content, fileName, documentType, score }[]
283
+
284
+ // Delete a document and all its chunks
285
+ await client.deleteDocument(doc.id)
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Cache management
291
+
292
+ All GET methods cache responses client-side for `cacheTtl` milliseconds (default 5 minutes). To invalidate:
293
+
294
+ ```typescript
295
+ client.clearCache() // clear everything
296
+ client.clearCache('/employees') // clear a specific path
297
+ ```
298
+
299
+ ---
300
+
301
+ ## Types
97
302
 
98
303
  ```typescript
99
304
  import type {
@@ -107,15 +312,21 @@ import type {
107
312
  ChatMessage,
108
313
  ChatRequest,
109
314
  ChatResponse,
315
+ StreamChatRequest,
316
+ StreamChatUserContext,
317
+ DocumentRecord,
318
+ SearchResult,
110
319
  ApiHubClientOptions,
111
320
  DropdownOption,
112
321
  PaytypeDropdownOption,
113
322
  } from '@brownandroot/api'
114
323
  ```
115
324
 
116
- ## Error Handling
325
+ ---
117
326
 
118
- Methods throw an `Error` when the API returns a non-OK response. The error message contains the server's error detail or the HTTP status code.
327
+ ## Error handling
328
+
329
+ All methods throw an `Error` when the API returns a non-OK response. The error message contains the server's detail or the HTTP status.
119
330
 
120
331
  ```typescript
121
332
  try {
@@ -124,3 +335,5 @@ try {
124
335
  console.error(err.message) // "Employee not found"
125
336
  }
126
337
  ```
338
+
339
+ `verifyIdentity` is the exception — it returns `null` when no employee matches the inputs, and only throws for request errors (400, 503, network failure).
package/dist/index.d.ts CHANGED
@@ -18,10 +18,15 @@ export interface Employee {
18
18
  payClass: string | null;
19
19
  jobType: string | null;
20
20
  jobStep: string | null;
21
+ jobDescription: string | null;
21
22
  workSchedule: string | null;
22
23
  shift: string | null;
24
+ hourlyRate: string | null;
25
+ annualSalary: string | null;
23
26
  termDate: string | null;
24
27
  hireDate: string | null;
28
+ topFlexPtoDate: string | null;
29
+ clientPtoDate: string | null;
25
30
  securityLevel: string | null;
26
31
  reportingLevel: string | null;
27
32
  recordType: string | null;
@@ -30,6 +35,11 @@ export interface Employee {
30
35
  supervisor: string | null;
31
36
  adjustedServiceDate: string | null;
32
37
  departmentCode: string | null;
38
+ division: string | null;
39
+ sector: string | null;
40
+ subsector: string | null;
41
+ phone: string | null;
42
+ identityHash: string | null;
33
43
  nccerNumber: string | null;
34
44
  mentor: string | null;
35
45
  source: string | null;
@@ -218,6 +228,19 @@ export declare class ApiHubClient {
218
228
  jde: string | null;
219
229
  employee: Employee | null;
220
230
  }>;
231
+ /** Search employees by home business unit (case-insensitive partial match) */
232
+ searchByHbu(hbu: string): Promise<Employee[]>;
233
+ /**
234
+ * Verify an employee's identity from name/dob/SSN inputs alone — no employee ID needed.
235
+ * Returns the Employee on a match, or null if no employee matches the inputs.
236
+ * Throws on 400 (missing fields) or 503 (not configured server-side).
237
+ */
238
+ verifyIdentity(inputs: {
239
+ first3FirstName: string;
240
+ first3LastName: string;
241
+ dob: string;
242
+ ssn4: string;
243
+ }): Promise<Employee | null>;
221
244
  /** List all LLM log entries (newest first) */
222
245
  getLlmLogs(): Promise<LlmLog[]>;
223
246
  /** Send a chat completion request to the LLM */
package/dist/index.js CHANGED
@@ -81,6 +81,37 @@ export class ApiHubClient {
81
81
  async getJdeFromEmail(email) {
82
82
  return this.request(`/employees/jde-from-email/${encodeURIComponent(email)}`);
83
83
  }
84
+ /** Search employees by home business unit (case-insensitive partial match) */
85
+ async searchByHbu(hbu) {
86
+ return this.request(`/employees/search?hbu=${encodeURIComponent(hbu)}`);
87
+ }
88
+ /**
89
+ * Verify an employee's identity from name/dob/SSN inputs alone — no employee ID needed.
90
+ * Returns the Employee on a match, or null if no employee matches the inputs.
91
+ * Throws on 400 (missing fields) or 503 (not configured server-side).
92
+ */
93
+ async verifyIdentity(inputs) {
94
+ let res;
95
+ try {
96
+ res = await fetch(`${this.baseUrl}/employees/verify-identity`, {
97
+ method: 'POST',
98
+ headers: { 'x-api-key': this.apiKey, 'Content-Type': 'application/json' },
99
+ body: JSON.stringify(inputs),
100
+ });
101
+ }
102
+ catch {
103
+ throw new Error(`APIHub unavailable: ${this.baseUrl}/employees/verify-identity`);
104
+ }
105
+ if (res.status === 401)
106
+ return null;
107
+ if (!res.ok) {
108
+ const body = await res.json().catch(() => null);
109
+ const message = (body && typeof body === 'object' && 'error' in body && typeof body.error === 'string' ? body.error : null) ??
110
+ `Request failed: ${res.status} ${res.statusText}`;
111
+ throw new Error(message);
112
+ }
113
+ return res.json();
114
+ }
84
115
  // -----------------------------------------------------------------------
85
116
  // LLM Logs
86
117
  // -----------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brownandroot/api",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",