lt-open-data-sdk 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/LICENSE +21 -0
- package/README.md +366 -0
- package/dist/builder/FilterBuilder.d.ts +19 -0
- package/dist/builder/FilterBuilder.d.ts.map +1 -0
- package/dist/builder/FilterBuilder.js +178 -0
- package/dist/builder/FilterBuilder.js.map +1 -0
- package/dist/builder/QueryBuilder.d.ts +99 -0
- package/dist/builder/QueryBuilder.d.ts.map +1 -0
- package/dist/builder/QueryBuilder.js +162 -0
- package/dist/builder/QueryBuilder.js.map +1 -0
- package/dist/builder/index.d.ts +7 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +6 -0
- package/dist/builder/index.js.map +1 -0
- package/dist/builder/types.d.ts +70 -0
- package/dist/builder/types.d.ts.map +1 -0
- package/dist/builder/types.js +5 -0
- package/dist/builder/types.js.map +1 -0
- package/dist/cli/crawler.d.ts +50 -0
- package/dist/cli/crawler.d.ts.map +1 -0
- package/dist/cli/crawler.js +161 -0
- package/dist/cli/crawler.js.map +1 -0
- package/dist/cli/generator.d.ts +9 -0
- package/dist/cli/generator.d.ts.map +1 -0
- package/dist/cli/generator.js +81 -0
- package/dist/cli/generator.js.map +1 -0
- package/dist/cli/index.d.ts +19 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +125 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/typeMapper.d.ts +34 -0
- package/dist/cli/typeMapper.d.ts.map +1 -0
- package/dist/cli/typeMapper.js +150 -0
- package/dist/cli/typeMapper.js.map +1 -0
- package/dist/client/SpintaClient.d.ts +168 -0
- package/dist/client/SpintaClient.d.ts.map +1 -0
- package/dist/client/SpintaClient.js +262 -0
- package/dist/client/SpintaClient.js.map +1 -0
- package/dist/client/auth.d.ts +31 -0
- package/dist/client/auth.d.ts.map +1 -0
- package/dist/client/auth.js +96 -0
- package/dist/client/auth.js.map +1 -0
- package/dist/client/errors.d.ts +35 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +73 -0
- package/dist/client/errors.js.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +6 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +96 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +5 -0
- package/dist/client/types.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Giedrius Macevičius
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# lt-open-data-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the **Lithuanian Open Data platform** ([data.gov.lt](https://data.gov.lt)) powered by the Spinta engine.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔍 **QueryBuilder** - Fluent API for constructing DSQL queries
|
|
8
|
+
- 🌐 **SpintaClient** - HTTP client with automatic pagination
|
|
9
|
+
- 🛠️ **CLI Type Generator** - Generate TypeScript interfaces from live API
|
|
10
|
+
- 🔐 **OAuth Support** - Client credentials authentication _(untested)_
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install lt-open-data-sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires Node.js ≥18 (uses native `fetch`).
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { SpintaClient, QueryBuilder } from "lt-open-data-sdk";
|
|
24
|
+
|
|
25
|
+
const client = new SpintaClient();
|
|
26
|
+
|
|
27
|
+
// Fetch data with a query
|
|
28
|
+
const query = new QueryBuilder()
|
|
29
|
+
.select("_id", "pavadinimas")
|
|
30
|
+
.filter((f) => f.field("sav_kodas").gt(10))
|
|
31
|
+
.sort("pavadinimas")
|
|
32
|
+
.limit(10);
|
|
33
|
+
|
|
34
|
+
const data = await client.getAll(
|
|
35
|
+
"datasets/gov/rc/ar/savivaldybe/Savivaldybe",
|
|
36
|
+
query
|
|
37
|
+
);
|
|
38
|
+
console.log(data);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## QueryBuilder
|
|
44
|
+
|
|
45
|
+
Build type-safe queries with a fluent API:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
const query = new QueryBuilder<MyType>()
|
|
49
|
+
.select("field1", "field2") // Select specific fields
|
|
50
|
+
.filter((f) => f.field("x").eq(1)) // Add filters
|
|
51
|
+
.sort("field1") // Sort ascending
|
|
52
|
+
.sortDesc("field2") // Sort descending
|
|
53
|
+
.limit(100); // Limit results
|
|
54
|
+
|
|
55
|
+
const queryString = query.toQueryString();
|
|
56
|
+
// Returns: ?select(field1,field2)&x=1&sort(field1,-field2)&limit(100)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Filter Operators
|
|
60
|
+
|
|
61
|
+
| Method | Query | Description |
|
|
62
|
+
| ------------------ | ------------------------- | --------------------- |
|
|
63
|
+
| `.eq(value)` | `field=value` | Equals |
|
|
64
|
+
| `.ne(value)` | `field!=value` | Not equals |
|
|
65
|
+
| `.lt(value)` | `field<value` | Less than |
|
|
66
|
+
| `.le(value)` | `field<=value` | Less than or equal |
|
|
67
|
+
| `.gt(value)` | `field>value` | Greater than |
|
|
68
|
+
| `.ge(value)` | `field>=value` | Greater than or equal |
|
|
69
|
+
| `.contains(str)` | `field.contains("str")` | Contains substring |
|
|
70
|
+
| `.startswith(str)` | `field.startswith("str")` | Starts with |
|
|
71
|
+
|
|
72
|
+
### Combining Filters
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// AND
|
|
76
|
+
.filter(f => f.field('a').eq(1).and(f.field('b').eq(2)))
|
|
77
|
+
// Output: a=1&b=2
|
|
78
|
+
|
|
79
|
+
// OR
|
|
80
|
+
.filter(f => f.field('a').eq(1).or(f.field('b').eq(2)))
|
|
81
|
+
// Output: a=1|b=2
|
|
82
|
+
|
|
83
|
+
// Complex (OR inside AND - auto-wrapped in parentheses)
|
|
84
|
+
.filter(f => f.field('a').gt(10).and(
|
|
85
|
+
f.field('b').eq(1).or(f.field('b').eq(2))
|
|
86
|
+
))
|
|
87
|
+
// Output: a>10&(b=1|b=2)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## SpintaClient
|
|
93
|
+
|
|
94
|
+
### Basic Usage
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const client = new SpintaClient();
|
|
98
|
+
// Uses https://get.data.gov.lt by default
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Methods
|
|
102
|
+
|
|
103
|
+
#### `getAll(model, query?)` - Fetch one page
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const cities = await client.getAll("datasets/gov/example/City", query);
|
|
107
|
+
// Returns: Array of objects (unwrapped from _data)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> ⚠️ **Note**: Returns ONE page only. Use `stream()` for all records.
|
|
111
|
+
|
|
112
|
+
#### `getAllRaw(model, query?)` - Fetch with metadata
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const response = await client.getAllRaw("datasets/gov/example/City", query);
|
|
116
|
+
// Returns: { _type, _data: [...], _page: { next } }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `getOne(model, id)` - Fetch by UUID
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const city = await client.getOne("datasets/gov/example/City", "uuid-here");
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### `count(model, query?)` - Count records
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const total = await client.count("datasets/gov/example/City");
|
|
129
|
+
const filtered = await client.count(
|
|
130
|
+
"datasets/gov/example/City",
|
|
131
|
+
new QueryBuilder().filter((f) => f.field("population").gt(100000))
|
|
132
|
+
);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### `stream(model, query?)` - Paginated iteration
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
for await (const city of client.stream("datasets/gov/example/City")) {
|
|
139
|
+
console.log(city.pavadinimas);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### `listNamespace(namespace)` - List namespace contents
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const items = await client.listNamespace("datasets/gov/rc");
|
|
147
|
+
// Returns: [{ _id: 'path', _type: 'ns' | 'model', title? }]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### `discoverModels(namespace)` - Find all models recursively
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// Discover all available models in a namespace
|
|
154
|
+
const models = await client.discoverModels("datasets/gov/rc/ar");
|
|
155
|
+
console.log(`Found ${models.length} models`);
|
|
156
|
+
|
|
157
|
+
for (const model of models) {
|
|
158
|
+
console.log(`${model.path} - ${model.title}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Then generate types for a specific model:
|
|
162
|
+
// npx lt-gen datasets/gov/rc/ar/savivaldybe -o ./types/savivaldybe.d.ts
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Returns: `{ path, title?, namespace }[]`
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Type Safety & Autocomplete
|
|
170
|
+
|
|
171
|
+
The SDK provides full TypeScript support. The workflow is:
|
|
172
|
+
|
|
173
|
+
1. **Generate types** for your dataset:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npx lt-gen datasets/gov/rc/ar/savivaldybe -o ./types/savivaldybe.d.ts
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
2. **Import and use** in your code:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { SpintaClient } from "lt-open-data-sdk";
|
|
183
|
+
import type { GovRcArSavivaldybe_Savivaldybe } from "./types/savivaldybe";
|
|
184
|
+
|
|
185
|
+
const client = new SpintaClient();
|
|
186
|
+
|
|
187
|
+
// Pass the type to the method to get full autocomplete!
|
|
188
|
+
const data = await client.getAll<GovRcArSavivaldybe_Savivaldybe>(
|
|
189
|
+
"datasets/gov/rc/ar/savivaldybe/Savivaldybe"
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// ✅ TypeScript knows these fields exist:
|
|
193
|
+
console.log(data[0].pavadinimas); // string
|
|
194
|
+
console.log(data[0].sav_kodas); // number
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Pagination
|
|
200
|
+
|
|
201
|
+
The API uses cursor-based pagination with `_page.next` tokens.
|
|
202
|
+
|
|
203
|
+
### Automatic Pagination with `stream()`
|
|
204
|
+
|
|
205
|
+
Use `stream()` to iterate through all records automatically:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const query = new QueryBuilder().limit(100); // 100 items per page
|
|
209
|
+
|
|
210
|
+
for await (const item of client.stream("datasets/gov/example/City", query)) {
|
|
211
|
+
console.log(item.pavadinimas);
|
|
212
|
+
// Automatically fetches next page when current page is exhausted
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**How it works:**
|
|
217
|
+
|
|
218
|
+
1. Fetches first page with your query
|
|
219
|
+
2. Yields items one by one
|
|
220
|
+
3. When page exhausted, uses `_page.next` token to fetch next page
|
|
221
|
+
4. Continues until no more pages
|
|
222
|
+
|
|
223
|
+
### Manual Pagination with `getAllRaw()`
|
|
224
|
+
|
|
225
|
+
For more control, handle pagination yourself:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
let pageToken: string | undefined;
|
|
229
|
+
|
|
230
|
+
do {
|
|
231
|
+
// Build query with page token
|
|
232
|
+
let query = new QueryBuilder().limit(100);
|
|
233
|
+
|
|
234
|
+
const response = await client.getAllRaw("datasets/gov/example/City", query);
|
|
235
|
+
|
|
236
|
+
// Process this page
|
|
237
|
+
for (const item of response._data) {
|
|
238
|
+
console.log(item);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Get next page token
|
|
242
|
+
pageToken = response._page?.next;
|
|
243
|
+
|
|
244
|
+
// Note: You need to add page(token) to next request manually
|
|
245
|
+
// This is handled automatically by stream()
|
|
246
|
+
} while (pageToken);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
> **Tip**: Use `stream()` for most cases. Use `getAllRaw()` when you need access to page metadata or custom page handling.
|
|
250
|
+
|
|
251
|
+
## CLI Type Generator
|
|
252
|
+
|
|
253
|
+
Generate TypeScript interfaces from live API metadata:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# Install globally or use npx
|
|
257
|
+
npx lt-gen datasets/gov/rc/ar/savivaldybe
|
|
258
|
+
|
|
259
|
+
# Save to file
|
|
260
|
+
npx lt-gen datasets/gov/rc/ar/savivaldybe -o ./types/savivaldybe.d.ts
|
|
261
|
+
|
|
262
|
+
# Custom API URL
|
|
263
|
+
npx lt-gen datasets/gov/rc/ar/savivaldybe --base-url https://get-test.data.gov.lt
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Generated Output
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
export interface GovRcArSavivaldybe_Savivaldybe {
|
|
270
|
+
_id: string;
|
|
271
|
+
_type: string;
|
|
272
|
+
_revision?: string;
|
|
273
|
+
sav_kodas?: number;
|
|
274
|
+
pavadinimas?: string;
|
|
275
|
+
apskritis?: string | { _id: string }; // ref type
|
|
276
|
+
sav_nuo?: string; // date
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export interface ModelMap {
|
|
280
|
+
"datasets/gov/rc/ar/savivaldybe/Savivaldybe": GovRcArSavivaldybe_Savivaldybe;
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
> **Note**: Types are inferred from data samples since schema endpoints require authentication.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Authentication _(Untested)_
|
|
289
|
+
|
|
290
|
+
For write operations or private data, provide OAuth credentials:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
const client = new SpintaClient({
|
|
294
|
+
clientId: "your-client-id",
|
|
295
|
+
clientSecret: "your-client-secret",
|
|
296
|
+
authUrl: "https://put.data.gov.lt", // optional, default
|
|
297
|
+
scopes: ["spinta_getone", "spinta_getall"], // optional
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
The SDK handles:
|
|
302
|
+
|
|
303
|
+
- OAuth client credentials flow
|
|
304
|
+
- Automatic token caching
|
|
305
|
+
- Token refresh before expiry (5-minute buffer)
|
|
306
|
+
|
|
307
|
+
> ⚠️ **Note**: Authentication has been implemented but not tested against a live auth server.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Error Handling
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import {
|
|
315
|
+
SpintaError,
|
|
316
|
+
NotFoundError,
|
|
317
|
+
AuthenticationError,
|
|
318
|
+
ValidationError,
|
|
319
|
+
} from "lt-open-data-sdk";
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const data = await client.getOne("datasets/example", "invalid-id");
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (error instanceof NotFoundError) {
|
|
325
|
+
console.log("Not found:", error.message);
|
|
326
|
+
} else if (error instanceof AuthenticationError) {
|
|
327
|
+
console.log("Auth failed:", error.status);
|
|
328
|
+
} else if (error instanceof ValidationError) {
|
|
329
|
+
console.log("Bad request:", error.body);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## API Reference
|
|
337
|
+
|
|
338
|
+
### Exports
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
// Client
|
|
342
|
+
export { SpintaClient } from "./client/SpintaClient";
|
|
343
|
+
export {
|
|
344
|
+
SpintaError,
|
|
345
|
+
AuthenticationError,
|
|
346
|
+
NotFoundError,
|
|
347
|
+
ValidationError,
|
|
348
|
+
} from "./client/errors";
|
|
349
|
+
|
|
350
|
+
// Query Builder
|
|
351
|
+
export { QueryBuilder } from "./builder/QueryBuilder";
|
|
352
|
+
export { FilterBuilder } from "./builder/FilterBuilder";
|
|
353
|
+
|
|
354
|
+
// Types
|
|
355
|
+
export type {
|
|
356
|
+
ClientConfig,
|
|
357
|
+
SpintaResponse,
|
|
358
|
+
SpintaObject,
|
|
359
|
+
} from "./client/types";
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## License
|
|
365
|
+
|
|
366
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FilterBuilder - Fluent interface for constructing filter expressions
|
|
3
|
+
*
|
|
4
|
+
* Supports AND/OR combinations with proper precedence handling.
|
|
5
|
+
* Per the Spinta grammar, AND binds tighter than OR.
|
|
6
|
+
* Auto-wraps expressions in parentheses when mixing operators.
|
|
7
|
+
*/
|
|
8
|
+
import type { FilterBuilderInterface, FieldFilterInterface, FilterExpression } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* FilterBuilder - Entry point for constructing filter expressions
|
|
11
|
+
*/
|
|
12
|
+
export declare class FilterBuilder<T> implements FilterBuilderInterface<T> {
|
|
13
|
+
field(name: keyof T | string): FieldFilterInterface;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Converts a filter expression tree to a Spinta query string
|
|
17
|
+
*/
|
|
18
|
+
export declare function filterToString(expr: FilterExpression): string;
|
|
19
|
+
//# sourceMappingURL=FilterBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterBuilder.d.ts","sourceRoot":"","sources":["../../src/builder/FilterBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,oBAAoB,EAEpB,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAsFpB;;GAEG;AACH,qBAAa,aAAa,CAAC,CAAC,CAAE,YAAW,sBAAsB,CAAC,CAAC,CAAC;IAChE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,oBAAoB;CAGpD;AA6CD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAkD7D"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FilterBuilder - Fluent interface for constructing filter expressions
|
|
3
|
+
*
|
|
4
|
+
* Supports AND/OR combinations with proper precedence handling.
|
|
5
|
+
* Per the Spinta grammar, AND binds tighter than OR.
|
|
6
|
+
* Auto-wraps expressions in parentheses when mixing operators.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Creates a filter expression builder from a filter node
|
|
10
|
+
*/
|
|
11
|
+
function createExpressionBuilder(node) {
|
|
12
|
+
return {
|
|
13
|
+
node,
|
|
14
|
+
and(other) {
|
|
15
|
+
return createExpressionBuilder({
|
|
16
|
+
type: 'and',
|
|
17
|
+
left: this.node,
|
|
18
|
+
right: other.node,
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
or(other) {
|
|
22
|
+
return createExpressionBuilder({
|
|
23
|
+
type: 'or',
|
|
24
|
+
left: this.node,
|
|
25
|
+
right: other.node,
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Field filter for building comparison expressions
|
|
32
|
+
*/
|
|
33
|
+
class FieldFilter {
|
|
34
|
+
fieldName;
|
|
35
|
+
constructor(fieldName) {
|
|
36
|
+
this.fieldName = fieldName;
|
|
37
|
+
}
|
|
38
|
+
comparison(operator, value) {
|
|
39
|
+
return createExpressionBuilder({
|
|
40
|
+
type: 'comparison',
|
|
41
|
+
field: this.fieldName,
|
|
42
|
+
operator,
|
|
43
|
+
value,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
stringOp(operator, value) {
|
|
47
|
+
return createExpressionBuilder({
|
|
48
|
+
type: 'string_op',
|
|
49
|
+
field: this.fieldName,
|
|
50
|
+
operator,
|
|
51
|
+
value,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
eq(value) {
|
|
55
|
+
return this.comparison('eq', value);
|
|
56
|
+
}
|
|
57
|
+
ne(value) {
|
|
58
|
+
return this.comparison('ne', value);
|
|
59
|
+
}
|
|
60
|
+
lt(value) {
|
|
61
|
+
return this.comparison('lt', value);
|
|
62
|
+
}
|
|
63
|
+
le(value) {
|
|
64
|
+
return this.comparison('le', value);
|
|
65
|
+
}
|
|
66
|
+
gt(value) {
|
|
67
|
+
return this.comparison('gt', value);
|
|
68
|
+
}
|
|
69
|
+
ge(value) {
|
|
70
|
+
return this.comparison('ge', value);
|
|
71
|
+
}
|
|
72
|
+
contains(value) {
|
|
73
|
+
return this.stringOp('contains', value);
|
|
74
|
+
}
|
|
75
|
+
startswith(value) {
|
|
76
|
+
return this.stringOp('startswith', value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* FilterBuilder - Entry point for constructing filter expressions
|
|
81
|
+
*/
|
|
82
|
+
export class FilterBuilder {
|
|
83
|
+
field(name) {
|
|
84
|
+
return new FieldFilter(String(name));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Formats a value for use in a Spinta query
|
|
89
|
+
* Values are URL-encoded to ensure safe transport in URLs
|
|
90
|
+
*/
|
|
91
|
+
function formatValue(value) {
|
|
92
|
+
if (value === null) {
|
|
93
|
+
return 'null';
|
|
94
|
+
}
|
|
95
|
+
if (typeof value === 'string') {
|
|
96
|
+
// 1. Escape internal quotes
|
|
97
|
+
const escaped = value.replace(/"/g, '\\"');
|
|
98
|
+
// 2. Wrap in quotes
|
|
99
|
+
const quoted = `"${escaped}"`;
|
|
100
|
+
// 3. Encode the final token so it survives URL transport
|
|
101
|
+
return encodeURIComponent(quoted);
|
|
102
|
+
}
|
|
103
|
+
if (typeof value === 'boolean') {
|
|
104
|
+
return value ? 'true' : 'false';
|
|
105
|
+
}
|
|
106
|
+
if (typeof value === 'number') {
|
|
107
|
+
return String(value);
|
|
108
|
+
}
|
|
109
|
+
if (value instanceof Date) {
|
|
110
|
+
// Encode ISO date string for URL safety
|
|
111
|
+
return encodeURIComponent(`"${value.toISOString()}"`);
|
|
112
|
+
}
|
|
113
|
+
// For other objects, use JSON.stringify and encode
|
|
114
|
+
if (typeof value === 'object') {
|
|
115
|
+
return encodeURIComponent(JSON.stringify(value));
|
|
116
|
+
}
|
|
117
|
+
// For symbols, functions, or other types - stringify safely
|
|
118
|
+
return encodeURIComponent(`"${typeof value === 'symbol' ? value.toString() : 'unknown'}"`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Checks if an expression needs parentheses when used as a child of the given parent type
|
|
122
|
+
* AND binds tighter than OR, so OR children inside AND need parens
|
|
123
|
+
*/
|
|
124
|
+
function needsParens(child, parentType) {
|
|
125
|
+
// OR inside AND needs parentheses because AND has higher precedence
|
|
126
|
+
return parentType === 'and' && child.type === 'or';
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Converts a filter expression tree to a Spinta query string
|
|
130
|
+
*/
|
|
131
|
+
export function filterToString(expr) {
|
|
132
|
+
switch (expr.type) {
|
|
133
|
+
case 'comparison': {
|
|
134
|
+
const value = formatValue(expr.value);
|
|
135
|
+
// Use operator syntax: field=value, field!=value, etc.
|
|
136
|
+
switch (expr.operator) {
|
|
137
|
+
case 'eq':
|
|
138
|
+
return `${expr.field}=${value}`;
|
|
139
|
+
case 'ne':
|
|
140
|
+
return `${expr.field}!=${value}`;
|
|
141
|
+
case 'lt':
|
|
142
|
+
return `${expr.field}<${value}`;
|
|
143
|
+
case 'le':
|
|
144
|
+
return `${expr.field}<=${value}`;
|
|
145
|
+
case 'gt':
|
|
146
|
+
return `${expr.field}>${value}`;
|
|
147
|
+
case 'ge':
|
|
148
|
+
return `${expr.field}>=${value}`;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case 'string_op': {
|
|
153
|
+
const value = formatValue(expr.value);
|
|
154
|
+
// Use method syntax: field.contains(value)
|
|
155
|
+
return `${expr.field}.${expr.operator}(${value})`;
|
|
156
|
+
}
|
|
157
|
+
case 'and': {
|
|
158
|
+
const leftStr = needsParens(expr.left, 'and')
|
|
159
|
+
? `(${filterToString(expr.left)})`
|
|
160
|
+
: filterToString(expr.left);
|
|
161
|
+
const rightStr = needsParens(expr.right, 'and')
|
|
162
|
+
? `(${filterToString(expr.right)})`
|
|
163
|
+
: filterToString(expr.right);
|
|
164
|
+
return `${leftStr}&${rightStr}`;
|
|
165
|
+
}
|
|
166
|
+
case 'or': {
|
|
167
|
+
const leftStr = needsParens(expr.left, 'or')
|
|
168
|
+
? `(${filterToString(expr.left)})`
|
|
169
|
+
: filterToString(expr.left);
|
|
170
|
+
const rightStr = needsParens(expr.right, 'or')
|
|
171
|
+
? `(${filterToString(expr.right)})`
|
|
172
|
+
: filterToString(expr.right);
|
|
173
|
+
return `${leftStr}|${rightStr}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return '';
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=FilterBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterBuilder.js","sourceRoot":"","sources":["../../src/builder/FilterBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH;;GAEG;AACH,SAAS,uBAAuB,CAAC,IAAsB;IACrD,OAAO;QACL,IAAI;QACJ,GAAG,CAAC,KAA8B;YAChC,OAAO,uBAAuB,CAAC;gBAC7B,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,KAAK,CAAC,IAAI;aAClB,CAAC,CAAC;QACL,CAAC;QACD,EAAE,CAAC,KAA8B;YAC/B,OAAO,uBAAuB,CAAC;gBAC7B,IAAI,EAAE,IAAI;gBACV,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,KAAK,CAAC,IAAI;aAClB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,WAAW;IACE,SAAS,CAAS;IAEnC,YAAY,SAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAEO,UAAU,CAAC,QAA4B,EAAE,KAAc;QAC7D,OAAO,uBAAuB,CAAC;YAC7B,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,IAAI,CAAC,SAAS;YACrB,QAAQ;YACR,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAEO,QAAQ,CAAC,QAAwB,EAAE,KAAa;QACtD,OAAO,uBAAuB,CAAC;YAC7B,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,IAAI,CAAC,SAAS;YACrB,QAAQ;YACR,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,EAAE,CAAC,KAAc;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,EAAE,CAAC,KAAc;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,EAAE,CAAC,KAAc;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,EAAE,CAAC,KAAc;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,EAAE,CAAC,KAAc;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,EAAE,CAAC,KAAc;QACf,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,UAAU,CAAC,KAAa;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,aAAa;IACxB,KAAK,CAAC,IAAsB;QAC1B,OAAO,IAAI,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,4BAA4B;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC3C,oBAAoB;QACpB,MAAM,MAAM,GAAG,IAAI,OAAO,GAAG,CAAC;QAC9B,yDAAyD;QACzD,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAClC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QAC1B,wCAAwC;QACxC,OAAO,kBAAkB,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACxD,CAAC;IACD,mDAAmD;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACnD,CAAC;IACD,4DAA4D;IAC5D,OAAO,kBAAkB,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;AAC7F,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,KAAuB,EAAE,UAAwB;IACpE,oEAAoE;IACpE,OAAO,UAAU,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAAsB;IACnD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtC,uDAAuD;YACvD,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACtB,KAAK,IAAI;oBACP,OAAO,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;gBAClC,KAAK,IAAI;oBACP,OAAO,GAAG,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBACnC,KAAK,IAAI;oBACP,OAAO,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;gBAClC,KAAK,IAAI;oBACP,OAAO,GAAG,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBACnC,KAAK,IAAI;oBACP,OAAO,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC;gBAClC,KAAK,IAAI;oBACP,OAAO,GAAG,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YACrC,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtC,2CAA2C;YAC3C,OAAO,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,GAAG,CAAC;QACpD,CAAC;QAED,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;gBAC3C,CAAC,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAClC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC;gBAC7C,CAAC,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG;gBACnC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,OAAO,GAAG,OAAO,IAAI,QAAQ,EAAE,CAAC;QAClC,CAAC;QAED,KAAK,IAAI,CAAC,CAAC,CAAC;YACV,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;gBAC1C,CAAC,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAClC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC;gBAC5C,CAAC,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG;gBACnC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,OAAO,GAAG,OAAO,IAAI,QAAQ,EAAE,CAAC;QAClC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QueryBuilder - Fluent interface for constructing Spinta DSQL query strings
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const query = new QueryBuilder<City>()
|
|
7
|
+
* .select('name', 'population')
|
|
8
|
+
* .filter(f => f.field('country').eq('lt'))
|
|
9
|
+
* .sort('name')
|
|
10
|
+
* .limit(10);
|
|
11
|
+
*
|
|
12
|
+
* // Generates: ?select(name,population)&country="lt"&sort(name)&limit(10)
|
|
13
|
+
* const url = `/datasets/gov/example/City${query.toQueryString()}`;
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import type { FilterCallback } from './types.js';
|
|
17
|
+
export declare class QueryBuilder<T = Record<string, unknown>> {
|
|
18
|
+
private selectFields;
|
|
19
|
+
private sortSpecs;
|
|
20
|
+
private limitValue;
|
|
21
|
+
private countMode;
|
|
22
|
+
private filterExpression;
|
|
23
|
+
/**
|
|
24
|
+
* Select specific fields to return
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* .select('id', 'name') → ?select(id,name)
|
|
28
|
+
* .select('country.name') → ?select(country.name) // Supports dot notation for joins
|
|
29
|
+
*/
|
|
30
|
+
select(...fields: (keyof T | string)[]): this;
|
|
31
|
+
/**
|
|
32
|
+
* Sort by field in ascending order
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* .sort('name') → ?sort(name)
|
|
36
|
+
*/
|
|
37
|
+
sort(field: keyof T | string): this;
|
|
38
|
+
/**
|
|
39
|
+
* Sort by field in descending order
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* .sortDesc('date') → ?sort(-date)
|
|
43
|
+
*/
|
|
44
|
+
sortDesc(field: keyof T | string): this;
|
|
45
|
+
/**
|
|
46
|
+
* Limit the number of results returned
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* .limit(10) → ?limit(10)
|
|
50
|
+
*/
|
|
51
|
+
limit(n: number): this;
|
|
52
|
+
/**
|
|
53
|
+
* Request count of objects instead of the objects themselves
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* .count() → ?count()
|
|
57
|
+
*/
|
|
58
|
+
count(): this;
|
|
59
|
+
/**
|
|
60
|
+
* Add filter conditions
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Simple equality
|
|
64
|
+
* .filter(f => f.field('code').eq('lt'))
|
|
65
|
+
*
|
|
66
|
+
* // String operations
|
|
67
|
+
* .filter(f => f.field('name').contains('Vilnius'))
|
|
68
|
+
*
|
|
69
|
+
* // AND combination
|
|
70
|
+
* .filter(f => f.field('a').eq(1).and(f.field('b').eq(2)))
|
|
71
|
+
*
|
|
72
|
+
* // OR combination
|
|
73
|
+
* .filter(f => f.field('a').eq(1).or(f.field('b').eq(2)))
|
|
74
|
+
*
|
|
75
|
+
* // Complex: OR inside AND (auto-wrapped in parens)
|
|
76
|
+
* .filter(f =>
|
|
77
|
+
* f.field('a').eq(1).and(
|
|
78
|
+
* f.field('b').eq(2).or(f.field('c').eq(3))
|
|
79
|
+
* )
|
|
80
|
+
* )
|
|
81
|
+
* // Generates: a=1&(b=2|c=3)
|
|
82
|
+
*/
|
|
83
|
+
filter(callback: FilterCallback<T>): this;
|
|
84
|
+
/**
|
|
85
|
+
* Generate the URL query string
|
|
86
|
+
*
|
|
87
|
+
* @returns Query string starting with '?' if there are any parameters, empty string otherwise
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* new QueryBuilder().select('name').sort('name').toQueryString()
|
|
91
|
+
* // Returns: '?select(name)&sort(name)'
|
|
92
|
+
*/
|
|
93
|
+
toQueryString(): string;
|
|
94
|
+
/**
|
|
95
|
+
* Clone this query builder (useful for creating variants)
|
|
96
|
+
*/
|
|
97
|
+
clone(): QueryBuilder<T>;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=QueryBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueryBuilder.d.ts","sourceRoot":"","sources":["../../src/builder/QueryBuilder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAqC,MAAM,YAAY,CAAC;AAEpF,qBAAa,YAAY,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACnD,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAwC;IAEhE;;;;;;OAMG;IACH,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI;IAK7C;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI;IAKnC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI;IAKvC;;;;;OAKG;IACH,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAKtB;;;;;OAKG;IACH,KAAK,IAAI,IAAI;IAKb;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI;IAczC;;;;;;;;OAQG;IACH,aAAa,IAAI,MAAM;IAwCvB;;OAEG;IACH,KAAK,IAAI,YAAY,CAAC,CAAC,CAAC;CASzB"}
|