hlquery-node-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.
@@ -0,0 +1,159 @@
1
+ /**
2
+ * hlquery Node.js Client - Collections API
3
+ *
4
+ * Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
5
+ *
6
+ * This file is part of hlquery, released under the BSD License version 3.
7
+ */
8
+
9
+ const Validator = require('../utils/Validator');
10
+ const Response = require('./Response');
11
+
12
+ /**
13
+ * Collections API operations
14
+ */
15
+ class Collections {
16
+ constructor(request) {
17
+ this.request = request;
18
+ }
19
+
20
+ /**
21
+ * List all collections
22
+ *
23
+ * @param {number} offset Pagination offset
24
+ * @param {number} limit Number of results
25
+ * @returns {Promise<Response>}
26
+ */
27
+ async list(offset = 0, limit = 10) {
28
+ Validator.validatePagination(offset, limit);
29
+ return await this.request.execute('GET', '/collections', null, {
30
+ offset: offset,
31
+ limit: limit
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Get collection details
37
+ *
38
+ * @param {string} name Collection name
39
+ * @returns {Promise<Response>}
40
+ */
41
+ async get(name) {
42
+ Validator.validateCollectionName(name);
43
+ return await this.request.execute('GET', `/collections/${encodeURIComponent(name)}`);
44
+ }
45
+
46
+ /**
47
+ * Create a new collection
48
+ *
49
+ * @param {string} name Collection name
50
+ * @param {object} schema Collection schema
51
+ * @returns {Promise<Response>}
52
+ */
53
+ async create(name, schema) {
54
+ Validator.validateCollectionName(name);
55
+ // Merge schema with name at top level (server expects fields/searchable_fields at root)
56
+ const requestBody = {
57
+ name: name,
58
+ ...schema
59
+ };
60
+ return await this.request.execute('POST', '/collections', requestBody);
61
+ }
62
+
63
+ /**
64
+ * Delete a collection
65
+ *
66
+ * @param {string} name Collection name
67
+ * @returns {Promise<Response>}
68
+ */
69
+ async delete(name) {
70
+ Validator.validateCollectionName(name);
71
+ return await this.request.execute('DELETE', `/collections/${encodeURIComponent(name)}`);
72
+ }
73
+
74
+ /**
75
+ * Update collection schema
76
+ *
77
+ * @param {string} name Collection name
78
+ * @param {object} schema Updated schema
79
+ * @returns {Promise<Response>}
80
+ */
81
+ async update(name, schema) {
82
+ Validator.validateCollectionName(name);
83
+ return await this.request.execute('POST', `/collections/${encodeURIComponent(name)}/update`, schema);
84
+ }
85
+
86
+ /**
87
+ * Get collection fields (formatted)
88
+ *
89
+ * @param {string} name Collection name
90
+ * @returns {Promise<Response>}
91
+ */
92
+ async getFields(name) {
93
+ const response = await this.get(name);
94
+
95
+ if (response.getStatusCode() !== 200) {
96
+ return response;
97
+ }
98
+
99
+ const body = response.getBody();
100
+ const allFields = [];
101
+ const fieldTypes = {};
102
+
103
+ // Collect searchable fields
104
+ if (body.searchable_fields) {
105
+ for (const field of body.searchable_fields) {
106
+ if (!allFields.includes(field)) {
107
+ allFields.push(field);
108
+ }
109
+ if (!fieldTypes[field]) {
110
+ fieldTypes[field] = [];
111
+ }
112
+ fieldTypes[field].push('searchable');
113
+ }
114
+ }
115
+
116
+ // Collect filterable fields
117
+ if (body.filterable_fields) {
118
+ for (const field of body.filterable_fields) {
119
+ if (!allFields.includes(field)) {
120
+ allFields.push(field);
121
+ }
122
+ if (!fieldTypes[field]) {
123
+ fieldTypes[field] = [];
124
+ }
125
+ fieldTypes[field].push('filterable');
126
+ }
127
+ }
128
+
129
+ // Collect sortable fields
130
+ if (body.sortable_fields) {
131
+ for (const field of body.sortable_fields) {
132
+ if (!allFields.includes(field)) {
133
+ allFields.push(field);
134
+ }
135
+ if (!fieldTypes[field]) {
136
+ fieldTypes[field] = [];
137
+ }
138
+ fieldTypes[field].push('sortable');
139
+ }
140
+ }
141
+
142
+ // Format fields
143
+ const fields = allFields.map(field => ({
144
+ name: field,
145
+ type: fieldTypes[field].join(', ')
146
+ }));
147
+
148
+ return new Response(200, {
149
+ collection: name,
150
+ fields: fields,
151
+ field_count: fields.length,
152
+ searchable_fields: body.searchable_fields || [],
153
+ filterable_fields: body.filterable_fields || [],
154
+ sortable_fields: body.sortable_fields || []
155
+ });
156
+ }
157
+ }
158
+
159
+ module.exports = Collections;
@@ -0,0 +1,244 @@
1
+ /**
2
+ * hlquery Node.js Client - Documents API
3
+ *
4
+ * Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
5
+ *
6
+ * This file is part of hlquery, released under the BSD License version 3.
7
+ */
8
+
9
+ const Validator = require('../utils/Validator');
10
+ const PDFParser = require('../utils/PDFParser');
11
+ const CSVParser = require('../utils/CSVParser');
12
+
13
+ /**
14
+ * Documents API operations
15
+ */
16
+ class Documents {
17
+ constructor(request) {
18
+ this.request = request;
19
+ }
20
+
21
+ /**
22
+ * List documents in a collection
23
+ *
24
+ * @param {string} collectionName
25
+ * @param {object} params Pagination parameters
26
+ * @returns {Promise<Response>}
27
+ */
28
+ async list(collectionName, params = {}) {
29
+ Validator.validateCollectionName(collectionName);
30
+ Validator.validateSearchParams(params);
31
+
32
+ const offset = params.offset !== undefined ? params.offset : (params.from !== undefined ? params.from : 0);
33
+ const limit = params.limit !== undefined ? params.limit : (params.size !== undefined ? params.size : 10);
34
+
35
+ return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/documents`, null, {
36
+ offset: offset,
37
+ limit: limit
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Get a single document by ID
43
+ *
44
+ * @param {string} collectionName
45
+ * @param {string} documentId
46
+ * @returns {Promise<Response>}
47
+ */
48
+ async get(collectionName, documentId) {
49
+ Validator.validateCollectionName(collectionName);
50
+ Validator.validateDocumentId(documentId);
51
+
52
+ return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/documents/${encodeURIComponent(documentId)}`);
53
+ }
54
+
55
+ /**
56
+ * Add a document
57
+ *
58
+ * @param {string} collectionName
59
+ * @param {object} document
60
+ * @returns {Promise<Response>}
61
+ */
62
+ async add(collectionName, document) {
63
+ Validator.validateCollectionName(collectionName);
64
+ Validator.validateDocumentFields(document);
65
+
66
+ return await this.request.execute('POST', `/collections/${encodeURIComponent(collectionName)}/documents`, document);
67
+ }
68
+
69
+ /**
70
+ * Parse a local PDF file into normalized text/metadata.
71
+ *
72
+ * @param {string} filePath
73
+ * @param {object} options
74
+ * @returns {Promise<object>}
75
+ */
76
+ async parsePDF(filePath, options = {}) {
77
+ return await PDFParser.parseFile(filePath, options);
78
+ }
79
+
80
+ /**
81
+ * Parse a local PDF and add it as a standard hlquery document.
82
+ *
83
+ * @param {string} collectionName
84
+ * @param {string} filePath
85
+ * @param {object} options
86
+ * @returns {Promise<Response>}
87
+ */
88
+ async addPDF(collectionName, filePath, options = {}) {
89
+ Validator.validateCollectionName(collectionName);
90
+
91
+ const parsed = await this.parsePDF(filePath, options);
92
+ const document = PDFParser.buildDocumentFromParsedPDF(parsed, options);
93
+
94
+ Validator.validateDocumentFields(document);
95
+
96
+ return await this.request.execute('POST', `/collections/${encodeURIComponent(collectionName)}/documents`, document);
97
+ }
98
+
99
+ /**
100
+ * Parse a local CSV file into normalized text/metadata.
101
+ *
102
+ * @param {string} filePath
103
+ * @param {object} options
104
+ * @returns {Promise<object>}
105
+ */
106
+ async parseCSV(filePath, options = {}) {
107
+ return CSVParser.parseFile(filePath, options);
108
+ }
109
+
110
+ /**
111
+ * Parse a local CSV and add it as a standard hlquery document.
112
+ *
113
+ * @param {string} collectionName
114
+ * @param {string} filePath
115
+ * @param {object} options
116
+ * @returns {Promise<Response>}
117
+ */
118
+ async addCSV(collectionName, filePath, options = {}) {
119
+ Validator.validateCollectionName(collectionName);
120
+
121
+ const parsed = await this.parseCSV(filePath, options);
122
+ const document = CSVParser.buildDocumentFromParsedCSV(parsed, options);
123
+
124
+ Validator.validateDocumentFields(document);
125
+
126
+ return await this.request.execute('POST', `/collections/${encodeURIComponent(collectionName)}/documents`, document);
127
+ }
128
+
129
+ /**
130
+ * Update a document
131
+ *
132
+ * @param {string} collectionName
133
+ * @param {string} documentId
134
+ * @param {object} document
135
+ * @returns {Promise<Response>}
136
+ */
137
+ async update(collectionName, documentId, document) {
138
+ Validator.validateCollectionName(collectionName);
139
+ Validator.validateDocumentId(documentId);
140
+ Validator.validateDocumentFields(document);
141
+
142
+ return await this.request.execute('PUT', `/collections/${encodeURIComponent(collectionName)}/documents/${encodeURIComponent(documentId)}`, document);
143
+ }
144
+
145
+ /**
146
+ * Delete a document
147
+ *
148
+ * @param {string} collectionName
149
+ * @param {string} documentId
150
+ * @returns {Promise<Response>}
151
+ */
152
+ async delete(collectionName, documentId) {
153
+ Validator.validateCollectionName(collectionName);
154
+ Validator.validateDocumentId(documentId);
155
+
156
+ return await this.request.execute('DELETE', `/collections/${encodeURIComponent(collectionName)}/documents/${encodeURIComponent(documentId)}`);
157
+ }
158
+
159
+ /**
160
+ * Import documents (bulk)
161
+ *
162
+ * @param {string} collectionName
163
+ * @param {Array} documents
164
+ * @returns {Promise<Response>}
165
+ */
166
+ async import(collectionName, documents) {
167
+ Validator.validateCollectionName(collectionName);
168
+
169
+ // Validate each document in the array
170
+ if (Array.isArray(documents)) {
171
+ for (const doc of documents) {
172
+ Validator.validateDocumentFields(doc);
173
+ }
174
+ }
175
+
176
+ // Server expects {documents: [...]} format
177
+ return await this.request.execute('POST', `/collections/${encodeURIComponent(collectionName)}/documents/import`, {
178
+ documents: documents
179
+ });
180
+ }
181
+
182
+ /**
183
+ * Compute facet counts for a collection.
184
+ *
185
+ * @param {string} collectionName
186
+ * @param {object} params
187
+ * @returns {Promise<Response>}
188
+ */
189
+ async facetCounts(collectionName, params = {}) {
190
+ Validator.validateCollectionName(collectionName);
191
+ Validator.validateSearchParams(params);
192
+
193
+ const method = params.body ? 'POST' : 'GET';
194
+ const body = params.body || (method === 'POST' ? params : null);
195
+ const queryParams = method === 'GET' ? params : {};
196
+
197
+ return await this.request.execute(
198
+ method,
199
+ `/collections/${encodeURIComponent(collectionName)}/documents/facet_counts`,
200
+ body,
201
+ queryParams
202
+ );
203
+ }
204
+
205
+ /**
206
+ * Export documents from a collection.
207
+ *
208
+ * @param {string} collectionName
209
+ * @param {object} params
210
+ * @returns {Promise<Response>}
211
+ */
212
+ async export(collectionName, params = {}) {
213
+ Validator.validateCollectionName(collectionName);
214
+ Validator.validateSearchParams(params);
215
+
216
+ const method = params.body ? 'POST' : 'GET';
217
+ const body = params.body || (method === 'POST' ? params : null);
218
+ const queryParams = method === 'GET' ? params : {};
219
+
220
+ return await this.request.execute(
221
+ method,
222
+ `/collections/${encodeURIComponent(collectionName)}/documents/export`,
223
+ body,
224
+ queryParams
225
+ );
226
+ }
227
+
228
+ /**
229
+ * Delete documents by filter
230
+ *
231
+ * @param {string} collectionName
232
+ * @param {string} filter
233
+ * @returns {Promise<Response>}
234
+ */
235
+ async deleteByFilter(collectionName, filter) {
236
+ Validator.validateCollectionName(collectionName);
237
+
238
+ return await this.request.execute('DELETE', `/collections/${encodeURIComponent(collectionName)}/documents`, null, {
239
+ filter_by: filter
240
+ });
241
+ }
242
+ }
243
+
244
+ module.exports = Documents;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * hlquery Node.js Client - Exceptions
3
+ *
4
+ * Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
5
+ *
6
+ * This file is part of hlquery, released under the BSD License version 3.
7
+ */
8
+
9
+ /**
10
+ * Base exception class for hlquery client
11
+ */
12
+ class HlqueryException extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'HlqueryException';
16
+ Error.captureStackTrace(this, this.constructor);
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Exception thrown when authentication fails
22
+ */
23
+ class AuthenticationException extends HlqueryException {
24
+ constructor(message) {
25
+ super(message);
26
+ this.name = 'AuthenticationException';
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Exception thrown when a request fails
32
+ */
33
+ class RequestException extends HlqueryException {
34
+ constructor(message, statusCode = 0, responseBody = null) {
35
+ super(message);
36
+ this.name = 'RequestException';
37
+ this.statusCode = statusCode;
38
+ this.responseBody = responseBody;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Exception thrown when validation fails
44
+ */
45
+ class ValidationException extends HlqueryException {
46
+ constructor(message) {
47
+ super(message);
48
+ this.name = 'ValidationException';
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Exception thrown when a collection operation fails
54
+ */
55
+ class CollectionException extends HlqueryException {
56
+ constructor(message) {
57
+ super(message);
58
+ this.name = 'CollectionException';
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Exception thrown when a document operation fails
64
+ */
65
+ class DocumentException extends HlqueryException {
66
+ constructor(message) {
67
+ super(message);
68
+ this.name = 'DocumentException';
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Exception thrown when a search operation fails
74
+ */
75
+ class SearchException extends HlqueryException {
76
+ constructor(message) {
77
+ super(message);
78
+ this.name = 'SearchException';
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Exception thrown when demo mode is enabled and write operations are blocked
84
+ */
85
+ class DemoModeException extends HlqueryException {
86
+ constructor(message) {
87
+ super(message);
88
+ this.name = 'DemoModeException';
89
+ }
90
+ }
91
+
92
+ module.exports = {
93
+ HlqueryException,
94
+ AuthenticationException,
95
+ RequestException,
96
+ ValidationException,
97
+ CollectionException,
98
+ DocumentException,
99
+ SearchException,
100
+ DemoModeException
101
+ };
package/lib/Keys.js ADDED
@@ -0,0 +1,85 @@
1
+ /**
2
+ * hlquery Node.js Client - API Keys
3
+ *
4
+ * Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
5
+ *
6
+ * This file is part of hlquery, released under the BSD License version 3.
7
+ */
8
+
9
+ const Validator = require('../utils/Validator');
10
+
11
+ /**
12
+ * API Keys management operations
13
+ */
14
+ class Keys {
15
+ constructor(request) {
16
+ this.request = request;
17
+ }
18
+
19
+ /**
20
+ * List all API keys
21
+ *
22
+ * @returns {Promise<Response>}
23
+ */
24
+ async list() {
25
+ return await this.request.execute('GET', '/keys');
26
+ }
27
+
28
+ /**
29
+ * Get API key details
30
+ *
31
+ * @param {string} id Key ID
32
+ * @returns {Promise<Response>}
33
+ */
34
+ async get(id) {
35
+ if (!id) {
36
+ throw new Error('Key ID is required');
37
+ }
38
+ return await this.request.execute('GET', `/keys/${id}`);
39
+ }
40
+
41
+ /**
42
+ * Create a new API key
43
+ *
44
+ * @param {object} params Key parameters
45
+ * @returns {Promise<Response>}
46
+ */
47
+ async create(params) {
48
+ if (!params.collections || !Array.isArray(params.collections)) {
49
+ throw new Error('Collections array is required');
50
+ }
51
+ if (!params.actions || !Array.isArray(params.actions)) {
52
+ throw new Error('Actions array is required');
53
+ }
54
+ return await this.request.execute('POST', '/keys', params);
55
+ }
56
+
57
+ /**
58
+ * Delete an API key
59
+ *
60
+ * @param {string} id Key ID
61
+ * @returns {Promise<Response>}
62
+ */
63
+ async delete(id) {
64
+ if (!id) {
65
+ throw new Error('Key ID is required');
66
+ }
67
+ return await this.request.execute('DELETE', `/keys/${id}`);
68
+ }
69
+
70
+ /**
71
+ * Update an API key
72
+ *
73
+ * @param {string} id Key ID
74
+ * @param {object} params Updated parameters
75
+ * @returns {Promise<Response>}
76
+ */
77
+ async update(id, params) {
78
+ if (!id) {
79
+ throw new Error('Key ID is required');
80
+ }
81
+ return await this.request.execute('PUT', `/keys/${id}`, params);
82
+ }
83
+ }
84
+
85
+ module.exports = Keys;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * hlquery Node.js Client - Overrides API
3
+ *
4
+ * Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
5
+ *
6
+ * This file is part of hlquery, released under the BSD License version 3.
7
+ */
8
+
9
+ const Validator = require('../utils/Validator');
10
+
11
+ /**
12
+ * Overrides API operations
13
+ */
14
+ class Overrides {
15
+ constructor(request) {
16
+ this.request = request;
17
+ }
18
+
19
+ async list(collectionName) {
20
+ Validator.validateCollectionName(collectionName);
21
+ return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/overrides`);
22
+ }
23
+
24
+ async get(collectionName, id) {
25
+ Validator.validateCollectionName(collectionName);
26
+ Validator.validateDocumentId(id);
27
+ return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/overrides/${encodeURIComponent(id)}`);
28
+ }
29
+
30
+ async create(collectionName, id, override) {
31
+ Validator.validateCollectionName(collectionName);
32
+ Validator.validateDocumentId(id);
33
+ return await this.request.execute('POST', `/collections/${encodeURIComponent(collectionName)}/overrides/${encodeURIComponent(id)}`, override);
34
+ }
35
+
36
+ async update(collectionName, id, override) {
37
+ Validator.validateCollectionName(collectionName);
38
+ Validator.validateDocumentId(id);
39
+ return await this.request.execute('PUT', `/collections/${encodeURIComponent(collectionName)}/overrides/${encodeURIComponent(id)}`, override);
40
+ }
41
+
42
+ async upsert(collectionName, id, override) {
43
+ return await this.create(collectionName, id, override);
44
+ }
45
+
46
+ async delete(collectionName, id) {
47
+ Validator.validateCollectionName(collectionName);
48
+ Validator.validateDocumentId(id);
49
+ return await this.request.execute('DELETE', `/collections/${encodeURIComponent(collectionName)}/overrides/${encodeURIComponent(id)}`);
50
+ }
51
+ }
52
+
53
+ module.exports = Overrides;