refactor: made createModel return a function that returns ISearchModel; allowing stage based offsets in Meili API

This commit is contained in:
Ken Snyder
2022-02-07 03:37:33 -08:00
parent b153e3266d
commit 5aaaafe7ff
34 changed files with 264 additions and 132 deletions

View File

@@ -21,6 +21,7 @@
"@vueuse/core": "^7.5.5",
"@vueuse/head": "^0.7.5",
"date-fns": "^2.28.0",
"docs-searchbar.js": "^2.1.0",
"floating-vue": "^2.0.0-beta.5",
"inferred-types": "^0.18.4",
"markdown-it-expandable": "^1.0.0",

View File

@@ -30,6 +30,7 @@ declare module 'vue' {
'Ph:info': typeof import('~icons/ph/info')['default']
'Ph:linkLight': typeof import('~icons/ph/link-light')['default']
README: typeof import('./components/README.md')['default']
SearchBar: typeof import('./components/SearchBar.vue')['default']
SearchHit: typeof import('./components/SearchHit.vue')['default']
SearchIndexes: typeof import('./components/SearchIndexes.vue')['default']
SearchResults: typeof import('./components/SearchResults.vue')['default']

View File

@@ -15,7 +15,7 @@ const indexName = computed(() => props.idx.name as "api" | "repo" | "prose");
const remove = async () => {
// just using ApiModel at random, as we can state which index to remove
await ApiModel.query.deleteIndex(indexName.value);
await ApiModel().query.deleteIndex(indexName.value);
};
const pushCache = async() => {

View File

@@ -5,10 +5,10 @@ const props = defineProps({
idx: {type: String as PropType<"api" | "prose" | "repo" | "consolidated">, required: true}
});
const models = {
api: ApiModel,
prose: ProseModel,
repo: RepoModel,
consolidated: ConsolidatedModel
api: ApiModel(),
prose: ProseModel(),
repo: RepoModel(),
consolidated: ConsolidatedModel()
};
async function addIndex() {

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
import docsSearchBar from "docs-searchbar.js";
docsSearchBar({
hostUrl: "http://localhost:7700",
apiKey: "",
indexUid: "docs",
inputSelector: "#search-bar-input",
});
</script>
<template>
<div>
<input id="search-bar-input" type="text">
</div>
</template>

View File

@@ -1,18 +1,19 @@
export const SERVERS = [
{
export const SERVERS = {
local: {
name: "local",
url: "http://localhost:7700",
},
{
name: "prod",
production: {
name: "production",
url: "https://search.tauri.studio",
token: "",
indexes: ["unknown"],
search_key: "ea0105f56bb5a2111ed28c7a0c637fc0bed07273f571dc7cb1f73900e44f8e7f",
indexes: ["v1_0_0_beta_8"],
},
{
staging: {
name: "staging",
url: "https://search2.tauri.studio",
token: "XZEH8BS90ee09c45215a8421c06857bcbde5c1a6797bdf4859a57a3ac1228a2b81df0994",
search_key: "XZEH8BS90ee09c45215a8421c06857bcbde5c1a6797bdf4859a57a3ac1228a2b81df0994",
indexes: ["consolidated"],
},
];
};

View File

@@ -2,15 +2,17 @@
/* eslint-disable no-use-before-define */
import { acceptHMRUpdate, defineStore } from "pinia";
import type { UserModule } from "~/types";
import type {
import {
IMeiliSearchHealth,
IMeilisearchIndex,
IMeilisearchInterface,
IMeilisearchSearchResponse,
IMeiliSearchStats,
IMeilisearchIndexSettings,
ConsolidatedModel,
} from "tauri-search";
import { ApiModel } from "tauri-search";
import { SERVERS } from "~/constants";
//#region STORE
export interface SearchState {
@@ -34,6 +36,8 @@ export interface SearchState {
searchQuery: string;
searchResults: { id: string; _idx: string; [key: string]: unknown }[];
prodResults: { id: string; _idx: string; [key: string]: unknown }[];
}
export const useSearch = defineStore("search", {
@@ -47,6 +51,7 @@ export const useSearch = defineStore("search", {
searchQuery: "",
searchStatus: "not-ready",
searchResults: [],
prodResults: []
} as SearchState),
actions: {
async search(text: string, force: boolean = false) {
@@ -67,6 +72,7 @@ export const useSearch = defineStore("search", {
console.time("search");
this.$state.searchStatus = "searching";
// local search
const waitFor: Promise<any>[] = [];
for (const idx of indexes) {
const addIndex = (
@@ -92,12 +98,31 @@ export const useSearch = defineStore("search", {
console.groupEnd();
this.$state.searchResults = hits;
// prod search
const waitForProd: Promise<any>[] = [];
for (const idx of SERVERS.production.indexes) {
const addIndex = (
result: IMeilisearchSearchResponse
): IMeilisearchSearchResponse => ({
...result,
hits: result.hits.map((i) => ({ ...i, _idx: idx })),
});
const model = ConsolidatedModel("production");
waitForProd.push(
model.query.search(text, SERVERS.production.indexes[0]).then(r => addIndex(r))
);
}
this.$state.prodResults = await Promise.all(waitForProd);
return results;
},
/** updates settings for all active indexes */
async updateIndexSettings() {
for (const idx of this.indexes.map((i) => i.name)) {
const result = (await ApiModel.query.getIndexSettings(
const result = (await ApiModel().query.getIndexSettings(
idx
)) as IMeilisearchIndexSettings<any>;
@@ -196,7 +221,7 @@ export const install: UserModule = ({ isClient }) => {
};
async function get<T extends {}, U extends {} = never>(url: string, cb?: (r: T) => U) {
const res = await fetch(url);
const res = await fetch(url, {headers: { "Access-Control-Allow-Origin" : "*"}});
if (res.ok) {
const result = res.json();
@@ -211,12 +236,28 @@ async function get<T extends {}, U extends {} = never>(url: string, cb?: (r: T)
}
}
function api(base: string = "http://localhost:7700") {
async function post<T extends {}, U extends {} = never>(url: string, body: string, cb?: (r: T) => U) {
const res = await fetch(url, { method: "post", body, headers: { "Access-Control-Allow-Origin" : "*", "Content-Type": "application/json"}});
if (res.ok) {
const result = res.json();
return (
cb ? (result.then((r) => cb(r)) as Promise<U>) : (result as Promise<T>)
) as never extends U ? Promise<T> : Promise<U>;
} else {
console.groupCollapsed(`Error with API`);
console.info(`Request: GET ${url}`);
console.warn(`Error [${res.status}]: ${res.statusText}`);
console.groupEnd();
}
}
function api(baseUrl: string = "http://localhost:7700") {
return {
search: (idx: string, text: string) => `${base}/indexes/${idx}/search?q=${text}`,
stats: `${base}/stats`,
health: `${base}/health`,
indexes: `${base}/indexes`,
search: (idx: string, text: string) => `${baseUrl}/indexes/${idx}/search?q=${text}`,
stats: `${baseUrl}/stats`,
health: `${baseUrl}/health`,
indexes: `${baseUrl}/indexes`,
};
}

View File

@@ -1,5 +1,6 @@
# MeiliSearch Search Bar
![docssearch bar](./docs-searchbar.png)
## First Considerations

View File

@@ -1,12 +1,10 @@
<script setup lang="ts">
import { onStartTyping } from "@vueuse/core";
import { useSearch } from "~/modules/search";
import { SERVERS } from "~/constants";
const el = ref();
const s = useSearch();
const searchText = ref(s.$state.searchQuery);
const { t } = useI18n();
const serverChosen = ref("local");
debouncedWatch(
searchText,
@@ -20,9 +18,6 @@ onStartTyping(() => {
if (!el.value.active) {el.value.focus();};
});
const changeServer = (_server: {name: string; url: string}) => {
//
};
</script>
@@ -34,17 +29,7 @@ const changeServer = (_server: {name: string; url: string}) => {
<div class="grid grid-cols-3 gap-x-4">
<div class="left flex flex-grow items-center justify-center">
<div class="flex flex-row space-x-2">
<div class="mr-2">Server:</div>
<div
v-for="server in SERVERS"
:key="server.name"
v-tooltip="{content: server.url}"
class="server text-gray-500 font-light"
:class="server.name === serverChosen ? `text-gray-800 dark:text-gray-200 border-b-1 cursor-default` : `cursor-pointer`"
@click="() => changeServer(server)"
>
{{server.name}}
</div>
<!-- -->
</div>
</div>

View File

@@ -14,3 +14,5 @@ declare module "*.vue" {
const component: DefineComponent<{}, {}, any>;
export default component;
}
declare module "docs-searchbar.js";

View File

@@ -3,7 +3,7 @@ import { ApiModel } from "..";
(async () => {
try {
const result = await ApiModel.query.currentIndexes();
const result = await ApiModel().query.currentIndexes();
console.log(`MeiliSearch currently has the following indexes:\n`);
for (const r of result) {
console.log(` > ${r.name} - created at ${r.createdAt}`);

View File

@@ -2,9 +2,9 @@
import { ApiModel } from "~/models";
(async () => {
const active = (await ApiModel.query.currentIndexes()).map((i) => i.name);
const active = (await ApiModel().query.currentIndexes()).map((i) => i.name);
console.log(`- clearing all active indexes: ${active.join(", ")}`);
for (const idx of active) {
await ApiModel.query.deleteIndex(idx);
await ApiModel().query.deleteIndex(idx);
}
})();

View File

@@ -3,21 +3,18 @@ import { existsSync } from "node:fs";
import { REPO_DOCS_CACHE, TS_DOCS_CACHE } from "~/constants";
import { ApiModel, ConsolidatedModel, ProseModel, RepoModel } from "~/models";
import {
proseDocsCacheFile,
pushProseDocs,
pushRepoDocs,
pushTypescriptDocs,
refreshProse,
refreshRepos,
refreshTypescript,
} from "~/pipelines";
import { pushConsolidatedDocs } from "~/pipelines/pushConsolidatedDocs";
import { communicateTaskStatus } from "~/utils/communicateTaskStatus";
import { getEnv } from "~/utils/getEnv";
(async () => {
console.log(`- Pushing ALL document caches into local MeiliSearch server`);
const idx = (await ApiModel.query.currentIndexes()).map((i) => i.name);
const idx = (await ApiModel().query.currentIndexes()).map((i) => i.name);
if (idx.length > 0) {
console.log(
`- found the following indexes setup: ${idx.join(
@@ -26,7 +23,7 @@ import { getEnv } from "~/utils/getEnv";
);
}
[ApiModel, RepoModel, ProseModel, ConsolidatedModel].forEach(async (model) => {
[ApiModel(), RepoModel(), ProseModel(), ConsolidatedModel()].forEach(async (model) => {
if (!idx.includes(model.name)) {
console.log(
`- creating the "${model.name}" index with the following config: ${JSON.stringify(
@@ -37,18 +34,13 @@ import { getEnv } from "~/utils/getEnv";
}
});
const { repo, branch } = getEnv();
if (!existsSync(proseDocsCacheFile(repo, branch))) {
await refreshProse(repo, branch);
}
console.log(`- Pushing "prose" documents to MeiliSearch`);
const proseTasks = await pushProseDocs(repo, branch);
const proseTasks = await pushProseDocs();
console.log(
`- all ${proseTasks.length} documents were pushed via API; monitoring task status ...`
);
await communicateTaskStatus(ProseModel, proseTasks, { timeout: 45000 });
await communicateTaskStatus(ProseModel(), proseTasks, { timeout: 45000 });
console.log(`- Pushing Repo document cache into MeiliSearch`);
@@ -70,11 +62,11 @@ import { getEnv } from "~/utils/getEnv";
`- Completed pushing all ${docs.length} Repo docs to MeiliSearch; monitoring queue status`
);
await communicateTaskStatus(RepoModel, repoTasks);
await communicateTaskStatus(RepoModel(), repoTasks);
if (!existsSync(TS_DOCS_CACHE)) {
console.log(`- The Typescript documents cache wasn't found; creating first`);
await refreshTypescript(repo, branch);
await refreshTypescript();
}
console.log(`- Starting update process for Typescript API documents`);
@@ -94,15 +86,15 @@ import { getEnv } from "~/utils/getEnv";
console.log(
`- Completed pushing all Typescript docs [${tasks.length}] to MeiliSearch. Now monitoring task progress ...`
);
communicateTaskStatus(ApiModel, tasks, { timeout: 45000 });
communicateTaskStatus(ApiModel(), tasks, { timeout: 45000 });
}
const { tasks: consolidatedTasks } = await pushConsolidatedDocs(repo, branch);
const { tasks: consolidatedTasks } = await pushConsolidatedDocs();
console.log();
console.log(
`- all consolidated documents [${tasks.length}] have been pushed to MeiliSearch queue`
);
communicateTaskStatus(ConsolidatedModel, consolidatedTasks, { timeout: 75000 });
communicateTaskStatus(ConsolidatedModel(), consolidatedTasks, { timeout: 75000 });
}
})();

View File

@@ -12,5 +12,5 @@ import { ConsolidatedModel } from "~/models";
`- all consolidated documents [${tasks.length}] have been pushed to MeiliSearch queue`
);
communicateTaskStatus(ConsolidatedModel, tasks, { timeout: 75000 });
communicateTaskStatus(ConsolidatedModel(), tasks, { timeout: 75000 });
})();

View File

@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import { pushProseDocs } from "~/pipelines/pushProseDocs";
import { pushProseDocs } from "~/pipelines";
import { communicateTaskStatus } from "~/utils/communicateTaskStatus";
import { ProseModel } from "..";
import { ProseModel } from "~/models";
(async () => {
console.log(`- Pushing "prose" documents to MeiliSearch`);
@@ -10,5 +10,5 @@ import { ProseModel } from "..";
`- all ${tasks.length} documents were pushed via API; monitoring task status ...`
);
await communicateTaskStatus(ProseModel, tasks, { timeout: 75000 });
await communicateTaskStatus(ProseModel(), tasks, { timeout: 75000 });
})();

View File

@@ -20,6 +20,6 @@ import { RepoModel } from "..";
`- Completed pushing all ${docs.length} Repo docs to MeiliSearch; monitoring queue status`
);
await communicateTaskStatus(RepoModel, tasks);
await communicateTaskStatus(RepoModel(), tasks);
}
})();

View File

@@ -21,6 +21,6 @@ import { communicateTaskStatus } from "~/utils/communicateTaskStatus";
console.log(
`- Completed pushing all Typescript docs [${tasks.length}] to MeiliSearch. Now monitoring task progress ...`
);
communicateTaskStatus(ApiModel, tasks, { timeout: 65000 });
communicateTaskStatus(ApiModel(), tasks, { timeout: 65000 });
}
})();

View File

@@ -1,3 +1,5 @@
import { Stage } from "~/types";
export const TAURI_BASE_URL = `https://tauri.studio`;
export const TAURI_JS_DOCS_URL = `${TAURI_BASE_URL}/docs/api/js`;
@@ -9,6 +11,12 @@ export const TS_DOCS_CACHE = `src/generated/ast/api/ts-documents.json`;
export const TS_AST_CACHE = `src/generated/ast/api/ts-ast.json`;
export const RS_DOCS_CACHE = `src/generated/ast/api/rs-documents.json`;
export const SERVERS: Record<Stage, { url: string; search_key: string }> = {
local: { url: "http://localhost:7700", search_key: "" },
staging: { url: "https://search2.tauri.studio", search_key: "" },
production: { url: "https://search.tauri.studio", search_key: "" },
};
export const REPOS: `${string}/${string}`[] = [
"tauri-apps/tauri",
"tauri-apps/wry",

File diff suppressed because one or more lines are too long

View File

@@ -1,21 +0,0 @@
import { IApiModel, IProseModel, IRepoModel } from "~/models";
/**
* Gets the document cache for a given index
*/
export async function getCache(
index: "api" | "prose" | "repo",
_options: { repo?: string; branch?: string } = {}
): Promise<(IProseModel | IRepoModel | IApiModel)[]> {
switch (index) {
case "prose":
return (await import(`~/generated/ast/prose/tauri_dev/documents.json`))
.default as IProseModel[];
case "repo":
return (await import("~/generated/ast/repo/documents.json"))
.default as IRepoModel[];
case "api":
return (await import("~/generated/ast/api/ts-documents.json"))
.default as IApiModel[];
}
}

View File

@@ -23,13 +23,13 @@ export async function pushDocs(
const t: Promise<IMeilisearchTaskStatus>[] = [];
for (const doc of docs) {
if (isProseDocument(doc)) {
t.push(ProseModel.query.addOrReplaceDocuments(doc));
t.push(ProseModel().query.addOrReplaceDocuments(doc));
} else if (isRepoDocument(doc)) {
t.push(RepoModel.query.addOrReplaceDocuments(doc));
t.push(RepoModel().query.addOrReplaceDocuments(doc));
} else if (isApiDocument(doc)) {
t.push(ApiModel.query.addOrReplaceDocuments(doc));
t.push(ApiModel().query.addOrReplaceDocuments(doc));
} else if (isConsolidatedDocument(doc)) {
t.push(ConsolidatedModel.query.addOrReplaceDocuments(doc));
t.push(ConsolidatedModel().query.addOrReplaceDocuments(doc));
}
}
const tasks = await Promise.all(t);

View File

@@ -2,10 +2,10 @@
import { ProseModel, ApiModel, RepoModel, ConsolidatedModel } from "~/models";
const models = {
api: ApiModel,
repo: RepoModel,
prose: ProseModel,
consolidated: ConsolidatedModel,
api: ApiModel(),
repo: RepoModel(),
prose: ProseModel(),
consolidated: ConsolidatedModel(),
};
/**
@@ -13,7 +13,7 @@ const models = {
* present in server.
*/
export async function createIndexes() {
const skipping = (await ProseModel.query.currentIndexes()).map((i) => i.name);
const skipping = (await ProseModel().query.currentIndexes()).map((i) => i.name);
const created: string[] = [];
for (const key of Object.keys(models)) {
const model = models[key as keyof typeof models];

View File

@@ -24,7 +24,7 @@ export async function pushConsolidatedDocs(options: Partial<IEnv> = {}) {
const errors: IConsolidatedModel[] = [];
const tasks: IMonitoredTask[] = [];
for (const doc of docs) {
const res = await ConsolidatedModel.query.addOrReplaceDocuments(doc);
const res = await ConsolidatedModel().query.addOrReplaceDocuments(doc);
if (res.status !== "enqueued") {
errors.push(doc);
} else {

View File

@@ -1,7 +1,7 @@
import { ProseModel } from "~/models/ProseModel";
import { CacheKind, getCache } from "~/utils/getCache";
import { getEnv, IEnv } from "~/utils/getEnv";
import { IMonitoredTask } from "..";
import { IMonitoredTask } from "~/types";
/**
* Pushes the cached prose documents into the MeiliSearch "prose" index
@@ -14,8 +14,8 @@ export async function pushProseDocs(options: Partial<IEnv> = {}) {
for (const doc of cache) {
tasks.push(
await ProseModel.query
.addOrReplaceDocuments(doc)
await ProseModel()
.query.addOrReplaceDocuments(doc)
.then((i) => ({ docId: doc.id, taskId: i.uid }))
);
}

View File

@@ -13,7 +13,7 @@ export async function pushRepoDocs(options: Partial<IEnv> = {}) {
const tasks: IMonitoredTask[] = [];
for (const doc of docs) {
const res = await RepoModel.query.addOrReplaceDocuments(doc);
const res = await RepoModel().query.addOrReplaceDocuments(doc);
if (res.status !== "enqueued") {
errors.push(doc);
} else {

View File

@@ -20,7 +20,7 @@ export async function pushTypescriptDocs(options: Partial<IEnv> = {}) {
const tasks: IMonitoredTask[] = [];
for (const doc of docs) {
const res = await ApiModel.query.addOrReplaceDocuments(doc);
const res = await ApiModel().query.addOrReplaceDocuments(doc);
if (res.status !== "enqueued") {
errors.push(doc);
} else {

View File

@@ -10,16 +10,12 @@ import { IApiModel, TypescriptBlock } from "..";
* Refreshes the document cache
*/
export async function refreshTypescript(options: Partial<IEnv> = {}) {
const { org, repo, branch } = { ...getEnv(), ...options };
const { org, repo, branch, tsAstPath } = { ...getEnv(), ...options };
const { cacheFile } = await getCache(CacheKind.typescriptDocs, {
...getEnv(),
...options,
});
const ast = (await getRepoFile(
`${org}/${repo}`,
"docs/api/js/js-api.json",
branch
)) as TypescriptBlock;
const ast = (await getRepoFile(`${org}/${repo}`, tsAstPath, branch)) as TypescriptBlock;
const simplified = await parseTypescriptAst(ast);

View File

@@ -1,5 +1,5 @@
import { AxiosRequestConfig } from 'axios';
import { RankingRule, Wildcard } from '.';
import { AxiosRequestConfig } from "axios";
import { RankingRule, Wildcard } from ".";
import { ApiOptions } from "~/utils/MeiliSearchApi";
export interface MsIndexStatusResponse {
@@ -200,7 +200,7 @@ export interface IMeiliSearchQueryApi<TDoc extends {}> {
addOrReplaceDocuments: (doc: TDoc, o?: ApiOptions) => Promise<IMeilisearchAddOrReplace>;
addOrUpdateDocuments: (doc: TDoc, o?: ApiOptions) => Promise<IMeilisearchAddOrReplace>;
search: (text: string) => Promise<IMeilisearchSearchResponse>;
search: (text: string, altIndex?: string) => Promise<IMeilisearchSearchResponse>;
getIndexSettings: (override?: string) => Promise<IMeilisearchIndexSettings<TDoc>>;
updateIndexSettings: (

View File

@@ -2,6 +2,8 @@ import { IndexSynonyms, RankingRule, RankingRulesApi } from "~/types/apis";
import { MeiliSearchApi } from "~/utils/MeiliSearchApi";
import { IMeiliSearchQueryApi } from ".";
export type Stage = "production" | "staging" | "local";
export type MeiliApi = ReturnType<typeof MeiliSearchApi>;
export type Wildcard<T> = (keyof T)[] | ["*"];

View File

@@ -13,9 +13,11 @@ import {
ISearchConfig,
IMeilisearchAllTasks,
} from "~/types";
import { getEnv } from "./getEnv";
export interface MeiliSearchOptions {
url?: string;
search_key?: string;
}
export type PagingOptions = {
limit?: number;
@@ -30,6 +32,12 @@ export function MeiliSearchApi<TDoc extends {}>(
) {
const baseURL = options.url || "http://localhost:7700";
const idx = model.name;
const { adminKey, searchKey } = getEnv();
const headers = {
"X-Meili-API-Key": options.search_key || adminKey || searchKey || "",
"Access-Control-Allow-Origin": "*",
};
const call = async <T>(
method: "get" | "post" | "put" | "delete",
@@ -45,6 +53,7 @@ export function MeiliSearchApi<TDoc extends {}>(
...options,
headers: {
"Content-Type": options.data ? "application/json" : "application/text",
...headers,
},
},
}).catch((err) => {
@@ -155,8 +164,8 @@ export function MeiliSearchApi<TDoc extends {}>(
post<IMeilisearchAddOrReplace>(`indexes/${idx}/documents`, JSON.stringify(doc), o),
addOrUpdateDocuments: (doc: TDoc, o: ApiOptions = {}) =>
put<IMeilisearchAddOrReplace>(`indexes/${idx}/documents`, JSON.stringify(doc), o),
search: (text: string) =>
get<IMeilisearchSearchResponse>(`indexes/${idx}/search?q=${text}`),
search: (text: string, altIndex?: string) =>
get<IMeilisearchSearchResponse>(`indexes/${altIndex || idx}/search?q=${text}`),
getIndexSettings: (override?: string) =>
get<IMeilisearchIndexSettings<TDoc>>(`indexes/${override || idx}/settings`),
updateIndexSettings: (settings: IMeilisearchIndexSettings<TDoc>) =>

View File

@@ -1,10 +1,13 @@
import { SERVERS } from "~/constants";
import {
IndexApi,
ISearchConfig,
ISearchModel,
RankingRule,
RankingRulesApi,
Stage,
} from "~/types";
import { getEnv } from "./getEnv";
import { MeiliSearchApi } from "./MeiliSearchApi";
import { rankingRules } from "./model-api/rankingRules";
@@ -79,8 +82,7 @@ const modelConfigApi = <TDoc extends {}>(update: (s: PartialModel<TDoc>) => void
export const createModel = <TDoc extends Record<string, any>>(
/** the MeiliSearch index name which this model is servicing */
name: string,
cb?: (api: IndexApi<TDoc>) => void,
url: string = "http://localhost:7700"
cb?: (api: IndexApi<TDoc>) => void
) => {
const state: ISearchConfig<TDoc> = {
name,
@@ -99,11 +101,17 @@ export const createModel = <TDoc extends Record<string, any>>(
cb(modelConfigApi<TDoc>(updateState));
}
return {
...state,
query: MeiliSearchApi<TDoc>(state, { url }),
toString() {
return `Model(${name}[${state.index.pk}])`;
},
} as ISearchModel<TDoc>;
return (stage?: Stage) => {
const url = stage ? SERVERS[stage]?.url : SERVERS[getEnv().stage]?.url;
const search_key = stage
? SERVERS[stage]?.search_key
: SERVERS[getEnv().stage]?.search_key;
return {
...state,
query: MeiliSearchApi<TDoc>(state, { url, search_key }),
toString() {
return `Model(${name}[${state.index.pk}])`;
},
} as ISearchModel<TDoc>;
};
};

View File

@@ -1,6 +1,6 @@
import { config } from "dotenv";
import { Stage } from "~/types";
export type Stage = "production" | "staging" | "local" | undefined;
export interface IEnv {
org: string;
repo: string;
@@ -9,6 +9,14 @@ export interface IEnv {
stage: Stage;
docsPath: string;
/**
* the full filename path to the AST JSON file exported by
*/
tsAstPath: string;
adminKey?: string;
searchKey?: string;
github_token?: string;
github_user?: string;
force?: boolean;
@@ -20,8 +28,13 @@ export function getEnv(): IEnv {
org: process.env.ORG || "tauri-apps",
repo: process.env.REPO || "tauri-docs",
branch: process.env.BRANCH || "dev",
stage: process.env.NODE_ENV as Stage,
stage: (process.env.NODE_ENV as Stage) || "local",
docsPath: process.env.DOCS_PATH || "docs",
tsAstPath: process.env.TS_AST_PATH || "docs/api/js/js-api.json",
adminKey: process.env.ADMIN_KEY || undefined,
searchKey: process.env.SEARCH_KEY || undefined,
github_token: process.env.GH_TOKEN || process.env.GITHUB_TOKEN || undefined,
github_user: process.env.GH_USER || undefined,
force: process.env.FORCE ? Boolean(process.env.FORCE) : false,

View File

@@ -16,18 +16,18 @@ describe("createModel()", () => {
.filterable("lastUpdated")
);
expect(m.index.displayed).toContain("title");
expect(m.index.displayed).toContain("section");
expect(m.index.displayed).not.toContain("lastUpdated");
expect(m().index.displayed).toContain("title");
expect(m().index.displayed).toContain("section");
expect(m().index.displayed).not.toContain("lastUpdated");
expect(m.index.sortable).toContain("section");
expect(m.index.filterable).toContain("lastUpdated");
expect(m().index.sortable).toContain("section");
expect(m().index.filterable).toContain("lastUpdated");
});
it("toString() produces a clear and concise indication of what model it is", async () => {
const m = createModel<Model>("foobar");
expect(m.toString()).toContain("Model");
expect(m.toString()).toContain("foobar");
expect(m().toString()).toContain("Model");
expect(m().toString()).toContain("foobar");
});
it("model can set ranking rules to any order they like", () => {
@@ -35,8 +35,10 @@ describe("createModel()", () => {
m.rankingRules((r) => r.attribute().exactness().words())
);
expect(
m.index.rules,
`the ranking rules were expected to be [attribute, exactness, words] but instead were ${m.index.rules}`
m().index.rules,
`the ranking rules were expected to be [attribute, exactness, words] but instead were ${
m().index.rules
}`
).toEqual(["attribute", "exactness", "words"]);
});
});

74
pnpm-lock.yaml generated
View File

@@ -26,6 +26,7 @@ importers:
critters: ^0.0.16
cross-env: ^7.0.3
date-fns: ^2.28.0
docs-searchbar.js: ^2.1.0
dotenv: ^14.3.2
eslint: ^8.8.0
eslint-plugin-cypress: ^2.12.1
@@ -63,6 +64,7 @@ importers:
'@vueuse/core': 7.5.5_vue@3.2.29
'@vueuse/head': 0.7.5_vue@3.2.29
date-fns: 2.28.0
docs-searchbar.js: 2.1.0
floating-vue: 2.0.0-beta.5_vue@3.2.29
inferred-types: 0.18.4
markdown-it-expandable: 1.0.0_markdown-it@12.3.2
@@ -2435,6 +2437,12 @@ packages:
engines: {node: '>= 4.0.0'}
dev: true
/autocomplete.js/0.38.1:
resolution: {integrity: sha512-6pSJzuRMY3pqpozt+SXThl2DmJfma8Bi3SVFbZHS0PW/N72bOUv+Db0jAh2cWOhTsA4X+GNmKvIl8wExJTnN9w==}
dependencies:
immediate: 3.3.0
dev: false
/available-typed-arrays/1.0.5:
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
engines: {node: '>= 0.4'}
@@ -2851,6 +2859,14 @@ packages:
cross-spawn: 7.0.3
dev: true
/cross-fetch/3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
dependencies:
node-fetch: 2.6.7
transitivePeerDependencies:
- encoding
dev: false
/cross-spawn/6.0.5:
resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
engines: {node: '>=4.8'}
@@ -3028,6 +3044,17 @@ packages:
resolution: {integrity: sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=}
dev: false
/docs-searchbar.js/2.1.0:
resolution: {integrity: sha512-lHDeJelI8NrtmTrX38rZN86BQ6pxL8QzlfMqR6dHhfAXdrpDX2nNRT51sNSg6nIuI0ubpCVyJMyncedglmK/FA==}
dependencies:
autocomplete.js: 0.38.1
meilisearch: 0.24.0
to-factory: 1.0.0
zepto: 1.2.0
transitivePeerDependencies:
- encoding
dev: false
/doctrine/2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
@@ -4511,6 +4538,10 @@ packages:
engines: {node: '>= 4'}
dev: true
/immediate/3.3.0:
resolution: {integrity: sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==}
dev: false
/import-fresh/3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
@@ -5139,6 +5170,14 @@ packages:
engines: {node: '>= 0.6'}
dev: true
/meilisearch/0.24.0:
resolution: {integrity: sha512-qME1dsHZePBQi8qFdhbilcFzaL+oZJgUuls+FZ23hHpdhJI+iMFSmjjcfsxq5hdg2qczbCXv7yAo3Sh8xgfkgA==}
dependencies:
cross-fetch: 3.1.5
transitivePeerDependencies:
- encoding
dev: false
/memorystream/0.3.1:
resolution: {integrity: sha1-htcJCzDORV1j+64S3aUaR93K+bI=}
engines: {node: '>= 0.10.0'}
@@ -5284,6 +5323,18 @@ packages:
lower-case: 1.1.4
dev: true
/node-fetch/2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/node-releases/2.0.1:
resolution: {integrity: sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==}
dev: true
@@ -6553,6 +6604,10 @@ packages:
engines: {node: '>=14.0.0'}
dev: true
/to-factory/1.0.0:
resolution: {integrity: sha1-hzivi9lxIK0dQEeXKtpVY7+UebE=}
dev: false
/to-fast-properties/2.0.0:
resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=}
engines: {node: '>=4'}
@@ -6587,6 +6642,10 @@ packages:
universalify: 0.1.2
dev: true
/tr46/0.0.3:
resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
dev: false
/tr46/1.0.1:
resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=}
dependencies:
@@ -7454,6 +7513,10 @@ packages:
minimalistic-assert: 1.0.1
dev: true
/webidl-conversions/3.0.1:
resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
dev: false
/webidl-conversions/4.0.2:
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
dev: true
@@ -7487,6 +7550,13 @@ packages:
webidl-conversions: 7.0.0
dev: true
/whatwg-url/5.0.0:
resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false
/whatwg-url/7.1.0:
resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
dependencies:
@@ -7808,3 +7878,7 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
dev: true
/zepto/1.2.0:
resolution: {integrity: sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g=}
dev: false