<commit_before>/**
 * @fileOverview Exports the Sajari javascript api.
 * @name index.ts
 * @author Sajari
 * @license MIT
 * @module sajari
 */

import { AggregateResponse, Result, Results, ResultValues } from "./results";

import {
  newAggregates,
  newResult,
  newResults,
  valueFromProto
} from "./constructors";

const UserAgent = "sdk-js-1.0.0";

export interface Tracking {
  // Tracking specifies which kind (if any) tokens should be generated and returned
  // with the query results.
  type: TrackingType;

  // QueryID is a unique identifier for a single search query.  In the
  // case of live querying this is defined to be multiple individual queries
  // (i.e. as a user types the query is re-run).
  query_id: string;

  // Sequence (i.e. sequential identifier) of this  in the context of a
  // sequence of queries.
  sequence: number;

  // Field is the field to be used for adding identifier information to
  // generated tokens (see TrackingType).
  field: string;

  // Data are values which will be recorded along with tracking data produced
  // for the request.
  data: Values;
}

/**
 * Values is a plan object with only string values.
 */
export interface Values {
  [id: string]: string;
}

export interface ISession {
  next(values: Values): [Tracking, undefined] | [undefined, Error];
  reset(): void;
}

export const TrackingNone: string = "NONE";
export const TrackingClick: string = "CLICK";
export const TrackingPosNeg: string = "POS_NEG";

export const enum TrackingType {
  TrackingNone = "NONE",
  TrackingClick = "CLICK",
  TrackingPosNeg = "POS_NEG"
}

// randString constructs a random string of 16 characters.
const randString = (): string => {
  let queryID = "";
  for (let i = 0; i < 16; i++) {
    queryID += "abcdefghijklmnopqrstuvwxyz0123456789".charAt(
      Math.floor(Math.random() * 36)
    );
  }
  return queryID;
};

/**
 * TextSession creates a session based on text searches.
 * It automatically resets once the value specified by the query label has changed in certain ways.
 */
export class TextSession implements ISession {
  private queryLabel: string;
  private session: ISession;
  private lastQuery: string = "";

  public constructor(queryLabel: string, session: ISession) {
    this.queryLabel = queryLabel;
    this.session = session;
  }

  public next(values: Values): [Tracking, undefined] | [undefined, Error] {
    const text = values[this.queryLabel];
    if (text === undefined) {
      this.reset();
      return this.session.next(values);
    }

    const shortenedPrevQ = this.lastQuery.substr(0, Math.min(text.length, 3));
    const first3CharactersChanged = !(
      text.substr(0, shortenedPrevQ.length) === shortenedPrevQ
    );
    const queryCleared = this.lastQuery.length > 0 && text.length === 0;
    if (first3CharactersChanged || queryCleared) {
      this.reset();
    }
    this.lastQuery = text;

    return this.session.next(values);
  }

  public reset(): void {
    this.session.reset();
  }
}

export class Session implements ISession {
  private queryID: string = "";
  private sequence: number = 0;

  private trackingType: TrackingType;
  private field: string;
  private sessionData: Values;

  public constructor(trackingType: TrackingType, field: string, data: Values) {
    this.trackingType = trackingType;
    this.field = field;
    this.sessionData = data;
  }

  public next(values: Values): [Tracking, undefined] | [undefined, Error] {
    if (this.queryID === "") {
      this.queryID = randString();
      this.sequence = 0;
    } else {
      this.sequence++;
    }

    return [
      {
        type: this.trackingType,
        query_id: this.queryID,
        sequence: this.sequence,
        field: this.field,
        data: this.sessionData
      } as Tracking,
      undefined
    ];
  }

  public reset(): void {
    this.queryID = "";
    this.sequence = 0;
  }
}

export type ClientOption = (client: Client) => void;

export const withEndpoint = (endpoint: string) => (client: Client) => {
  client.endpoint = endpoint;
};

/**
 * Client takes configuration and constructs pipelines clients
 */
export class Client {
  public project: string;
  public collection: string;
  public endpoint: string;

