New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@internetarchive/search-service

Package Overview
Dependencies
Maintainers
11
Versions
158
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@internetarchive/search-service - npm Package Compare versions

Comparing version 0.0.1-alpha.11 to 0.0.1-alpha.12

.travis.yml

10

dist/index.d.ts
export { Metadata } from './src/models/metadata';
export { File } from './src/models/file';
export { Item } from './src/models/item';
export { DateField, NumberField, DurationField, StringField, BooleanField, } from './src/models/metadata-fields/field-types';
export { NumberParser, StringParser, DateParser, DurationParser, BooleanParser, } from './src/models/metadata-fields/field-parsers';
export { DateField, DateParser, } from './src/models/metadata-fields/field-types/date';
export { NumberField, NumberParser, } from './src/models/metadata-fields/field-types/number';
export { StringField, StringParser, } from './src/models/metadata-fields/field-types/string';
export { BooleanField, BooleanParser, } from './src/models/metadata-fields/field-types/boolean';
export { DurationField, DurationParser, } from './src/models/metadata-fields/field-types/duration';
export { PageProgressionField, PageProgressionParser, PageProgression, } from './src/models/metadata-fields/field-types/page-progression';
export { MetadataField } from './src/models/metadata-fields/metadata-field';

@@ -12,5 +16,5 @@ export { SpeechMusicASREntry } from './src/models/speech-music-asr-entry';

export { SearchResponseParams } from './src/responses/search/search-response-params';
export { DefaultSearchBackend } from './src/default-search-backend';
export { DefaultSearchBackend } from './src/search-backend/default-search-backend';
export { SearchServiceInterface } from './src/search-service-interface';
export { SearchService } from './src/search-service';
export { SearchParams } from './src/search-params';
export { Metadata } from './src/models/metadata';
export { File } from './src/models/file';
export { Item } from './src/models/item';
export { DateField, NumberField, DurationField, StringField, BooleanField, } from './src/models/metadata-fields/field-types';
export { NumberParser, StringParser, DateParser, DurationParser, BooleanParser, } from './src/models/metadata-fields/field-parsers';
export { DateField, DateParser, } from './src/models/metadata-fields/field-types/date';
export { NumberField, NumberParser, } from './src/models/metadata-fields/field-types/number';
export { StringField, StringParser, } from './src/models/metadata-fields/field-types/string';
export { BooleanField, BooleanParser, } from './src/models/metadata-fields/field-types/boolean';
export { DurationField, DurationParser, } from './src/models/metadata-fields/field-types/duration';
export { PageProgressionField, PageProgressionParser, PageProgression, } from './src/models/metadata-fields/field-types/page-progression';
export { MetadataField } from './src/models/metadata-fields/metadata-field';

@@ -12,5 +16,5 @@ export { SpeechMusicASREntry } from './src/models/speech-music-asr-entry';

export { SearchResponseParams } from './src/responses/search/search-response-params';
export { DefaultSearchBackend } from './src/default-search-backend';
export { DefaultSearchBackend } from './src/search-backend/default-search-backend';
export { SearchService } from './src/search-service';
export { SearchParams } from './src/search-params';
//# sourceMappingURL=index.js.map

@@ -1,2 +0,2 @@

import { DurationField, NumberField } from './metadata-fields/field-types';
import { Duration } from './metadata-fields/field-types/duration';
/**

@@ -18,8 +18,8 @@ * This represents an Internet Archive File

original?: string;
size?: NumberField;
size?: number;
title?: string;
length?: DurationField;
height?: NumberField;
width?: NumberField;
track?: NumberField;
length?: Duration;
height?: number;
width?: number;
track?: number;
external_identifier?: string;

@@ -26,0 +26,0 @@ creator?: string;

/* eslint-disable @typescript-eslint/camelcase */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { DurationField, NumberField } from './metadata-fields/field-types';
import { DurationParser, } from './metadata-fields/field-types/duration';
import { NumberParser } from './metadata-fields/field-types/number';
/**

@@ -21,8 +22,18 @@ * This represents an Internet Archive File

this.original = json.original;
this.size = json.size ? new NumberField(json.size) : undefined;
this.title = json.title;
this.length = json.length ? new DurationField(json.length) : undefined;
this.height = json.height ? new NumberField(json.height) : undefined;
this.width = json.width ? new NumberField(json.width) : undefined;
this.track = json.track ? new NumberField(json.track) : undefined;
this.length = json.length
? DurationParser.shared.parseValue(json.length)
: undefined;
this.size = json.size
? NumberParser.shared.parseValue(json.size)
: undefined;
this.height = json.height
? NumberParser.shared.parseValue(json.height)
: undefined;
this.width = json.width
? NumberParser.shared.parseValue(json.width)
: undefined;
this.track = json.track
? NumberParser.shared.parseValue(json.track)
: undefined;
this.external_identifier = json['external-identifier'];

@@ -29,0 +40,0 @@ this.creator = json.creator;

@@ -1,17 +0,1 @@

import { MetadataField } from './metadata-field';
import { Duration, DateParser, DurationParser, NumberParser, StringParser, BooleanParser } from './field-parsers';
export declare class DateField extends MetadataField<Date, DateParser> {
constructor(rawValue: any);
}
export declare class DurationField extends MetadataField<Duration, DurationParser> {
constructor(rawValue: any);
}
export declare class NumberField extends MetadataField<number, NumberParser> {
constructor(rawValue: any);
}
export declare class StringField extends MetadataField<string, StringParser> {
constructor(rawValue: any);
}
export declare class BooleanField extends MetadataField<boolean, BooleanParser> {
constructor(rawValue: any);
}
export declare type PageProgression = 'rl' | 'lr';

@@ -1,36 +0,2 @@

/* eslint-disable @typescript-eslint/no-explicit-any */
import { MetadataField } from './metadata-field';
import { DateParser, DurationParser, NumberParser, StringParser, BooleanParser, } from './field-parsers';
class SharedParsers {
}
SharedParsers.dateParser = new DateParser();
SharedParsers.durationParser = new DurationParser();
SharedParsers.numberParser = new NumberParser();
SharedParsers.stringParser = new StringParser();
SharedParsers.booleanParser = new BooleanParser();
export class DateField extends MetadataField {
constructor(rawValue) {
super(SharedParsers.dateParser, rawValue);
}
}
export class DurationField extends MetadataField {
constructor(rawValue) {
super(SharedParsers.durationParser, rawValue);
}
}
export class NumberField extends MetadataField {
constructor(rawValue) {
super(SharedParsers.numberParser, rawValue);
}
}
export class StringField extends MetadataField {
constructor(rawValue) {
super(SharedParsers.stringParser, rawValue);
}
}
export class BooleanField extends MetadataField {
constructor(rawValue) {
super(SharedParsers.booleanParser, rawValue);
}
}
export {};
//# sourceMappingURL=field-types.js.map

