@backstage/plugin-search-backend-node 1.3.3-next.0 → 1.3.3-next.2

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/index.cjs.js CHANGED
@@ -1,736 +1,22 @@
1
1
  'use strict';
2
2
 
3
- var stream = require('stream');
4
- var ndjson = require('ndjson');
5
- var errors = require('@backstage/errors');
6
- var lunr = require('lunr');
7
- var uuid = require('uuid');
8
-
9
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
10
-
11
- var lunr__default = /*#__PURE__*/_interopDefaultCompat(lunr);
12
-
13
- class Scheduler {
14
- logger;
15
- schedule;
16
- abortControllers;
17
- isRunning;
18
- constructor(options) {
19
- this.logger = options.logger;
20
- this.schedule = {};
21
- this.abortControllers = [];
22
- this.isRunning = false;
23
- }
24
- /**
25
- * Adds each task and interval to the schedule.
26
- * When running the tasks, the scheduler waits at least for the time specified
27
- * in the interval once the task was completed, before running it again.
28
- */
29
- addToSchedule(options) {
30
- const { id, task, scheduledRunner } = options;
31
- if (this.isRunning) {
32
- throw new Error(
33
- "Cannot add task to schedule that has already been started."
34
- );
35
- }
36
- if (this.schedule[id]) {
37
- throw new Error(`Task with id ${id} already exists.`);
38
- }
39
- this.schedule[id] = { task, scheduledRunner };
40
- }
41
- /**
42
- * Starts the scheduling process for each task
43
- */
44
- start() {
45
- this.logger.info("Starting all scheduled search tasks.");
46
- this.isRunning = true;
47
- Object.keys(this.schedule).forEach((id) => {
48
- const abortController = new AbortController();
49
- this.abortControllers.push(abortController);
50
- const { task, scheduledRunner } = this.schedule[id];
51
- scheduledRunner.run({
52
- id,
53
- fn: task,
54
- signal: abortController.signal
55
- });
56
- });
57
- }
58
- /**
59
- * Stop all scheduled tasks.
60
- */
61
- stop() {
62
- this.logger.info("Stopping all scheduled search tasks.");
63
- for (const abortController of this.abortControllers) {
64
- abortController.abort();
65
- }
66
- this.abortControllers = [];
67
- this.isRunning = false;
68
- }
69
- }
70
-
71
- class IndexBuilder {
72
- collators;
73
- decorators;
74
- documentTypes;
75
- searchEngine;
76
- logger;
77
- constructor(options) {
78
- this.collators = {};
79
- this.decorators = {};
80
- this.documentTypes = {};
81
- this.logger = options.logger;
82
- this.searchEngine = options.searchEngine;
83
- }
84
- /**
85
- * Responsible for returning the registered search engine.
86
- */
87
- getSearchEngine() {
88
- return this.searchEngine;
89
- }
90
- /**
91
- * Responsible for returning the registered document types.
92
- */
93
- getDocumentTypes() {
94
- return this.documentTypes;
95
- }
96
- /**
97
- * Makes the index builder aware of a collator that should be executed at the
98
- * given refresh interval.
99
- */
100
- addCollator(options) {
101
- const { factory, schedule } = options;
102
- this.logger.info(
103
- `Added ${factory.constructor.name} collator factory for type ${factory.type}`
104
- );
105
- this.collators[factory.type] = {
106
- factory,
107
- schedule
108
- };
109
- this.documentTypes[factory.type] = {
110
- visibilityPermission: factory.visibilityPermission
111
- };
112
- }
113
- /**
114
- * Makes the index builder aware of a decorator. If no types are provided on
115
- * the decorator, it will be applied to documents from all known collators,
116
- * otherwise it will only be applied to documents of the given types.
117
- */
118
- addDecorator(options) {
119
- const { factory } = options;
120
- const types = factory.types || ["*"];
121
- this.logger.info(
122
- `Added decorator ${factory.constructor.name} to types ${types.join(
123
- ", "
124
- )}`
125
- );
126
- types.forEach((type) => {
127
- if (this.decorators.hasOwnProperty(type)) {
128
- this.decorators[type].push(factory);
129
- } else {
130
- this.decorators[type] = [factory];
131
- }
132
- });
133
- }
134
- /**
135
- * Compiles collators and decorators into tasks, which are added to a
136
- * scheduler returned to the caller.
137
- */
138
- async build() {
139
- const scheduler = new Scheduler({
140
- logger: this.logger
141
- });
142
- Object.keys(this.collators).forEach((type) => {
143
- const taskLogger = this.logger.child({ documentType: type });
144
- scheduler.addToSchedule({
145
- id: `search_index_${type.replace("-", "_").toLocaleLowerCase("en-US")}`,
146
- scheduledRunner: this.collators[type].schedule,
147
- task: async () => {
148
- const collator = await this.collators[type].factory.getCollator();
149
- taskLogger.info(
150
- `Collating documents for ${type} via ${this.collators[type].factory.constructor.name}`
151
- );
152
- const decorators = await Promise.all(
153
- (this.decorators["*"] || []).concat(this.decorators[type] || []).map(async (factory) => {
154
- const decorator = await factory.getDecorator();
155
- taskLogger.info(
156
- `Attached decorator via ${factory.constructor.name} to ${type} index pipeline.`
157
- );
158
- return decorator;
159
- })
160
- );
161
- const indexer = await this.searchEngine.getIndexer(type);
162
- return new Promise((resolve, reject) => {
163
- stream.pipeline(
164
- [collator, ...decorators, indexer],
165
- (error) => {
166
- if (error) {
167
- taskLogger.error(
168
- `Collating documents for ${type} failed: ${error}`
169
- );
170
- reject(error);
171
- } else {
172
- taskLogger.info(`Collating documents for ${type} succeeded`);
173
- resolve();
174
- }
175
- }
176
- );
177
- });
178
- }
179
- });
180
- });
181
- return {
182
- scheduler
183
- };
184
- }
185
- }
186
-
187
- class NewlineDelimitedJsonCollatorFactory {
188
- constructor(type, searchPattern, reader, logger, visibilityPermission) {
189
- this.searchPattern = searchPattern;
190
- this.reader = reader;
191
- this.logger = logger;
192
- this.type = type;
193
- this.visibilityPermission = visibilityPermission;
194
- }
195
- type;
196
- visibilityPermission;
197
- /**
198
- * Returns a NewlineDelimitedJsonCollatorFactory instance from configuration
199
- * and a set of options.
200
- */
201
- static fromConfig(_config, options) {
202
- return new NewlineDelimitedJsonCollatorFactory(
203
- options.type,
204
- options.searchPattern,
205
- options.reader,
206
- options.logger.child({ documentType: options.type }),
207
- options.visibilityPermission
208
- );
209
- }
210
- /**
211
- * Returns the "latest" URL for the given search pattern (e.g. the one at the
212
- * end of the list, sorted alphabetically).
213
- */
214
- async lastUrl() {
215
- try {
216
- this.logger.info(
217
- `Attempting to find latest .ndjson matching ${this.searchPattern}`
218
- );
219
- const { files } = await this.reader.search(this.searchPattern);
220
- const candidates = files.filter((file) => file.url.endsWith(".ndjson")).sort((a, b) => a.url.localeCompare(b.url)).reverse();
221
- return candidates[0]?.url;
222
- } catch (e) {
223
- this.logger.error(`Could not search for ${this.searchPattern}`, e);
224
- throw e;
225
- }
226
- }
227
- async getCollator() {
228
- const lastUrl = await this.lastUrl();
229
- if (!lastUrl) {
230
- const noMatchingFile = `Could not find an .ndjson file matching ${this.searchPattern}`;
231
- this.logger.error(noMatchingFile);
232
- throw new Error(noMatchingFile);
233
- } else {
234
- this.logger.info(`Using latest .ndjson file ${lastUrl}`);
235
- }
236
- const readerResponse = await this.reader.readUrl(lastUrl);
237
- const stream = readerResponse.stream();
238
- return stream.pipe(ndjson.parse());
239
- }
240
- }
241
-
242
- class MissingIndexError extends Error {
243
- /**
244
- * An inner error that caused this error to be thrown, if any.
245
- */
246
- cause;
247
- constructor(message, cause) {
248
- super(message);
249
- Error.captureStackTrace?.(this, this.constructor);
250
- this.name = this.constructor.name;
251
- this.cause = errors.isError(cause) ? cause : void 0;
252
- }
253
- }
254
-
255
- class BatchSearchEngineIndexer extends stream.Writable {
256
- batchSize;
257
- currentBatch = [];
258
- constructor(options) {
259
- super({ objectMode: true });
260
- this.batchSize = options.batchSize;
261
- }
262
- /**
263
- * Encapsulates initialization logic.
264
- * @internal
265
- */
266
- async _construct(done) {
267
- try {
268
- await this.initialize();
269
- done();
270
- } catch (e) {
271
- errors.assertError(e);
272
- done(e);
273
- }
274
- }
275
- /**
276
- * Encapsulates batch stream write logic.
277
- * @internal
278
- */
279
- async _write(doc, _e, done) {
280
- this.currentBatch.push(doc);
281
- if (this.currentBatch.length < this.batchSize) {
282
- done();
283
- return;
284
- }
285
- try {
286
- await this.index(this.currentBatch);
287
- this.currentBatch = [];
288
- done();
289
- } catch (e) {
290
- errors.assertError(e);
291
- done(e);
292
- }
293
- }
294
- /**
295
- * Encapsulates finalization and final error handling logic.
296
- * @internal
297
- */
298
- async _final(done) {
299
- try {
300
- if (this.currentBatch.length) {
301
- await this.index(this.currentBatch);
302
- this.currentBatch = [];
303
- }
304
- await this.finalize();
305
- done();
306
- } catch (e) {
307
- errors.assertError(e);
308
- done(e);
309
- }
310
- }
311
- }
312
-
313
- class DecoratorBase extends stream.Transform {
314
- constructor() {
315
- super({ objectMode: true });
316
- }
317
- /**
318
- * Encapsulates initialization logic.
319
- * @internal
320
- */
321
- async _construct(done) {
322
- try {
323
- await this.initialize();
324
- done();
325
- } catch (e) {
326
- errors.assertError(e);
327
- done(e);
328
- }
329
- }
330
- /**
331
- * Encapsulates simple transform stream logic.
332
- * @internal
333
- */
334
- async _transform(document, _, done) {
335
- try {
336
- const decorated = await this.decorate(document);
337
- if (decorated === void 0) {
338
- done();
339
- return;
340
- }
341
- if (Array.isArray(decorated)) {
342
- decorated.forEach((doc) => {
343
- this.push(doc);
344
- });
345
- done();
346
- return;
347
- }
348
- this.push(decorated);
349
- done();
350
- } catch (e) {
351
- errors.assertError(e);
352
- done(e);
353
- }
354
- }
355
- /**
356
- * Encapsulates finalization and final error handling logic.
357
- * @internal
358
- */
359
- async _final(done) {
360
- try {
361
- await this.finalize();
362
- done();
363
- } catch (e) {
364
- errors.assertError(e);
365
- done(e);
366
- }
367
- }
368
- }
369
-
370
- class LunrSearchEngineIndexer extends BatchSearchEngineIndexer {
371
- schemaInitialized = false;
372
- builder;
373
- docStore = {};
374
- constructor() {
375
- super({ batchSize: 1e3 });
376
- this.builder = new lunr__default.default.Builder();
377
- this.builder.pipeline.add(lunr__default.default.trimmer, lunr__default.default.stopWordFilter, lunr__default.default.stemmer);
378
- this.builder.searchPipeline.add(lunr__default.default.stemmer);
379
- this.builder.metadataWhitelist = ["position"];
380
- }
381
- // No async initialization required.
382
- async initialize() {
383
- }
384
- async finalize() {
385
- }
386
- async index(documents) {
387
- if (!this.schemaInitialized) {
388
- Object.keys(documents[0]).forEach((field) => {
389
- this.builder.field(field);
390
- });
391
- this.builder.ref("location");
392
- this.schemaInitialized = true;
393
- }
394
- documents.forEach((document) => {
395
- this.builder.add(document);
396
- this.docStore[document.location] = document;
397
- });
398
- }
399
- buildIndex() {
400
- return this.builder.build();
401
- }
402
- getDocumentStore() {
403
- return this.docStore;
404
- }
405
- }
406
-
407
- class LunrSearchEngine {
408
- lunrIndices = {};
409
- docStore;
410
- logger;
411
- highlightPreTag;
412
- highlightPostTag;
413
- constructor(options) {
414
- this.logger = options.logger;
415
- this.docStore = {};
416
- const uuidTag = uuid.v4();
417
- this.highlightPreTag = `<${uuidTag}>`;
418
- this.highlightPostTag = `</${uuidTag}>`;
419
- }
420
- translator = ({
421
- term,
422
- filters,
423
- types,
424
- pageLimit
425
- }) => {
426
- const pageSize = pageLimit || 25;
427
- return {
428
- lunrQueryBuilder: (q) => {
429
- const termToken = lunr__default.default.tokenizer(term);
430
- q.term(termToken, {
431
- usePipeline: true,
432
- boost: 100
433
- });
434
- q.term(termToken, {
435
- usePipeline: false,
436
- boost: 10,
437
- wildcard: lunr__default.default.Query.wildcard.TRAILING
438
- });
439
- q.term(termToken, {
440
- usePipeline: false,
441
- editDistance: 2,
442
- boost: 1
443
- });
444
- if (filters) {
445
- Object.entries(filters).forEach(([field, fieldValue]) => {
446
- if (!q.allFields.includes(field)) {
447
- throw new Error(`unrecognised field ${field}`);
448
- }
449
- const value = Array.isArray(fieldValue) && fieldValue.length === 1 ? fieldValue[0] : fieldValue;
450
- if (["string", "number", "boolean"].includes(typeof value)) {
451
- q.term(
452
- lunr__default.default.tokenizer(value?.toString()).map(lunr__default.default.stopWordFilter).filter((element) => element !== void 0),
453
- {
454
- presence: lunr__default.default.Query.presence.REQUIRED,
455
- fields: [field]
456
- }
457
- );
458
- } else if (Array.isArray(value)) {
459
- this.logger.warn(
460
- `Non-scalar filter value used for field ${field}. Consider using a different Search Engine for better results.`
461
- );
462
- q.term(lunr__default.default.tokenizer(value), {
463
- presence: lunr__default.default.Query.presence.OPTIONAL,
464
- fields: [field]
465
- });
466
- } else {
467
- this.logger.warn(`Unknown filter type used on field ${field}`);
468
- }
469
- });
470
- }
471
- },
472
- documentTypes: types,
473
- pageSize
474
- };
475
- };
476
- setTranslator(translator) {
477
- this.translator = translator;
478
- }
479
- async getIndexer(type) {
480
- const indexer = new LunrSearchEngineIndexer();
481
- const indexerLogger = this.logger.child({ documentType: type });
482
- let errorThrown;
483
- indexer.on("error", (err) => {
484
- errorThrown = err;
485
- });
486
- indexer.on("close", () => {
487
- const newDocuments = indexer.getDocumentStore();
488
- const docStoreExists = this.lunrIndices[type] !== void 0;
489
- const documentsIndexed = Object.keys(newDocuments).length;
490
- if (!errorThrown && documentsIndexed > 0) {
491
- this.lunrIndices[type] = indexer.buildIndex();
492
- this.docStore = { ...this.docStore, ...newDocuments };
493
- } else {
494
- indexerLogger.warn(
495
- `Index for ${type} was not ${docStoreExists ? "replaced" : "created"}: ${errorThrown ? "an error was encountered" : "indexer received 0 documents"}`
496
- );
497
- }
498
- });
499
- return indexer;
500
- }
501
- async query(query) {
502
- const { lunrQueryBuilder, documentTypes, pageSize } = this.translator(
503
- query
504
- );
505
- const results = [];
506
- const indexKeys = Object.keys(this.lunrIndices).filter(
507
- (type) => !documentTypes || documentTypes.includes(type)
508
- );
509
- if (documentTypes?.length && !indexKeys.length) {
510
- throw new MissingIndexError(
511
- `Missing index for ${documentTypes?.toString()}. This could be because the index hasn't been created yet or there was a problem during index creation.`
512
- );
513
- }
514
- indexKeys.forEach((type) => {
515
- try {
516
- results.push(
517
- ...this.lunrIndices[type].query(lunrQueryBuilder).map((result) => {
518
- return {
519
- result,
520
- type
521
- };
522
- })
523
- );
524
- } catch (err) {
525
- if (err instanceof Error && err.message.startsWith("unrecognised field")) {
526
- return;
527
- }
528
- throw err;
529
- }
530
- });
531
- results.sort((doc1, doc2) => {
532
- return doc2.result.score - doc1.result.score;
533
- });
534
- const { page } = decodePageCursor(query.pageCursor);
535
- const offset = page * pageSize;
536
- const hasPreviousPage = page > 0;
537
- const hasNextPage = results.length > offset + pageSize;
538
- const nextPageCursor = hasNextPage ? encodePageCursor({ page: page + 1 }) : void 0;
539
- const previousPageCursor = hasPreviousPage ? encodePageCursor({ page: page - 1 }) : void 0;
540
- const realResultSet = {
541
- results: results.slice(offset, offset + pageSize).map((d, index) => ({
542
- type: d.type,
543
- document: this.docStore[d.result.ref],
544
- rank: page * pageSize + index + 1,
545
- highlight: {
546
- preTag: this.highlightPreTag,
547
- postTag: this.highlightPostTag,
548
- fields: parseHighlightFields({
549
- preTag: this.highlightPreTag,
550
- postTag: this.highlightPostTag,
551
- doc: this.docStore[d.result.ref],
552
- positionMetadata: d.result.matchData.metadata
553
- })
554
- }
555
- })),
556
- numberOfResults: results.length,
557
- nextPageCursor,
558
- previousPageCursor
559
- };
560
- return realResultSet;
561
- }
562
- }
563
- function decodePageCursor(pageCursor) {
564
- if (!pageCursor) {
565
- return { page: 0 };
566
- }
567
- return {
568
- page: Number(Buffer.from(pageCursor, "base64").toString("utf-8"))
569
- };
570
- }
571
- function encodePageCursor({ page }) {
572
- return Buffer.from(`${page}`, "utf-8").toString("base64");
573
- }
574
- function parseHighlightFields({
575
- preTag,
576
- postTag,
577
- doc,
578
- positionMetadata
579
- }) {
580
- const highlightFieldPositions = Object.values(positionMetadata).reduce(
581
- (fieldPositions, metadata) => {
582
- Object.keys(metadata).map((fieldKey) => {
583
- const validFieldMetadataPositions = metadata[fieldKey]?.position?.filter((position) => Array.isArray(position));
584
- if (validFieldMetadataPositions.length) {
585
- fieldPositions[fieldKey] = fieldPositions[fieldKey] ?? [];
586
- fieldPositions[fieldKey].push(...validFieldMetadataPositions);
587
- }
588
- });
589
- return fieldPositions;
590
- },
591
- {}
592
- );
593
- return Object.fromEntries(
594
- Object.entries(highlightFieldPositions).map(([field, positions]) => {
595
- positions.sort((a, b) => b[0] - a[0]);
596
- const highlightedField = positions.reduce((content, pos) => {
597
- return `${String(content).substring(0, pos[0])}${preTag}${String(content).substring(pos[0], pos[0] + pos[1])}${postTag}${String(content).substring(pos[0] + pos[1])}`;
598
- }, doc[field] ?? "");
599
- return [field, highlightedField];
600
- })
601
- );
602
- }
603
-
604
- class TestPipeline {
605
- collator;
606
- decorator;
607
- indexer;
608
- constructor({
609
- collator,
610
- decorator,
611
- indexer
612
- }) {
613
- this.collator = collator;
614
- this.decorator = decorator;
615
- this.indexer = indexer;
616
- }
617
- /**
618
- * Provide the collator, decorator, or indexer to be tested.
619
- *
620
- * @deprecated Use `fromCollator`, `fromDecorator` or `fromIndexer` static
621
- * methods to create a test pipeline instead.
622
- */
623
- static withSubject(subject) {
624
- if (subject instanceof stream.Transform) {
625
- return new TestPipeline({ decorator: subject });
626
- }
627
- if (subject instanceof stream.Writable) {
628
- return new TestPipeline({ indexer: subject });
629
- }
630
- if (subject.readable || subject instanceof stream.Readable) {
631
- return new TestPipeline({ collator: subject });
632
- }
633
- throw new Error(
634
- "Unknown test subject: are you passing a readable, writable, or transform stream?"
635
- );
636
- }
637
- /**
638
- * Create a test pipeline given a collator you want to test.
639
- */
640
- static fromCollator(collator) {
641
- return new TestPipeline({ collator });
642
- }
643
- /**
644
- * Add a collator to the test pipeline.
645
- */
646
- withCollator(collator) {
647
- this.collator = collator;
648
- return this;
649
- }
650
- /**
651
- * Create a test pipeline given a decorator you want to test.
652
- */
653
- static fromDecorator(decorator) {
654
- return new TestPipeline({ decorator });
655
- }
656
- /**
657
- * Add a decorator to the test pipeline.
658
- */
659
- withDecorator(decorator) {
660
- this.decorator = decorator;
661
- return this;
662
- }
663
- /**
664
- * Create a test pipeline given an indexer you want to test.
665
- */
666
- static fromIndexer(indexer) {
667
- return new TestPipeline({ indexer });
668
- }
669
- /**
670
- * Add an indexer to the test pipeline.
671
- */
672
- withIndexer(indexer) {
673
- this.indexer = indexer;
674
- return this;
675
- }
676
- /**
677
- * Provide documents for testing decorators and indexers.
678
- */
679
- withDocuments(documents) {
680
- if (this.collator) {
681
- throw new Error("Cannot provide documents when testing a collator.");
682
- }
683
- this.collator = new stream.Readable({ objectMode: true });
684
- this.collator._read = () => {
685
- };
686
- process.nextTick(() => {
687
- documents.forEach((document) => {
688
- this.collator.push(document);
689
- });
690
- this.collator.push(null);
691
- });
692
- return this;
693
- }
694
- /**
695
- * Execute the test pipeline so that you can make assertions about the result
696
- * or behavior of the given test subject.
697
- */
698
- async execute() {
699
- const documents = [];
700
- if (!this.collator) {
701
- throw new Error(
702
- "Cannot execute pipeline without a collator or documents"
703
- );
704
- }
705
- if (!this.indexer) {
706
- this.indexer = new stream.Writable({ objectMode: true });
707
- this.indexer._write = (document, _, done) => {
708
- documents.push(document);
709
- done();
710
- };
711
- }
712
- return new Promise((done) => {
713
- const pipes = [this.collator];
714
- if (this.decorator) {
715
- pipes.push(this.decorator);
716
- }
717
- pipes.push(this.indexer);
718
- stream.pipeline(pipes, (error) => {
719
- done({
720
- error,
721
- documents
722
- });
723
- });
724
- });
725
- }
726
- }
727
-
728
- exports.BatchSearchEngineIndexer = BatchSearchEngineIndexer;
729
- exports.DecoratorBase = DecoratorBase;
730
- exports.IndexBuilder = IndexBuilder;
731
- exports.LunrSearchEngine = LunrSearchEngine;
732
- exports.MissingIndexError = MissingIndexError;
733
- exports.NewlineDelimitedJsonCollatorFactory = NewlineDelimitedJsonCollatorFactory;
734
- exports.Scheduler = Scheduler;
735
- exports.TestPipeline = TestPipeline;
3
+ var IndexBuilder = require('./IndexBuilder.cjs.js');
4
+ var Scheduler = require('./Scheduler.cjs.js');
5
+ var NewlineDelimitedJsonCollatorFactory = require('./collators/NewlineDelimitedJsonCollatorFactory.cjs.js');
6
+ var LunrSearchEngine = require('./engines/LunrSearchEngine.cjs.js');
7
+ var errors = require('./errors.cjs.js');
8
+ var BatchSearchEngineIndexer = require('./indexing/BatchSearchEngineIndexer.cjs.js');
9
+ var DecoratorBase = require('./indexing/DecoratorBase.cjs.js');
10
+ var TestPipeline = require('./test-utils/TestPipeline.cjs.js');
11
+
12
+
13
+
14
+ exports.IndexBuilder = IndexBuilder.IndexBuilder;
15
+ exports.Scheduler = Scheduler.Scheduler;
16
+ exports.NewlineDelimitedJsonCollatorFactory = NewlineDelimitedJsonCollatorFactory.NewlineDelimitedJsonCollatorFactory;
17
+ exports.LunrSearchEngine = LunrSearchEngine.LunrSearchEngine;
18
+ exports.MissingIndexError = errors.MissingIndexError;
19
+ exports.BatchSearchEngineIndexer = BatchSearchEngineIndexer.BatchSearchEngineIndexer;
20
+ exports.DecoratorBase = DecoratorBase.DecoratorBase;
21
+ exports.TestPipeline = TestPipeline.TestPipeline;
736
22
  //# sourceMappingURL=index.cjs.js.map