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
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpintaClient - HTTP client for the Lithuanian Open Data API (data.gov.lt)
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { SpintaClient, QueryBuilder } from 'lt-data-sdk';
|
|
7
|
+
*
|
|
8
|
+
* const client = new SpintaClient();
|
|
9
|
+
*
|
|
10
|
+
* // Get all items (single page - uses limit from query or API default)
|
|
11
|
+
* const cities = await client.getAll('datasets/gov/example/City');
|
|
12
|
+
*
|
|
13
|
+
* // Stream all items with automatic pagination
|
|
14
|
+
* for await (const city of client.stream('datasets/gov/example/City')) {
|
|
15
|
+
* console.log(city.name);
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* // With query builder
|
|
19
|
+
* const query = new QueryBuilder()
|
|
20
|
+
* .select('name', 'population')
|
|
21
|
+
* .filter(f => f.field('population').gt(100000))
|
|
22
|
+
* .limit(100);
|
|
23
|
+
*
|
|
24
|
+
* const largeCities = await client.getAll('datasets/gov/example/City', query);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import { TokenCache } from './auth.js';
|
|
28
|
+
import { handleErrorResponse } from './errors.js';
|
|
29
|
+
/** Default configuration values */
|
|
30
|
+
const DEFAULT_BASE_URL = 'https://get.data.gov.lt';
|
|
31
|
+
const DEFAULT_AUTH_URL = 'https://put.data.gov.lt';
|
|
32
|
+
const DEFAULT_SCOPES = [
|
|
33
|
+
'spinta_getone',
|
|
34
|
+
'spinta_getall',
|
|
35
|
+
'spinta_search',
|
|
36
|
+
'spinta_changes',
|
|
37
|
+
];
|
|
38
|
+
export class SpintaClient {
|
|
39
|
+
baseUrl;
|
|
40
|
+
tokenCache;
|
|
41
|
+
constructor(config = {}) {
|
|
42
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
43
|
+
// Initialize token cache only if credentials are provided
|
|
44
|
+
if (config.clientId !== undefined && config.clientSecret !== undefined) {
|
|
45
|
+
this.tokenCache = new TokenCache(config.authUrl ?? DEFAULT_AUTH_URL, config.clientId, config.clientSecret, config.scopes ?? DEFAULT_SCOPES);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.tokenCache = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build headers for API requests
|
|
53
|
+
* Refreshes token if needed before each request
|
|
54
|
+
*/
|
|
55
|
+
async getHeaders() {
|
|
56
|
+
const headers = {
|
|
57
|
+
Accept: 'application/json',
|
|
58
|
+
};
|
|
59
|
+
if (this.tokenCache !== null) {
|
|
60
|
+
const token = await this.tokenCache.getToken();
|
|
61
|
+
headers.Authorization = `Bearer ${token}`;
|
|
62
|
+
}
|
|
63
|
+
return headers;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Make an HTTP request to the API
|
|
67
|
+
*/
|
|
68
|
+
async request(path, query = '') {
|
|
69
|
+
const url = `${this.baseUrl}${path}${query}`;
|
|
70
|
+
const headers = await this.getHeaders();
|
|
71
|
+
const response = await fetch(url, { headers });
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
await handleErrorResponse(response);
|
|
74
|
+
}
|
|
75
|
+
return response.json();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get a single object by its UUID
|
|
79
|
+
*
|
|
80
|
+
* @param model - Full model path (e.g., 'datasets/gov/example/City')
|
|
81
|
+
* @param id - Object UUID
|
|
82
|
+
* @returns The object with metadata
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const city = await client.getOne('datasets/gov/example/City', 'abc123-...');
|
|
86
|
+
*/
|
|
87
|
+
async getOne(model, id) {
|
|
88
|
+
const path = `/${model}/${id}`;
|
|
89
|
+
return this.request(path);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get objects from a model (single page only)
|
|
93
|
+
*
|
|
94
|
+
* **Note**: This fetches ONE page of results based on the limit in your query
|
|
95
|
+
* (or the API's default limit). It does NOT download all records.
|
|
96
|
+
* For large datasets, use `stream()` which handles pagination automatically.
|
|
97
|
+
*
|
|
98
|
+
* @param model - Full model path (e.g., 'datasets/gov/example/City')
|
|
99
|
+
* @param query - Optional query builder for filtering, sorting, limiting
|
|
100
|
+
* @returns Array of objects with metadata (unwrapped from _data)
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* // Get first 100 cities
|
|
104
|
+
* const query = new QueryBuilder().limit(100);
|
|
105
|
+
* const cities = await client.getAll('datasets/gov/example/City', query);
|
|
106
|
+
*/
|
|
107
|
+
async getAll(model, query) {
|
|
108
|
+
const response = await this.getAllRaw(model, query);
|
|
109
|
+
return response._data;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get raw response with metadata (includes _type, _page info)
|
|
113
|
+
*
|
|
114
|
+
* Use this when you need pagination info or the response type.
|
|
115
|
+
*
|
|
116
|
+
* @param model - Full model path
|
|
117
|
+
* @param query - Optional query builder
|
|
118
|
+
* @returns Full Spinta response with _data array and _page info
|
|
119
|
+
*/
|
|
120
|
+
async getAllRaw(model, query) {
|
|
121
|
+
const path = `/${model}`;
|
|
122
|
+
const queryString = query?.toQueryString() ?? '';
|
|
123
|
+
return this.request(path, queryString);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Stream all objects with automatic pagination
|
|
127
|
+
*
|
|
128
|
+
* Implements an async iterator that automatically fetches subsequent pages
|
|
129
|
+
* using the `_page.next` token. Validates token before each page fetch to
|
|
130
|
+
* avoid mid-stream auth failures on large datasets.
|
|
131
|
+
*
|
|
132
|
+
* @param model - Full model path
|
|
133
|
+
* @param query - Optional query builder (should include limit for page size)
|
|
134
|
+
* @yields Objects one at a time with metadata
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* // Stream all cities with a filter
|
|
138
|
+
* const query = new QueryBuilder()
|
|
139
|
+
* .filter(f => f.field('population').gt(10000))
|
|
140
|
+
* .limit(1000); // Page size
|
|
141
|
+
*
|
|
142
|
+
* for await (const city of client.stream('datasets/gov/example/City', query)) {
|
|
143
|
+
* console.log(city.name);
|
|
144
|
+
* }
|
|
145
|
+
*/
|
|
146
|
+
async *stream(model, query) {
|
|
147
|
+
const path = `/${model}`;
|
|
148
|
+
const baseQuery = query?.toQueryString() ?? '';
|
|
149
|
+
let pageToken;
|
|
150
|
+
do {
|
|
151
|
+
// Build query with page token if we have one
|
|
152
|
+
let queryString = baseQuery;
|
|
153
|
+
if (pageToken !== undefined) {
|
|
154
|
+
const separator = baseQuery !== '' ? '&' : '?';
|
|
155
|
+
queryString = `${baseQuery}${separator}page("${pageToken}")`;
|
|
156
|
+
}
|
|
157
|
+
// Fetch page (getHeaders validates token before each request)
|
|
158
|
+
const response = await this.request(path, queryString);
|
|
159
|
+
// Yield each item
|
|
160
|
+
for (const item of response._data) {
|
|
161
|
+
yield item;
|
|
162
|
+
}
|
|
163
|
+
// Get next page token
|
|
164
|
+
pageToken = response._page?.next;
|
|
165
|
+
} while (pageToken !== undefined);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Count objects matching a query
|
|
169
|
+
*
|
|
170
|
+
* @param model - Full model path
|
|
171
|
+
* @param query - Optional query builder for filtering (sort/select are ignored)
|
|
172
|
+
* @returns Number of matching objects
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* const query = new QueryBuilder().filter(f => f.field('population').gt(100000));
|
|
176
|
+
* const count = await client.count('datasets/gov/example/City', query);
|
|
177
|
+
*/
|
|
178
|
+
async count(model, query) {
|
|
179
|
+
const path = `/${model}`;
|
|
180
|
+
// Build count query
|
|
181
|
+
let queryString = query?.toQueryString() ?? '';
|
|
182
|
+
const separator = queryString !== '' ? '&' : '?';
|
|
183
|
+
queryString = `${queryString}${separator}count()`;
|
|
184
|
+
const response = await this.request(path, queryString);
|
|
185
|
+
const countValue = response._data[0];
|
|
186
|
+
return countValue['count()'];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* List contents of a namespace
|
|
190
|
+
*
|
|
191
|
+
* @param namespace - Namespace path (e.g., 'datasets/gov/ivpk')
|
|
192
|
+
* @returns Array of namespace items (sub-namespaces and models)
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* const items = await client.listNamespace('datasets/gov/ivpk');
|
|
196
|
+
* for (const item of items) {
|
|
197
|
+
* if (item._type === 'ns') {
|
|
198
|
+
* console.log('Namespace:', item._id);
|
|
199
|
+
* } else {
|
|
200
|
+
* console.log('Model:', item._id);
|
|
201
|
+
* }
|
|
202
|
+
* }
|
|
203
|
+
*/
|
|
204
|
+
async listNamespace(namespace) {
|
|
205
|
+
const path = `/${namespace}/:ns`;
|
|
206
|
+
const response = await this.request(path);
|
|
207
|
+
// Transform API response to NamespaceItem format
|
|
208
|
+
// API returns: { name: "path/:ns" or "path/Model", title, description }
|
|
209
|
+
return response._data.map((item) => {
|
|
210
|
+
const isNamespace = item.name.endsWith('/:ns');
|
|
211
|
+
const cleanPath = isNamespace
|
|
212
|
+
? item.name.slice(0, -4) // Remove '/:ns' suffix
|
|
213
|
+
: item.name;
|
|
214
|
+
return {
|
|
215
|
+
_id: cleanPath,
|
|
216
|
+
_type: isNamespace ? 'ns' : 'model',
|
|
217
|
+
title: item.title,
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Discover all models within a namespace (recursively)
|
|
223
|
+
*
|
|
224
|
+
* Traverses the namespace hierarchy and returns all model paths found.
|
|
225
|
+
* Useful for exploring what data is available in a given area.
|
|
226
|
+
*
|
|
227
|
+
* @param namespace - Starting namespace path (e.g., 'datasets/gov/rc')
|
|
228
|
+
* @returns Array of discovered models with path and title
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* // Find all models in the Registry Centre
|
|
233
|
+
* const models = await client.discoverModels('datasets/gov/rc');
|
|
234
|
+
* console.log(`Found ${models.length} models`);
|
|
235
|
+
* for (const model of models) {
|
|
236
|
+
* console.log(`- ${model.title ?? model.path}`);
|
|
237
|
+
* }
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
async discoverModels(namespace) {
|
|
241
|
+
const models = [];
|
|
242
|
+
const traverse = async (ns) => {
|
|
243
|
+
const items = await this.listNamespace(ns);
|
|
244
|
+
for (const item of items) {
|
|
245
|
+
if (item._type === 'model') {
|
|
246
|
+
models.push({
|
|
247
|
+
path: item._id,
|
|
248
|
+
title: item.title,
|
|
249
|
+
namespace: ns,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
// Recurse into sub-namespace
|
|
254
|
+
await traverse(item._id);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
await traverse(namespace);
|
|
259
|
+
return models;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=SpintaClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpintaClient.js","sourceRoot":"","sources":["../../src/client/SpintaClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth token management for Spinta API
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Token cache for managing OAuth tokens
|
|
6
|
+
*/
|
|
7
|
+
export declare class TokenCache {
|
|
8
|
+
private cachedToken;
|
|
9
|
+
private readonly authUrl;
|
|
10
|
+
private readonly clientId;
|
|
11
|
+
private readonly clientSecret;
|
|
12
|
+
private readonly scopes;
|
|
13
|
+
constructor(authUrl: string, clientId: string, clientSecret: string, scopes: readonly string[]);
|
|
14
|
+
/**
|
|
15
|
+
* Check if current token is valid (exists and not near expiry)
|
|
16
|
+
*/
|
|
17
|
+
isValid(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Get current token, fetching or refreshing if needed
|
|
20
|
+
*/
|
|
21
|
+
getToken(): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Fetch a new token from the auth server
|
|
24
|
+
*/
|
|
25
|
+
private refresh;
|
|
26
|
+
/**
|
|
27
|
+
* Clear the cached token (useful for logout or forced refresh)
|
|
28
|
+
*/
|
|
29
|
+
clear(): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;gBAGzC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,SAAS,MAAM,EAAE;IAQ3B;;OAEG;IACH,OAAO,IAAI,OAAO;IAQlB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAajC;;OAEG;YACW,OAAO;IAyCrB;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth token management for Spinta API
|
|
3
|
+
*/
|
|
4
|
+
import { AuthenticationError } from './errors.js';
|
|
5
|
+
/** Buffer time before token expiry to trigger refresh (5 minutes) */
|
|
6
|
+
const EXPIRY_BUFFER_MS = 5 * 60 * 1000;
|
|
7
|
+
/**
|
|
8
|
+
* Token cache for managing OAuth tokens
|
|
9
|
+
*/
|
|
10
|
+
export class TokenCache {
|
|
11
|
+
cachedToken = null;
|
|
12
|
+
authUrl;
|
|
13
|
+
clientId;
|
|
14
|
+
clientSecret;
|
|
15
|
+
scopes;
|
|
16
|
+
constructor(authUrl, clientId, clientSecret, scopes) {
|
|
17
|
+
this.authUrl = authUrl;
|
|
18
|
+
this.clientId = clientId;
|
|
19
|
+
this.clientSecret = clientSecret;
|
|
20
|
+
this.scopes = scopes;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if current token is valid (exists and not near expiry)
|
|
24
|
+
*/
|
|
25
|
+
isValid() {
|
|
26
|
+
if (this.cachedToken === null) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// Check if token expires in less than buffer time
|
|
30
|
+
return Date.now() < this.cachedToken.expiresAt - EXPIRY_BUFFER_MS;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get current token, fetching or refreshing if needed
|
|
34
|
+
*/
|
|
35
|
+
async getToken() {
|
|
36
|
+
if (!this.isValid()) {
|
|
37
|
+
await this.refresh();
|
|
38
|
+
}
|
|
39
|
+
// After refresh, cachedToken is guaranteed to be set
|
|
40
|
+
if (this.cachedToken === null) {
|
|
41
|
+
throw new AuthenticationError('Failed to obtain access token');
|
|
42
|
+
}
|
|
43
|
+
return this.cachedToken.accessToken;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fetch a new token from the auth server
|
|
47
|
+
*/
|
|
48
|
+
async refresh() {
|
|
49
|
+
const url = `${this.authUrl}/auth/token`;
|
|
50
|
+
// Create Basic auth header
|
|
51
|
+
const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
|
|
52
|
+
const body = new URLSearchParams({
|
|
53
|
+
grant_type: 'client_credentials',
|
|
54
|
+
scope: this.scopes.join(' '),
|
|
55
|
+
});
|
|
56
|
+
const response = await fetch(url, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: {
|
|
59
|
+
Authorization: `Basic ${credentials}`,
|
|
60
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
|
+
},
|
|
62
|
+
body: body.toString(),
|
|
63
|
+
});
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
let errorMessage = 'Failed to obtain access token';
|
|
66
|
+
try {
|
|
67
|
+
const errorBody = await response.json();
|
|
68
|
+
if (isOAuthErrorResponse(errorBody) && typeof errorBody.error_description === 'string') {
|
|
69
|
+
errorMessage = errorBody.error_description;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Ignore JSON parse errors
|
|
74
|
+
}
|
|
75
|
+
throw new AuthenticationError(errorMessage, response.status);
|
|
76
|
+
}
|
|
77
|
+
const data = (await response.json());
|
|
78
|
+
this.cachedToken = {
|
|
79
|
+
accessToken: data.access_token,
|
|
80
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Clear the cached token (useful for logout or forced refresh)
|
|
85
|
+
*/
|
|
86
|
+
clear() {
|
|
87
|
+
this.cachedToken = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Type guard for OAuth error response
|
|
92
|
+
*/
|
|
93
|
+
function isOAuthErrorResponse(value) {
|
|
94
|
+
return typeof value === 'object' && value !== null;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/client/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error classes for Spinta API errors
|
|
3
|
+
*/
|
|
4
|
+
import type { SpintaErrorResponse } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Base error class for Spinta-related errors
|
|
7
|
+
*/
|
|
8
|
+
export declare class SpintaError extends Error {
|
|
9
|
+
readonly status: number;
|
|
10
|
+
readonly body: SpintaErrorResponse | null;
|
|
11
|
+
constructor(message: string, status: number, body?: SpintaErrorResponse | null);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Authentication error (401/403)
|
|
15
|
+
*/
|
|
16
|
+
export declare class AuthenticationError extends SpintaError {
|
|
17
|
+
constructor(message?: string, status?: number, body?: SpintaErrorResponse | null);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resource not found error (404)
|
|
21
|
+
*/
|
|
22
|
+
export declare class NotFoundError extends SpintaError {
|
|
23
|
+
constructor(message?: string, body?: SpintaErrorResponse | null);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validation error (400) - usually bad query parameters
|
|
27
|
+
*/
|
|
28
|
+
export declare class ValidationError extends SpintaError {
|
|
29
|
+
constructor(message?: string, body?: SpintaErrorResponse | null);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse error response body and throw appropriate error
|
|
33
|
+
*/
|
|
34
|
+
export declare function handleErrorResponse(response: Response): Promise<never>;
|
|
35
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/client/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAAC;gBAErC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,mBAAmB,GAAG,IAAW;CASrF;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;gBACtC,OAAO,SAA0B,EAAE,MAAM,SAAM,EAAE,IAAI,GAAE,mBAAmB,GAAG,IAAW;CAIrG;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW;gBAChC,OAAO,SAAuB,EAAE,IAAI,GAAE,mBAAmB,GAAG,IAAW;CAIpF;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,WAAW;gBAClC,OAAO,SAAqB,EAAE,IAAI,GAAE,mBAAmB,GAAG,IAAW;CAIlF;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAyB5E"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error classes for Spinta API errors
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Base error class for Spinta-related errors
|
|
6
|
+
*/
|
|
7
|
+
export class SpintaError extends Error {
|
|
8
|
+
status;
|
|
9
|
+
body;
|
|
10
|
+
constructor(message, status, body = null) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'SpintaError';
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.body = body;
|
|
15
|
+
// Maintains proper stack trace for where error was thrown (V8 only)
|
|
16
|
+
Error.captureStackTrace(this, SpintaError);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Authentication error (401/403)
|
|
21
|
+
*/
|
|
22
|
+
export class AuthenticationError extends SpintaError {
|
|
23
|
+
constructor(message = 'Authentication failed', status = 401, body = null) {
|
|
24
|
+
super(message, status, body);
|
|
25
|
+
this.name = 'AuthenticationError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resource not found error (404)
|
|
30
|
+
*/
|
|
31
|
+
export class NotFoundError extends SpintaError {
|
|
32
|
+
constructor(message = 'Resource not found', body = null) {
|
|
33
|
+
super(message, 404, body);
|
|
34
|
+
this.name = 'NotFoundError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validation error (400) - usually bad query parameters
|
|
39
|
+
*/
|
|
40
|
+
export class ValidationError extends SpintaError {
|
|
41
|
+
constructor(message = 'Validation error', body = null) {
|
|
42
|
+
super(message, 400, body);
|
|
43
|
+
this.name = 'ValidationError';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse error response body and throw appropriate error
|
|
48
|
+
*/
|
|
49
|
+
export async function handleErrorResponse(response) {
|
|
50
|
+
let body = null;
|
|
51
|
+
try {
|
|
52
|
+
const text = await response.text();
|
|
53
|
+
if (text !== '') {
|
|
54
|
+
body = JSON.parse(text);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Ignore JSON parse errors
|
|
59
|
+
}
|
|
60
|
+
const message = body?.message ?? body?.errors?.[0]?.message ?? response.statusText;
|
|
61
|
+
switch (response.status) {
|
|
62
|
+
case 400:
|
|
63
|
+
throw new ValidationError(message, body);
|
|
64
|
+
case 401:
|
|
65
|
+
case 403:
|
|
66
|
+
throw new AuthenticationError(message, response.status, body);
|
|
67
|
+
case 404:
|
|
68
|
+
throw new NotFoundError(message, body);
|
|
69
|
+
default:
|
|
70
|
+
throw new SpintaError(message, response.status, body);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/client/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpB,MAAM,CAAS;IACf,IAAI,CAA6B;IAEjD,YAAY,OAAe,EAAE,MAAc,EAAE,OAAmC,IAAI;QAClF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAEjB,oEAAoE;QACpE,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC7C,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,WAAW;IAClD,YAAY,OAAO,GAAG,uBAAuB,EAAE,MAAM,GAAG,GAAG,EAAE,OAAmC,IAAI;QAClG,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,WAAW;IAC5C,YAAY,OAAO,GAAG,oBAAoB,EAAE,OAAmC,IAAI;QACjF,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,WAAW;IAC9C,YAAY,OAAO,GAAG,kBAAkB,EAAE,OAAmC,IAAI;QAC/E,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAkB;IAC1D,IAAI,IAAI,GAA+B,IAAI,CAAC;IAE5C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAwB,CAAC;QACjD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,QAAQ,CAAC,UAAU,CAAC;IAEnF,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,GAAG;YACN,MAAM,IAAI,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,KAAK,GAAG,CAAC;QACT,KAAK,GAAG;YACN,MAAM,IAAI,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAChE,KAAK,GAAG;YACN,MAAM,IAAI,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACzC;YACE,MAAM,IAAI,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpintaClient module - HTTP client for Lithuanian Open Data API
|
|
3
|
+
*/
|
|
4
|
+
export { SpintaClient } from './SpintaClient.js';
|
|
5
|
+
export type { DiscoveredModel } from './SpintaClient.js';
|
|
6
|
+
export { SpintaError, AuthenticationError, NotFoundError, ValidationError, } from './errors.js';
|
|
7
|
+
export type { ClientConfig, SpintaObject, SpintaResponse, PageInfo, TokenResponse, CachedToken, CountResponse, SpintaErrorResponse, NamespaceItem, NamespaceResponse, } from './types.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,aAAa,EACb,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the SpintaClient module
|
|
3
|
+
*/
|
|
4
|
+
/** OAuth token response from Spinta auth endpoint */
|
|
5
|
+
export interface TokenResponse {
|
|
6
|
+
access_token: string;
|
|
7
|
+
token_type: string;
|
|
8
|
+
expires_in: number;
|
|
9
|
+
scope?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Cached token with expiry tracking */
|
|
12
|
+
export interface CachedToken {
|
|
13
|
+
accessToken: string;
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
}
|
|
16
|
+
/** Client configuration options */
|
|
17
|
+
export interface ClientConfig {
|
|
18
|
+
/**
|
|
19
|
+
* Base URL for the Spinta API
|
|
20
|
+
* @default 'https://get.data.gov.lt'
|
|
21
|
+
*/
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
/**
|
|
24
|
+
* OAuth client ID for authenticated requests
|
|
25
|
+
* If not provided, only public data can be accessed
|
|
26
|
+
*/
|
|
27
|
+
clientId?: string;
|
|
28
|
+
/**
|
|
29
|
+
* OAuth client secret for authenticated requests
|
|
30
|
+
*/
|
|
31
|
+
clientSecret?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Auth server URL (different from data server for data.gov.lt)
|
|
34
|
+
* @default 'https://put.data.gov.lt'
|
|
35
|
+
*/
|
|
36
|
+
authUrl?: string;
|
|
37
|
+
/**
|
|
38
|
+
* OAuth scopes to request
|
|
39
|
+
* @default ['spinta_getone', 'spinta_getall', 'spinta_search', 'spinta_changes']
|
|
40
|
+
*/
|
|
41
|
+
scopes?: string[];
|
|
42
|
+
}
|
|
43
|
+
/** Spinta object base metadata */
|
|
44
|
+
export interface SpintaObject {
|
|
45
|
+
_id: string;
|
|
46
|
+
_type: string;
|
|
47
|
+
_revision?: string;
|
|
48
|
+
_txn?: string;
|
|
49
|
+
}
|
|
50
|
+
/** Page information for pagination */
|
|
51
|
+
export interface PageInfo {
|
|
52
|
+
/**
|
|
53
|
+
* Base64-encoded token for the next page
|
|
54
|
+
* If undefined, there are no more pages
|
|
55
|
+
*/
|
|
56
|
+
next?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Response wrapper from Spinta getall endpoints
|
|
60
|
+
* Use `getAllRaw()` to get this structure, or `getAll()` for just the data array
|
|
61
|
+
*/
|
|
62
|
+
export interface SpintaResponse<T> {
|
|
63
|
+
_type: string;
|
|
64
|
+
_data: (T & SpintaObject)[];
|
|
65
|
+
_page?: PageInfo;
|
|
66
|
+
}
|
|
67
|
+
/** Response from count queries */
|
|
68
|
+
export interface CountResponse {
|
|
69
|
+
_type: string;
|
|
70
|
+
_data: [{
|
|
71
|
+
'count()': number;
|
|
72
|
+
}];
|
|
73
|
+
}
|
|
74
|
+
/** Spinta error response structure */
|
|
75
|
+
export interface SpintaErrorResponse {
|
|
76
|
+
type?: string;
|
|
77
|
+
code?: string;
|
|
78
|
+
message?: string;
|
|
79
|
+
errors?: {
|
|
80
|
+
type?: string;
|
|
81
|
+
code?: string;
|
|
82
|
+
context?: Record<string, unknown>;
|
|
83
|
+
message?: string;
|
|
84
|
+
}[];
|
|
85
|
+
}
|
|
86
|
+
/** Namespace item from /:ns endpoint */
|
|
87
|
+
export interface NamespaceItem {
|
|
88
|
+
_id: string;
|
|
89
|
+
_type: 'ns' | 'model';
|
|
90
|
+
title?: string;
|
|
91
|
+
}
|
|
92
|
+
/** Namespace listing response */
|
|
93
|
+
export interface NamespaceResponse {
|
|
94
|
+
_data: NamespaceItem[];
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sCAAsC;AACtC,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChC;AAED,sCAAsC;AACtC,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,EAAE,CAAC;CACL;AAED,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,iCAAiC;AACjC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|