@@ -1,6 +0,7 @@

import { FieldParserInterface } from './field-parsers';
import { FieldParserInterface } from './field-parser-interface';
/**
* The MetadataField is responsible for three things:
* 1. Take in some raw data (strings, arrays, numbers, etc)
* 2. Normalize the input to an array of the input, ([string, string], [number, number], etc)
* 2. Normalize the input to an array of the input,
* ie. [string, string], [number, number], [Date, Date], etc
* 3. Cast the values to their expected `Type`

@@ -32,3 +33,3 @@ *

*/
rawValue?: any;
rawValue?: string[] | string;
/**

@@ -35,0 +36,0 @@ * The array of all values for the field.

/**
* The MetadataField is responsible for three things:
* 1. Take in some raw data (strings, arrays, numbers, etc)
* 2. Normalize the input to an array of the input, ([string, string], [number, number], etc)
* 2. Normalize the input to an array of the input,
* ie. [string, string], [number, number], [Date, Date], etc
* 3. Cast the values to their expected `Type`

@@ -6,0 +7,0 @@ *

@@ -1,2 +0,7 @@

import { DateField, StringField, NumberField, DurationField, BooleanField } from './metadata-fields/field-types';
import { BooleanField } from './metadata-fields/field-types/boolean';
import { DateField } from './metadata-fields/field-types/date';
import { DurationField } from './metadata-fields/field-types/duration';
import { NumberField } from './metadata-fields/field-types/number';
import { StringField } from './metadata-fields/field-types/string';
import { PageProgressionField } from './metadata-fields/field-types/page-progression';
/**

@@ -19,5 +24,7 @@ * Metadata is an expansive model that describes an Item.

*/
rawMetadata?: any;
identifier: string;
addeddate: DateField;
rawMetadata?: {
[key: string]: any;
};
identifier?: string;
addeddate?: DateField;
audio_codec?: StringField;

@@ -31,5 +38,5 @@ audio_sample_rate?: NumberField;

description?: StringField;
downloads: NumberField;
downloads?: NumberField;
duration?: DurationField;
indexdate: DateField;
indexdate?: DateField;
language?: StringField;

@@ -42,3 +49,4 @@ length?: DurationField;

num_reviews?: NumberField;
publicdate: DateField;
page_progression?: PageProgressionField;
publicdate?: DateField;
runtime?: DurationField;

@@ -45,0 +53,0 @@ scanner?: StringField;

/* eslint-disable @typescript-eslint/camelcase */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable camelcase */
import { DateField, StringField, NumberField, DurationField, BooleanField, } from './metadata-fields/field-types';
import { BooleanField } from './metadata-fields/field-types/boolean';
import { DateField } from './metadata-fields/field-types/date';
import { DurationField } from './metadata-fields/field-types/duration';
import { NumberField } from './metadata-fields/field-types/number';
import { StringField } from './metadata-fields/field-types/string';
/**

@@ -19,5 +23,7 @@ * Metadata is an expansive model that describes an Item.

this.identifier = json.identifier;
this.addeddate = new DateField(json.addeddate);
this.publicdate = new DateField(json.publicdate);
this.indexdate = new DateField(json.indexdate);
this.addeddate = json.addeddate ? new DateField(json.addeddate) : undefined;
this.publicdate = json.publicdate
? new DateField(json.publicdate)
: undefined;
this.indexdate = json.indexdate ? new DateField(json.indexdate) : undefined;
this.audio_codec = json.audio_codec

@@ -41,3 +47,5 @@ ? new StringField(json.audio_codec)

: undefined;
this.downloads = new NumberField(json.downloads);
this.downloads = json.downloads
? new NumberField(json.downloads)
: undefined;
this.duration = json.duration

@@ -44,0 +52,0 @@ ? new DurationField(json.duration)

export declare class Review {
reviewbody?: string;
reviewtitle?: string;
reviewer: string;
reviewdate: Date;
createdate: Date;
stars: number;
reviewer?: string;
reviewdate?: Date;
createdate?: Date;
stars?: number;
constructor(json: any);
}

@@ -0,1 +1,2 @@

import { DateParser } from './metadata-fields/field-types/date';
/* eslint-disable @typescript-eslint/no-explicit-any */

