Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@findkit/fetch

Package Overview
Dependencies
Maintainers
3
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@findkit/fetch - npm Package Compare versions

Comparing version
2.0.0-dev.6f4e684575
to
2.0.0-dev.6f6a1c6044
+1
dist/__tests__/fetch.test.d.ts.map
{"version":3,"file":"fetch.test.d.ts","sourceRoot":"","sources":["../../__tests__/fetch.test.ts"],"names":[],"mappings":""}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC1B;AAED;;GAEG;AACH,oBAAY,YAAY,GAAG;IAC1B,CAAC,WAAW,EAAE,MAAM,GACjB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAC/B;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAClC;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GACjC,SAAS,CAAC;CACb,CAAC;AAEF;;GAEG;AACH,UAAU,QAAQ;IACjB,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC/D;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,IAAI,CAAC,EAAE,aAAa,GAAG,aAAa,GAAG,uBAAuB,CAAC;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAmBD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,CAAC,OAAO,EAAE;QACT,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACb;AAmBD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,gBAAgB;;;;EA6H3D;AAiBD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,UAEvD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACpC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,oBAAY,qBAAqB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE;QACV,IAAI,EAAE,QAAQ,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KAChB,EAAE,CAAC;CACJ,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,YAAY,EAAE,YAAY,CAAC;KAC3B,EAAE,CAAC;CACJ"}
MIT License
Copyright (c) 2022 Valu Digital Oy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+138
-173
import { expect, test, describe, beforeEach, afterEach, vi } from "vitest";
import { findkitFetch } from "..";
import { rest } from "msw";

@@ -11,4 +10,4 @@ import { sign } from "jsonwebtoken";

createFindkitFetcher,
getRequestBody,
JwtErrorResponse,
FindkitSearchResponse,
FindkitErrorResponse,
} from "../src/index";

@@ -37,99 +36,11 @@

