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.7 to 0.0.1-alpha.8

dist/src/responses/result.d.ts

12

dist/src/default-search-backend.d.ts
import { SearchBackendInterface } from './search-backend-interface';
import { SearchResponse } from './responses/search/search-response';
import { MetadataResponse } from './responses/metadata/metadata-response';
import { SearchParams } from './search-params';
import { Result } from './responses/result';
import { SearchServiceError } from './search-service-error';
/**
* The DefaultSearchBackend performs `fetch` requests to archive.org
*/
export declare class DefaultSearchBackend implements SearchBackendInterface {
private baseUrl;
constructor(baseUrl?: string);
performSearch(params: SearchParams): Promise<SearchResponse>;
fetchMetadata(identifier: string): Promise<MetadataResponse>;
performSearch(params: SearchParams): Promise<Result<any, SearchServiceError>>;
fetchMetadata(identifier: string): Promise<Result<any, SearchServiceError>>;
private fetchUrl;
}

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

import { Result } from './responses/result';
import { SearchServiceError, SearchServiceErrorType, } from './search-service-error';
/**
* The DefaultSearchBackend performs `fetch` requests to archive.org
*/
export class DefaultSearchBackend {

@@ -9,13 +14,34 @@ constructor(baseUrl = 'archive.org') {

const url = `https://${this.baseUrl}/advancedsearch.php?${queryAsString}`;
const response = await fetch(url);
const json = await response.json();
return new Promise(resolve => resolve(json));
return this.fetchUrl(url);
}
async fetchMetadata(identifier) {
const url = `https://${this.baseUrl}/metadata/${identifier}`;
const response = await fetch(url);
const json = await response.json();
return new Promise(resolve => resolve(json));
return this.fetchUrl(url);
}
async fetchUrl(url) {
let response;
// first try the fetch and return a networkError if it fails
try {
response = await fetch(url);
}
catch (err) {
const message = err instanceof Error ? err.message : err;
const error = new SearchServiceError(SearchServiceErrorType.networkError, message);
const result = new Result(undefined, error);
return result;
}
// then try json decoding and return a decodingError if it fails
try {
const json = await response.json();
const result = new Result(json);
return result;
}
catch (err) {
const message = err instanceof Error ? err.message : err;
const error = new SearchServiceError(SearchServiceErrorType.decodingError, message);
const result = new Result(undefined, error);
return result;
}
}
}
//# sourceMappingURL=default-search-backend.js.map

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