@@ -7,7 +8,7 @@ export class Review {

this.reviewer = json.reviewer;
this.reviewdate = new Date(json.reviewdate);
this.createdate = new Date(json.createdate);
this.stars = parseFloat(json.stars);
this.reviewdate = DateParser.shared.parseValue(json.reviewdate);
this.createdate = DateParser.shared.parseValue(json.createdate);
this.stars = json.stars ? parseFloat(json.stars) : undefined;
}
}
//# sourceMappingURL=review.js.map
/**
* The Result is a container for a response.
*
* It contains an optional success result which is generic and can be anything depending on
* the context, or an Error or subclass of an error.
* It contains an optional success result which is generic
* and can be anything depending on the context,
* or an Error or subclass of an error.
*
* This allows us to return rich, typed errors instead of
* an untyped Promise rejection.
*
* This is modeled after Swift's Result type:

@@ -8,0 +12,0 @@ * https://developer.apple.com/documentation/swift/result

/**
* The Result is a container for a response.
*
* It contains an optional success result which is generic and can be anything depending on
* the context, or an Error or subclass of an error.
* It contains an optional success result which is generic
* and can be anything depending on the context,
* or an Error or subclass of an error.
*
* This allows us to return rich, typed errors instead of
* an untyped Promise rejection.
*
* This is modeled after Swift's Result type:

@@ -8,0 +12,0 @@ * https://developer.apple.com/documentation/swift/result

@@ -6,6 +6,6 @@ export declare class SearchResponseParams {

wt: string;
sort: string;
rows: string;
sort?: string;
rows?: string;
start: number;
constructor(json: any);
}

@@ -16,3 +16,5 @@ import { SearchResponseHeader } from './search-response-header';

*/
rawResponse: any;
rawResponse: {
[key: string]: any;
};
/**

@@ -19,0 +21,0 @@ * The resonse header

@@ -0,1 +1,11 @@

export declare enum SortDirection {
Asc = "asc",
Desc = "desc"
}
export declare class SortParam {
field: string;
direction: SortDirection;
constructor(field: string, direction: SortDirection);
get asString(): string;
}
/**

@@ -5,3 +15,3 @@ * SearchParams provides an encapsulation to all of the search parameters

*
* It also provides an `asUrlSearchParams` helper method for converting the
* It also provides an `asUrlSearchParams` method for converting the
* parameters to an IA-style query string. ie. it converts the `fields` array

@@ -12,7 +22,13 @@ * to `fl[]=identifier&fl[]=collection` and `sort` to `sort[]=date+desc&sort[]=downloads+asc`

query: string;
sort: string[];
rows: number;
start: number;
fields: string[];
constructor(query: string, sort?: string[], rows?: number, start?: number, fields?: string[]);
sort?: SortParam[];
rows?: number;
page?: number;
fields?: string[];
constructor(options: {
query: string;
sort?: SortParam[];
rows?: number;
page?: number;
fields?: string[];
});
/**

@@ -19,0 +35,0 @@ * Return a URLSearchParams representation of the parameters for use in network requests.

@@ -0,1 +1,15 @@

export var SortDirection;
(function (SortDirection) {
SortDirection["Asc"] = "asc";
SortDirection["Desc"] = "desc";
})(SortDirection || (SortDirection = {}));
export class SortParam {
constructor(field, direction) {
this.field = field;
this.direction = direction;
}
get asString() {
return `${this.field} ${this.direction}`;
}
}
/**

@@ -5,3 +19,3 @@ * SearchParams provides an encapsulation to all of the search parameters

*
* It also provides an `asUrlSearchParams` helper method for converting the
* It also provides an `asUrlSearchParams` method for converting the
* parameters to an IA-style query string. ie. it converts the `fields` array

@@ -11,8 +25,8 @@ * to `fl[]=identifier&fl[]=collection` and `sort` to `sort[]=date+desc&sort[]=downloads+asc`

export class SearchParams {
constructor(query, sort = [], rows = 25, start = 0, fields = ['identifier']) {
this.query = query;
this.sort = sort;
this.rows = rows;
this.start = start;
this.fields = fields;
constructor(options) {
this.query = options.query;
this.sort = options.sort;
this.rows = options.rows;
this.page = options.page;
this.fields = options.fields;
}

@@ -27,12 +41,17 @@ /**

get asUrlSearchParams() {
var _a, _b;
const params = new URLSearchParams();
params.append('q', this.query);
params.append('rows', String(this.rows));
params.append('page', String(this.start));
params.append('output', 'json');
this.fields.forEach(field => {
if (this.rows) {
params.append('rows', String(this.rows));
}
if (this.page) {
params.append('page', String(this.page));
}
(_a = this.fields) === null || _a === void 0 ? void 0 : _a.forEach(field => {
params.append('fl[]', field);
});
this.sort.forEach(sort => {
params.append('sort[]', sort);
(_b = this.sort) === null || _b === void 0 ? void 0 : _b.forEach(sort => {
params.append('sort[]', `${sort.asString}`);
});

@@ -39,0 +58,0 @@ return params;

@@ -7,4 +7,18 @@ import { MetadataResponse } from './responses/metadata/metadata-response';

export interface SearchServiceInterface {
/**
* Perform a search for given search params
*
* @param {SearchParams} params
* @returns {Promise<Result<SearchResponse, SearchServiceError>>}
* @memberof SearchServiceInterface
*/
search(params: SearchParams): Promise<Result<SearchResponse, SearchServiceError>>;
/**
* Fetch metadata for a given identifier
*
* @param {string} identifier
* @returns {Promise<Result<MetadataResponse, SearchServiceError>>}
* @memberof SearchServiceInterface
*/
fetchMetadata(identifier: string): Promise<Result<MetadataResponse, SearchServiceError>>;
}
import { SearchResponse } from './responses/search/search-response';
import { SearchBackendInterface } from './search-backend-interface';
import { SearchParams } from './search-params';

