@brownandroot/api 0.14.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 +234 -21
- package/dist/index.d.ts +5 -6
- package/dist/index.js +8 -5
- package/package.json +1 -1
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
|
|
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
|
-
|
|
23
|
+
---
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
## Employees
|
|
26
|
+
|
|
27
|
+
### Fetching
|
|
25
28
|
|
|
26
29
|
```typescript
|
|
30
|
+
// All employees
|
|
27
31
|
const employees = await client.getEmployees()
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
225
|
+
---
|
|
78
226
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
227
|
+
## LLM
|
|
228
|
+
|
|
229
|
+
### Chat completion
|
|
82
230
|
|
|
83
|
-
|
|
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
|
-
|
|
245
|
+
### Streaming chat
|
|
95
246
|
|
|
96
|
-
|
|
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
|
-
|
|
325
|
+
---
|
|
117
326
|
|
|
118
|
-
|
|
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
|
@@ -231,17 +231,16 @@ export declare class ApiHubClient {
|
|
|
231
231
|
/** Search employees by home business unit (case-insensitive partial match) */
|
|
232
232
|
searchByHbu(hbu: string): Promise<Employee[]>;
|
|
233
233
|
/**
|
|
234
|
-
* Verify an employee's identity
|
|
235
|
-
*
|
|
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).
|
|
236
237
|
*/
|
|
237
|
-
verifyIdentity(
|
|
238
|
+
verifyIdentity(inputs: {
|
|
238
239
|
first3FirstName: string;
|
|
239
240
|
first3LastName: string;
|
|
240
241
|
dob: string;
|
|
241
242
|
ssn4: string;
|
|
242
|
-
}): Promise<
|
|
243
|
-
verified: boolean;
|
|
244
|
-
}>;
|
|
243
|
+
}): Promise<Employee | null>;
|
|
245
244
|
/** List all LLM log entries (newest first) */
|
|
246
245
|
getLlmLogs(): Promise<LlmLog[]>;
|
|
247
246
|
/** Send a chat completion request to the LLM */
|
package/dist/index.js
CHANGED
|
@@ -86,21 +86,24 @@ export class ApiHubClient {
|
|
|
86
86
|
return this.request(`/employees/search?hbu=${encodeURIComponent(hbu)}`);
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
|
-
* Verify an employee's identity
|
|
90
|
-
*
|
|
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).
|
|
91
92
|
*/
|
|
92
|
-
async verifyIdentity(
|
|
93
|
+
async verifyIdentity(inputs) {
|
|
93
94
|
let res;
|
|
94
95
|
try {
|
|
95
|
-
res = await fetch(`${this.baseUrl}/employees
|
|
96
|
+
res = await fetch(`${this.baseUrl}/employees/verify-identity`, {
|
|
96
97
|
method: 'POST',
|
|
97
98
|
headers: { 'x-api-key': this.apiKey, 'Content-Type': 'application/json' },
|
|
98
99
|
body: JSON.stringify(inputs),
|
|
99
100
|
});
|
|
100
101
|
}
|
|
101
102
|
catch {
|
|
102
|
-
throw new Error(`APIHub unavailable: ${this.baseUrl}/employees
|
|
103
|
+
throw new Error(`APIHub unavailable: ${this.baseUrl}/employees/verify-identity`);
|
|
103
104
|
}
|
|
105
|
+
if (res.status === 401)
|
|
106
|
+
return null;
|
|
104
107
|
if (!res.ok) {
|
|
105
108
|
const body = await res.json().catch(() => null);
|
|
106
109
|
const message = (body && typeof body === 'object' && 'error' in body && typeof body.error === 'string' ? body.error : null) ??
|