constructor(json) {
var _a;
this.rawResponse = json;

@@ -20,3 +21,3 @@ this.created = json.created;

this.dir = json.dir;
this.files = json.files.map((file) => new File(file));
this.files = (_a = json.files) === null || _a === void 0 ? void 0 : _a.map((file) => new File(file));
this.files_count = json.files_count;

@@ -23,0 +24,0 @@ this.item_last_updated = json.item_last_updated;

import { SearchResponseHeader } from './search-response-header';
import { Response } from './response';
import { SearchResponseDetails } from './search-response-details';
/**

@@ -27,7 +27,7 @@ * The top-level response model when retrieving a response from the advanced search endpoint.

*
* @type {Response}
* @type {SearchResponseDetails}
* @memberof SearchResponse
*/
response: Response;
response: SearchResponseDetails;
constructor(json: any);
}
/* eslint-disable @typescript-eslint/no-explicit-any */
import { SearchResponseHeader } from './search-response-header';
import { Response } from './response';
import { SearchResponseDetails } from './search-response-details';
/**

@@ -14,5 +14,5 @@ * The top-level response model when retrieving a response from the advanced search endpoint.

this.responseHeader = new SearchResponseHeader(json.responseHeader);
this.response = new Response(json.response);
this.response = new SearchResponseDetails(json.response);
}
}
//# sourceMappingURL=search-response.js.map

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

import { MetadataResponse } from './responses/metadata/metadata-response';
import { SearchResponse } from './responses/search/search-response';
import { Result } from './responses/result';
import { SearchParams } from './search-params';
import { SearchServiceError } from './search-service-error';
/**

@@ -15,4 +15,4 @@ * An interface to provide the network layer to the `SearchService`.

export interface SearchBackendInterface {
performSearch(params: SearchParams): Promise<SearchResponse>;
fetchMetadata(identifier: string): Promise<MetadataResponse>;
performSearch(params: SearchParams): Promise<Result<any, SearchServiceError>>;
fetchMetadata(identifier: string): Promise<Result<any, SearchServiceError>>;
}

@@ -5,6 +5,5 @@ import { SearchResponse } from './responses/search/search-response';

import { MetadataResponse } from './responses/metadata/metadata-response';
export interface SearchServiceInterface {
search(params: SearchParams): Promise<SearchResponse>;
fetchMetadata(identifier: string): Promise<MetadataResponse>;
}
import { Result } from './responses/result';
import { SearchServiceError } from './search-service-error';
import { SearchServiceInterface } from './search-service-interface';
/**

@@ -24,3 +23,3 @@ * The Search Service is responsible for taking the raw response provided by

*/
search(params: SearchParams): Promise<SearchResponse>;
search(params: SearchParams): Promise<Result<SearchResponse, SearchServiceError>>;
/**

@@ -31,3 +30,3 @@ * Fetch the item's metadata.

*/
fetchMetadata(identifier: string): Promise<MetadataResponse>;
fetchMetadata(identifier: string): Promise<Result<MetadataResponse, SearchServiceError>>;
}
import { SearchResponse } from './responses/search/search-response';
import { MetadataResponse } from './responses/metadata/metadata-response';
import { DefaultSearchBackend } from './default-search-backend';
import { Result } from './responses/result';
import { SearchServiceError, SearchServiceErrorType, } from './search-service-error';
/**

@@ -20,4 +22,7 @@ * The Search Service is responsible for taking the raw response provided by

const rawResponse = await this.searchBackend.performSearch(params);
const modeledResponse = new SearchResponse(rawResponse);
return new Promise(resolve => resolve(modeledResponse));
if (rawResponse.error) {
return rawResponse;
}
const modeledResponse = new SearchResponse(rawResponse.success);
return new Result(modeledResponse);
}

@@ -30,5 +35,12 @@ /**

async fetchMetadata(identifier) {
var _a;
const rawResponse = await this.searchBackend.fetchMetadata(identifier);
const modeledResponse = new MetadataResponse(rawResponse);
return new Promise(resolve => resolve(modeledResponse));
if (rawResponse.error) {
return rawResponse;
}
if (((_a = rawResponse.success) === null || _a === void 0 ? void 0 : _a.metadata) === undefined) {
return new Result(undefined, new SearchServiceError(SearchServiceErrorType.itemNotFound));
}
const modeledResponse = new MetadataResponse(rawResponse.success);
return new Result(modeledResponse);
}

@@ -35,0 +47,0 @@ }

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

/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@open-wc/testing';

@@ -5,4 +7,7 @@ import { SearchService } from '../src/search-service';

import { MockResponseGenerator } from './mock-response-generator';
import { Result } from '../src/responses/result';
import { SearchServiceError, SearchServiceErrorType, } from '../src/search-service-error';
describe('SearchService', () => {
it('can search when requested', async () => {
var _a;
class MockSearchBackend {

@@ -15,3 +20,3 @@ async fetchMetadata(identifier) {

const mockResponse = responseGenerator.generateMockSearchResponse(params);
return new Promise(resolve => resolve(mockResponse));
return new Result(mockResponse);
}

@@ -24,5 +29,6 @@ }

const result = await service.search(params);
expect(result.responseHeader.params.query).to.equal(query);
expect((_a = result.success) === null || _a === void 0 ? void 0 : _a.responseHeader.params.query).to.equal(query);
});
it('can request metadata when requested', async () => {
var _a;
class MockSearchBackend {

@@ -35,3 +41,3 @@ performSearch(params) {

const mockResponse = responseGenerator.generateMockMetadataResponse(identifier);
return new Promise(resolve => resolve(mockResponse));
return new Result(mockResponse);
}

@@ -42,5 +48,71 @@ }

const result = await service.fetchMetadata('foo');
expect(result.metadata.identifier).to.equal('foo');
expect((_a = result.success) === null || _a === void 0 ? void 0 : _a.metadata.identifier).to.equal('foo');
});
it('returns an error result if the item is not found', async () => {
var _a;
class MockSearchBackend {
performSearch(params) {
throw new Error('Method not implemented.');
}
async fetchMetadata(identifier) {
// this is unfortunate.. instead of getting an http 404 error,
// we get an empty JSON object when an item is not found
return new Result({});
}
}
const backend = new MockSearchBackend();
const service = new SearchService(backend);
const result = await service.fetchMetadata('foo');
expect(result.error).to.not.equal(undefined);
expect((_a = result.error) === null || _a === void 0 ? void 0 : _a.type).to.equal(SearchServiceErrorType.itemNotFound);
});
it('returns the search backend network error if one occurs', async () => {
var _a, _b, _c, _d;
class MockSearchBackend {
async performSearch(params) {
const error = new SearchServiceError(SearchServiceErrorType.networkError, 'network error');
return new Result(undefined, error);
}
async fetchMetadata(identifier) {
const error = new SearchServiceError(SearchServiceErrorType.networkError, 'network error');
return new Result(undefined, error);
}
}
const backend = new MockSearchBackend();
const service = new SearchService(backend);
const metadataResult = await service.fetchMetadata('foo');
expect(metadataResult.error).to.not.equal(undefined);
expect((_a = metadataResult.error) === null || _a === void 0 ? void 0 : _a.type).to.equal(SearchServiceErrorType.networkError);
expect((_b = metadataResult.error) === null || _b === void 0 ? void 0 : _b.message).to.equal('network error');
const params = new SearchParams('boop');
const searchResult = await service.search(params);
expect(searchResult.error).to.not.equal(undefined);
expect((_c = searchResult.error) === null || _c === void 0 ? void 0 : _c.type).to.equal(SearchServiceErrorType.networkError);
expect((_d = searchResult.error) === null || _d === void 0 ? void 0 : _d.message).to.equal('network error');
});
it('returns the search backend decoding error if one occurs', async () => {
var _a, _b, _c, _d;
class MockSearchBackend {
async performSearch(params) {
const error = new SearchServiceError(SearchServiceErrorType.decodingError, 'decoding error');
return new Result(undefined, error);
}
async fetchMetadata(identifier) {
const error = new SearchServiceError(SearchServiceErrorType.decodingError, 'decoding error');
return new Result(undefined, error);
}
}
const backend = new MockSearchBackend();
const service = new SearchService(backend);
const metadataResult = await service.fetchMetadata('foo');
expect(metadataResult.error).to.not.equal(undefined);
expect((_a = metadataResult.error) === null || _a === void 0 ? void 0 : _a.type).to.equal(SearchServiceErrorType.decodingError);
expect((_b = metadataResult.error) === null || _b === void 0 ? void 0 : _b.message).to.equal('decoding error');
const params = new SearchParams('boop');
const searchResult = await service.search(params);
expect(searchResult.error).to.not.equal(undefined);
expect((_c = searchResult.error) === null || _c === void 0 ? void 0 : _c.type).to.equal(SearchServiceErrorType.decodingError);
expect((_d = searchResult.error) === null || _d === void 0 ? void 0 : _d.message).to.equal('decoding error');
});
});
//# sourceMappingURL=search-service.test.js.map
{
"name": "@internetarchive/search-service",
"version": "0.0.1-alpha.7",
"version": "0.0.1-alpha.8",
"description": "A search service for the Internet Archive",

@@ -5,0 +5,0 @@ "license": "AGPL-3.0-only",

@@ -0,6 +1,13 @@

/* eslint-disable @typescript-eslint/no-explicit-any */
import { SearchBackendInterface } from './search-backend-interface';
import { SearchResponse } from './responses/search/search-response';
import { MetadataResponse } from './responses/metadata/metadata-response';
import { SearchParams } from './search-params';
import { Result } from './responses/result';
import {
SearchServiceError,
SearchServiceErrorType,
} from './search-service-error';
/**
* The DefaultSearchBackend performs `fetch` requests to archive.org
*/
export class DefaultSearchBackend implements SearchBackendInterface {

@@ -13,17 +20,50 @@ private baseUrl: string;

async performSearch(params: SearchParams): Promise<SearchResponse> {
async performSearch(
params: SearchParams
): Promise<Result<any, SearchServiceError>> {
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));
return this.fetchUrl(url);
}
async fetchMetadata(identifier: string): Promise<MetadataResponse> {
async fetchMetadata(
identifier: string
): Promise<Result<any, SearchServiceError>> {
const url = `https://${this.baseUrl}/metadata/${identifier}`;
const response = await fetch(url);
const json = await response.json();
return new Promise(resolve => resolve(json));
return this.fetchUrl(url);
}
private async fetchUrl(
url: string
): Promise<Result<any, SearchServiceError>> {
let response: Response;
// first try the fetch and return a networkError if it fails
try {
response = await fetch(url);
} catch (err) {
const message = err instanceof Error ? err.message : err;
const error = new SearchServiceError(
SearchServiceErrorType.networkError,
message
);
const result = new Result<any, SearchServiceError>(undefined, error);
return result;
}
// then try json decoding and return a decodingError if it fails
try {
const json = await response.json();
const result = new Result<any, SearchServiceError>(json);
return result;
} catch (err) {
const message = err instanceof Error ? err.message : err;
const error = new SearchServiceError(
SearchServiceErrorType.decodingError,
message
);
const result = new Result<any, SearchServiceError>(undefined, error);
return result;
}
}
}

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