@@ -8,2 +7,3 @@ import { MetadataResponse } from './responses/metadata/metadata-response';

import { SearchServiceInterface } from './search-service-interface';
import { SearchBackendInterface } from './search-backend/search-backend-interface';
/**

@@ -10,0 +10,0 @@ * The Search Service is responsible for taking the raw response provided by

import { SearchResponse } from './responses/search/search-response';
import { MetadataResponse } from './responses/metadata/metadata-response';
import { DefaultSearchBackend } from './default-search-backend';
import { DefaultSearchBackend } from './search-backend/default-search-backend';
import { Result } from './responses/result';

@@ -5,0 +5,0 @@ import { SearchServiceError, SearchServiceErrorType, } from './search-service-error';

@@ -5,4 +5,4 @@ /* eslint-disable @typescript-eslint/no-unused-vars */

import { SearchParams } from '../src/search-params';
import { SearchServiceErrorType, } from '../src/search-service-error';
import { DefaultSearchBackend } from '../src/default-search-backend';
import { SearchServiceErrorType } from '../src/search-service-error';
import { DefaultSearchBackend } from '../src/search-backend/default-search-backend';
describe('DefaultSearchBackend', () => {

@@ -33,3 +33,3 @@ it('can fetch metadata', async () => {

const backend = new DefaultSearchBackend();
const params = new SearchParams('foo');
const params = new SearchParams({ query: 'foo' });
const result = await backend.performSearch(params);

@@ -36,0 +36,0 @@ expect((_a = result.success) === null || _a === void 0 ? void 0 : _a.foo).to.equal('bar');

@@ -0,4 +1,6 @@

import { SearchResponse } from '../src/responses/search/search-response';
import { SearchParams } from '../src/search-params';
export declare class MockResponseGenerator {
generateMockSearchResponse(params: any): any;
generateMockSearchResponse(params: SearchParams): SearchResponse;
generateMockMetadataResponse(identifier: any): any;
}

@@ -5,4 +5,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

generateMockSearchResponse(params) {
const fieldsAsString = params.fields.join(',');
var _a, _b, _c;
const fieldsAsString = (_a = params.fields) === null || _a === void 0 ? void 0 : _a.join(',');
return {
rawResponse: {
foo: 'bar',
},
responseHeader: {

@@ -14,7 +18,11 @@ status: 0,

qin: params.query,
fields: fieldsAsString,
fields: fieldsAsString !== null && fieldsAsString !== void 0 ? fieldsAsString : '',
wt: 'json',
sort: params.sort,
rows: params.rows,
start: params.start,
sort: (_b = params.sort) === null || _b === void 0 ? void 0 : _b.reduce((prev, current, index) => {
const isFirst = index === 0;
const commaPrefix = isFirst ? '' : ', ';
return `${prev}${commaPrefix}${current.field} ${current.direction}`;
}, ''),
rows: params.rows ? `${params.rows}` : '',
start: (_c = params.page) !== null && _c !== void 0 ? _c : 0,
},

@@ -25,3 +33,8 @@ },

start: 0,
docs: [{ identifier: 'foo' }, { identifier: 'bar' }],
docs: [
{
identifier: 'foo',
},
{ identifier: 'bar' },
],
},

@@ -28,0 +41,0 @@ };

import { expect } from '@open-wc/testing';
import { SearchParams } from '../src/search-params';
import { SearchParams, SortDirection, SortParam } from '../src/search-params';
describe('SearchParams', () => {
it('can be instantiated with just a query', async () => {
const query = 'title:foo AND collection:bar';
const params = new SearchParams(query);
const params = new SearchParams({ query });
expect(params.query).to.equal(query);

@@ -12,3 +12,6 @@ });

const fields = ['identifier', 'foo', 'bar'];
const params = new SearchParams(query, undefined, undefined, undefined, fields);
const params = new SearchParams({
query,
fields,
});
expect(params.fields).to.deep.equal(fields);

@@ -23,6 +26,6 @@ });

const query = 'title:foo AND collection:bar';
const params = new SearchParams(query);
const params = new SearchParams({ query });
const urlSearchParam = params.asUrlSearchParams;
const queryAsString = urlSearchParam.toString();
const expected = 'q=title%3Afoo+AND+collection%3Abar&rows=25&page=0&output=json&fl%5B%5D=identifier';
const expected = 'q=title%3Afoo+AND+collection%3Abar&output=json';
expect(queryAsString).to.equal(expected);

@@ -33,6 +36,9 @@ });

const fields = ['identifier', 'foo', 'bar'];
const params = new SearchParams(query, undefined, undefined, undefined, fields);
const params = new SearchParams({
query,
fields,
});
const urlSearchParam = params.asUrlSearchParams;
const queryAsString = urlSearchParam.toString();
const expected = 'q=title%3Afoo+AND+collection%3Abar&rows=25&page=0&output=json&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar';
const expected = 'q=title%3Afoo+AND+collection%3Abar&output=json&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar';
expect(queryAsString).to.equal(expected);

@@ -43,7 +49,13 @@ });

const fields = ['identifier', 'foo', 'bar'];
const sort = ['downloads desc'];
const params = new SearchParams(query, sort, 53, 27, fields);
const sort = [new SortParam('downloads', SortDirection.Desc)];
const params = new SearchParams({
query,
sort,
rows: 53,
page: 27,
fields,
});
const urlSearchParam = params.asUrlSearchParams;
const queryAsString = urlSearchParam.toString();
const expected = 'q=title%3Afoo+AND+collection%3Abar&rows=53&page=27&output=json&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar&sort%5B%5D=downloads+desc';
const expected = 'q=title%3Afoo+AND+collection%3Abar&output=json&rows=53&page=27&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar&sort%5B%5D=downloads+desc';
expect(queryAsString).to.equal(expected);