  public constructor(
    project: string,<commit_msg>client: add doc string to client<commit_after>/**
 * @fileOverview Exports the Sajari javascript api.
 * @name index.ts
 * @author Sajari
 * @license MIT
 * @module sajari
 */

import { AggregateResponse, Result, Results, ResultValues } from "./results";

import {
  newAggregates,
  newResult,
  newResults,
  valueFromProto
} from "./constructors";

const UserAgent = "sdk-js-1.0.0";

export interface Tracking {
  // Tracking specifies which kind (if any) tokens should be generated and returned
  // with the query results.
  type: TrackingType;

  // QueryID is a unique identifier for a single search query.  In the
  // case of live querying this is defined to be multiple individual queries
  // (i.e. as a user types the query is re-run).
  query_id: string;

  // Sequence (i.e. sequential identifier) of this  in the context of a
  // sequence of queries.
  sequence: number;

  // Field is the field to be used for adding identifier information to
  // generated tokens (see TrackingType).
  field: string;

  // Data are values which will be recorded along with tracking data produced
  // for the request.
  data: Values;
}

/**
 * Values is a plan object with only string values.
 */
export interface Values {
  [id: string]: string;
}

export interface ISession {
  next(values: Values): [Tracking, undefined] | [undefined, Error];
  reset(): void;
}

export const TrackingNone: string = "NONE";
export const TrackingClick: string = "CLICK";
export const TrackingPosNeg: string = "POS_NEG";

export const enum TrackingType {
  TrackingNone = "NONE",
  TrackingClick = "CLICK",
  TrackingPosNeg = "POS_NEG"
}

// randString constructs a random string of 16 characters.
const randString = (): string => {
  let queryID = "";
  for (let i = 0; i < 16; i++) {
    queryID += "abcdefghijklmnopqrstuvwxyz0123456789".charAt(
      Math.floor(Math.random() * 36)
    );
  }
  return queryID;
};

/**
 * TextSession creates a session based on text searches.
 * It automatically resets once the value specified by the query label has changed in certain ways.
 */
export class TextSession implements ISession {
  private queryLabel: string;
  private session: ISession;
  private lastQuery: string = "";

  public constructor(queryLabel: string, session: ISession) {
    this.queryLabel = queryLabel;
    this.session = session;
  }

  public next(values: Values): [Tracking, undefined] | [undefined, Error] {
    const text = values[this.queryLabel];
    if (text === undefined) {
      this.reset();
      return this.session.next(values);
    }

    const shortenedPrevQ = this.lastQuery.substr(0, Math.min(text.length, 3));
    const first3CharactersChanged = !(
      text.substr(0, shortenedPrevQ.length) === shortenedPrevQ
    );
    const queryCleared = this.lastQuery.length > 0 && text.length === 0;
    if (first3CharactersChanged || queryCleared) {
      this.reset();
    }
    this.lastQuery = text;

    return this.session.next(values);
  }

  public reset(): void {
    this.session.reset();
  }
}

export class Session implements ISession {
  private queryID: string = "";
  private sequence: number = 0;

  private trackingType: TrackingType;
  private field: string;
  private sessionData: Values;

  public constructor(trackingType: TrackingType, field: string, data: Values) {
    this.trackingType = trackingType;
    this.field = field;
    this.sessionData = data;
  }

  public next(values: Values): [Tracking, undefined] | [undefined, Error] {
    if (this.queryID === "") {
      this.queryID = randString();
      this.sequence = 0;
    } else {
      this.sequence++;
    }

    return [
      {
        type: this.trackingType,
        query_id: this.queryID,
        sequence: this.sequence,
        field: this.field,
        data: this.sessionData
      } as Tracking,
      undefined
    ];
  }

  public reset(): void {
    this.queryID = "";
    this.sequence = 0;
  }
}

export type ClientOption = (client: Client) => void;

export const withEndpoint = (endpoint: string) => (client: Client) => {
  client.endpoint = endpoint;
};

/**
 * Client takes configuration and constructs pipelines clients.
 *
 * ```javascript
 * const client = new Client("<project>", "<collection>");
 * const webPipeline = client.pipeline("website");
 * // webPipeline.search(...);
 * ```
 *
 * An optional array of [[ClientOption]] may be given to the client constructor to modify it's behaviour.
 *
 * ```javascript
 * const client = new Client("<project>", "<collection>", withEndpoint("https://example.com"));
 * ```
 */
export class Client {
  public project: string;
  public collection: string;
  public endpoint: string;

  public constructor(
    project: string,
{'fraction_non_alphanumeric': 0.11588222172652242, 'fraction_numerical': 0.0066919473566807944, 'mean_word_length': 3.2761087267525038, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 17, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1010584', 'n_tokens_mistral': 2725, 'n_tokens_neox': 2605, 'n_words': 865}