this.dir = json.dir;
this.files = json.files.map((file: any) => new File(file));
this.files = json.files?.map((file: any) => new File(file));
this.files_count = json.files_count;

@@ -49,0 +49,0 @@ this.item_last_updated = json.item_last_updated;

/* eslint-disable @typescript-eslint/no-explicit-any */
import { SearchResponseHeader } from './search-response-header';
import { Response } from './response';
import { SearchResponseDetails } from './search-response-details';

@@ -31,6 +31,6 @@ /**

*
* @type {Response}
* @type {SearchResponseDetails}
* @memberof SearchResponse
*/
response: Response;
response: SearchResponseDetails;

@@ -40,4 +40,4 @@ constructor(json: any) {

this.responseHeader = new SearchResponseHeader(json.responseHeader);
this.response = new Response(json.response);
this.response = new SearchResponseDetails(json.response);
}
}
/* eslint-disable @typescript-eslint/no-explicit-any */
import { MetadataResponse } from './responses/metadata/metadata-response';
import { SearchResponse } from './responses/search/search-response';
import { Result } from './responses/result';
import { SearchParams } from './search-params';
import { SearchServiceError } from './search-service-error';

@@ -17,4 +17,4 @@ /**

export interface SearchBackendInterface {
performSearch(params: SearchParams): Promise<SearchResponse>;
fetchMetadata(identifier: string): Promise<MetadataResponse>;
performSearch(params: SearchParams): Promise<Result<any, SearchServiceError>>;
fetchMetadata(identifier: string): Promise<Result<any, SearchServiceError>>;
}

@@ -6,8 +6,9 @@ import { SearchResponse } from './responses/search/search-response';

import { DefaultSearchBackend } from './default-search-backend';
import { Result } from './responses/result';
import {
SearchServiceError,
SearchServiceErrorType,
} from './search-service-error';
import { SearchServiceInterface } from './search-service-interface';
export interface SearchServiceInterface {
search(params: SearchParams): Promise<SearchResponse>;
fetchMetadata(identifier: string): Promise<MetadataResponse>;
}
/**

@@ -34,6 +35,12 @@ * The Search Service is responsible for taking the raw response provided by

*/
async search(params: SearchParams): Promise<SearchResponse> {
async search(
params: SearchParams
): Promise<Result<SearchResponse, SearchServiceError>> {
const rawResponse = await this.searchBackend.performSearch(params);
const modeledResponse = new SearchResponse(rawResponse);
return new Promise(resolve => resolve(modeledResponse));
if (rawResponse.error) {
return rawResponse;
}
const modeledResponse = new SearchResponse(rawResponse.success);
return new Result<SearchResponse, SearchServiceError>(modeledResponse);
}

@@ -46,7 +53,20 @@

*/
async fetchMetadata(identifier: string): Promise<MetadataResponse> {
async fetchMetadata(
identifier: string
): Promise<Result<MetadataResponse, SearchServiceError>> {
const rawResponse = await this.searchBackend.fetchMetadata(identifier);
const modeledResponse = new MetadataResponse(rawResponse);
return new Promise(resolve => resolve(modeledResponse));
if (rawResponse.error) {
return rawResponse;
}
if (rawResponse.success?.metadata === undefined) {
return new Result<MetadataResponse, SearchServiceError>(
undefined,
new SearchServiceError(SearchServiceErrorType.itemNotFound)
);
}
const modeledResponse = new MetadataResponse(rawResponse.success);
return new Result<MetadataResponse, SearchServiceError>(modeledResponse);
}
}

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

/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { expect } from '@open-wc/testing';

@@ -10,2 +12,7 @@

import { MetadataResponse } from '../src/responses/metadata/metadata-response';
import { Result } from '../src/responses/result';
import {
SearchServiceError,
SearchServiceErrorType,
} from '../src/search-service-error';

@@ -15,7 +22,11 @@ describe('SearchService', () => {

class MockSearchBackend implements SearchBackendInterface {
async fetchMetadata(identifier: string): Promise<MetadataResponse> {
async fetchMetadata(
identifier: string
): Promise<Result<MetadataResponse, SearchServiceError>> {
throw new Error('Method not implemented.');
}
async performSearch(params: any): Promise<SearchResponse> {
async performSearch(
params: SearchParams
): Promise<Result<SearchResponse, SearchServiceError>> {
const responseGenerator = new MockResponseGenerator();

@@ -25,3 +36,3 @@ const mockResponse = responseGenerator.generateMockSearchResponse(

);
return new Promise(resolve => resolve(mockResponse));
return new Result<SearchResponse, SearchServiceError>(mockResponse);
}

@@ -35,3 +46,3 @@ }

const result = await service.search(params);
expect(result.responseHeader.params.query).to.equal(query);
expect(result.success?.responseHeader.params.query).to.equal(query);
});

@@ -41,6 +52,10 @@

class MockSearchBackend implements SearchBackendInterface {
performSearch(params: SearchParams): Promise<SearchResponse> {
performSearch(
params: SearchParams
): Promise<Result<SearchResponse, SearchServiceError>> {
throw new Error('Method not implemented.');
}
async fetchMetadata(identifier: string): Promise<MetadataResponse> {
async fetchMetadata(
identifier: string
): Promise<Result<MetadataResponse, SearchServiceError>> {
const responseGenerator = new MockResponseGenerator();

@@ -50,3 +65,3 @@ const mockResponse = responseGenerator.generateMockMetadataResponse(

);
return new Promise(resolve => resolve(mockResponse));
return new Result<MetadataResponse, SearchServiceError>(mockResponse);
}

@@ -58,4 +73,113 @@ }

const result = await service.fetchMetadata('foo');
expect(result.metadata.identifier).to.equal('foo');
expect(result.success?.metadata.identifier).to.equal('foo');
});
it('returns an error result if the item is not found', async () => {
class MockSearchBackend implements SearchBackendInterface {
performSearch(
params: SearchParams
): Promise<Result<SearchResponse, SearchServiceError>> {
throw new Error('Method not implemented.');
}
async fetchMetadata(
identifier: string
): Promise<Result<MetadataResponse, SearchServiceError>> {
// this is unfortunate.. instead of getting an http 404 error,
// we get an empty JSON object when an item is not found
return new Result<MetadataResponse, SearchServiceError>({} as any);
}
}
const backend = new MockSearchBackend();
const service = new SearchService(backend);
const result = await service.fetchMetadata('foo');
expect(result.error).to.not.equal(undefined);
expect(result.error?.type).to.equal(SearchServiceErrorType.itemNotFound);
});
it('returns the search backend network error if one occurs', async () => {
class MockSearchBackend implements SearchBackendInterface {
async performSearch(
params: SearchParams
): Promise<Result<SearchResponse, SearchServiceError>> {
const error = new SearchServiceError(
SearchServiceErrorType.networkError,
'network error'
);
return new Result<SearchResponse, SearchServiceError>(undefined, error);
}
async fetchMetadata(
identifier: string
): Promise<Result<MetadataResponse, SearchServiceError>> {
const error = new SearchServiceError(
SearchServiceErrorType.networkError,
'network error'
);
return new Result<MetadataResponse, SearchServiceError>(
undefined,
error
);
}
}
const backend = new MockSearchBackend();
const service = new SearchService(backend);
const metadataResult = await service.fetchMetadata('foo');
expect(metadataResult.error).to.not.equal(undefined);
expect(metadataResult.error?.type).to.equal(
SearchServiceErrorType.networkError
);
expect(metadataResult.error?.message).to.equal('network error');
const params = new SearchParams('boop');
const searchResult = await service.search(params);
expect(searchResult.error).to.not.equal(undefined);
expect(searchResult.error?.type).to.equal(
SearchServiceErrorType.networkError
);
expect(searchResult.error?.message).to.equal('network error');
});
it('returns the search backend decoding error if one occurs', async () => {
class MockSearchBackend implements SearchBackendInterface {
async performSearch(
params: SearchParams
): Promise<Result<SearchResponse, SearchServiceError>> {
const error = new SearchServiceError(
SearchServiceErrorType.decodingError,
'decoding error'
);
return new Result<SearchResponse, SearchServiceError>(undefined, error);
}
async fetchMetadata(
identifier: string
): Promise<Result<MetadataResponse, SearchServiceError>> {
const error = new SearchServiceError(
SearchServiceErrorType.decodingError,
'decoding error'
);
return new Result<MetadataResponse, SearchServiceError>(
undefined,
error
);
}
}
const backend = new MockSearchBackend();
const service = new SearchService(backend);
const metadataResult = await service.fetchMetadata('foo');
expect(metadataResult.error).to.not.equal(undefined);
expect(metadataResult.error?.type).to.equal(
SearchServiceErrorType.decodingError
);
expect(metadataResult.error?.message).to.equal('decoding error');
const params = new SearchParams('boop');
const searchResult = await service.search(params);
expect(searchResult.error).to.not.equal(undefined);
expect(searchResult.error?.type).to.equal(
SearchServiceErrorType.decodingError
);
expect(searchResult.error?.message).to.equal('decoding error');
});
});

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