@@ -50,0 +62,0 @@ });

@@ -23,3 +23,3 @@ /* eslint-disable @typescript-eslint/no-unused-vars */

const query = 'title:foo AND collection:bar';
const params = new SearchParams(query);
const params = new SearchParams({ query });
const backend = new MockSearchBackend();

@@ -83,3 +83,3 @@ const service = new SearchService(backend);

expect((_b = metadataResult.error) === null || _b === void 0 ? void 0 : _b.message).to.equal('network error');
const params = new SearchParams('boop');
const params = new SearchParams({ query: 'boop' });
const searchResult = await service.search(params);

@@ -108,3 +108,3 @@ expect(searchResult.error).to.not.equal(undefined);

expect((_b = metadataResult.error) === null || _b === void 0 ? void 0 : _b.message).to.equal('decoding error');
const params = new SearchParams('boop');
const params = new SearchParams({ query: 'boop' });
const searchResult = await service.search(params);

@@ -111,0 +111,0 @@ expect(searchResult.error).to.not.equal(undefined);

@@ -7,16 +7,31 @@ export { Metadata } from './src/models/metadata';

DateField,
DateParser,
} from './src/models/metadata-fields/field-types/date';
export {
NumberField,
DurationField,
NumberParser,
} from './src/models/metadata-fields/field-types/number';
export {
StringField,
StringParser,
} from './src/models/metadata-fields/field-types/string';
export {
BooleanField,
} from './src/models/metadata-fields/field-types';
BooleanParser,
} from './src/models/metadata-fields/field-types/boolean';
export {
NumberParser,
StringParser,
DateParser,
DurationField,
DurationParser,
BooleanParser,
} from './src/models/metadata-fields/field-parsers';
} from './src/models/metadata-fields/field-types/duration';
export {
PageProgressionField,
PageProgressionParser,
PageProgression,
} from './src/models/metadata-fields/field-types/page-progression';
export { MetadataField } from './src/models/metadata-fields/metadata-field';

@@ -31,5 +46,5 @@

export { DefaultSearchBackend } from './src/default-search-backend';
export { DefaultSearchBackend } from './src/search-backend/default-search-backend';
export { SearchServiceInterface } from './src/search-service-interface';
export { SearchService } from './src/search-service';
export { SearchParams } from './src/search-params';
{
"name": "@internetarchive/search-service",
"version": "0.0.1-alpha.11",
"version": "0.0.1-alpha.12",
"description": "A search service for the Internet Archive",

@@ -40,2 +40,3 @@ "license": "AGPL-3.0-only",

"lint-staged": "^10.0.0",
"lit-element": "^2.4.0",
"lit-html": "^1.3.0",

@@ -42,0 +43,0 @@ "madge": "^3.12.0",

@@ -8,3 +8,3 @@ @@ -1,117 +0,0 @@

```bash
yarn add @internetarchive/ia-search-service
npm install @internetarchive/search-service
```

@@ -14,65 +14,29 @@

### 1. Implement a SearchBackend
In order to abstract the network connectivity layer and the environment-specific configuration from the result handling, the consumer should implement a search backend that implements the `SearchBackendInterface`.
There are just two methods, one for fetching search results and the other for fetching metadata. They both return a `Promise` that should contain JSON that matches a `SearchResponse` object or a `MetadataResponse` object.
#### Example Search Backend
### Searching
```ts
// search-backend.ts
import { SearchBackendInterface, SearchResponse, MetadataResponse } from '@internetarchive/ia-search-service';
import { SearchService } from '@internetarchive/search-service';
export class SearchBackend implements SearchBackendInterface {
constructor(baseUrl = 'archive.org') {
this.baseUrl = baseUrl;
}
const searchService = SearchService.default;
const params = new SearchParams({
query: 'collection:books AND title:(little bunny foo foo)',
sort: 'downloads desc',
rows: 25,
start: 0,
fields: ['identifier', 'collection', 'title', 'creator']
});
async performSearch(params): Promise<SearchResponse> {
const urlSearchParam = params.asUrlSearchParams;
const queryAsString = urlSearchParam.toString();
const url = `https://${this.baseUrl}/advancedsearch.php?${queryAsString}`;
const response = await fetch(url);
const json = await response.json();
return new Promise(resolve => resolve(json));
}
async fetchMetadata(identifier): Promise<MetadataResponse> {
const url = `https://${this.baseUrl}/metadata/${identifier}`;
const response = await fetch(url);
const json = await response.json();
return new Promise(resolve => resolve(json));
}
const result = await searchService.performSearch(params);
if (result.success) {
const searchResponse = result.success;
searchResponse.response.numFound // => number
searchResponse.response.docs // => Metadata[] array
searchResponse.response.docs[0].identifier // => 'identifier-foo'
}
```
### 2. Make Search Requests
### Fetch Metadata
Using the backend you created above, instantiate a `SearchService` with it, craft some `SearchParams`, and perform your search:
#### Example
```ts
// search-consumer.ts
import { SearchService, SearchParams, SearchResponse } from '@internetarchive/ia-search-service';
import { SearchBackend } from './search-backend';
const metadataResponse: MetadataResponse = await searchService.fetchMetadata('some-identifier');
...
const searchBackend = new SearchBackend();
const searchService = new SearchService(searchBackend);
const params = new SearchParams('collection:books AND title:(little bunny foo foo)');
params.sort = 'downloads desc';
params.rows = 33;
params.start = 0;
params.fields = ['identifier', 'collection', 'creator', 'downloads'];
const searchResponse: SearchResponse = await searchService.performSearch(params);
searchResponse.response.numFound // => number
searchResponse.response.docs // => Metadata[] array
searchResponse.response.docs[0].identifier // => 'identifier-foo'
const metadataResponse: MetadataResponse = await searchService.fetchMetadata('some-identifier');
metadataResponse.metadata.identifier // => 'some-identifier'

