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,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lt-gen CLI - Generate TypeScript definitions from Spinta API metadata
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx lt-gen <namespace> [options]
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* npx lt-gen datasets/gov/ivpk/adk
|
|
10
|
+
* npx lt-gen datasets/gov/ivpk/adk --output ./types/adk.d.ts
|
|
11
|
+
* npx lt-gen datasets/gov/ivpk/adk --base-url https://get.data.gov.lt
|
|
12
|
+
*
|
|
13
|
+
* Options:
|
|
14
|
+
* --output, -o Output file path (default: stdout)
|
|
15
|
+
* --base-url Base URL for the API (default: https://get.data.gov.lt)
|
|
16
|
+
* --help, -h Show help
|
|
17
|
+
*/
|
|
18
|
+
import { writeFileSync } from 'fs';
|
|
19
|
+
import { SpintaClient } from '../client/SpintaClient.js';
|
|
20
|
+
import { crawlNamespace, fetchAllModelsMetadata } from './crawler.js';
|
|
21
|
+
import { generateDeclarationFile } from './generator.js';
|
|
22
|
+
import { modelPathToInterfaceName } from './typeMapper.js';
|
|
23
|
+
function parseArgs(args) {
|
|
24
|
+
const options = {
|
|
25
|
+
namespace: '',
|
|
26
|
+
output: undefined,
|
|
27
|
+
baseUrl: 'https://get.data.gov.lt',
|
|
28
|
+
help: false,
|
|
29
|
+
};
|
|
30
|
+
let skipNext = false;
|
|
31
|
+
for (const [index, arg] of args.entries()) {
|
|
32
|
+
if (skipNext) {
|
|
33
|
+
skipNext = false;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (arg === '--help' || arg === '-h') {
|
|
37
|
+
options.help = true;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === '--output' || arg === '-o') {
|
|
40
|
+
const nextArg = args[index + 1];
|
|
41
|
+
if (nextArg !== undefined) {
|
|
42
|
+
options.output = nextArg;
|
|
43
|
+
skipNext = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (arg === '--base-url') {
|
|
47
|
+
const nextArg = args[index + 1];
|
|
48
|
+
if (nextArg !== undefined) {
|
|
49
|
+
options.baseUrl = nextArg;
|
|
50
|
+
skipNext = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (!arg.startsWith('-') && options.namespace === '') {
|
|
54
|
+
options.namespace = arg;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return options;
|
|
58
|
+
}
|
|
59
|
+
function showHelp() {
|
|
60
|
+
console.log(`
|
|
61
|
+
lt-gen - Generate TypeScript definitions from Spinta API metadata
|
|
62
|
+
|
|
63
|
+
Usage:
|
|
64
|
+
lt-gen <namespace> [options]
|
|
65
|
+
|
|
66
|
+
Arguments:
|
|
67
|
+
namespace Namespace path to generate types for (e.g., datasets/gov/ivpk/adk)
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--output, -o Output file path (default: stdout)
|
|
71
|
+
--base-url Base URL for the API (default: https://get.data.gov.lt)
|
|
72
|
+
--help, -h Show this help message
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
lt-gen datasets/gov/ivpk/adk
|
|
76
|
+
lt-gen datasets/gov/ivpk/adk -o ./types/adk.d.ts
|
|
77
|
+
lt-gen datasets/gov/ivpk/adk --base-url https://get-test.data.gov.lt
|
|
78
|
+
`);
|
|
79
|
+
}
|
|
80
|
+
async function main() {
|
|
81
|
+
const args = process.argv.slice(2);
|
|
82
|
+
const options = parseArgs(args);
|
|
83
|
+
if (options.help || options.namespace === '') {
|
|
84
|
+
showHelp();
|
|
85
|
+
process.exit(options.help ? 0 : 1);
|
|
86
|
+
}
|
|
87
|
+
console.error(`🔍 Discovering models in ${options.namespace}...`);
|
|
88
|
+
const client = new SpintaClient({ baseUrl: options.baseUrl });
|
|
89
|
+
try {
|
|
90
|
+
// Crawl namespace to find all models
|
|
91
|
+
const modelPaths = await crawlNamespace(client, options.namespace);
|
|
92
|
+
if (modelPaths.length === 0) {
|
|
93
|
+
console.error(`⚠️ No models found in namespace: ${options.namespace}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
console.error(`📦 Found ${String(modelPaths.length)} model(s):`);
|
|
97
|
+
for (const path of modelPaths) {
|
|
98
|
+
console.error(` - ${modelPathToInterfaceName(path)} (${path})`);
|
|
99
|
+
}
|
|
100
|
+
// Fetch metadata for all models
|
|
101
|
+
console.error(`\n📥 Fetching model metadata...`);
|
|
102
|
+
const metadata = await fetchAllModelsMetadata(client, modelPaths);
|
|
103
|
+
// Generate declaration file
|
|
104
|
+
console.error(`\n📝 Generating TypeScript definitions...`);
|
|
105
|
+
const output = generateDeclarationFile(metadata, options.namespace);
|
|
106
|
+
// Write output
|
|
107
|
+
if (options.output !== undefined) {
|
|
108
|
+
writeFileSync(options.output, output, 'utf-8');
|
|
109
|
+
console.error(`\n✅ Written to ${options.output}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(output);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
117
|
+
console.error(`\n❌ Error: ${errorMessage}`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
main().catch((error) => {
|
|
122
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
123
|
+
console.error(errorMessage);
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type mapper for converting Spinta types to TypeScript types
|
|
3
|
+
*
|
|
4
|
+
* Based on duomenu-tipai.rst.txt documentation
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Map a Spinta type to its TypeScript equivalent
|
|
8
|
+
*
|
|
9
|
+
* @param spintaType - The type string from Spinta model metadata
|
|
10
|
+
* @returns TypeScript type as a string
|
|
11
|
+
*/
|
|
12
|
+
export declare function mapSpintaType(spintaType: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Check if a Spinta type is required
|
|
15
|
+
*/
|
|
16
|
+
export declare function isRequired(spintaType: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Convert a model path to a TypeScript interface name
|
|
19
|
+
* Adds namespace prefix to avoid collisions
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* 'datasets/gov/ivpk/adk/Dataset' → 'GovIvpkAdk_Dataset'
|
|
23
|
+
*/
|
|
24
|
+
export declare function modelPathToInterfaceName(modelPath: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Convert a property name to a safe TypeScript property name
|
|
27
|
+
* Handles special characters like @lt for language codes
|
|
28
|
+
*/
|
|
29
|
+
export declare function sanitizePropertyName(name: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Generate JSDoc comment from title and description
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateJsDoc(title?: string, description?: string): string;
|
|
34
|
+
//# sourceMappingURL=typeMapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeMapper.d.ts","sourceRoot":"","sources":["../../src/cli/typeMapper.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA0FxD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAiBlE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWzD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAwB1E"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type mapper for converting Spinta types to TypeScript types
|
|
3
|
+
*
|
|
4
|
+
* Based on duomenu-tipai.rst.txt documentation
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Map a Spinta type to its TypeScript equivalent
|
|
8
|
+
*
|
|
9
|
+
* @param spintaType - The type string from Spinta model metadata
|
|
10
|
+
* @returns TypeScript type as a string
|
|
11
|
+
*/
|
|
12
|
+
export function mapSpintaType(spintaType) {
|
|
13
|
+
// Handle parameterized types like geometry(point, 4326)
|
|
14
|
+
const baseType = spintaType.split('(')[0]?.trim().toLowerCase() ?? '';
|
|
15
|
+
switch (baseType) {
|
|
16
|
+
// Numeric types
|
|
17
|
+
case 'integer':
|
|
18
|
+
case 'number':
|
|
19
|
+
return 'number';
|
|
20
|
+
// Text types
|
|
21
|
+
case 'string':
|
|
22
|
+
case 'text':
|
|
23
|
+
return 'string';
|
|
24
|
+
// Boolean
|
|
25
|
+
case 'boolean':
|
|
26
|
+
return 'boolean';
|
|
27
|
+
// Temporal types - return string since ISO 8601 format
|
|
28
|
+
case 'date':
|
|
29
|
+
case 'datetime':
|
|
30
|
+
case 'time':
|
|
31
|
+
case 'temporal':
|
|
32
|
+
return 'string';
|
|
33
|
+
// Geometry - WKT string format
|
|
34
|
+
case 'geometry':
|
|
35
|
+
case 'spatial':
|
|
36
|
+
return 'string';
|
|
37
|
+
// Reference - can be string ID or object depending on query
|
|
38
|
+
// Using union as per user feedback - runtime behavior varies
|
|
39
|
+
case 'ref':
|
|
40
|
+
return 'string | { _id: string }';
|
|
41
|
+
// Back reference (array of refs)
|
|
42
|
+
case 'backref':
|
|
43
|
+
return 'Array<string | { _id: string }>';
|
|
44
|
+
// Generic reference (polymorphic)
|
|
45
|
+
case 'generic':
|
|
46
|
+
return '{ object_model: string; object_id: string }';
|
|
47
|
+
// File types
|
|
48
|
+
case 'file':
|
|
49
|
+
case 'image':
|
|
50
|
+
return '{ _id: string; name: string; type: string; size?: number }';
|
|
51
|
+
// Binary data (base64 encoded)
|
|
52
|
+
case 'binary':
|
|
53
|
+
return 'string';
|
|
54
|
+
// URL/URI types
|
|
55
|
+
case 'url':
|
|
56
|
+
case 'uri':
|
|
57
|
+
return 'string';
|
|
58
|
+
// Money
|
|
59
|
+
case 'money':
|
|
60
|
+
return 'string | number';
|
|
61
|
+
// Composite types
|
|
62
|
+
case 'object':
|
|
63
|
+
return 'Record<string, unknown>';
|
|
64
|
+
case 'array':
|
|
65
|
+
return 'unknown[]';
|
|
66
|
+
// Required modifier - strip it
|
|
67
|
+
case 'required':
|
|
68
|
+
return 'unknown';
|
|
69
|
+
// Absent (deleted property)
|
|
70
|
+
case 'absent':
|
|
71
|
+
return 'never';
|
|
72
|
+
// Empty type
|
|
73
|
+
case '':
|
|
74
|
+
return 'unknown';
|
|
75
|
+
// Unknown type - fallback
|
|
76
|
+
default:
|
|
77
|
+
// Check if it's a required variant
|
|
78
|
+
if (spintaType.includes('required')) {
|
|
79
|
+
const cleanType = spintaType.replace(/\s*required\s*/i, '').trim();
|
|
80
|
+
return mapSpintaType(cleanType);
|
|
81
|
+
}
|
|
82
|
+
return 'unknown';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check if a Spinta type is required
|
|
87
|
+
*/
|
|
88
|
+
export function isRequired(spintaType) {
|
|
89
|
+
return spintaType.toLowerCase().includes('required');
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Convert a model path to a TypeScript interface name
|
|
93
|
+
* Adds namespace prefix to avoid collisions
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* 'datasets/gov/ivpk/adk/Dataset' → 'GovIvpkAdk_Dataset'
|
|
97
|
+
*/
|
|
98
|
+
export function modelPathToInterfaceName(modelPath) {
|
|
99
|
+
const parts = modelPath.split('/');
|
|
100
|
+
const modelName = parts[parts.length - 1] ?? modelPath;
|
|
101
|
+
// Skip 'datasets' prefix if present
|
|
102
|
+
const namespaceParts = parts.slice(0, -1).filter((p) => p !== 'datasets');
|
|
103
|
+
if (namespaceParts.length === 0) {
|
|
104
|
+
return modelName;
|
|
105
|
+
}
|
|
106
|
+
// Capitalize first letter of each namespace part
|
|
107
|
+
const prefix = namespaceParts
|
|
108
|
+
.map((p) => p.charAt(0).toUpperCase() + p.slice(1))
|
|
109
|
+
.join('');
|
|
110
|
+
return `${prefix}_${modelName}`;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Convert a property name to a safe TypeScript property name
|
|
114
|
+
* Handles special characters like @lt for language codes
|
|
115
|
+
*/
|
|
116
|
+
export function sanitizePropertyName(name) {
|
|
117
|
+
// Handle language suffix @lt, @en, etc.
|
|
118
|
+
if (name.includes('@')) {
|
|
119
|
+
const parts = name.split('@');
|
|
120
|
+
const baseName = parts[0] ?? '';
|
|
121
|
+
const lang = parts[1] ?? '';
|
|
122
|
+
return `${baseName}_${lang}`;
|
|
123
|
+
}
|
|
124
|
+
// Replace any invalid characters
|
|
125
|
+
return name.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Generate JSDoc comment from title and description
|
|
129
|
+
*/
|
|
130
|
+
export function generateJsDoc(title, description) {
|
|
131
|
+
if (title === undefined && description === undefined) {
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
const lines = ['/**'];
|
|
135
|
+
if (title !== undefined) {
|
|
136
|
+
lines.push(` * ${title}`);
|
|
137
|
+
}
|
|
138
|
+
if (description !== undefined) {
|
|
139
|
+
if (title !== undefined) {
|
|
140
|
+
lines.push(' *');
|
|
141
|
+
}
|
|
142
|
+
// Handle multi-line descriptions
|
|
143
|
+
for (const line of description.split('\n')) {
|
|
144
|
+
lines.push(` * ${line.trim()}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
lines.push(' */');
|
|
148
|
+
return lines.join('\n');
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=typeMapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeMapper.js","sourceRoot":"","sources":["../../src/cli/typeMapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
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 type { ClientConfig, SpintaObject, SpintaResponse } from './types.js';
|
|
28
|
+
import type { QueryBuilder } from '../builder/QueryBuilder.js';
|
|
29
|
+
export declare class SpintaClient {
|
|
30
|
+
private readonly baseUrl;
|
|
31
|
+
private readonly tokenCache;
|
|
32
|
+
constructor(config?: ClientConfig);
|
|
33
|
+
/**
|
|
34
|
+
* Build headers for API requests
|
|
35
|
+
* Refreshes token if needed before each request
|
|
36
|
+
*/
|
|
37
|
+
private getHeaders;
|
|
38
|
+
/**
|
|
39
|
+
* Make an HTTP request to the API
|
|
40
|
+
*/
|
|
41
|
+
private request;
|
|
42
|
+
/**
|
|
43
|
+
* Get a single object by its UUID
|
|
44
|
+
*
|
|
45
|
+
* @param model - Full model path (e.g., 'datasets/gov/example/City')
|
|
46
|
+
* @param id - Object UUID
|
|
47
|
+
* @returns The object with metadata
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* const city = await client.getOne('datasets/gov/example/City', 'abc123-...');
|
|
51
|
+
*/
|
|
52
|
+
getOne<T>(model: string, id: string): Promise<T & SpintaObject>;
|
|
53
|
+
/**
|
|
54
|
+
* Get objects from a model (single page only)
|
|
55
|
+
*
|
|
56
|
+
* **Note**: This fetches ONE page of results based on the limit in your query
|
|
57
|
+
* (or the API's default limit). It does NOT download all records.
|
|
58
|
+
* For large datasets, use `stream()` which handles pagination automatically.
|
|
59
|
+
*
|
|
60
|
+
* @param model - Full model path (e.g., 'datasets/gov/example/City')
|
|
61
|
+
* @param query - Optional query builder for filtering, sorting, limiting
|
|
62
|
+
* @returns Array of objects with metadata (unwrapped from _data)
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Get first 100 cities
|
|
66
|
+
* const query = new QueryBuilder().limit(100);
|
|
67
|
+
* const cities = await client.getAll('datasets/gov/example/City', query);
|
|
68
|
+
*/
|
|
69
|
+
getAll<T>(model: string, query?: QueryBuilder<T>): Promise<(T & SpintaObject)[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Get raw response with metadata (includes _type, _page info)
|
|
72
|
+
*
|
|
73
|
+
* Use this when you need pagination info or the response type.
|
|
74
|
+
*
|
|
75
|
+
* @param model - Full model path
|
|
76
|
+
* @param query - Optional query builder
|
|
77
|
+
* @returns Full Spinta response with _data array and _page info
|
|
78
|
+
*/
|
|
79
|
+
getAllRaw<T>(model: string, query?: QueryBuilder<T>): Promise<SpintaResponse<T>>;
|
|
80
|
+
/**
|
|
81
|
+
* Stream all objects with automatic pagination
|
|
82
|
+
*
|
|
83
|
+
* Implements an async iterator that automatically fetches subsequent pages
|
|
84
|
+
* using the `_page.next` token. Validates token before each page fetch to
|
|
85
|
+
* avoid mid-stream auth failures on large datasets.
|
|
86
|
+
*
|
|
87
|
+
* @param model - Full model path
|
|
88
|
+
* @param query - Optional query builder (should include limit for page size)
|
|
89
|
+
* @yields Objects one at a time with metadata
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* // Stream all cities with a filter
|
|
93
|
+
* const query = new QueryBuilder()
|
|
94
|
+
* .filter(f => f.field('population').gt(10000))
|
|
95
|
+
* .limit(1000); // Page size
|
|
96
|
+
*
|
|
97
|
+
* for await (const city of client.stream('datasets/gov/example/City', query)) {
|
|
98
|
+
* console.log(city.name);
|
|
99
|
+
* }
|
|
100
|
+
*/
|
|
101
|
+
stream<T>(model: string, query?: QueryBuilder<T>): AsyncGenerator<T & SpintaObject, void, undefined>;
|
|
102
|
+
/**
|
|
103
|
+
* Count objects matching a query
|
|
104
|
+
*
|
|
105
|
+
* @param model - Full model path
|
|
106
|
+
* @param query - Optional query builder for filtering (sort/select are ignored)
|
|
107
|
+
* @returns Number of matching objects
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* const query = new QueryBuilder().filter(f => f.field('population').gt(100000));
|
|
111
|
+
* const count = await client.count('datasets/gov/example/City', query);
|
|
112
|
+
*/
|
|
113
|
+
count<T>(model: string, query?: QueryBuilder<T>): Promise<number>;
|
|
114
|
+
/**
|
|
115
|
+
* List contents of a namespace
|
|
116
|
+
*
|
|
117
|
+
* @param namespace - Namespace path (e.g., 'datasets/gov/ivpk')
|
|
118
|
+
* @returns Array of namespace items (sub-namespaces and models)
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* const items = await client.listNamespace('datasets/gov/ivpk');
|
|
122
|
+
* for (const item of items) {
|
|
123
|
+
* if (item._type === 'ns') {
|
|
124
|
+
* console.log('Namespace:', item._id);
|
|
125
|
+
* } else {
|
|
126
|
+
* console.log('Model:', item._id);
|
|
127
|
+
* }
|
|
128
|
+
* }
|
|
129
|
+
*/
|
|
130
|
+
listNamespace(namespace: string): Promise<NamespaceItem[]>;
|
|
131
|
+
/**
|
|
132
|
+
* Discover all models within a namespace (recursively)
|
|
133
|
+
*
|
|
134
|
+
* Traverses the namespace hierarchy and returns all model paths found.
|
|
135
|
+
* Useful for exploring what data is available in a given area.
|
|
136
|
+
*
|
|
137
|
+
* @param namespace - Starting namespace path (e.g., 'datasets/gov/rc')
|
|
138
|
+
* @returns Array of discovered models with path and title
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* // Find all models in the Registry Centre
|
|
143
|
+
* const models = await client.discoverModels('datasets/gov/rc');
|
|
144
|
+
* console.log(`Found ${models.length} models`);
|
|
145
|
+
* for (const model of models) {
|
|
146
|
+
* console.log(`- ${model.title ?? model.path}`);
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
discoverModels(namespace: string): Promise<DiscoveredModel[]>;
|
|
151
|
+
}
|
|
152
|
+
/** Discovered model from namespace traversal */
|
|
153
|
+
export interface DiscoveredModel {
|
|
154
|
+
/** Full model path (e.g., 'datasets/gov/rc/ar/savivaldybe/Savivaldybe') */
|
|
155
|
+
path: string;
|
|
156
|
+
/** Human-readable title from API metadata */
|
|
157
|
+
title?: string;
|
|
158
|
+
/** Parent namespace path */
|
|
159
|
+
namespace: string;
|
|
160
|
+
}
|
|
161
|
+
/** Namespace item type (transformed) */
|
|
162
|
+
interface NamespaceItem {
|
|
163
|
+
_id: string;
|
|
164
|
+
_type: 'ns' | 'model';
|
|
165
|
+
title?: string;
|
|
166
|
+
}
|
|
167
|
+
export {};
|
|
168
|
+
//# sourceMappingURL=SpintaClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpintaClient.d.ts","sourceRoot":"","sources":["../../src/client/SpintaClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,cAAc,EAEf,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAY/D,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;gBAEnC,MAAM,GAAE,YAAiB;IAgBrC;;;OAGG;YACW,UAAU;IAaxB;;OAEG;YACW,OAAO;IAarB;;;;;;;;;OASG;IACG,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,YAAY,CAAC;IAKrE;;;;;;;;;;;;;;;OAeG;IACG,MAAM,CAAC,CAAC,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC;IAKhC;;;;;;;;OAQG;IACG,SAAS,CAAC,CAAC,EACf,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAM7B;;;;;;;;;;;;;;;;;;;;OAoBG;IACI,MAAM,CAAC,CAAC,EACb,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,cAAc,CAAC,CAAC,GAAG,YAAY,EAAE,IAAI,EAAE,SAAS,CAAC;IA0BpD;;;;;;;;;;OAUG;IACG,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAcvE;;;;;;;;;;;;;;;OAeG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAoBhE;;;;;;;;;;;;;;;;;;OAkBG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAuBpE;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAcD,wCAAwC;AACxC,UAAU,aAAa;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|