@truenorth-it/dataverse-client 1.0.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 +405 -0
- package/package.json +73 -0
- package/sdk/dist/cli.d.ts +10 -0
- package/sdk/dist/cli.d.ts.map +1 -0
- package/sdk/dist/cli.js +119 -0
- package/sdk/dist/cli.js.map +1 -0
- package/sdk/dist/client.d.ts +42 -0
- package/sdk/dist/client.d.ts.map +1 -0
- package/sdk/dist/client.js +116 -0
- package/sdk/dist/client.js.map +1 -0
- package/sdk/dist/codegen.d.ts +41 -0
- package/sdk/dist/codegen.d.ts.map +1 -0
- package/sdk/dist/codegen.js +150 -0
- package/sdk/dist/codegen.js.map +1 -0
- package/sdk/dist/index.d.ts +6 -0
- package/sdk/dist/index.d.ts.map +1 -0
- package/sdk/dist/index.js +4 -0
- package/sdk/dist/index.js.map +1 -0
- package/sdk/dist/tables.generated.d.ts +1606 -0
- package/sdk/dist/tables.generated.d.ts.map +1 -0
- package/sdk/dist/tables.generated.js +7 -0
- package/sdk/dist/tables.generated.js.map +1 -0
- package/sdk/dist/types.d.ts +124 -0
- package/sdk/dist/types.d.ts.map +1 -0
- package/sdk/dist/types.js +2 -0
- package/sdk/dist/types.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
<!-- This file is published to npmjs.com as the package README. Edit here, not the root README.md. -->
|
|
2
|
+
|
|
3
|
+
# @truenorth-it/dataverse-client
|
|
4
|
+
|
|
5
|
+
A type-safe JavaScript client for the Dataverse Contact API. Works with **any** deployment — generate TypeScript types specific to your Dataverse org, or use it untyped.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @truenorth-it/dataverse-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start (no types)
|
|
14
|
+
|
|
15
|
+
The SDK works out of the box without any type generation. Response data is `Record<string, unknown>`:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createClient } from "@truenorth-it/dataverse-client";
|
|
19
|
+
|
|
20
|
+
const client = createClient({
|
|
21
|
+
baseUrl: "https://your-api.vercel.app",
|
|
22
|
+
getToken: () => getAccessTokenSilently(), // your Auth0 token provider
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const result = await client.me.list("incident", {
|
|
26
|
+
select: ["title", "ticketnumber", "statuscode", "createdon"],
|
|
27
|
+
top: 20,
|
|
28
|
+
orderBy: "modifiedon:desc",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
for (const record of result.data) {
|
|
32
|
+
console.log(record.ticketnumber, record.title);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Generate types for your API
|
|
37
|
+
|
|
38
|
+
For full TypeScript autocomplete and type safety, generate interfaces from your own API deployment:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx dataverse-client generate --url https://your-api.vercel.app
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This fetches the schema and choice values from your API (public endpoints, no auth required) and creates `dataverse-tables.generated.ts` with:
|
|
45
|
+
- Typed interfaces for every table
|
|
46
|
+
- Const enum objects for every choice (picklist) field
|
|
47
|
+
|
|
48
|
+
### CLI options
|
|
49
|
+
|
|
50
|
+
| Flag | Default | Description |
|
|
51
|
+
|------|---------|-------------|
|
|
52
|
+
| `--url`, `-u` | (required) | Base URL of your API deployment |
|
|
53
|
+
| `--output`, `-o` | `./dataverse-tables.generated.ts` | Output file path |
|
|
54
|
+
| `--api-base` | `/api/v2` | API base path |
|
|
55
|
+
| `--no-choices` | `false` | Skip fetching choice values (no enums generated) |
|
|
56
|
+
|
|
57
|
+
### Using generated types
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { createClient } from "@truenorth-it/dataverse-client";
|
|
61
|
+
import type { Incident, Contact } from "./dataverse-tables.generated";
|
|
62
|
+
|
|
63
|
+
const client = createClient({
|
|
64
|
+
baseUrl: "https://your-api.vercel.app",
|
|
65
|
+
getToken: () => getAccessTokenSilently(),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Fully typed — result.data is Incident[]
|
|
69
|
+
const result = await client.me.list<Incident>("incident", {
|
|
70
|
+
select: ["title", "ticketnumber", "statuscode", "createdon"],
|
|
71
|
+
top: 20,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
for (const c of result.data) {
|
|
75
|
+
console.log(c.ticketnumber, c.title, c.statuscode_label);
|
|
76
|
+
// ^ string ^ string ^ "In Progress" etc.
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Using choice enums
|
|
81
|
+
|
|
82
|
+
The generated file also includes const objects for choice (picklist) fields, so you can compare values without magic numbers:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { createClient } from "@truenorth-it/dataverse-client";
|
|
86
|
+
import type { Incident } from "./dataverse-tables.generated";
|
|
87
|
+
import { IncidentStatuscode, IncidentPrioritycode } from "./dataverse-tables.generated";
|
|
88
|
+
|
|
89
|
+
const cases = await client.me.list<Incident>("incident", {
|
|
90
|
+
filter: `statuscode eq ${IncidentStatuscode.InProgress}`,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
for (const c of cases.data) {
|
|
94
|
+
if (c.statuscode === IncidentStatuscode.OnHold) {
|
|
95
|
+
console.log("Case is on hold:", c.title);
|
|
96
|
+
}
|
|
97
|
+
if (c.prioritycode === IncidentPrioritycode.High) {
|
|
98
|
+
console.log("High priority:", c.title);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Each choice const is also a type — use it for function signatures:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
function handleStatus(status: IncidentStatuscode) {
|
|
107
|
+
// status is narrowed to 1 | 2 | 3 | 5 etc.
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
To skip choice generation (faster, interfaces only): `npx dataverse-client generate --url ... --no-choices`
|
|
112
|
+
|
|
113
|
+
### CI / build integration
|
|
114
|
+
|
|
115
|
+
Add to your `package.json` to regenerate types when your schema changes:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"scripts": {
|
|
120
|
+
"generate:types": "dataverse-client generate --url $API_URL --output src/dataverse-types.ts",
|
|
121
|
+
"prebuild": "npm run generate:types"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Programmatic usage
|
|
127
|
+
|
|
128
|
+
The codegen function is also exported for use in custom build scripts:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { generateTableTypes } from "@truenorth-it/dataverse-client";
|
|
132
|
+
|
|
133
|
+
const response = await fetch("https://your-api.vercel.app/api/v2/schema");
|
|
134
|
+
const schema = await response.json();
|
|
135
|
+
const tsSource = generateTableTypes(schema);
|
|
136
|
+
// Write tsSource to a file, pipe it, etc.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Usage with React + Auth0
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { useMemo } from "react";
|
|
143
|
+
import { useAuth0 } from "@auth0/auth0-react";
|
|
144
|
+
import { createClient } from "@truenorth-it/dataverse-client";
|
|
145
|
+
|
|
146
|
+
function useApiClient() {
|
|
147
|
+
const { getAccessTokenSilently } = useAuth0();
|
|
148
|
+
|
|
149
|
+
return useMemo(
|
|
150
|
+
() =>
|
|
151
|
+
createClient({
|
|
152
|
+
baseUrl: import.meta.env.VITE_API_BASE_URL,
|
|
153
|
+
getToken: () => getAccessTokenSilently(),
|
|
154
|
+
}),
|
|
155
|
+
[getAccessTokenSilently],
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Client configuration
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
createClient({
|
|
164
|
+
baseUrl: string; // API deployment URL
|
|
165
|
+
getToken: () => Promise<string>; // Returns a Bearer token
|
|
166
|
+
apiBase?: string; // Defaults to "/api/v2"
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Access scopes
|
|
171
|
+
|
|
172
|
+
The client exposes three scopes, each backed by server-side authorization:
|
|
173
|
+
|
|
174
|
+
| Scope | Description | Operations |
|
|
175
|
+
|-------|-------------|------------|
|
|
176
|
+
| `client.me` | Your own records (contact-scoped) | list, get, update, lookup, create, whoami |
|
|
177
|
+
| `client.team` | Records belonging to your team/account | list, get, update, lookup |
|
|
178
|
+
| `client.all` | All records (requires elevated permissions) | list, get, update, lookup |
|
|
179
|
+
|
|
180
|
+
## Operations
|
|
181
|
+
|
|
182
|
+
### List records
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import type { Incident } from "./dataverse-tables.generated";
|
|
186
|
+
|
|
187
|
+
const result = await client.me.list<Incident>("incident", {
|
|
188
|
+
select: ["title", "ticketnumber", "statuscode", "prioritycode", "createdon"],
|
|
189
|
+
top: 50,
|
|
190
|
+
orderBy: "createdon:desc",
|
|
191
|
+
filter: "statuscode eq 1",
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// result.data — Incident[] with full autocomplete
|
|
195
|
+
// result.page — { top, skip, next }
|
|
196
|
+
|
|
197
|
+
for (const c of result.data) {
|
|
198
|
+
console.log(c.ticketnumber, c.title);
|
|
199
|
+
console.log(c.statuscode, c.statuscode_label); // 1, "In Progress"
|
|
200
|
+
console.log(c.prioritycode, c.prioritycode_label); // 2, "Normal"
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Get a single record
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
const result = await client.me.get<Incident>("incident", caseId, {
|
|
208
|
+
select: ["title", "description", "statuscode", "customerid"],
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const c = result.data;
|
|
212
|
+
console.log(c.title); // "Network outage in building 3"
|
|
213
|
+
console.log(c._customerid_value); // GUID of the linked customer
|
|
214
|
+
console.log(c.statuscode_label); // "In Progress"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Create a record (me scope only)
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import type { Casenotes } from "./dataverse-tables.generated";
|
|
221
|
+
|
|
222
|
+
const result = await client.me.create<Casenotes>("casenotes", {
|
|
223
|
+
subject: "Follow-up call",
|
|
224
|
+
notetext: "Spoke with customer — issue resolved.",
|
|
225
|
+
objectid_incident: incidentId, // link to parent case
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
console.log(result.data.annotationid); // new note GUID
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Update a record
|
|
232
|
+
|
|
233
|
+
Available on all scopes (`me`, `team`, `all`):
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
const result = await client.me.update<Incident>("incident", caseId, {
|
|
237
|
+
description: "Updated with latest findings",
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
console.log(result.data.modifiedon); // "2026-02-13T10:30:00Z"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Lookup (type-ahead search)
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import type { Contact } from "./dataverse-tables.generated";
|
|
247
|
+
|
|
248
|
+
const result = await client.me.lookup<Contact>("contact", {
|
|
249
|
+
search: "john",
|
|
250
|
+
select: ["fullname", "emailaddress1", "jobtitle"],
|
|
251
|
+
top: 10,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Great for autocomplete dropdowns
|
|
255
|
+
for (const contact of result.data) {
|
|
256
|
+
console.log(contact.fullname, contact.emailaddress1);
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Who am I
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const me = await client.me.whoami();
|
|
264
|
+
console.log(me.fullname); // "Jane Developer"
|
|
265
|
+
console.log(me.email); // "jane@example.com"
|
|
266
|
+
console.log(me.contactid); // Dataverse contact GUID
|
|
267
|
+
console.log(me.accountid); // Dataverse account GUID (if linked)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Choices (option sets)
|
|
271
|
+
|
|
272
|
+
Choice fields (picklists) have their values managed in Dataverse. Fetch them for building dropdowns:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// All choice fields for a table
|
|
276
|
+
const allChoices = await client.choices("incident");
|
|
277
|
+
// allChoices.fields.statuscode → [{ value: 1, label: "In Progress" }, ...]
|
|
278
|
+
// allChoices.fields.prioritycode → [{ value: 1, label: "High" }, ...]
|
|
279
|
+
|
|
280
|
+
// Single field
|
|
281
|
+
const statusChoices = await client.choices("incident", "statuscode");
|
|
282
|
+
// statusChoices.choices → [{ value: 1, label: "In Progress" }, ...]
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Schema
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// All table schemas
|
|
289
|
+
const schemas = await client.schema();
|
|
290
|
+
|
|
291
|
+
// Single table
|
|
292
|
+
const incidentSchema = await client.schema("incident");
|
|
293
|
+
console.log(incidentSchema.fields); // field definitions with types + descriptions
|
|
294
|
+
console.log(incidentSchema.defaultFields); // fields returned when no `select` is specified
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Query options
|
|
298
|
+
|
|
299
|
+
Used by `list()`:
|
|
300
|
+
|
|
301
|
+
| Option | Type | Description |
|
|
302
|
+
|--------|------|-------------|
|
|
303
|
+
| `select` | `string[]` | Fields to return |
|
|
304
|
+
| `top` | `number` | Page size (1-100) |
|
|
305
|
+
| `skip` | `number` | Records to skip |
|
|
306
|
+
| `orderBy` | `string` | Sort field and direction, e.g. `"createdon:desc"` |
|
|
307
|
+
| `filter` | `string \| string[]` | OData-style filter expressions |
|
|
308
|
+
| `filterLogic` | `"and" \| "or"` | How to combine multiple filters (default: `"and"`) |
|
|
309
|
+
| `expand` | `string` | Expand related lookups |
|
|
310
|
+
|
|
311
|
+
`lookup()` accepts the same options plus:
|
|
312
|
+
|
|
313
|
+
| Option | Type | Description |
|
|
314
|
+
|--------|------|-------------|
|
|
315
|
+
| `search` | `string` | Search term for type-ahead filtering |
|
|
316
|
+
|
|
317
|
+
`get()` accepts only `select` and `expand`.
|
|
318
|
+
|
|
319
|
+
## Error handling
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
import { ApiError } from "@truenorth-it/dataverse-client";
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
await client.me.list("incident");
|
|
326
|
+
} catch (err) {
|
|
327
|
+
if (err instanceof ApiError) {
|
|
328
|
+
console.error(err.status); // HTTP status code
|
|
329
|
+
console.error(err.body); // Error response body
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
| Status | Meaning |
|
|
335
|
+
|--------|---------|
|
|
336
|
+
| 401 | Missing or invalid token |
|
|
337
|
+
| 403 | Insufficient permissions |
|
|
338
|
+
| 404 | Unknown table or contact not found |
|
|
339
|
+
|
|
340
|
+
## Generated type patterns
|
|
341
|
+
|
|
342
|
+
### Choice and lookup field siblings
|
|
343
|
+
|
|
344
|
+
Choice fields (picklists) include a `_label` sibling with the display text:
|
|
345
|
+
```typescript
|
|
346
|
+
record.statuscode // 1 (numeric value)
|
|
347
|
+
record.statuscode_label // "In Progress" (display text from Dataverse)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Lookup fields include a `_value` sibling with the referenced record's GUID:
|
|
351
|
+
```typescript
|
|
352
|
+
record._customerid_value // "a1b2c3d4-..." (GUID of linked customer)
|
|
353
|
+
record._primarycontactid_value // "e5f6g7h8-..." (GUID of linked contact)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### TableName union
|
|
357
|
+
|
|
358
|
+
The generated file includes a `TableName` union of all valid table names and aliases:
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
import type { TableName } from "./dataverse-tables.generated";
|
|
362
|
+
|
|
363
|
+
// Type-safe table parameter — accepts "incident", "case", "contact", etc.
|
|
364
|
+
function fetchRecords(table: TableName) {
|
|
365
|
+
return client.me.list(table);
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Exported types
|
|
370
|
+
|
|
371
|
+
All types are importable from the main package:
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import type {
|
|
375
|
+
// Client & scope interfaces
|
|
376
|
+
DataverseClient, ScopeClient, MeScopeClient, ClientConfig,
|
|
377
|
+
// Query types
|
|
378
|
+
QueryOptions, LookupOptions,
|
|
379
|
+
// Response types
|
|
380
|
+
PaginatedResponse, SingleResponse, WhoamiResponse,
|
|
381
|
+
// Schema & choices
|
|
382
|
+
TableSchema, SchemaField, ExpandSchema, Choice,
|
|
383
|
+
FieldChoicesResponse, TableChoicesResponse,
|
|
384
|
+
// Codegen types (for programmatic usage)
|
|
385
|
+
SchemaResponse, SchemaTableInput,
|
|
386
|
+
// Errors
|
|
387
|
+
ApiErrorBody,
|
|
388
|
+
} from "@truenorth-it/dataverse-client";
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Table-specific types come from your generated file:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import type { Incident, Contact, TableName } from "./dataverse-tables.generated";
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Requirements
|
|
398
|
+
|
|
399
|
+
- Node.js 18+
|
|
400
|
+
- An Auth0 token provider (or any async function that returns a Bearer token)
|
|
401
|
+
- Access to a deployed Dataverse Contact API instance
|
|
402
|
+
|
|
403
|
+
## License
|
|
404
|
+
|
|
405
|
+
ISC
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@truenorth-it/dataverse-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./sdk/dist/index.js",
|
|
6
|
+
"types": "./sdk/dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./sdk/dist/index.js",
|
|
10
|
+
"types": "./sdk/dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"dataverse-client": "./sdk/dist/cli.js"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"sdk/dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"generate:sdk-types": "tsx scripts/generate-sdk-types.ts",
|
|
21
|
+
"build:sdk": "tsc -p sdk/tsconfig.json",
|
|
22
|
+
"prepare": "tsc -p sdk/tsconfig.json",
|
|
23
|
+
"dev": "vite",
|
|
24
|
+
"build": "tsc && vite build",
|
|
25
|
+
"preview": "vite preview",
|
|
26
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"generate-tables": "tsx scripts/generate-tables.ts",
|
|
29
|
+
"sync-auth0-perms": "tsx scripts/sync-auth0-permissions.ts",
|
|
30
|
+
"sync-auth0-perms:apply": "tsx scripts/sync-auth0-permissions.ts --apply",
|
|
31
|
+
"add-table": "tsx scripts/add-table.ts",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"test:coverage": "vitest run --coverage"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@auth0/auth0-react": "^2.2.4",
|
|
38
|
+
"@azure/identity": "^4.2.0",
|
|
39
|
+
"@microsoft/applicationinsights-web": "^3.3.11",
|
|
40
|
+
"applicationinsights": "^2.9.8",
|
|
41
|
+
"jose": "^5.2.0",
|
|
42
|
+
"mermaid": "^11.12.2",
|
|
43
|
+
"react": "^18.2.0",
|
|
44
|
+
"react-dom": "^18.2.0",
|
|
45
|
+
"react-markdown": "^10.1.0",
|
|
46
|
+
"react-router-dom": "^7.13.0",
|
|
47
|
+
"remark-gfm": "^4.0.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
51
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
52
|
+
"@testing-library/react": "^16.3.2",
|
|
53
|
+
"@testing-library/user-event": "^14.6.1",
|
|
54
|
+
"@types/node": "^20.11.0",
|
|
55
|
+
"@types/react": "^18.2.48",
|
|
56
|
+
"@types/react-dom": "^18.2.18",
|
|
57
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
58
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
59
|
+
"@vercel/node": "^3.0.0",
|
|
60
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
61
|
+
"eslint": "^8.56.0",
|
|
62
|
+
"happy-dom": "^20.6.1",
|
|
63
|
+
"tailwindcss": "^4.1.18",
|
|
64
|
+
"tsx": "^4.21.0",
|
|
65
|
+
"typescript": "^5.3.3",
|
|
66
|
+
"vercel": "^33.0.0",
|
|
67
|
+
"vite": "^5.0.12",
|
|
68
|
+
"vitest": "^4.0.18"
|
|
69
|
+
},
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=18"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for generating TypeScript table types from a Dataverse Contact API deployment.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx dataverse-client generate --url https://your-api.vercel.app
|
|
7
|
+
* npx dataverse-client generate --url https://your-api.vercel.app --output src/types.ts
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
|
package/sdk/dist/cli.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for generating TypeScript table types from a Dataverse Contact API deployment.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx dataverse-client generate --url https://your-api.vercel.app
|
|
7
|
+
* npx dataverse-client generate --url https://your-api.vercel.app --output src/types.ts
|
|
8
|
+
*/
|
|
9
|
+
import { parseArgs } from "node:util";
|
|
10
|
+
import { writeFileSync } from "node:fs";
|
|
11
|
+
import { resolve } from "node:path";
|
|
12
|
+
import { generateTableTypes } from "./codegen.js";
|
|
13
|
+
const HELP = `
|
|
14
|
+
dataverse-client — TypeScript SDK for Dataverse Contact API
|
|
15
|
+
|
|
16
|
+
Commands:
|
|
17
|
+
generate Fetch schema from your API and generate TypeScript types
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--url, -u Base URL of your API deployment (required)
|
|
21
|
+
--output, -o Output file path (default: ./dataverse-tables.generated.ts)
|
|
22
|
+
--api-base API base path (default: /api/v2)
|
|
23
|
+
--no-choices Skip fetching choice values (faster, no enums generated)
|
|
24
|
+
--help, -h Show this help message
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
npx dataverse-client generate --url https://my-api.vercel.app
|
|
28
|
+
npx dataverse-client generate -u https://my-api.vercel.app -o src/tables.ts
|
|
29
|
+
npx dataverse-client generate --url https://my-api.vercel.app --no-choices
|
|
30
|
+
`.trim();
|
|
31
|
+
async function main() {
|
|
32
|
+
const { values, positionals } = parseArgs({
|
|
33
|
+
args: process.argv.slice(2),
|
|
34
|
+
options: {
|
|
35
|
+
url: { type: "string", short: "u" },
|
|
36
|
+
output: { type: "string", short: "o", default: "./dataverse-tables.generated.ts" },
|
|
37
|
+
"api-base": { type: "string", default: "/api/v2" },
|
|
38
|
+
"no-choices": { type: "boolean", default: false },
|
|
39
|
+
help: { type: "boolean", short: "h", default: false },
|
|
40
|
+
},
|
|
41
|
+
allowPositionals: true,
|
|
42
|
+
});
|
|
43
|
+
if (values.help || positionals.length === 0) {
|
|
44
|
+
console.log(HELP);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
const command = positionals[0];
|
|
48
|
+
if (command !== "generate") {
|
|
49
|
+
console.error(`Unknown command: ${command}\n\nRun 'dataverse-client --help' for usage.`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
if (!values.url) {
|
|
53
|
+
console.error("Error: --url is required.\n\nExample: npx dataverse-client generate --url https://your-api.vercel.app");
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const baseUrl = values.url.replace(/\/+$/, "");
|
|
57
|
+
const apiBase = values["api-base"] ?? "/api/v2";
|
|
58
|
+
const skipChoices = values["no-choices"] ?? false;
|
|
59
|
+
const schemaUrl = `${baseUrl}${apiBase}/schema`;
|
|
60
|
+
// 1. Fetch schema
|
|
61
|
+
console.log(`Fetching schema from ${schemaUrl} ...`);
|
|
62
|
+
let schemaResponse;
|
|
63
|
+
try {
|
|
64
|
+
schemaResponse = await fetch(schemaUrl);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
68
|
+
console.error(`Failed to fetch schema: ${message}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
if (!schemaResponse.ok) {
|
|
72
|
+
console.error(`Schema endpoint returned ${schemaResponse.status} ${schemaResponse.statusText}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const schema = await schemaResponse.json();
|
|
76
|
+
if (!schema.tables || !Array.isArray(schema.tables)) {
|
|
77
|
+
console.error("Unexpected response shape — expected { tables: [...] }");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
// 2. Fetch all public choices in a single request
|
|
81
|
+
let choices;
|
|
82
|
+
if (!skipChoices) {
|
|
83
|
+
const choicesUrl = `${baseUrl}${apiBase}/choices`;
|
|
84
|
+
console.log(`Fetching choices from ${choicesUrl} ...`);
|
|
85
|
+
try {
|
|
86
|
+
const choicesResponse = await fetch(choicesUrl);
|
|
87
|
+
if (choicesResponse.ok) {
|
|
88
|
+
const data = await choicesResponse.json();
|
|
89
|
+
if (data.tables && Array.isArray(data.tables)) {
|
|
90
|
+
choices = data.tables;
|
|
91
|
+
console.log(` Got choices for ${choices.length} table(s)`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.warn(` Choices endpoint returned ${choicesResponse.status}, skipping enums`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
console.warn(" Failed to fetch choices, skipping enums");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// 3. Generate TypeScript
|
|
103
|
+
const source = generateTableTypes(schema, choices);
|
|
104
|
+
const outputPath = resolve(process.cwd(), values.output);
|
|
105
|
+
writeFileSync(outputPath, source, "utf-8");
|
|
106
|
+
const tableCount = schema.tables.length;
|
|
107
|
+
const fieldCount = schema.tables.reduce((sum, t) => sum + t.fields.length, 0);
|
|
108
|
+
console.log(`Generated ${tableCount} table(s) with ${fieldCount} total fields`);
|
|
109
|
+
if (choices && choices.length > 0) {
|
|
110
|
+
const enumCount = choices.reduce((sum, c) => sum + Object.keys(c.fields).length, 0);
|
|
111
|
+
console.log(`Generated ${enumCount} choice enum(s)`);
|
|
112
|
+
}
|
|
113
|
+
console.log(`Written to ${outputPath}`);
|
|
114
|
+
}
|
|
115
|
+
main().catch((err) => {
|
|
116
|
+
console.error(err);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
|
119
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGlD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;CAiBZ,CAAC,IAAI,EAAE,CAAC;AAET,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;QACxC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,OAAO,EAAE;YACP,GAAG,EAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YAC5C,MAAM,EAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,iCAAiC,EAAE;YACxF,UAAU,EAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE;YACpD,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YACjD,IAAI,EAAU,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;SAC9D;QACD,gBAAgB,EAAE,IAAI;KACvB,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAE/B,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,8CAA8C,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,uGAAuG,CAAC,CAAC;QACvH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC;IAChD,MAAM,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC;IAClD,MAAM,SAAS,GAAG,GAAG,OAAO,GAAG,OAAO,SAAS,CAAC;IAEhD,kBAAkB;IAClB,OAAO,CAAC,GAAG,CAAC,wBAAwB,SAAS,MAAM,CAAC,CAAC;IAErD,IAAI,cAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,cAAc,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,KAAK,CAAC,2BAA2B,OAAO,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,4BAA4B,cAAc,CAAC,MAAM,IAAI,cAAc,CAAC,UAAU,EAAE,CAAC,CAAC;QAChG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,CAAC;IAE3C,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,kDAAkD;IAClD,IAAI,OAAwC,CAAC;IAE7C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,UAAU,GAAG,GAAG,OAAO,GAAG,OAAO,UAAU,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,yBAAyB,UAAU,MAAM,CAAC,CAAC;QAEvD,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC;YAChD,IAAI,eAAe,CAAC,EAAE,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,CAAC;gBAC1C,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC9C,OAAO,GAAG,IAAI,CAAC,MAA6B,CAAC;oBAC7C,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,CAAC,MAAM,WAAW,CAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,+BAA+B,eAAe,CAAC,MAAM,kBAAkB,CAAC,CAAC;YACxF,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,MAAO,CAAC,CAAC;IAE1D,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3C,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,MAAgB,CAAC;IAClD,MAAM,UAAU,GAAI,MAAM,CAAC,MAAkC,CAAC,MAAM,CAClE,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,EACjC,CAAC,CACF,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,aAAa,UAAU,kBAAkB,UAAU,eAAe,CAAC,CAAC;IAChF,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAC9C,CAAC,CACF,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,aAAa,SAAS,iBAAiB,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,cAAc,UAAU,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ClientConfig, QueryOptions, LookupOptions, PaginatedResponse, SingleResponse, WhoamiResponse, TableSchema, FieldChoicesResponse, TableChoicesResponse, ApiErrorBody } from "./types.js";
|
|
2
|
+
/** Error thrown when the API returns a non-OK response */
|
|
3
|
+
export declare class ApiError extends Error {
|
|
4
|
+
readonly status: number;
|
|
5
|
+
readonly statusText: string;
|
|
6
|
+
readonly body: ApiErrorBody | null;
|
|
7
|
+
constructor(status: number, statusText: string, body: ApiErrorBody | null);
|
|
8
|
+
}
|
|
9
|
+
/** Scoped API operations (me, team, or all) */
|
|
10
|
+
export interface ScopeClient {
|
|
11
|
+
/** List records from a table */
|
|
12
|
+
list<T = Record<string, unknown>>(table: string, options?: QueryOptions): Promise<PaginatedResponse<T>>;
|
|
13
|
+
/** Get a single record by ID */
|
|
14
|
+
get<T = Record<string, unknown>>(table: string, id: string, options?: Pick<QueryOptions, "select" | "expand">): Promise<SingleResponse<T>>;
|
|
15
|
+
/** Update a record */
|
|
16
|
+
update<T = Record<string, unknown>>(table: string, id: string, data: Record<string, unknown>): Promise<SingleResponse<T>>;
|
|
17
|
+
/** Search lookup records (type-ahead) */
|
|
18
|
+
lookup<T = Record<string, unknown>>(table: string, options?: LookupOptions): Promise<PaginatedResponse<T>>;
|
|
19
|
+
}
|
|
20
|
+
/** Extended scope with create and whoami (only available on "me") */
|
|
21
|
+
export interface MeScopeClient extends ScopeClient {
|
|
22
|
+
/** Create a new record */
|
|
23
|
+
create<T = Record<string, unknown>>(table: string, data: Record<string, unknown>): Promise<SingleResponse<T>>;
|
|
24
|
+
/** Get authenticated identity + Dataverse contact info */
|
|
25
|
+
whoami(): Promise<WhoamiResponse>;
|
|
26
|
+
}
|
|
27
|
+
/** Dataverse Contact API client */
|
|
28
|
+
export interface DataverseClient {
|
|
29
|
+
/** Operations on the authenticated user's own records */
|
|
30
|
+
me: MeScopeClient;
|
|
31
|
+
/** Operations on records belonging to the user's account/team */
|
|
32
|
+
team: ScopeClient;
|
|
33
|
+
/** Operations on all records (admin) */
|
|
34
|
+
all: ScopeClient;
|
|
35
|
+
/** Fetch choice/option-set values for a table, or a specific field */
|
|
36
|
+
choices(table: string, field?: string): Promise<FieldChoicesResponse | TableChoicesResponse>;
|
|
37
|
+
/** Fetch table schema (single table or all tables) */
|
|
38
|
+
schema(table?: string): Promise<TableSchema | TableSchema[]>;
|
|
39
|
+
}
|
|
40
|
+
/** Create a configured API client instance */
|
|
41
|
+
export declare function createClient(config: ClientConfig): DataverseClient;
|
|
42
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,0DAA0D;AAC1D,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;gBAEvB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI;CAO1E;AA+DD,+CAA+C;AAC/C,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjC,gCAAgC;IAChC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,QAAQ,CAAC,GAChD,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9B,sBAAsB;IACtB,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9B,yCAAyC;IACzC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;CAClC;AAwDD,qEAAqE;AACrE,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,0BAA0B;IAC1B,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9B,0DAA0D;IAC1D,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;CACnC;AAoBD,mCAAmC;AACnC,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,EAAE,EAAE,aAAa,CAAC;IAClB,iEAAiE;IACjE,IAAI,EAAE,WAAW,CAAC;IAClB,wCAAwC;IACxC,GAAG,EAAE,WAAW,CAAC;IAEjB,sEAAsE;IACtE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,oBAAoB,CAAC,CAAC;IAE7F,sDAAsD;IACtD,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,WAAW,EAAE,CAAC,CAAC;CAC9D;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,eAAe,CAsBlE"}
|