@@ -97,3 +61,3 @@ metadataResponse.metadata.collection.value // => 'some-collection'

metadata.collection.value // return just the first item of the `values` array, ie. 'my-collection'
metadata.collection.value // returns all values of the array, ie. ['my-collection', 'other-collection']
metadata.collection.values // returns all values of the array, ie. ['my-collection', 'other-collection']
metadata.collection.rawValue // return the rawValue. This is useful for inspecting the raw response received.

@@ -110,3 +74,3 @@

```bash
yarn install
npm install
```

@@ -116,3 +80,3 @@

```bash
yarn test
npm run test
```

@@ -122,3 +86,3 @@

```bash
yarn lint
npm run formatting
```
/* eslint-disable @typescript-eslint/camelcase */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { DurationField, NumberField } from './metadata-fields/field-types';
import {
Duration,
DurationParser,
} from './metadata-fields/field-types/duration';
import { NumberParser } from './metadata-fields/field-types/number';

@@ -31,13 +35,13 @@ /**

size?: NumberField;
size?: number;
title?: string;
length?: DurationField;
length?: Duration;
height?: NumberField;
height?: number;
width?: NumberField;
width?: number;
track?: NumberField;
track?: number;

@@ -60,8 +64,18 @@ external_identifier?: string;

this.original = json.original;
this.size = json.size ? new NumberField(json.size) : undefined;
this.title = json.title;
this.length = json.length ? new DurationField(json.length) : undefined;
this.height = json.height ? new NumberField(json.height) : undefined;
this.width = json.width ? new NumberField(json.width) : undefined;
this.track = json.track ? new NumberField(json.track) : undefined;
this.length = json.length
? DurationParser.shared.parseValue(json.length)
: undefined;
this.size = json.size
? NumberParser.shared.parseValue(json.size)
: undefined;
this.height = json.height
? NumberParser.shared.parseValue(json.height)
: undefined;
this.width = json.width
? NumberParser.shared.parseValue(json.width)
: undefined;
this.track = json.track
? NumberParser.shared.parseValue(json.track)
: undefined;
this.external_identifier = json['external-identifier'];

@@ -68,0 +82,0 @@ this.creator = json.creator;

/* eslint-disable @typescript-eslint/no-explicit-any */
import { FieldParserInterface } from './field-parsers';
import { FieldParserInterface } from './field-parser-interface';

@@ -7,3 +7,4 @@ /**

* 1. Take in some raw data (strings, arrays, numbers, etc)
* 2. Normalize the input to an array of the input, ([string, string], [number, number], etc)
* 2. Normalize the input to an array of the input,
* ie. [string, string], [number, number], [Date, Date], etc
* 3. Cast the values to their expected `Type`

@@ -38,3 +39,3 @@ *

*/
rawValue?: any;
rawValue?: string[] | string;

@@ -41,0 +42,0 @@ /**

/* eslint-disable @typescript-eslint/camelcase */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable camelcase */
import {
DateField,
StringField,
NumberField,
DurationField,
BooleanField,
} from './metadata-fields/field-types';
import { BooleanField } from './metadata-fields/field-types/boolean';
import { DateField } from './metadata-fields/field-types/date';
import { DurationField } from './metadata-fields/field-types/duration';
import { NumberField } from './metadata-fields/field-types/number';
import { StringField } from './metadata-fields/field-types/string';
import { PageProgressionField } from './metadata-fields/field-types/page-progression';
/**

@@ -29,7 +29,7 @@ * Metadata is an expansive model that describes an Item.

*/
rawMetadata?: any;
rawMetadata?: { [key: string]: any };
identifier: string;
identifier?: string;
addeddate: DateField;
addeddate?: DateField;

@@ -52,7 +52,7 @@ audio_codec?: StringField;

downloads: NumberField;
downloads?: NumberField;
duration?: DurationField;
indexdate: DateField;
indexdate?: DateField;

@@ -73,4 +73,6 @@ language?: StringField;

publicdate: DateField;
page_progression?: PageProgressionField;
publicdate?: DateField;
runtime?: DurationField;

@@ -112,5 +114,7 @@

this.addeddate = new DateField(json.addeddate);
this.publicdate = new DateField(json.publicdate);
this.indexdate = new DateField(json.indexdate);
this.addeddate = json.addeddate ? new DateField(json.addeddate) : undefined;
this.publicdate = json.publicdate
? new DateField(json.publicdate)
: undefined;
this.indexdate = json.indexdate ? new DateField(json.indexdate) : undefined;

@@ -135,3 +139,5 @@ this.audio_codec = json.audio_codec

: undefined;
this.downloads = new NumberField(json.downloads);
this.downloads = json.downloads
? new NumberField(json.downloads)
: undefined;
this.duration = json.duration

@@ -138,0 +144,0 @@ ? new DurationField(json.duration)

@@ -0,1 +1,3 @@

import { DateParser } from './metadata-fields/field-types/date';
/* eslint-disable @typescript-eslint/no-explicit-any */

