@goatlab/typesense 0.1.3 → 0.1.4
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/dist/TypesenseApi.js +16 -16
- package/dist/actions/aliases/createOrUpdateAlias.js +1 -1
- package/dist/actions/collections/createCollection.js +2 -2
- package/dist/actions/collections/deleteCollection.js +1 -1
- package/dist/actions/collections/updateCollection.js +1 -1
- package/dist/actions/documents/clearCollection.js +1 -1
- package/dist/actions/documents/deleteByFilter.js +1 -1
- package/dist/actions/documents/deleteDocument.js +1 -1
- package/dist/actions/documents/exportDocuments.js +3 -3
- package/dist/actions/documents/importDocuments.js +2 -2
- package/dist/actions/documents/insertDocument.js +3 -3
- package/dist/actions/documents/updateDocument.js +1 -1
- package/dist/actions/documents/upsertDocument.js +1 -1
- package/dist/actions/overrides/upsertOverride.js +1 -1
- package/dist/actions/presets/listPresets.js +1 -1
- package/dist/actions/presets/upsertPreset.js +1 -1
- package/dist/actions/search/multiSearch.js +1 -1
- package/dist/actions/search/search.js +1 -1
- package/dist/actions/synonyms/upsertSynonym.js +1 -1
- package/dist/components/export-formatter.js +5 -5
- package/dist/components/http-client.js +8 -8
- package/dist/components/resilience-policy.js +4 -4
- package/dist/components/schema-manager.js +6 -6
- package/dist/examples/multitenancy-example.js +21 -21
- package/dist/index.d.ts +1 -1
- package/dist/tests/type-inference-example.js +9 -9
- package/dist/tests/typesense.js +2 -2
- package/dist/typesense.model.js +1 -1
- package/dist/utils/schema-typed-api.js +1 -1
- package/dist/utils/tenant.js +1 -1
- package/package.json +3 -3
package/dist/TypesenseApi.js
CHANGED
|
@@ -170,7 +170,7 @@ class TypesenseApi {
|
|
|
170
170
|
this.resilience = new resilience_policy_1.ResiliencePolicy({
|
|
171
171
|
...options.resilience,
|
|
172
172
|
onStateChange: options.onCircuitBreakerStateChange,
|
|
173
|
-
onRateLimitUpdate: options.onRateLimitUpdate
|
|
173
|
+
onRateLimitUpdate: options.onRateLimitUpdate,
|
|
174
174
|
});
|
|
175
175
|
// Create request/response interceptors for circuit breaker
|
|
176
176
|
const beforeRequestHooks = [
|
|
@@ -184,7 +184,7 @@ class TypesenseApi {
|
|
|
184
184
|
}
|
|
185
185
|
return undefined;
|
|
186
186
|
},
|
|
187
|
-
...(options.beforeRequest || [])
|
|
187
|
+
...(options.beforeRequest || []),
|
|
188
188
|
];
|
|
189
189
|
const afterResponseHooks = [
|
|
190
190
|
async (_request, _options, response) => {
|
|
@@ -196,7 +196,7 @@ class TypesenseApi {
|
|
|
196
196
|
this.resilience.updateRateLimit(response.headers);
|
|
197
197
|
return response;
|
|
198
198
|
},
|
|
199
|
-
...(options.afterResponse || [])
|
|
199
|
+
...(options.afterResponse || []),
|
|
200
200
|
];
|
|
201
201
|
const beforeErrorHooks = [
|
|
202
202
|
(error) => {
|
|
@@ -211,7 +211,7 @@ class TypesenseApi {
|
|
|
211
211
|
}
|
|
212
212
|
// Re-throw the original error
|
|
213
213
|
throw error;
|
|
214
|
-
}
|
|
214
|
+
},
|
|
215
215
|
];
|
|
216
216
|
// Initialize components with interceptors
|
|
217
217
|
this.httpClient = new http_client_1.TypesenseHttpClient({
|
|
@@ -224,13 +224,13 @@ class TypesenseApi {
|
|
|
224
224
|
afterResponse: afterResponseHooks,
|
|
225
225
|
beforeError: beforeErrorHooks,
|
|
226
226
|
kyInstance: options.kyInstance,
|
|
227
|
-
enforceTLS: options.enforceTLS
|
|
227
|
+
enforceTLS: options.enforceTLS,
|
|
228
228
|
});
|
|
229
229
|
this.schemaManager = new schema_manager_1.CollectionSchemaManager({
|
|
230
230
|
typesenseVersion: options.typesenseVersion,
|
|
231
231
|
cacheSize: options.schemaCacheSize,
|
|
232
232
|
cacheTtl: options.schemaCacheTtl,
|
|
233
|
-
suppressLogs: options.suppressLogs
|
|
233
|
+
suppressLogs: options.suppressLogs,
|
|
234
234
|
});
|
|
235
235
|
// Validate and sanitize tenant ID if provided
|
|
236
236
|
const sanitizedTenantId = options.tenantId
|
|
@@ -249,7 +249,7 @@ class TypesenseApi {
|
|
|
249
249
|
fqcn: (baseCollectionName) => {
|
|
250
250
|
const base = baseCollectionName || this.ctx.collectionName;
|
|
251
251
|
return sanitizedTenantId ? (0, tenant_1.createFQCN)(sanitizedTenantId, base) : base;
|
|
252
|
-
}
|
|
252
|
+
},
|
|
253
253
|
};
|
|
254
254
|
this.withCtx = bindCtx(this.ctx);
|
|
255
255
|
// Check version if enabled
|
|
@@ -282,7 +282,7 @@ class TypesenseApi {
|
|
|
282
282
|
update: this.withCtx(updateCollection_1.updateCollection),
|
|
283
283
|
delete: this.withCtx(deleteCollection_1.deleteCollection),
|
|
284
284
|
list: this.withCtx(listCollections_1.listCollections),
|
|
285
|
-
getOrCreate: this.withCtx(getOrCreateCollection_1.getOrCreateCollection)
|
|
285
|
+
getOrCreate: this.withCtx(getOrCreateCollection_1.getOrCreateCollection),
|
|
286
286
|
};
|
|
287
287
|
}
|
|
288
288
|
/**
|
|
@@ -304,7 +304,7 @@ class TypesenseApi {
|
|
|
304
304
|
// Search operations
|
|
305
305
|
search: this.withCtx(search_1.search),
|
|
306
306
|
searchText: this.withCtx(search_1.searchText),
|
|
307
|
-
searchVector: this.withCtx(search_1.searchVector)
|
|
307
|
+
searchVector: this.withCtx(search_1.searchVector),
|
|
308
308
|
};
|
|
309
309
|
}
|
|
310
310
|
/**
|
|
@@ -315,7 +315,7 @@ class TypesenseApi {
|
|
|
315
315
|
query: this.withCtx(search_1.search),
|
|
316
316
|
text: this.withCtx(search_1.searchText),
|
|
317
317
|
vector: this.withCtx(search_1.searchVector),
|
|
318
|
-
multi: this.withCtx(multiSearch_1.multiSearch)
|
|
318
|
+
multi: this.withCtx(multiSearch_1.multiSearch),
|
|
319
319
|
};
|
|
320
320
|
}
|
|
321
321
|
/**
|
|
@@ -327,7 +327,7 @@ class TypesenseApi {
|
|
|
327
327
|
waitForHealth: this.withCtx(health_1.waitForHealth),
|
|
328
328
|
getMetrics: this.withCtx(metrics_1.getMetrics),
|
|
329
329
|
getStats: this.withCtx(metrics_1.getStats),
|
|
330
|
-
getCollectionStats: this.withCtx(getCollectionStats_1.getCollectionStats)
|
|
330
|
+
getCollectionStats: this.withCtx(getCollectionStats_1.getCollectionStats),
|
|
331
331
|
};
|
|
332
332
|
}
|
|
333
333
|
/**
|
|
@@ -338,7 +338,7 @@ class TypesenseApi {
|
|
|
338
338
|
createOrUpdate: this.withCtx(createOrUpdateAlias_1.createOrUpdateAlias),
|
|
339
339
|
get: this.withCtx(getAlias_1.getAlias),
|
|
340
340
|
list: this.withCtx(listAliases_1.listAliases),
|
|
341
|
-
delete: this.withCtx(deleteAlias_1.deleteAlias)
|
|
341
|
+
delete: this.withCtx(deleteAlias_1.deleteAlias),
|
|
342
342
|
};
|
|
343
343
|
}
|
|
344
344
|
/**
|
|
@@ -349,7 +349,7 @@ class TypesenseApi {
|
|
|
349
349
|
upsert: this.withCtx(upsertSynonym_1.upsertSynonym),
|
|
350
350
|
get: this.withCtx(getSynonym_1.getSynonym),
|
|
351
351
|
list: this.withCtx(listSynonyms_1.listSynonyms),
|
|
352
|
-
delete: this.withCtx(deleteSynonym_1.deleteSynonym)
|
|
352
|
+
delete: this.withCtx(deleteSynonym_1.deleteSynonym),
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
355
|
/**
|
|
@@ -360,7 +360,7 @@ class TypesenseApi {
|
|
|
360
360
|
upsert: this.withCtx(upsertOverride_1.upsertOverride),
|
|
361
361
|
get: this.withCtx(getOverride_1.getOverride),
|
|
362
362
|
list: this.withCtx(listOverrides_1.listOverrides),
|
|
363
|
-
delete: this.withCtx(deleteOverride_1.deleteOverride)
|
|
363
|
+
delete: this.withCtx(deleteOverride_1.deleteOverride),
|
|
364
364
|
};
|
|
365
365
|
}
|
|
366
366
|
/**
|
|
@@ -371,7 +371,7 @@ class TypesenseApi {
|
|
|
371
371
|
upsert: this.withCtx(upsertPreset_1.upsertPreset),
|
|
372
372
|
get: this.withCtx(getPreset_1.getPreset),
|
|
373
373
|
list: this.withCtx(listPresets_1.listPresets),
|
|
374
|
-
delete: this.withCtx(deletePreset_1.deletePreset)
|
|
374
|
+
delete: this.withCtx(deletePreset_1.deletePreset),
|
|
375
375
|
};
|
|
376
376
|
}
|
|
377
377
|
/**
|
|
@@ -438,7 +438,7 @@ class TypesenseApi {
|
|
|
438
438
|
const tenantCollections = await this.listTenantCollections();
|
|
439
439
|
for (const collectionName of tenantCollections) {
|
|
440
440
|
await this.httpClient.request(`/collections/${collectionName}`, {
|
|
441
|
-
method: 'DELETE'
|
|
441
|
+
method: 'DELETE',
|
|
442
442
|
});
|
|
443
443
|
}
|
|
444
444
|
}
|
|
@@ -9,7 +9,7 @@ async function createOrUpdateAlias(ctx, aliasName, collectionName) {
|
|
|
9
9
|
const qualifiedCollectionName = ctx.fqcn(collectionName);
|
|
10
10
|
return await ctx.httpClient.request(`/aliases/${qualifiedAliasName}`, {
|
|
11
11
|
method: 'PUT',
|
|
12
|
-
body: { collection_name: qualifiedCollectionName }
|
|
12
|
+
body: { collection_name: qualifiedCollectionName },
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
//# sourceMappingURL=createOrUpdateAlias.js.map
|
|
@@ -5,11 +5,11 @@ async function createCollection(ctx, collection) {
|
|
|
5
5
|
// Use fqcn for the collection name if tenant is configured
|
|
6
6
|
const collectionWithFqcn = {
|
|
7
7
|
...collection,
|
|
8
|
-
name: ctx.fqcn(collection.name)
|
|
8
|
+
name: ctx.fqcn(collection.name),
|
|
9
9
|
};
|
|
10
10
|
const result = await ctx.httpClient.request('/collections', {
|
|
11
11
|
method: 'POST',
|
|
12
|
-
body: collectionWithFqcn
|
|
12
|
+
body: collectionWithFqcn,
|
|
13
13
|
});
|
|
14
14
|
// Cache the schema with the fully qualified name
|
|
15
15
|
ctx.schemaManager.setCachedSchema(collectionWithFqcn.name, collectionWithFqcn);
|
|
@@ -4,7 +4,7 @@ exports.deleteCollection = deleteCollection;
|
|
|
4
4
|
async function deleteCollection(ctx, collectionName) {
|
|
5
5
|
const collection = collectionName || ctx.fqcn();
|
|
6
6
|
const result = await ctx.httpClient.request(`/collections/${collection}`, {
|
|
7
|
-
method: 'DELETE'
|
|
7
|
+
method: 'DELETE',
|
|
8
8
|
});
|
|
9
9
|
// Remove from cache
|
|
10
10
|
ctx.schemaManager.deleteCachedSchema(collection);
|
|
@@ -5,7 +5,7 @@ async function updateCollection(ctx, collection, options) {
|
|
|
5
5
|
const collectionName = options?.collection || collection.name || ctx.fqcn();
|
|
6
6
|
const result = await ctx.httpClient.request(`/collections/${collectionName}`, {
|
|
7
7
|
method: 'PATCH',
|
|
8
|
-
body: collection
|
|
8
|
+
body: collection,
|
|
9
9
|
});
|
|
10
10
|
// Update cache
|
|
11
11
|
ctx.schemaManager.setCachedSchema(collectionName, result);
|
|
@@ -5,7 +5,7 @@ async function clearCollection(ctx, options) {
|
|
|
5
5
|
const collectionName = options?.collection || ctx.fqcn();
|
|
6
6
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents`, {
|
|
7
7
|
method: 'DELETE',
|
|
8
|
-
searchParams: { filter_by: '*' }
|
|
8
|
+
searchParams: { filter_by: '*' },
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=clearCollection.js.map
|
|
@@ -9,7 +9,7 @@ async function deleteByFilter(ctx, filter, options) {
|
|
|
9
9
|
}
|
|
10
10
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents`, {
|
|
11
11
|
method: 'DELETE',
|
|
12
|
-
searchParams: params
|
|
12
|
+
searchParams: params,
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
//# sourceMappingURL=deleteByFilter.js.map
|
|
@@ -8,7 +8,7 @@ async function deleteDocument(ctx, id, options) {
|
|
|
8
8
|
}
|
|
9
9
|
const collectionName = options?.collection || ctx.fqcn();
|
|
10
10
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents/${id}`, {
|
|
11
|
-
method: 'DELETE'
|
|
11
|
+
method: 'DELETE',
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
//# sourceMappingURL=deleteDocument.js.map
|
|
@@ -14,7 +14,7 @@ async function exportDocuments(ctx, format = 'jsonl', options) {
|
|
|
14
14
|
const collectionName = options?.collection || ctx.fqcn();
|
|
15
15
|
const { collection: _, ...exportOptions } = options || {};
|
|
16
16
|
const searchParams = {
|
|
17
|
-
...exportOptions
|
|
17
|
+
...exportOptions,
|
|
18
18
|
// Note: Typesense export always returns JSONL regardless of format param
|
|
19
19
|
};
|
|
20
20
|
// Get response as text (JSONL format)
|
|
@@ -40,11 +40,11 @@ async function exportDocumentsStream(ctx, options) {
|
|
|
40
40
|
const collectionName = options?.collection || ctx.fqcn();
|
|
41
41
|
const { collection: _, ...exportOptions } = options || {};
|
|
42
42
|
const searchParams = {
|
|
43
|
-
...exportOptions
|
|
43
|
+
...exportOptions,
|
|
44
44
|
};
|
|
45
45
|
return ctx.httpClient
|
|
46
46
|
.stream(`/collections/${collectionName}/documents/export`, {
|
|
47
|
-
searchParams
|
|
47
|
+
searchParams,
|
|
48
48
|
})
|
|
49
49
|
.then(stream => node_stream_1.Readable.fromWeb(stream));
|
|
50
50
|
}
|
|
@@ -42,14 +42,14 @@ async function importDocuments(ctx, documents, format = 'jsonl', importOptions,
|
|
|
42
42
|
}
|
|
43
43
|
const searchParams = {
|
|
44
44
|
...importOptions,
|
|
45
|
-
action: importOptions?.action || 'create'
|
|
45
|
+
action: importOptions?.action || 'create',
|
|
46
46
|
};
|
|
47
47
|
// Stream directly to HTTP body for large files
|
|
48
48
|
const response = await ctx.httpClient.requestTextWithRawBody(`/collections/${collectionName}/documents/import`, {
|
|
49
49
|
method: 'POST',
|
|
50
50
|
body: bodyStream,
|
|
51
51
|
searchParams,
|
|
52
|
-
timeout: ctx.httpClient.importTimeout
|
|
52
|
+
timeout: ctx.httpClient.importTimeout,
|
|
53
53
|
});
|
|
54
54
|
// Parse JSONL response to array
|
|
55
55
|
return response
|
|
@@ -11,7 +11,7 @@ async function insertDocument(ctx, document, options) {
|
|
|
11
11
|
try {
|
|
12
12
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents`, {
|
|
13
13
|
method: 'POST',
|
|
14
|
-
body: document
|
|
14
|
+
body: document,
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
catch (error) {
|
|
@@ -23,12 +23,12 @@ async function insertDocument(ctx, document, options) {
|
|
|
23
23
|
// Create collection
|
|
24
24
|
await (0, getOrCreateCollection_1.getOrCreateCollection)(ctx, {
|
|
25
25
|
...inferredSchema,
|
|
26
|
-
name: collectionName
|
|
26
|
+
name: collectionName,
|
|
27
27
|
});
|
|
28
28
|
// Retry insert
|
|
29
29
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents`, {
|
|
30
30
|
method: 'POST',
|
|
31
|
-
body: document
|
|
31
|
+
body: document,
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
throw error;
|
|
@@ -9,7 +9,7 @@ async function updateDocument(ctx, document, options) {
|
|
|
9
9
|
const collectionName = options?.collection || ctx.fqcn();
|
|
10
10
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents/${document.id}`, {
|
|
11
11
|
method: 'PATCH',
|
|
12
|
-
body: document
|
|
12
|
+
body: document,
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
//# sourceMappingURL=updateDocument.js.map
|
|
@@ -10,7 +10,7 @@ async function upsertDocument(ctx, document, options) {
|
|
|
10
10
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents`, {
|
|
11
11
|
method: 'POST',
|
|
12
12
|
body: document,
|
|
13
|
-
searchParams: { action: 'upsert' }
|
|
13
|
+
searchParams: { action: 'upsert' },
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
//# sourceMappingURL=upsertDocument.js.map
|
|
@@ -5,7 +5,7 @@ async function upsertOverride(ctx, override, options) {
|
|
|
5
5
|
const collectionName = options?.collection || ctx.fqcn();
|
|
6
6
|
return await ctx.httpClient.request(`/collections/${collectionName}/overrides/${override.id}`, {
|
|
7
7
|
method: 'PUT',
|
|
8
|
-
body: override
|
|
8
|
+
body: override,
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=upsertOverride.js.map
|
|
@@ -10,7 +10,7 @@ async function listPresets(ctx) {
|
|
|
10
10
|
// Remove tenant prefix from names for clean API response
|
|
11
11
|
const cleanedPresets = filteredPresets.map(preset => ({
|
|
12
12
|
...preset,
|
|
13
|
-
name: preset.name.substring(tenantPrefix.length)
|
|
13
|
+
name: preset.name.substring(tenantPrefix.length),
|
|
14
14
|
}));
|
|
15
15
|
return { presets: cleanedPresets };
|
|
16
16
|
}
|
|
@@ -8,7 +8,7 @@ async function upsertPreset(ctx, preset) {
|
|
|
8
8
|
const qualifiedPreset = { ...preset, name: qualifiedName };
|
|
9
9
|
return await ctx.httpClient.request(`/presets/${qualifiedName}`, {
|
|
10
10
|
method: 'PUT',
|
|
11
|
-
body: qualifiedPreset
|
|
11
|
+
body: qualifiedPreset,
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
//# sourceMappingURL=upsertPreset.js.map
|
|
@@ -7,7 +7,7 @@ async function search(ctx, query, options) {
|
|
|
7
7
|
const collectionName = options?.collection || ctx.fqcn();
|
|
8
8
|
return await ctx.httpClient.request(`/collections/${collectionName}/documents/search`, {
|
|
9
9
|
searchParams: query,
|
|
10
|
-
timeout: ctx.httpClient.getOptions().searchTimeout
|
|
10
|
+
timeout: ctx.httpClient.getOptions().searchTimeout,
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
async function searchVector(ctx, query, options) {
|
|
@@ -5,7 +5,7 @@ async function upsertSynonym(ctx, synonym, options) {
|
|
|
5
5
|
const collectionName = options?.collection || ctx.fqcn();
|
|
6
6
|
return await ctx.httpClient.request(`/collections/${collectionName}/synonyms/${synonym.id}`, {
|
|
7
7
|
method: 'PUT',
|
|
8
|
-
body: synonym
|
|
8
|
+
body: synonym,
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=upsertSynonym.js.map
|
|
@@ -61,7 +61,7 @@ class ExportFormatter {
|
|
|
61
61
|
catch (error) {
|
|
62
62
|
callback(error);
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
},
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
static createStreamingJSONLTransform() {
|
|
@@ -75,7 +75,7 @@ class ExportFormatter {
|
|
|
75
75
|
catch (error) {
|
|
76
76
|
callback(error);
|
|
77
77
|
}
|
|
78
|
-
}
|
|
78
|
+
},
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
static createGzipStream() {
|
|
@@ -124,7 +124,7 @@ class ExportFormatter {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
callback();
|
|
127
|
-
}
|
|
127
|
+
},
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
130
|
static createJSONParser() {
|
|
@@ -149,7 +149,7 @@ class ExportFormatter {
|
|
|
149
149
|
return callback(new Error(`Invalid JSON: ${error.message}`));
|
|
150
150
|
}
|
|
151
151
|
callback();
|
|
152
|
-
}
|
|
152
|
+
},
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
static escapeCsvValue(value) {
|
|
@@ -218,7 +218,7 @@ class ExportFormatter {
|
|
|
218
218
|
else {
|
|
219
219
|
this.push(null); // End stream
|
|
220
220
|
}
|
|
221
|
-
}
|
|
221
|
+
},
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
224
|
}
|
|
@@ -27,7 +27,7 @@ class TypesenseHttpClient {
|
|
|
27
27
|
timeout: options.defaultTimeout || 10000,
|
|
28
28
|
headers: {
|
|
29
29
|
'X-TYPESENSE-API-KEY': options.token,
|
|
30
|
-
'Content-Type': 'application/json'
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
31
|
},
|
|
32
32
|
hooks: {
|
|
33
33
|
beforeRequest: options.beforeRequest || [],
|
|
@@ -49,9 +49,9 @@ class TypesenseHttpClient {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
throw error;
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
}
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
sanitizeHeaders(headers) {
|
|
@@ -77,7 +77,7 @@ class TypesenseHttpClient {
|
|
|
77
77
|
const requestOptions = {
|
|
78
78
|
method,
|
|
79
79
|
timeout: timeout || 10000,
|
|
80
|
-
signal
|
|
80
|
+
signal,
|
|
81
81
|
};
|
|
82
82
|
if (body) {
|
|
83
83
|
requestOptions.json = body;
|
|
@@ -97,7 +97,7 @@ class TypesenseHttpClient {
|
|
|
97
97
|
const requestOptions = {
|
|
98
98
|
method,
|
|
99
99
|
timeout: timeout || 10000,
|
|
100
|
-
signal
|
|
100
|
+
signal,
|
|
101
101
|
};
|
|
102
102
|
if (body) {
|
|
103
103
|
requestOptions.json = body;
|
|
@@ -117,7 +117,7 @@ class TypesenseHttpClient {
|
|
|
117
117
|
const requestOptions = {
|
|
118
118
|
method,
|
|
119
119
|
timeout: timeout || 10000,
|
|
120
|
-
signal
|
|
120
|
+
signal,
|
|
121
121
|
};
|
|
122
122
|
if (body) {
|
|
123
123
|
// Use raw body instead of JSON encoding
|
|
@@ -137,7 +137,7 @@ class TypesenseHttpClient {
|
|
|
137
137
|
: endpoint;
|
|
138
138
|
const requestOptions = {
|
|
139
139
|
method,
|
|
140
|
-
signal
|
|
140
|
+
signal,
|
|
141
141
|
};
|
|
142
142
|
if (body) {
|
|
143
143
|
requestOptions.body = body;
|
|
@@ -15,7 +15,7 @@ class ResiliencePolicy {
|
|
|
15
15
|
retryDelay: 1000,
|
|
16
16
|
maxRetries: 3,
|
|
17
17
|
enabled: true, // Enabled by default
|
|
18
|
-
...options
|
|
18
|
+
...options,
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
isCircuitOpen() {
|
|
@@ -75,7 +75,7 @@ class ResiliencePolicy {
|
|
|
75
75
|
this.options.onStateChange('open', {
|
|
76
76
|
failures: this.failures,
|
|
77
77
|
openUntil: new Date(this.circuitOpenUntil),
|
|
78
|
-
resetTimeout: this.options.resetTimeout || 60000
|
|
78
|
+
resetTimeout: this.options.resetTimeout || 60000,
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -89,7 +89,7 @@ class ResiliencePolicy {
|
|
|
89
89
|
limit: limit ? Number.parseInt(limit, 10) : undefined,
|
|
90
90
|
remaining: remaining ? Number.parseInt(remaining, 10) : undefined,
|
|
91
91
|
resetMs: resetMs ? Number.parseInt(resetMs, 10) : undefined,
|
|
92
|
-
retryAfter: retryAfter ? Number.parseInt(retryAfter, 10) : undefined
|
|
92
|
+
retryAfter: retryAfter ? Number.parseInt(retryAfter, 10) : undefined,
|
|
93
93
|
};
|
|
94
94
|
// Set retry-after period if present
|
|
95
95
|
if (retryAfter) {
|
|
@@ -128,7 +128,7 @@ class ResiliencePolicy {
|
|
|
128
128
|
circuitOpenUntil: this.circuitOpenUntil,
|
|
129
129
|
rateLimited: this.isRateLimited(),
|
|
130
130
|
retryAfterUntil: this.retryAfterUntil,
|
|
131
|
-
rateLimit: this.rateLimitInfo
|
|
131
|
+
rateLimit: this.rateLimitInfo,
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
134
|
}
|
|
@@ -49,7 +49,7 @@ class CollectionSchemaManager {
|
|
|
49
49
|
cacheTtl: options.cacheTtl || 300000, // 5 minutes
|
|
50
50
|
enableNestedFields: options.enableNestedFields ?? false,
|
|
51
51
|
typesenseVersion: options.typesenseVersion || '0.24.0',
|
|
52
|
-
suppressLogs: options.suppressLogs ?? false
|
|
52
|
+
suppressLogs: options.suppressLogs ?? false,
|
|
53
53
|
};
|
|
54
54
|
this.schemaCache = new LRUCache(this.options.cacheSize);
|
|
55
55
|
}
|
|
@@ -87,7 +87,7 @@ class CollectionSchemaManager {
|
|
|
87
87
|
const cacheKey = `schema:${collectionName}`;
|
|
88
88
|
this.schemaCache.set(cacheKey, {
|
|
89
89
|
schema,
|
|
90
|
-
timestamp: Date.now()
|
|
90
|
+
timestamp: Date.now(),
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
deleteCachedSchema(collectionName) {
|
|
@@ -175,7 +175,7 @@ class CollectionSchemaManager {
|
|
|
175
175
|
}
|
|
176
176
|
const collection = {
|
|
177
177
|
name: collectionName,
|
|
178
|
-
fields
|
|
178
|
+
fields,
|
|
179
179
|
};
|
|
180
180
|
// Add version-gated features
|
|
181
181
|
if (this.isVersionSupported('nested_fields', '0.25.0') &&
|
|
@@ -213,7 +213,7 @@ class CollectionSchemaManager {
|
|
|
213
213
|
'object',
|
|
214
214
|
'object[]',
|
|
215
215
|
'auto',
|
|
216
|
-
'image'
|
|
216
|
+
'image',
|
|
217
217
|
];
|
|
218
218
|
schema.fields?.forEach(field => {
|
|
219
219
|
if (!validTypes.includes(field.type)) {
|
|
@@ -227,7 +227,7 @@ class CollectionSchemaManager {
|
|
|
227
227
|
}
|
|
228
228
|
return {
|
|
229
229
|
valid: errors.length === 0,
|
|
230
|
-
errors
|
|
230
|
+
errors,
|
|
231
231
|
};
|
|
232
232
|
}
|
|
233
233
|
clearCache() {
|
|
@@ -236,7 +236,7 @@ class CollectionSchemaManager {
|
|
|
236
236
|
getCacheStats() {
|
|
237
237
|
return {
|
|
238
238
|
size: this.schemaCache.size(),
|
|
239
|
-
maxSize: this.options.cacheSize
|
|
239
|
+
maxSize: this.options.cacheSize,
|
|
240
240
|
};
|
|
241
241
|
}
|
|
242
242
|
setTypesenseVersion(version) {
|
|
@@ -12,14 +12,14 @@ async function _example1() {
|
|
|
12
12
|
prefixUrl: 'http://localhost:8108',
|
|
13
13
|
token: 'xyz',
|
|
14
14
|
tenantId: 'acme',
|
|
15
|
-
collectionName: 'products'
|
|
15
|
+
collectionName: 'products',
|
|
16
16
|
});
|
|
17
17
|
// API instance for tenant "globex"
|
|
18
18
|
const globexApi = new TypesenseApi_1.TypesenseApi({
|
|
19
19
|
prefixUrl: 'http://localhost:8108',
|
|
20
20
|
token: 'xyz',
|
|
21
21
|
tenantId: 'globex',
|
|
22
|
-
collectionName: 'products'
|
|
22
|
+
collectionName: 'products',
|
|
23
23
|
});
|
|
24
24
|
// Define schema
|
|
25
25
|
const productSchema = {
|
|
@@ -27,8 +27,8 @@ async function _example1() {
|
|
|
27
27
|
fields: [
|
|
28
28
|
{ name: 'name', type: 'string' },
|
|
29
29
|
{ name: 'price', type: 'float' },
|
|
30
|
-
{ name: 'category', type: 'string', facet: true }
|
|
31
|
-
]
|
|
30
|
+
{ name: 'category', type: 'string', facet: true },
|
|
31
|
+
],
|
|
32
32
|
};
|
|
33
33
|
// Create collections - each tenant gets their own prefixed collection
|
|
34
34
|
// This creates "acme__products" collection
|
|
@@ -40,23 +40,23 @@ async function _example1() {
|
|
|
40
40
|
id: '1',
|
|
41
41
|
name: 'Acme Widget',
|
|
42
42
|
price: 19.99,
|
|
43
|
-
category: 'widgets'
|
|
43
|
+
category: 'widgets',
|
|
44
44
|
});
|
|
45
45
|
await globexApi.documents.insert({
|
|
46
46
|
id: '1', // Same ID is fine - different collection
|
|
47
47
|
name: 'Globex Gadget',
|
|
48
48
|
price: 29.99,
|
|
49
|
-
category: 'gadgets'
|
|
49
|
+
category: 'gadgets',
|
|
50
50
|
});
|
|
51
51
|
// Search - each tenant only sees their own data
|
|
52
52
|
const acmeResults = await acmeApi.search.text({
|
|
53
53
|
q: '*',
|
|
54
|
-
query_by: 'name'
|
|
54
|
+
query_by: 'name',
|
|
55
55
|
});
|
|
56
56
|
console.log('Acme products:', acmeResults.hits); // Only Acme Widget
|
|
57
57
|
const globexResults = await globexApi.search.text({
|
|
58
58
|
q: '*',
|
|
59
|
-
query_by: 'name'
|
|
59
|
+
query_by: 'name',
|
|
60
60
|
});
|
|
61
61
|
console.log('Globex products:', globexResults.hits); // Only Globex Gadget
|
|
62
62
|
}
|
|
@@ -65,7 +65,7 @@ async function _example2() {
|
|
|
65
65
|
const adminApi = new TypesenseApi_1.TypesenseApi({
|
|
66
66
|
prefixUrl: 'http://localhost:8108',
|
|
67
67
|
token: 'xyz',
|
|
68
|
-
tenantId: 'acme'
|
|
68
|
+
tenantId: 'acme',
|
|
69
69
|
});
|
|
70
70
|
// List all collections for the tenant
|
|
71
71
|
const tenantCollections = await adminApi.listTenantCollections();
|
|
@@ -86,48 +86,48 @@ async function _example3() {
|
|
|
86
86
|
const api = new TypesenseApi_1.TypesenseApi({
|
|
87
87
|
prefixUrl: 'http://localhost:8108',
|
|
88
88
|
token: 'xyz',
|
|
89
|
-
tenantId: 'acme'
|
|
89
|
+
tenantId: 'acme',
|
|
90
90
|
});
|
|
91
91
|
// Create multiple collections for the tenant
|
|
92
92
|
await api.collections.create({
|
|
93
93
|
name: 'users',
|
|
94
94
|
fields: [
|
|
95
95
|
{ name: 'email', type: 'string' },
|
|
96
|
-
{ name: 'name', type: 'string' }
|
|
97
|
-
]
|
|
96
|
+
{ name: 'name', type: 'string' },
|
|
97
|
+
],
|
|
98
98
|
});
|
|
99
99
|
await api.collections.create({
|
|
100
100
|
name: 'orders',
|
|
101
101
|
fields: [
|
|
102
102
|
{ name: 'order_id', type: 'string' },
|
|
103
103
|
{ name: 'user_email', type: 'string' },
|
|
104
|
-
{ name: 'total', type: 'float' }
|
|
105
|
-
]
|
|
104
|
+
{ name: 'total', type: 'float' },
|
|
105
|
+
],
|
|
106
106
|
});
|
|
107
107
|
// Work with different collections by changing the context
|
|
108
108
|
const userApi = new TypesenseApi_1.TypesenseApi({
|
|
109
109
|
prefixUrl: 'http://localhost:8108',
|
|
110
110
|
token: 'xyz',
|
|
111
111
|
tenantId: 'acme',
|
|
112
|
-
collectionName: 'users'
|
|
112
|
+
collectionName: 'users',
|
|
113
113
|
});
|
|
114
114
|
const orderApi = new TypesenseApi_1.TypesenseApi({
|
|
115
115
|
prefixUrl: 'http://localhost:8108',
|
|
116
116
|
token: 'xyz',
|
|
117
117
|
tenantId: 'acme',
|
|
118
|
-
collectionName: 'orders'
|
|
118
|
+
collectionName: 'orders',
|
|
119
119
|
});
|
|
120
120
|
// Insert data into different collections
|
|
121
121
|
await userApi.documents.insert({
|
|
122
122
|
id: 'user-1',
|
|
123
123
|
email: 'john@acme.com',
|
|
124
|
-
name: 'John Doe'
|
|
124
|
+
name: 'John Doe',
|
|
125
125
|
});
|
|
126
126
|
await orderApi.documents.insert({
|
|
127
127
|
id: 'ORD-001',
|
|
128
128
|
order_id: 'ORD-001',
|
|
129
129
|
user_email: 'john@acme.com',
|
|
130
|
-
total: 99.99
|
|
130
|
+
total: 99.99,
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
// Example 4: Migrating existing non-tenant data
|
|
@@ -136,14 +136,14 @@ async function _example4() {
|
|
|
136
136
|
const legacyApi = new TypesenseApi_1.TypesenseApi({
|
|
137
137
|
prefixUrl: 'http://localhost:8108',
|
|
138
138
|
token: 'xyz',
|
|
139
|
-
collectionName: 'products'
|
|
139
|
+
collectionName: 'products',
|
|
140
140
|
});
|
|
141
141
|
// API with tenant
|
|
142
142
|
const tenantApi = new TypesenseApi_1.TypesenseApi({
|
|
143
143
|
prefixUrl: 'http://localhost:8108',
|
|
144
144
|
token: 'xyz',
|
|
145
145
|
tenantId: 'legacy',
|
|
146
|
-
collectionName: 'products'
|
|
146
|
+
collectionName: 'products',
|
|
147
147
|
});
|
|
148
148
|
// Export from legacy collection
|
|
149
149
|
const legacyData = await legacyApi.documents.export();
|
|
@@ -174,7 +174,7 @@ async function _example5() {
|
|
|
174
174
|
'acme__products',
|
|
175
175
|
'acme__users',
|
|
176
176
|
'globex__products',
|
|
177
|
-
'legacy_collection'
|
|
177
|
+
'legacy_collection',
|
|
178
178
|
];
|
|
179
179
|
const acmeCollections = (0, tenant_1.filterCollectionsByTenant)(allCollections, 'acme');
|
|
180
180
|
console.log('Acme collections:', acmeCollections); // ['acme__products', 'acme__users']
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export { TypesenseApi } from './TypesenseApi';
|
|
|
8
8
|
export * from './types';
|
|
9
9
|
export type * from './typesense.model';
|
|
10
10
|
export * from './typesense.model';
|
|
11
|
-
export { defineCollection, type InferDocumentType, type InferFromCollection } from './utils/schema-to-types';
|
|
11
|
+
export { defineCollection, type InferDocumentType, type InferFromCollection, } from './utils/schema-to-types';
|
|
12
12
|
export { createSchemaTypedApi } from './utils/schema-typed-api';
|
|
@@ -15,26 +15,26 @@ const index_1 = require("../index");
|
|
|
15
15
|
{ name: 'description', type: 'string', optional: true },
|
|
16
16
|
{ name: 'price', type: 'float' },
|
|
17
17
|
{ name: 'inStock', type: 'bool' },
|
|
18
|
-
{ name: 'tags', type: 'string[]', optional: true }
|
|
19
|
-
]
|
|
18
|
+
{ name: 'tags', type: 'string[]', optional: true },
|
|
19
|
+
],
|
|
20
20
|
});
|
|
21
21
|
// These should compile without errors
|
|
22
22
|
const _validDoc1 = {
|
|
23
23
|
title: 'Laptop',
|
|
24
24
|
price: 999.99,
|
|
25
|
-
inStock: true
|
|
25
|
+
inStock: true,
|
|
26
26
|
};
|
|
27
27
|
const _validDoc2 = {
|
|
28
28
|
title: 'Gaming Laptop',
|
|
29
29
|
description: 'High-performance laptop',
|
|
30
30
|
price: 1999.99,
|
|
31
31
|
inStock: true,
|
|
32
|
-
tags: ['gaming', 'performance']
|
|
32
|
+
tags: ['gaming', 'performance'],
|
|
33
33
|
};
|
|
34
34
|
// Create typed API
|
|
35
35
|
const api = (0, index_1.createSchemaTypedApi)(ProductCollection)({
|
|
36
36
|
prefixUrl: 'http://localhost:8108',
|
|
37
|
-
token: 'xyz'
|
|
37
|
+
token: 'xyz',
|
|
38
38
|
});
|
|
39
39
|
// Verify API structure
|
|
40
40
|
(0, vitest_1.expect)(api).toBeDefined();
|
|
@@ -69,20 +69,20 @@ const index_1 = require("../index");
|
|
|
69
69
|
{ name: 'timestamp', type: 'int64' },
|
|
70
70
|
{ name: 'location', type: 'geopoint' },
|
|
71
71
|
{ name: 'attendees', type: 'int32[]', optional: true },
|
|
72
|
-
{ name: 'metadata', type: 'object', optional: true }
|
|
73
|
-
]
|
|
72
|
+
{ name: 'metadata', type: 'object', optional: true },
|
|
73
|
+
],
|
|
74
74
|
});
|
|
75
75
|
const validEvent = {
|
|
76
76
|
name: 'Tech Conference',
|
|
77
77
|
timestamp: Date.now(),
|
|
78
|
-
location: [37.7749, -122.4194]
|
|
78
|
+
location: [37.7749, -122.4194],
|
|
79
79
|
};
|
|
80
80
|
const validEventWithOptionals = {
|
|
81
81
|
name: 'Meetup',
|
|
82
82
|
timestamp: Date.now(),
|
|
83
83
|
location: [40.7128, -74.006],
|
|
84
84
|
attendees: [10, 20, 30],
|
|
85
|
-
metadata: { organizer: 'John', venue: 'Tech Hub' }
|
|
85
|
+
metadata: { organizer: 'John', venue: 'Tech Hub' },
|
|
86
86
|
};
|
|
87
87
|
(0, vitest_1.expect)(validEvent).toBeDefined();
|
|
88
88
|
(0, vitest_1.expect)(validEventWithOptionals).toBeDefined();
|
package/dist/tests/typesense.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.typesenseContainer = void 0;
|
|
|
4
4
|
const testcontainers_1 = require("testcontainers");
|
|
5
5
|
exports.typesenseContainer = new testcontainers_1.GenericContainer('typesense/typesense:29.0')
|
|
6
6
|
.withEnvironment({
|
|
7
|
-
TYPESENSE_API_KEY: 'MY_API_KEY'
|
|
7
|
+
TYPESENSE_API_KEY: 'MY_API_KEY',
|
|
8
8
|
})
|
|
9
9
|
.withCommand([
|
|
10
10
|
'--data-dir',
|
|
@@ -18,7 +18,7 @@ exports.typesenseContainer = new testcontainers_1.GenericContainer('typesense/ty
|
|
|
18
18
|
'--analytics-dir',
|
|
19
19
|
'/path/to/analytics-data',
|
|
20
20
|
'--analytics-flush-interval',
|
|
21
|
-
'300'
|
|
21
|
+
'300',
|
|
22
22
|
])
|
|
23
23
|
.withExposedPorts(8108);
|
|
24
24
|
//# sourceMappingURL=typesense.js.map
|
package/dist/typesense.model.js
CHANGED
package/dist/utils/tenant.js
CHANGED
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"name": "@goatlab/typesense",
|
|
5
5
|
"description": "Modern TypeScript wrapper for Typesense search engine API",
|
|
6
6
|
"author": "ignacio.cabrera@goatlab.io",
|
|
7
|
-
"version": "0.1.
|
|
7
|
+
"version": "0.1.4",
|
|
8
8
|
"private": false,
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"zod": "^4.0.10",
|
|
15
|
-
"@goatlab/js-utils": "0.10.
|
|
15
|
+
"@goatlab/js-utils": "0.10.3",
|
|
16
16
|
"@goatlab/tsconfig": "0.1.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"turbo": "^1.1.10",
|
|
30
30
|
"typescript": "^5.0.2",
|
|
31
31
|
"vitest": "^3.2.4",
|
|
32
|
-
"@goatlab/biome": "0.1.
|
|
32
|
+
"@goatlab/biome": "0.1.1"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=14.16.0"
|