type FindkitFetchResponse = Awaited<ReturnType<typeof findkitFetch>>;
describe("request body generation", () => {
test("without tags", () => {
const body = getRequestBody({
q: "test",
groups: [
{
tagQuery: [[]],
size: 5,
from: 0,
createdDecay: undefined,
modifiedDecay: undefined,
decayScale: undefined,
highlightLength: undefined,
lang: undefined,
},
],
describe("fetch", () => {
test("empty fetch", async () => {
const { findkitFetch } = createFindkitFetcher({
searchEndpoint: "https://test.invalid/multi-search2",
});
expect(body).toEqual({
q: "test",
groups: [
{
tagQuery: [[]],
size: 5,
from: 0,
},
],
});
});
test("without tags", () => {
const body = getRequestBody({
q: "test",
groups: [
{
tagQuery: [["tag"]],
size: 5,
from: 0,
createdDecay: undefined,
modifiedDecay: undefined,
decayScale: undefined,
highlightLength: undefined,
lang: undefined,
},
],
});
expect(body).toEqual({
q: "test",
groups: [
{
tagQuery: [["tag"]],
size: 5,
from: 0,
},
],
});
});
test("with lang", () => {
const body = getRequestBody({
q: "test",
groups: [
{
tagQuery: [["tag"]],
size: 5,
from: 0,
lang: "de",
createdDecay: undefined,
modifiedDecay: undefined,
decayScale: undefined,
highlightLength: undefined,
},
],
});
expect(body).toEqual({
q: "test",
groups: [
{
tagQuery: [["tag"]],
lang: "de",
size: 5,
from: 0,
},
],
});
});
});
describe("fetch", () => {
test("empty fetch", async () => {
server.use(
rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
const resData: FindkitFetchResponse = {
const resData: FindkitSearchResponse = {
groups: [],

@@ -141,4 +52,4 @@ duration: 123,

);
const res = await findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
q: "",

@@ -150,29 +61,25 @@ groups: [],

});
});
describe("jwt", () => {
test("calls getJwtToken() and passes the token as bearer", async () => {
test("can use publicToken to generate the endpoint", async () => {
const spy = vi.fn();
const { findkitFetch } = createFindkitFetcher({
publicToken: "thetoken",
});
server.use(
rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
spy(req.headers.get("authorization"));
const resData: FindkitFetchResponse = {
groups: [],
duration: 123,
};
return res(ctx.json(resData));
})
rest.post(
"https://search.findkit.com/c/thetoken/search",
(req, res, ctx) => {
spy(req.url.toString());
const resData: FindkitSearchResponse = {
groups: [],
duration: 123,
};
return res(ctx.json(resData));
}
)
);
const fetcher = createFindkitFetcher({
async getJwtToken() {
return { jwt };
},
});
const jwt = createJwtToken();
const res = await fetcher.findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
const res = await findkitFetch({
q: "",

@@ -182,13 +89,19 @@ groups: [],

expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith("Bearer " + jwt);
expect(res).toEqual({ duration: 123, groups: [] });
expect(spy).toHaveBeenCalledWith(
"https://search.findkit.com/c/thetoken/search?p=thetoken"
);
});
test("calls getJwtToken() only once for multiple fetches", async () => {
const spy = vi.fn();
test("when no groups are defined add a default that searches everything", async () => {
const { findkitFetch } = createFindkitFetcher({
searchEndpoint: "https://test.invalid/multi-search2",
});
server.use(
rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
const resData: FindkitFetchResponse = {
rest.post("https://test.invalid/multi-search2", async (req, res, ctx) => {
const requestBody = await req.json();
expect(requestBody).toEqual({ q: "test", groups: [{ tagQuery: [] }] });
const resData: FindkitSearchResponse = {
groups: [],

@@ -200,72 +113,124 @@ duration: 123,

);
const fetcher = createFindkitFetcher({
async getJwtToken() {
spy();
return { jwt };
},
const res = await findkitFetch({
q: "test",
});
const jwt = createJwtToken();
expect(res).toEqual({ duration: 123, groups: [] });
});
for await (const i of Array(5).keys()) {
await fetcher.findkitFetch({
describe("jwt", () => {
test("calls getJwtToken() and passes the token in the p query string", async () => {
const spy = vi.fn();
server.use(
rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
spy(req.url.searchParams.get("p"));
const resData: FindkitSearchResponse = {
groups: [],
duration: 123,
};
return res(ctx.json(resData));
})
);
const { findkitFetch } = createFindkitFetcher({
searchEndpoint: "https://test.invalid/multi-search2",
q: i.toString(),
async getJwtToken() {
return { jwt };
},
});
const jwt = createJwtToken();
const res = await findkitFetch({
q: "",
groups: [],
});
}
expect(spy).toHaveBeenCalledTimes(1);
});
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith("jwt:" + jwt);
expect(res).toEqual({ duration: 123, groups: [] });
});
test("calls getJwtToken() again when server responds with expired error", async () => {
let expired = false;
test("calls getJwtToken() only once for multiple fetches", async () => {
const spy = vi.fn();
server.use(
rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
if (expired) {
expired = false;
const data: JwtErrorResponse = {
error: { type: "jwt-expired" },
server.use(
rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
const resData: FindkitSearchResponse = {
groups: [],
duration: 123,
};
return res(ctx.status(403), ctx.json(data));
}
return res(ctx.json(resData));
})
);
const resData: FindkitFetchResponse = {
const fetcher = createFindkitFetcher({
searchEndpoint: "https://test.invalid/multi-search2",
async getJwtToken() {
spy();
return { jwt };
},
});
const jwt = createJwtToken();
for await (const i of Array(5).keys()) {
await fetcher.findkitFetch({
q: i.toString(),
groups: [],
duration: 123,
};
return res(ctx.json(resData));
})
);
});
}
const spy = vi.fn();
const fetcher = createFindkitFetcher({
async getJwtToken() {
spy();
const jwt = createJwtToken();
return { jwt };
},
expect(spy).toHaveBeenCalledTimes(1);
});
await fetcher.findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
test("calls getJwtToken() again when server responds with expired error", async () => {
let expired = false;
q: "",
groups: [],
});
server.use(
rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
if (expired) {
expired = false;
const data: FindkitErrorResponse = {
code: "jwt-expired",
};
return res(ctx.status(403), ctx.json(data));
}
expired = true;
const resData: FindkitSearchResponse = {
groups: [],
duration: 123,
};
return res(ctx.json(resData));
})
);
await fetcher.findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
const spy = vi.fn();
q: "",
groups: [],
const fetcher = createFindkitFetcher({
searchEndpoint: "https://test.invalid/multi-search2",
async getJwtToken() {
spy();
const jwt = createJwtToken();
return { jwt };
},
});
await fetcher.findkitFetch({
q: "",
groups: [],
});
expired = true;
await fetcher.findkitFetch({
q: "",
groups: [],
});
expect(spy).toBeCalledTimes(2);
});
expect(spy).toBeCalledTimes(2);
});
});
export {};
//# sourceMappingURL=fetch.test.d.ts.map

@@ -1,8 +0,9 @@

import { expect, test, describe, beforeEach, afterEach, vi } from "vitest";
import { findkitFetch } from "..";
import { rest } from "msw";
import { sign } from "jsonwebtoken";
import fetch from "node-fetch";
import { setupServer } from "msw/node";
import { createFindkitFetcher, getRequestBody, } from "../src/index";
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const msw_1 = require("msw");
const jsonwebtoken_1 = require("jsonwebtoken");
const node_fetch_1 = require("node-fetch");
const node_1 = require("msw/node");
const index_1 = require("../src/index");
/**

@@ -13,100 +14,19 @@ * The token content does not matter in these tests since @findkit/fetch does

function createJwtToken() {
return sign({ foo: "bar" }, "secret");
return (0, jsonwebtoken_1.sign)({ foo: "bar" }, "secret");
}
Object.assign(global, { fetch });
const server = setupServer();
beforeEach(async () => {
Object.assign(global, { fetch: node_fetch_1.default });
const server = (0, node_1.setupServer)();
(0, vitest_1.beforeEach)(async () => {
server.listen();
});
afterEach(async () => {
(0, vitest_1.afterEach)(async () => {
server.close();
server.resetHandlers();
});
describe("request body generation", () => {
test("without tags", () => {
const body = getRequestBody({
q: "test",
groups: [
{
tagQuery: [[]],
size: 5,
from: 0,
createdDecay: undefined,
modifiedDecay: undefined,
decayScale: undefined,
highlightLength: undefined,
lang: undefined,
},
],
(0, vitest_1.describe)("fetch", () => {
(0, vitest_1.test)("empty fetch", async () => {
const { findkitFetch } = (0, index_1.createFindkitFetcher)({
searchEndpoint: "https://test.invalid/multi-search2",
});
expect(body).toEqual({
q: "test",
groups: [
{
tagQuery: [[]],
size: 5,
from: 0,
},
],
});
});
test("without tags", () => {
const body = getRequestBody({
q: "test",
groups: [
{
tagQuery: [["tag"]],
size: 5,
from: 0,
createdDecay: undefined,
modifiedDecay: undefined,
decayScale: undefined,
highlightLength: undefined,
lang: undefined,
},
],
});
expect(body).toEqual({
q: "test",
groups: [
{
tagQuery: [["tag"]],
size: 5,
from: 0,
},
],
});
});
test("with lang", () => {
const body = getRequestBody({
q: "test",
groups: [
{
tagQuery: [["tag"]],
size: 5,
from: 0,
lang: "de",
createdDecay: undefined,
modifiedDecay: undefined,
decayScale: undefined,
highlightLength: undefined,
},
],
});
expect(body).toEqual({
q: "test",
groups: [
{
tagQuery: [["tag"]],
lang: "de",
size: 5,
from: 0,
},
],
});
});
});
describe("fetch", () => {
test("empty fetch", async () => {
server.use(rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
server.use(msw_1.rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
const resData = {

@@ -119,14 +39,14 @@ groups: [],

const res = await findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
q: "",
groups: [],
});
expect(res).toEqual({ duration: 123, groups: [] });
(0, vitest_1.expect)(res).toEqual({ duration: 123, groups: [] });
});
});
describe("jwt", () => {
test("calls getJwtToken() and passes the token as bearer", async () => {
const spy = vi.fn();
server.use(rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
spy(req.headers.get("authorization"));
(0, vitest_1.test)("can use publicToken to generate the endpoint", async () => {
const spy = vitest_1.vi.fn();
const { findkitFetch } = (0, index_1.createFindkitFetcher)({
publicToken: "thetoken",
});
server.use(msw_1.rest.post("https://search.findkit.com/c/thetoken/search", (req, res, ctx) => {
spy(req.url.toString());
const resData = {

@@ -138,20 +58,16 @@ groups: [],

}));
const fetcher = createFindkitFetcher({
async getJwtToken() {
return { jwt };
},
});
const jwt = createJwtToken();
const res = await fetcher.findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
const res = await findkitFetch({
q: "",
groups: [],
});
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith("Bearer " + jwt);
expect(res).toEqual({ duration: 123, groups: [] });
(0, vitest_1.expect)(res).toEqual({ duration: 123, groups: [] });
(0, vitest_1.expect)(spy).toHaveBeenCalledWith("https://search.findkit.com/c/thetoken/search?p=thetoken");
});
test("calls getJwtToken() only once for multiple fetches", async () => {
const spy = vi.fn();
server.use(rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
(0, vitest_1.test)("when no groups are defined add a default that searches everything", async () => {
const { findkitFetch } = (0, index_1.createFindkitFetcher)({
searchEndpoint: "https://test.invalid/multi-search2",
});
server.use(msw_1.rest.post("https://test.invalid/multi-search2", async (req, res, ctx) => {
const requestBody = await req.json();
(0, vitest_1.expect)(requestBody).toEqual({ q: "test", groups: [{ tagQuery: [] }] });
const resData = {

@@ -163,55 +79,95 @@ groups: [],

}));
const fetcher = createFindkitFetcher({
async getJwtToken() {
spy();
return { jwt };
},
const res = await findkitFetch({
q: "test",
});
const jwt = createJwtToken();
for await (const i of Array(5).keys()) {
await fetcher.findkitFetch({
(0, vitest_1.expect)(res).toEqual({ duration: 123, groups: [] });
});
(0, vitest_1.describe)("jwt", () => {
(0, vitest_1.test)("calls getJwtToken() and passes the token in the p query string", async () => {
const spy = vitest_1.vi.fn();
server.use(msw_1.rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
spy(req.url.searchParams.get("p"));
const resData = {
groups: [],
duration: 123,
};
return res(ctx.json(resData));
}));
const { findkitFetch } = (0, index_1.createFindkitFetcher)({
searchEndpoint: "https://test.invalid/multi-search2",
q: i.toString(),
async getJwtToken() {
return { jwt };
},
});
const jwt = createJwtToken();
const res = await findkitFetch({
q: "",
groups: [],
});
}
expect(spy).toHaveBeenCalledTimes(1);
});
test("calls getJwtToken() again when server responds with expired error", async () => {
let expired = false;
server.use(rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
if (expired) {
expired = false;
const data = {
error: { type: "jwt-expired" },
(0, vitest_1.expect)(spy).toHaveBeenCalledTimes(1);
(0, vitest_1.expect)(spy).toHaveBeenCalledWith("jwt:" + jwt);
(0, vitest_1.expect)(res).toEqual({ duration: 123, groups: [] });
});
(0, vitest_1.test)("calls getJwtToken() only once for multiple fetches", async () => {
const spy = vitest_1.vi.fn();
server.use(msw_1.rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
const resData = {
groups: [],
duration: 123,
};
return res(ctx.status(403), ctx.json(data));
return res(ctx.json(resData));
}));
const fetcher = (0, index_1.createFindkitFetcher)({
searchEndpoint: "https://test.invalid/multi-search2",
async getJwtToken() {
spy();
return { jwt };
},
});
const jwt = createJwtToken();
for await (const i of Array(5).keys()) {
await fetcher.findkitFetch({
q: i.toString(),
groups: [],
});
}
const resData = {
(0, vitest_1.expect)(spy).toHaveBeenCalledTimes(1);
});
(0, vitest_1.test)("calls getJwtToken() again when server responds with expired error", async () => {
let expired = false;
server.use(msw_1.rest.post("https://test.invalid/multi-search2", (req, res, ctx) => {
if (expired) {
expired = false;
const data = {
code: "jwt-expired",
};
return res(ctx.status(403), ctx.json(data));
}
const resData = {
groups: [],
duration: 123,
};
return res(ctx.json(resData));
}));
const spy = vitest_1.vi.fn();
const fetcher = (0, index_1.createFindkitFetcher)({
searchEndpoint: "https://test.invalid/multi-search2",
async getJwtToken() {
spy();
const jwt = createJwtToken();
return { jwt };
},
});
await fetcher.findkitFetch({
q: "",
groups: [],
duration: 123,
};
return res(ctx.json(resData));
}));
const spy = vi.fn();
const fetcher = createFindkitFetcher({
async getJwtToken() {
spy();
const jwt = createJwtToken();
return { jwt };
},
});
expired = true;
await fetcher.findkitFetch({
q: "",
groups: [],
});
(0, vitest_1.expect)(spy).toBeCalledTimes(2);
});
await fetcher.findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
q: "",
groups: [],
});
expired = true;
await fetcher.findkitFetch({
searchEndpoint: "https://test.invalid/multi-search2",
q: "",
groups: [],
});
expect(spy).toBeCalledTimes(2);
});
});
/**
* @public
*/
export interface FindKitDeveloperOptions {
export interface FindkitFetchInit {
staging?: boolean;
logResponseTimes?: boolean;
publicToken?: string;
searchEndpoint?: string;
getJwtToken?: GetJwtToken;
}

@@ -32,11 +35,4 @@ /**

*/
export interface FindkitFetchOptions extends FindkitSearchParams, FindKitDeveloperOptions {
publicToken?: string;
searchEndpoint?: string;
}
/**
* @public
*/
export interface FindkitFetch {
(options: FindkitFetchOptions): Promise<FindkitSearchResponse>;
(options: FindkitSearchParams): Promise<FindkitSearchResponse>;
}

@@ -46,6 +42,5 @@ /**

*/
export interface JwtErrorResponse {
error: {
type?: "jwt-expired" | "jwt-invalid";
};
export interface FindkitErrorResponse {
code?: "jwt-expired" | "jwt-invalid" | "invalid-response-body";
message?: string;
}

@@ -56,3 +51,6 @@ /**

export interface GetJwtToken {
(): Promise<JwtToken>;
(options: {
publicToken?: string;
searchEndpoint: string;
}): Promise<JwtToken>;
}

@@ -74,5 +72,3 @@ /**

*/
export declare function createFindkitFetcher(props?: {
getJwtToken?: GetJwtToken;
}): {
export declare function createFindkitFetcher(init?: FindkitFetchInit): {
findkitFetch: FindkitFetch;

@@ -82,8 +78,6 @@ clear: () => void;

};
export declare function getRequestBody(options: FindkitFetchOptions): FindkitSearchParams;
export declare function getProjectSearchEndpoint(publicToken: string): string;
/**
* @public
*/
export declare const findkitFetch: FindkitFetch;
export declare function createSearchEndpoint(publicToken: string): string;
/**

@@ -94,3 +88,4 @@ * @public

q: string;
groups: FindkitSearchGroupParams[];
groups?: FindkitSearchGroupParams[];
signal?: AbortSignal;
}

@@ -114,4 +109,8 @@ /**

export declare type FindkitSearchResponse = {
duration: number;
groups: GroupSearchResults[];
duration: number;
messages?: {
code: "string";
message: string;
}[];
};

@@ -138,1 +137,2 @@ /**

export {};
//# sourceMappingURL=index.d.ts.map

@@ -0,5 +1,28 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSearchEndpoint = exports.createFindkitFetcher = void 0;
async function safeErrorJson(response) {
const text = await response.text().catch(() => {
return "Failed to read response body";
});
try {
return JSON.parse(text);
}
catch {
return {
code: "invalid-response-body",
message: `body: ${text.slice(0, 500)}`,
};
}
}
let logResponseTimes = false;
if (typeof window !== "undefined") {
logResponseTimes =
window.localStorage.getItem("findkit-log-response-times") === "true";
try {
logResponseTimes =
window.localStorage.getItem("findkit-log-response-times") === "true";
}
catch {
// local storage access can throw in some enviroments such a
// codesandox.io due to permission issues
}
}

@@ -9,3 +32,3 @@ /**

*/
export function createFindkitFetcher(props) {
function createFindkitFetcher(init) {
let currentJwtTokenPromise = null;

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

}
function refresh() {
if (typeof props?.getJwtToken === "function") {
currentJwtTokenPromise = props.getJwtToken();
const searchEndpointString = inferSearchEndpoint(init);
const refresh = () => {
const getJwtTokenArg = {
searchEndpoint: searchEndpointString,
};
if (typeof (init === null || init === void 0 ? void 0 : init.getJwtToken) === "function") {
currentJwtTokenPromise = init.getJwtToken(getJwtTokenArg);
}
}
else if (typeof FINDKIT_GET_JWT_TOKEN === "function") {
currentJwtTokenPromise = FINDKIT_GET_JWT_TOKEN(getJwtTokenArg);
}
};
const findkitFetch = async (options) => {
// new implementation
const fetchUrl = getSearchEndpoint(options);
var _a, _b;
if (!options.groups) {
options = {
...options,
groups: [
{
tagQuery: [],
},
],
};
}
const started = Date.now();
const requestBody = getRequestBody(options);
if (!currentJwtTokenPromise) {

@@ -33,38 +71,49 @@ refresh();

const token = await currentJwtTokenPromise;
const headers = {
// This looks wrong but is intentional. We want to make "Simple CORS
// requests" eg. requests without the OPTIONS preflight and
// application/json is not allowed for those. So we just have to use
// one of the allowed ones.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
"Content-Type": "text/plain",
};
const fetchOptions = {
method: "POST",
signal: (_a = options.signal) !== null && _a !== void 0 ? _a : null,
mode: "cors",
credentials: "omit",
headers: {
// This looks wrong but is intentional. We want to make "Simple CORS
// requests" eg. requests without the OPTIONS preflight and
// application/json is not allowed for those. So we just have to use
// one of the allowed ones.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
"Content-Type": "text/plain",
},
body: JSON.stringify(requestBody),
headers,
body: JSON.stringify({
q: options.q,
groups: options.groups,
}),
};
if (token && fetchOptions.headers) {
fetchOptions.headers["authorization"] = "Bearer " + token.jwt;
const endpoint = new URL(searchEndpointString);
if (token) {
if (typeof token.jwt !== "string") {
throw new Error("[findkit] Expected GetJwtToken response to contain a 'jwt' property");
}
endpoint.searchParams.set("p", `jwt:${token.jwt}`);
}
const res = await fetch(fetchUrl, fetchOptions);
const res = await fetch(endpoint.toString(), fetchOptions);
if (res.status === 403) {
const error = await res.json();
if (error.error.type === "jwt-expired") {
const error = await safeErrorJson(res);
if (error.code === "jwt-expired") {
refresh();
return findkitFetch(options);
}
throw new Error("[findkit] Permission denied: " + error.error.type);
throw new Error("[findkit] Permission denied: " + error.message);
}
if (!res.ok) {
throw new Error("[findkit] Bad response from search: " + res.status);
const error = await safeErrorJson(res);
throw new Error(`[findkit] Bad response ${res.status} from search: ${error.message}`);
}
const responses = await res.json();
if (options.logResponseTimes || logResponseTimes) {
if ((init === null || init === void 0 ? void 0 : init.logResponseTimes) || logResponseTimes) {
const total = Date.now() - started;
const backendDuration = responses.duration;
console.log(`[findkit] Response total ${total}ms, backend ${backendDuration}ms, network ${total - backendDuration}ms`);
options.groups.forEach((group, index) => {
const duration = responses.groups[index]?.duration ?? 0;
(_b = options.groups) === null || _b === void 0 ? void 0 : _b.forEach((group, index) => {
var _a, _b;
const duration = (_b = (_a = responses.groups[index]) === null || _a === void 0 ? void 0 : _a.duration) !== null && _b !== void 0 ? _b : 0;
console.log(`[findkit] Group response ${duration}ms for group "${index}"`, group);

@@ -81,25 +130,22 @@ });

}
export function getRequestBody(options) {
return {
q: options.q,
groups: options.groups,
};
}
function getSearchEndpoint(options) {
if (options.searchEndpoint) {
return options.searchEndpoint;
exports.createFindkitFetcher = createFindkitFetcher;
function inferSearchEndpoint(options) {
if (options === null || options === void 0 ? void 0 : options.searchEndpoint) {
return options === null || options === void 0 ? void 0 : options.searchEndpoint;
}
else if (options.publicToken) {
return getProjectSearchEndpoint(options.publicToken);
else if (options === null || options === void 0 ? void 0 : options.publicToken) {
return createSearchEndpoint(options.publicToken);
}
else {
throw new Error("Unable to determine search endpoint");
const msg = "[findkit] Unable to determine search endpoint";
console.error(`${msg}. The options object must contain either a 'publicToken' or a 'searchEndpoint' property`, options);
throw new Error(`${msg}. See logs for details`);
}
}
export function getProjectSearchEndpoint(publicToken) {
return `https://search.findkit.com/c/${publicToken}/search?p=${publicToken}`;
}
/**
* @public
*/
export const findkitFetch = createFindkitFetcher().findkitFetch;
function createSearchEndpoint(publicToken) {
return `https://search.findkit.com/c/${publicToken}/search?p=${publicToken}`;
}
exports.createSearchEndpoint = createSearchEndpoint;
/**
* @public
*/
export interface FindKitDeveloperOptions {
export interface FindkitFetchInit {
staging?: boolean;
logResponseTimes?: boolean;
publicToken?: string;
searchEndpoint?: string;
getJwtToken?: GetJwtToken;
}

@@ -32,11 +35,4 @@ /**

*/
export interface FindkitFetchOptions extends FindkitSearchParams, FindKitDeveloperOptions {
publicToken?: string;
searchEndpoint?: string;
}
/**
* @public
*/
export interface FindkitFetch {
(options: FindkitFetchOptions): Promise<FindkitSearchResponse>;
(options: FindkitSearchParams): Promise<FindkitSearchResponse>;
}

@@ -46,6 +42,5 @@ /**

*/
export interface JwtErrorResponse {
error: {
type?: "jwt-expired" | "jwt-invalid";
};
export interface FindkitErrorResponse {
code?: "jwt-expired" | "jwt-invalid" | "invalid-response-body";
message?: string;
}

@@ -56,3 +51,6 @@ /**

export interface GetJwtToken {
(): Promise<JwtToken>;
(options: {
publicToken?: string;
searchEndpoint: string;
}): Promise<JwtToken>;
}

@@ -74,5 +72,3 @@ /**

*/
export declare function createFindkitFetcher(props?: {
getJwtToken?: GetJwtToken;
}): {
export declare function createFindkitFetcher(init?: FindkitFetchInit): {
findkitFetch: FindkitFetch;

@@ -82,8 +78,6 @@ clear: () => void;

};
export declare function getRequestBody(options: FindkitFetchOptions): FindkitSearchParams;
export declare function getProjectSearchEndpoint(publicToken: string): string;
/**
* @public
*/
export declare const findkitFetch: FindkitFetch;
export declare function createSearchEndpoint(publicToken: string): string;
/**

@@ -94,3 +88,4 @@ * @public

q: string;
groups: FindkitSearchGroupParams[];
groups?: FindkitSearchGroupParams[];
signal?: AbortSignal;
}

@@ -114,4 +109,8 @@ /**

export declare type FindkitSearchResponse = {
duration: number;
groups: GroupSearchResults[];
duration: number;
messages?: {
code: "string";
message: string;
}[];
};

@@ -138,1 +137,2 @@ /**

export {};
//# sourceMappingURL=index.d.ts.map

@@ -0,5 +1,28 @@

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSearchEndpoint = exports.createFindkitFetcher = void 0;
async function safeErrorJson(response) {
const text = await response.text().catch(() => {
return "Failed to read response body";
});
try {
return JSON.parse(text);
}
catch {
return {
code: "invalid-response-body",
message: `body: ${text.slice(0, 500)}`,
};
}
}
let logResponseTimes = false;
if (typeof window !== "undefined") {
logResponseTimes =
window.localStorage.getItem("findkit-log-response-times") === "true";
try {
logResponseTimes =
window.localStorage.getItem("findkit-log-response-times") === "true";
}
catch {
// local storage access can throw in some enviroments such a
// codesandox.io due to permission issues
}
}

@@ -9,3 +32,3 @@ /**

*/
export function createFindkitFetcher(props) {
function createFindkitFetcher(init) {
let currentJwtTokenPromise = null;

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

}
function refresh() {
if (typeof props?.getJwtToken === "function") {
currentJwtTokenPromise = props.getJwtToken();
const searchEndpointString = inferSearchEndpoint(init);
const refresh = () => {
const getJwtTokenArg = {
searchEndpoint: searchEndpointString,
};
if (typeof (init === null || init === void 0 ? void 0 : init.getJwtToken) === "function") {
currentJwtTokenPromise = init.getJwtToken(getJwtTokenArg);
}
}
else if (typeof FINDKIT_GET_JWT_TOKEN === "function") {
currentJwtTokenPromise = FINDKIT_GET_JWT_TOKEN(getJwtTokenArg);
}
};
const findkitFetch = async (options) => {
// new implementation
const fetchUrl = getSearchEndpoint(options);
var _a, _b;
if (!options.groups) {
options = {
...options,
groups: [
{
tagQuery: [],
},
],
};
}
const started = Date.now();
const requestBody = getRequestBody(options);
if (!currentJwtTokenPromise) {

@@ -33,38 +71,49 @@ refresh();

const token = await currentJwtTokenPromise;
const headers = {
// This looks wrong but is intentional. We want to make "Simple CORS
// requests" eg. requests without the OPTIONS preflight and
// application/json is not allowed for those. So we just have to use
// one of the allowed ones.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
"Content-Type": "text/plain",
};
const fetchOptions = {
method: "POST",
signal: (_a = options.signal) !== null && _a !== void 0 ? _a : null,
mode: "cors",
credentials: "omit",
headers: {
// This looks wrong but is intentional. We want to make "Simple CORS
// requests" eg. requests without the OPTIONS preflight and
// application/json is not allowed for those. So we just have to use
// one of the allowed ones.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
"Content-Type": "text/plain",
},
body: JSON.stringify(requestBody),
headers,
body: JSON.stringify({
q: options.q,
groups: options.groups,
}),
};
if (token && fetchOptions.headers) {
fetchOptions.headers["authorization"] = "Bearer " + token.jwt;
const endpoint = new URL(searchEndpointString);
if (token) {
if (typeof token.jwt !== "string") {
throw new Error("[findkit] Expected GetJwtToken response to contain a 'jwt' property");
}
endpoint.searchParams.set("p", `jwt:${token.jwt}`);
}
const res = await fetch(fetchUrl, fetchOptions);
const res = await fetch(endpoint.toString(), fetchOptions);
if (res.status === 403) {
const error = await res.json();
if (error.error.type === "jwt-expired") {
const error = await safeErrorJson(res);
if (error.code === "jwt-expired") {
refresh();
return findkitFetch(options);
}
throw new Error("[findkit] Permission denied: " + error.error.type);
throw new Error("[findkit] Permission denied: " + error.message);
}
if (!res.ok) {
throw new Error("[findkit] Bad response from search: " + res.status);
const error = await safeErrorJson(res);
throw new Error(`[findkit] Bad response ${res.status} from search: ${error.message}`);
}
const responses = await res.json();
if (options.logResponseTimes || logResponseTimes) {
if ((init === null || init === void 0 ? void 0 : init.logResponseTimes) || logResponseTimes) {
const total = Date.now() - started;
const backendDuration = responses.duration;
console.log(`[findkit] Response total ${total}ms, backend ${backendDuration}ms, network ${total - backendDuration}ms`);
options.groups.forEach((group, index) => {
const duration = responses.groups[index]?.duration ?? 0;
(_b = options.groups) === null || _b === void 0 ? void 0 : _b.forEach((group, index) => {
var _a, _b;
const duration = (_b = (_a = responses.groups[index]) === null || _a === void 0 ? void 0 : _a.duration) !== null && _b !== void 0 ? _b : 0;
console.log(`[findkit] Group response ${duration}ms for group "${index}"`, group);

@@ -81,25 +130,22 @@ });

}
export function getRequestBody(options) {
return {
q: options.q,
groups: options.groups,
};
}
function getSearchEndpoint(options) {
if (options.searchEndpoint) {
return options.searchEndpoint;
exports.createFindkitFetcher = createFindkitFetcher;
function inferSearchEndpoint(options) {
if (options === null || options === void 0 ? void 0 : options.searchEndpoint) {
return options === null || options === void 0 ? void 0 : options.searchEndpoint;
}
else if (options.publicToken) {
return getProjectSearchEndpoint(options.publicToken);
else if (options === null || options === void 0 ? void 0 : options.publicToken) {
return createSearchEndpoint(options.publicToken);
}
else {
throw new Error("Unable to determine search endpoint");
const msg = "[findkit] Unable to determine search endpoint";
console.error(`${msg}. The options object must contain either a 'publicToken' or a 'searchEndpoint' property`, options);
throw new Error(`${msg}. See logs for details`);
}
}
export function getProjectSearchEndpoint(publicToken) {
return `https://search.findkit.com/c/${publicToken}/search?p=${publicToken}`;
}
/**
* @public
*/
export const findkitFetch = createFindkitFetcher().findkitFetch;
function createSearchEndpoint(publicToken) {
return `https://search.findkit.com/c/${publicToken}/search?p=${publicToken}`;
}
exports.createSearchEndpoint = createSearchEndpoint;
{
"name": "@findkit/fetch",
"version": "2.0.0-dev.6f4e684575",
"version": "2.0.0-dev.6f6a1c6044",
"description": "minimal fetch for findkit",
"main": "dist/index.js",
"author": "Valu Digital Oy",
"license": "ISC",
"license": "MIT",
"devDependencies": {
"@types/jsonwebtoken": "^8.5.8",
"@valu/npm-tools": "^1.1.2",
"@valu/npm-tools": "^1.5.0",
"eslint": "8.21.0",

@@ -12,0 +12,0 @@ "eslint-plugin-react": "^7.28.0",

/**
* @public
*/
export interface FindKitDeveloperOptions {
export interface FindkitFetchInit {
staging?: boolean;
logResponseTimes?: boolean;
publicToken?: string;
searchEndpoint?: string;
getJwtToken?: GetJwtToken;
}

@@ -30,7 +33,4 @@

*/
export interface FindkitFetchOptions
extends FindkitSearchParams,
FindKitDeveloperOptions {
publicToken?: string;
searchEndpoint?: string;
export interface FindkitFetch {
(options: FindkitSearchParams): Promise<FindkitSearchResponse>;
}

@@ -41,13 +41,22 @@

*/
export interface FindkitFetch {
(options: FindkitFetchOptions): Promise<FindkitSearchResponse>;
export interface FindkitErrorResponse {
code?: "jwt-expired" | "jwt-invalid" | "invalid-response-body";
message?: string;
}
/**
* @public
*/
export interface JwtErrorResponse {
error: {
type?: "jwt-expired" | "jwt-invalid";
};
async function safeErrorJson(
response: Response
): Promise<FindkitErrorResponse> {
const text = await response.text().catch(() => {
return "Failed to read response body";
});
try {
return JSON.parse(text);
} catch {
return {
code: "invalid-response-body",
message: `body: ${text.slice(0, 500)}`,
};
}
}

@@ -59,3 +68,6 @@

export interface GetJwtToken {
(): Promise<JwtToken>;
(options: {
publicToken?: string;
searchEndpoint: string;
}): Promise<JwtToken>;
}

@@ -77,10 +89,20 @@

if (typeof window !== "undefined") {
logResponseTimes =
window.localStorage.getItem("findkit-log-response-times") === "true";
try {
logResponseTimes =
window.localStorage.getItem("findkit-log-response-times") === "true";
} catch {
// local storage access can throw in some enviroments such a
// codesandox.io due to permission issues
}
}
/**
* Global JWT token fethcer
*/
declare const FINDKIT_GET_JWT_TOKEN: GetJwtToken | undefined;
/**
* @public
*/
export function createFindkitFetcher(props?: { getJwtToken?: GetJwtToken }) {
export function createFindkitFetcher(init?: FindkitFetchInit) {
let currentJwtTokenPromise: Promise<JwtToken> | null = null;

@@ -95,13 +117,29 @@

function refresh() {
if (typeof props?.getJwtToken === "function") {
currentJwtTokenPromise = props.getJwtToken();
const searchEndpointString = inferSearchEndpoint(init);
const refresh = () => {
const getJwtTokenArg: Parameters<GetJwtToken>[0] = {
searchEndpoint: searchEndpointString,
};
if (typeof init?.getJwtToken === "function") {
currentJwtTokenPromise = init.getJwtToken(getJwtTokenArg);
} else if (typeof FINDKIT_GET_JWT_TOKEN === "function") {
currentJwtTokenPromise = FINDKIT_GET_JWT_TOKEN(getJwtTokenArg);
}
}
};
const findkitFetch: FindkitFetch = async (options: FindkitFetchOptions) => {
// new implementation
const fetchUrl = getSearchEndpoint(options);
const findkitFetch: FindkitFetch = async (options: FindkitSearchParams) => {
if (!options.groups) {
options = {
...options,
groups: [
{
tagQuery: [],
},
],
};
}
const started = Date.now();
const requestBody = getRequestBody(options);

@@ -114,27 +152,40 @@ if (!currentJwtTokenPromise) {

const fetchOptions: PostRequestInit = {
const headers: Record<string, string> = {
// This looks wrong but is intentional. We want to make "Simple CORS
// requests" eg. requests without the OPTIONS preflight and
// application/json is not allowed for those. So we just have to use
// one of the allowed ones.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
"Content-Type": "text/plain",
};
const fetchOptions: RequestInit = {
method: "POST",
signal: options.signal ?? null,
mode: "cors",
credentials: "omit",
headers: {
// This looks wrong but is intentional. We want to make "Simple CORS
// requests" eg. requests without the OPTIONS preflight and
// application/json is not allowed for those. So we just have to use
// one of the allowed ones.
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
"Content-Type": "text/plain",
},
body: JSON.stringify(requestBody),
headers,
body: JSON.stringify({
q: options.q,
groups: options.groups,
}),
};
if (token && fetchOptions.headers) {
fetchOptions.headers["authorization"] = "Bearer " + token.jwt;
const endpoint = new URL(searchEndpointString);
if (token) {
if (typeof token.jwt !== "string") {
throw new Error(
"[findkit] Expected GetJwtToken response to contain a 'jwt' property"
);
}
endpoint.searchParams.set("p", `jwt:${token.jwt}`);
}
const res = await fetch(fetchUrl, fetchOptions);
const res = await fetch(endpoint.toString(), fetchOptions);
if (res.status === 403) {
const error: JwtErrorResponse = await res.json();
const error = await safeErrorJson(res);
if (error.error.type === "jwt-expired") {
if (error.code === "jwt-expired") {
refresh();

@@ -144,7 +195,10 @@ return findkitFetch(options);

throw new Error("[findkit] Permission denied: " + error.error.type);
throw new Error("[findkit] Permission denied: " + error.message);
}
if (!res.ok) {
throw new Error("[findkit] Bad response from search: " + res.status);
const error = await safeErrorJson(res);
throw new Error(
`[findkit] Bad response ${res.status} from search: ${error.message}`
);
}

@@ -154,3 +208,3 @@

if (options.logResponseTimes || logResponseTimes) {
if (init?.logResponseTimes || logResponseTimes) {
const total = Date.now() - started;

@@ -165,3 +219,3 @@ const backendDuration = responses.duration;

options.groups.forEach((group, index) => {
options.groups?.forEach((group, index) => {
const duration = responses.groups[index]?.duration ?? 0;

@@ -185,29 +239,23 @@ console.log(

export function getRequestBody(
options: FindkitFetchOptions
): FindkitSearchParams {
return {
q: options.q,
groups: options.groups,
};
}
function getSearchEndpoint(options: FindkitFetchOptions) {
if (options.searchEndpoint) {
return options.searchEndpoint;
} else if (options.publicToken) {
return getProjectSearchEndpoint(options.publicToken);
function inferSearchEndpoint(options?: FindkitFetchInit) {
if (options?.searchEndpoint) {
return options?.searchEndpoint;
} else if (options?.publicToken) {
return createSearchEndpoint(options.publicToken);
} else {
throw new Error("Unable to determine search endpoint");
const msg = "[findkit] Unable to determine search endpoint";
console.error(
`${msg}. The options object must contain either a 'publicToken' or a 'searchEndpoint' property`,
options
);
throw new Error(`${msg}. See logs for details`);
}
}
export function getProjectSearchEndpoint(publicToken: string) {
return `https://search.findkit.com/c/${publicToken}/search?p=${publicToken}`;
}
/**
* @public
*/
export const findkitFetch: FindkitFetch = createFindkitFetcher().findkitFetch;
export function createSearchEndpoint(publicToken: string) {
return `https://search.findkit.com/c/${publicToken}/search?p=${publicToken}`;
}

@@ -219,3 +267,4 @@ /**

q: string;
groups: FindkitSearchGroupParams[];
groups?: FindkitSearchGroupParams[];
signal?: AbortSignal;
}

@@ -241,4 +290,8 @@

export type FindkitSearchResponse = {
duration: number;
groups: GroupSearchResults[];
duration: number;
messages?: {
code: "string";
message: string;
}[];
};

@@ -245,0 +298,0 @@

{
"compilerOptions": {
"target": "es2020",
"strict": true,
"moduleResolution": "node",
"noUncheckedIndexedAccess": true,
"declaration": true,
"declarationDir": "./dist",
"skipLibCheck": true,
"outDir": "dist"
}
"compilerOptions": {
"target": "es2019",
"strict": true,
"module": "commonjs",
"moduleResolution": "node",
"noUncheckedIndexedAccess": true,
"declaration": true,
"declarationDir": "./dist",
"declarationMap": true,
"skipLibCheck": true,
"outDir": "dist"
}
}