@@ -7,9 +9,9 @@ export class Review {

reviewer: string;
reviewer?: string;
reviewdate: Date;
reviewdate?: Date;
createdate: Date;
createdate?: Date;
stars: number;
stars?: number;

@@ -20,6 +22,6 @@ constructor(json: any) {

this.reviewer = json.reviewer;
this.reviewdate = new Date(json.reviewdate);
this.createdate = new Date(json.createdate);
this.stars = parseFloat(json.stars);
this.reviewdate = DateParser.shared.parseValue(json.reviewdate);
this.createdate = DateParser.shared.parseValue(json.createdate);
this.stars = json.stars ? parseFloat(json.stars) : undefined;
}
}
/**
* The Result is a container for a response.
*
* It contains an optional success result which is generic and can be anything depending on
* the context, or an Error or subclass of an error.
* It contains an optional success result which is generic
* and can be anything depending on the context,
* or an Error or subclass of an error.
*
* This allows us to return rich, typed errors instead of
* an untyped Promise rejection.
*
* This is modeled after Swift's Result type:

@@ -8,0 +12,0 @@ * https://developer.apple.com/documentation/swift/result

@@ -11,5 +11,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

sort: string;
sort?: string;
rows: string;
rows?: string;

@@ -16,0 +16,0 @@ start: number;

@@ -18,3 +18,3 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

*/
rawResponse: any;
rawResponse: { [key: string]: any };

@@ -21,0 +21,0 @@ /**

@@ -0,1 +1,20 @@

export enum SortDirection {
Asc = 'asc',
Desc = 'desc',
}
export class SortParam {
field: string;
direction: SortDirection;
constructor(field: string, direction: SortDirection) {
this.field = field;
this.direction = direction;
}
get asString(): string {
return `${this.field} ${this.direction}`;
}
}
/**

@@ -5,3 +24,3 @@ * SearchParams provides an encapsulation to all of the search parameters

*
* It also provides an `asUrlSearchParams` helper method for converting the
* It also provides an `asUrlSearchParams` method for converting the
* parameters to an IA-style query string. ie. it converts the `fields` array

@@ -13,22 +32,22 @@ * to `fl[]=identifier&fl[]=collection` and `sort` to `sort[]=date+desc&sort[]=downloads+asc`

sort: string[];
sort?: SortParam[];
rows: number;
rows?: number;
start: number;
page?: number;
fields: string[];
fields?: string[];
constructor(
query: string,
sort: string[] = [],
rows = 25,
start = 0,
fields: string[] = ['identifier']
) {
this.query = query;
this.sort = sort;
this.rows = rows;
this.start = start;
this.fields = fields;
constructor(options: {
query: string;
sort?: SortParam[];
rows?: number;
page?: number;
fields?: string[];
}) {
this.query = options.query;
this.sort = options.sort;
this.rows = options.rows;
this.page = options.page;
this.fields = options.fields;
}

@@ -46,12 +65,15 @@

params.append('q', this.query);
params.append('rows', String(this.rows));
params.append('page', String(this.start));
params.append('output', 'json');
this.fields.forEach(field => {
if (this.rows) {
params.append('rows', String(this.rows));
}
if (this.page) {
params.append('page', String(this.page));
}
this.fields?.forEach(field => {
params.append('fl[]', field);
});
this.sort.forEach(sort => {
params.append('sort[]', sort);
this.sort?.forEach(sort => {
params.append('sort[]', `${sort.asString}`);
});

@@ -58,0 +80,0 @@

@@ -8,5 +8,20 @@ import { MetadataResponse } from './responses/metadata/metadata-response';

export interface SearchServiceInterface {
/**
* Perform a search for given search params
*
* @param {SearchParams} params
* @returns {Promise<Result<SearchResponse, SearchServiceError>>}
* @memberof SearchServiceInterface
*/
search(
params: SearchParams
): Promise<Result<SearchResponse, SearchServiceError>>;
/**
* Fetch metadata for a given identifier
*
* @param {string} identifier
* @returns {Promise<Result<MetadataResponse, SearchServiceError>>}
* @memberof SearchServiceInterface
*/
fetchMetadata(

@@ -13,0 +28,0 @@ identifier: string

import { SearchResponse } from './responses/search/search-response';
import { SearchBackendInterface } from './search-backend-interface';
import { SearchParams } from './search-params';
import { MetadataResponse } from './responses/metadata/metadata-response';
import { DefaultSearchBackend } from './default-search-backend';
import { DefaultSearchBackend } from './search-backend/default-search-backend';
import { Result } from './responses/result';

@@ -12,2 +11,3 @@ import {

import { SearchServiceInterface } from './search-service-interface';
import { SearchBackendInterface } from './search-backend/search-backend-interface';

@@ -14,0 +14,0 @@ /**

@@ -5,15 +5,6 @@ /* eslint-disable @typescript-eslint/no-unused-vars */

import { SearchService } from '../src/search-service';
import { SearchParams } from '../src/search-params';
import { MockResponseGenerator } from './mock-response-generator';
import { SearchBackendInterface } from '../src/search-backend-interface';
import { SearchResponse } from '../src/responses/search/search-response';
import { MetadataResponse } from '../src/responses/metadata/metadata-response';
import { Result } from '../src/responses/result';
import {
SearchServiceError,
SearchServiceErrorType,
} from '../src/search-service-error';
import { DefaultSearchBackend } from '../src/default-search-backend';
import { SearchServiceErrorType } from '../src/search-service-error';
import { DefaultSearchBackend } from '../src/search-backend/default-search-backend';

@@ -46,3 +37,3 @@ describe('DefaultSearchBackend', () => {

const backend = new DefaultSearchBackend();
const params = new SearchParams('foo');
const params = new SearchParams({ query: 'foo' });
const result = await backend.performSearch(params);

@@ -49,0 +40,0 @@ expect(result.success?.foo).to.equal('bar');

/* eslint-disable @typescript-eslint/no-explicit-any */
import { SearchResponse } from '../src/responses/search/search-response';
import { SearchParams } from '../src/search-params';
/* eslint-disable @typescript-eslint/camelcase */
export class MockResponseGenerator {
generateMockSearchResponse(params: any): any {
const fieldsAsString = params.fields.join(',');
generateMockSearchResponse(params: SearchParams): SearchResponse {
const fieldsAsString = params.fields?.join(',');
return {
rawResponse: {
foo: 'bar',
},
responseHeader: {

@@ -14,7 +21,11 @@ status: 0,

qin: params.query,
fields: fieldsAsString,
fields: fieldsAsString ?? '',
wt: 'json',
sort: params.sort,
rows: params.rows,
start: params.start,
sort: params.sort?.reduce((prev, current, index) => {
const isFirst = index === 0;
const commaPrefix = isFirst ? '' : ', ';
return `${prev}${commaPrefix}${current.field} ${current.direction}`;
}, ''),
rows: params.rows ? `${params.rows}` : '',
start: params.page ?? 0,
},

@@ -25,3 +36,8 @@ },

start: 0,
docs: [{ identifier: 'foo' }, { identifier: 'bar' }],
docs: [
{
identifier: 'foo',
},
{ identifier: 'bar' },
],
},

@@ -28,0 +44,0 @@ };

import { expect } from '@open-wc/testing';
import { FieldParserInterface } from '../../../src/models/metadata-fields/field-parsers';
import { FieldParserInterface } from '../../../src/models/metadata-fields/field-parser-interface';

@@ -4,0 +4,0 @@ import { MetadataField } from '../../../src/models/metadata-fields/metadata-field';

import { expect } from '@open-wc/testing';
import { SearchParams } from '../src/search-params';
import { SearchParams, SortDirection, SortParam } from '../src/search-params';

@@ -8,3 +8,3 @@ describe('SearchParams', () => {

const query = 'title:foo AND collection:bar';
const params = new SearchParams(query);
const params = new SearchParams({ query });
expect(params.query).to.equal(query);

@@ -16,9 +16,6 @@ });

const fields = ['identifier', 'foo', 'bar'];
const params = new SearchParams(
const params = new SearchParams({
query,
undefined,
undefined,
undefined,
fields
);
fields,
});
expect(params.fields).to.deep.equal(fields);

@@ -35,7 +32,6 @@ });

const query = 'title:foo AND collection:bar';
const params = new SearchParams(query);
const params = new SearchParams({ query });
const urlSearchParam = params.asUrlSearchParams;
const queryAsString = urlSearchParam.toString();
const expected =
'q=title%3Afoo+AND+collection%3Abar&rows=25&page=0&output=json&fl%5B%5D=identifier';
const expected = 'q=title%3Afoo+AND+collection%3Abar&output=json';
expect(queryAsString).to.equal(expected);

@@ -47,13 +43,10 @@ });

const fields = ['identifier', 'foo', 'bar'];
const params = new SearchParams(
const params = new SearchParams({
query,
undefined,
undefined,
undefined,
fields
);
fields,
});
const urlSearchParam = params.asUrlSearchParams;
const queryAsString = urlSearchParam.toString();
const expected =
'q=title%3Afoo+AND+collection%3Abar&rows=25&page=0&output=json&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar';
'q=title%3Afoo+AND+collection%3Abar&output=json&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar';
expect(queryAsString).to.equal(expected);

@@ -65,10 +58,16 @@ });

const fields = ['identifier', 'foo', 'bar'];
const sort = ['downloads desc'];
const params = new SearchParams(query, sort, 53, 27, fields);
const sort = [new SortParam('downloads', SortDirection.Desc)];
const params = new SearchParams({
query,
sort,
rows: 53,
page: 27,
fields,
});
const urlSearchParam = params.asUrlSearchParams;
const queryAsString = urlSearchParam.toString();
const expected =
'q=title%3Afoo+AND+collection%3Abar&rows=53&page=27&output=json&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar&sort%5B%5D=downloads+desc';
'q=title%3Afoo+AND+collection%3Abar&output=json&rows=53&page=27&fl%5B%5D=identifier&fl%5B%5D=foo&fl%5B%5D=bar&sort%5B%5D=downloads+desc';
expect(queryAsString).to.equal(expected);
});
});

@@ -9,3 +9,2 @@ /* eslint-disable @typescript-eslint/no-unused-vars */

import { MockResponseGenerator } from './mock-response-generator';
import { SearchBackendInterface } from '../src/search-backend-interface';
import { SearchResponse } from '../src/responses/search/search-response';

@@ -18,2 +17,3 @@ import { MetadataResponse } from '../src/responses/metadata/metadata-response';

} from '../src/search-service-error';
import { SearchBackendInterface } from '../src/search-backend/search-backend-interface';

@@ -41,3 +41,3 @@ describe('SearchService', () => {

const query = 'title:foo AND collection:bar';
const params = new SearchParams(query);
const params = new SearchParams({ query });
const backend = new MockSearchBackend();

@@ -130,3 +130,3 @@ const service = new SearchService(backend);

const params = new SearchParams('boop');
const params = new SearchParams({ query: 'boop' });
const searchResult = await service.search(params);

@@ -174,3 +174,3 @@ expect(searchResult.error).to.not.equal(undefined);

const params = new SearchParams('boop');
const params = new SearchParams({ query: 'boop' });
const searchResult = await service.search(params);

@@ -177,0 +177,0 @@ expect(searchResult.error).to.not.equal(undefined);

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc