2
0
mirror of https://github.com/frappe/books.git synced 2024-09-20 03:29:00 +00:00

Merge pull request #457 from 18alantom/error-report-fixes

fix: patch issues from error reports
This commit is contained in:
Alan 2022-08-30 03:18:54 -07:00 committed by GitHub
commit 71bcae5ec8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 788 additions and 450 deletions

View File

@ -37,6 +37,7 @@ export class Converter {
schemaName: string, schemaName: string,
rawValueMap: RawValueMap | RawValueMap[] rawValueMap: RawValueMap | RawValueMap[]
): DocValueMap | DocValueMap[] { ): DocValueMap | DocValueMap[] {
rawValueMap ??= {};
if (Array.isArray(rawValueMap)) { if (Array.isArray(rawValueMap)) {
return rawValueMap.map((dv) => this.#toDocValueMap(schemaName, dv)); return rawValueMap.map((dv) => this.#toDocValueMap(schemaName, dv));
} else { } else {
@ -48,6 +49,7 @@ export class Converter {
schemaName: string, schemaName: string,
docValueMap: DocValueMap | DocValueMap[] docValueMap: DocValueMap | DocValueMap[]
): RawValueMap | RawValueMap[] { ): RawValueMap | RawValueMap[] {
docValueMap ??= {};
if (Array.isArray(docValueMap)) { if (Array.isArray(docValueMap)) {
return docValueMap.map((dv) => this.#toRawValueMap(schemaName, dv)); return docValueMap.map((dv) => this.#toRawValueMap(schemaName, dv));
} else { } else {

View File

@ -17,6 +17,7 @@ import {
import { getIsNullOrUndef, getMapFromList, getRandomString } from 'utils'; import { getIsNullOrUndef, getMapFromList, getRandomString } from 'utils';
import { markRaw } from 'vue'; import { markRaw } from 'vue';
import { isPesa } from '../utils/index'; import { isPesa } from '../utils/index';
import { getDbSyncError } from './errorHelpers';
import { import {
areDocValuesEqual, areDocValuesEqual,
getMissingMandatoryMessage, getMissingMandatoryMessage,
@ -682,7 +683,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
await this._preSync(); await this._preSync();
const validDict = this.getValidDict(false, true); const validDict = this.getValidDict(false, true);
const data = await this.fyo.db.insert(this.schemaName, validDict); let data: DocValueMap;
try {
data = await this.fyo.db.insert(this.schemaName, validDict);
} catch (err) {
throw await getDbSyncError(err as Error, this, this.fyo);
}
await this._syncValues(data); await this._syncValues(data);
this.fyo.telemetry.log(Verb.Created, this.schemaName); this.fyo.telemetry.log(Verb.Created, this.schemaName);
@ -695,7 +701,11 @@ export class Doc extends Observable<DocValue | Doc[]> {
await this._preSync(); await this._preSync();
const data = this.getValidDict(false, true); const data = this.getValidDict(false, true);
try {
await this.fyo.db.update(this.schemaName, data); await this.fyo.db.update(this.schemaName, data);
} catch (err) {
throw await getDbSyncError(err as Error, this, this.fyo);
}
await this._syncValues(data); await this._syncValues(data);
return this; return this;

170
fyo/model/errorHelpers.ts Normal file
View File

@ -0,0 +1,170 @@
import { Fyo } from 'fyo';
import { DuplicateEntryError, NotFoundError } from 'fyo/utils/errors';
import {
DynamicLinkField,
Field,
FieldTypeEnum,
TargetField,
} from 'schemas/types';
import { Doc } from './doc';
type NotFoundDetails = { label: string; value: string };
export async function getDbSyncError(
err: Error,
doc: Doc,
fyo: Fyo
): Promise<Error> {
if (err.message.includes('UNIQUE constraint failed:')) {
return getDuplicateEntryError(err, doc);
}
if (err.message.includes('FOREIGN KEY constraint failed')) {
return getNotFoundError(err, doc, fyo);
}
return err;
}
function getDuplicateEntryError(
err: Error,
doc: Doc
): Error | DuplicateEntryError {
const matches = err.message.match(/UNIQUE constraint failed:\s(\w+)\.(\w+)$/);
if (!matches) {
return err;
}
const schemaName = matches[1];
const fieldname = matches[2];
if (!schemaName || !fieldname) {
return err;
}
const duplicateEntryError = new DuplicateEntryError(err.message, false);
const validDict = doc.getValidDict(false, true);
duplicateEntryError.stack = err.stack;
duplicateEntryError.more = {
schemaName,
fieldname,
value: validDict[fieldname],
};
return duplicateEntryError;
}
async function getNotFoundError(
err: Error,
doc: Doc,
fyo: Fyo
): Promise<NotFoundError> {
const notFoundError = new NotFoundError(fyo.t`Cannot perform operation.`);
notFoundError.stack = err.stack;
notFoundError.more.message = err.message;
const details = await getNotFoundDetails(doc, fyo);
if (!details) {
notFoundError.shouldStore = true;
return notFoundError;
}
notFoundError.shouldStore = false;
notFoundError.message = fyo.t`${details.label} value ${details.value} does not exist.`;
return notFoundError;
}
async function getNotFoundDetails(
doc: Doc,
fyo: Fyo
): Promise<NotFoundDetails | null> {
/**
* Since 'FOREIGN KEY constraint failed' doesn't inform
* how the operation failed, all Link and DynamicLink fields
* must be checked for value existance so as to provide a
* decent error message.
*/
for (const field of doc.schema.fields) {
const details = await getNotFoundDetailsIfDoesNotExists(field, doc, fyo);
if (details) {
return details;
}
}
return null;
}
async function getNotFoundDetailsIfDoesNotExists(
field: Field,
doc: Doc,
fyo: Fyo
): Promise<NotFoundDetails | null> {
const value = doc.get(field.fieldname);
if (field.fieldtype === FieldTypeEnum.Link && value) {
return getNotFoundLinkDetails(field as TargetField, value as string, fyo);
}
if (field.fieldtype === FieldTypeEnum.DynamicLink && value) {
return getNotFoundDynamicLinkDetails(
field as DynamicLinkField,
value as string,
fyo,
doc
);
}
if (
field.fieldtype === FieldTypeEnum.Table &&
(value as Doc[] | undefined)?.length
) {
return getNotFoundTableDetails(value as Doc[], fyo);
}
return null;
}
async function getNotFoundLinkDetails(
field: TargetField,
value: string,
fyo: Fyo
): Promise<NotFoundDetails | null> {
const { target } = field;
const exists = await fyo.db.exists(target as string, value);
if (!exists) {
return { label: field.label, value };
}
return null;
}
async function getNotFoundDynamicLinkDetails(
field: DynamicLinkField,
value: string,
fyo: Fyo,
doc: Doc
): Promise<NotFoundDetails | null> {
const { references } = field;
const target = doc.get(references);
if (!target) {
return null;
}
const exists = await fyo.db.exists(target as string, value);
if (!exists) {
return { label: field.label, value };
}
return null;
}
async function getNotFoundTableDetails(
value: Doc[],
fyo: Fyo
): Promise<NotFoundDetails | null> {
for (const childDoc of value) {
const details = getNotFoundDetails(childDoc, fyo);
if (details) {
return details;
}
}
return null;
}

View File

@ -1,9 +1,14 @@
export class BaseError extends Error { export class BaseError extends Error {
more: Record<string, unknown> = {};
message: string; message: string;
statusCode: number; statusCode: number;
shouldStore: boolean; shouldStore: boolean;
constructor(statusCode: number, message: string, shouldStore: boolean = true) { constructor(
statusCode: number,
message: string,
shouldStore: boolean = true
) {
super(message); super(message);
this.name = 'BaseError'; this.name = 'BaseError';
this.statusCode = statusCode; this.statusCode = statusCode;
@ -96,7 +101,7 @@ export function getDbError(err: Error) {
return CannotCommitError; return CannotCommitError;
} }
if (err.message.includes('SQLITE_CONSTRAINT: UNIQUE constraint failed:')) { if (err.message.includes('UNIQUE constraint failed:')) {
return DuplicateEntryError; return DuplicateEntryError;
} }

View File

@ -4,7 +4,7 @@ import {
app, app,
BrowserWindow, BrowserWindow,
BrowserWindowConstructorOptions, BrowserWindowConstructorOptions,
protocol, protocol
} from 'electron'; } from 'electron';
import Store from 'electron-store'; import Store from 'electron-store';
import { autoUpdater } from 'electron-updater'; import { autoUpdater } from 'electron-updater';
@ -41,6 +41,13 @@ export class Main {
autoUpdater.logger = console; autoUpdater.logger = console;
} }
// https://github.com/electron-userland/electron-builder/issues/4987
app.commandLine.appendSwitch('disable-http2');
autoUpdater.requestHeaders = {
'Cache-Control':
'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0',
};
Store.initRenderer(); Store.initRenderer();
this.registerListeners(); this.registerListeners();

View File

@ -1,9 +1,10 @@
import { autoUpdater } from 'electron-updater'; import { app, dialog } from 'electron';
import { autoUpdater, UpdateInfo } from 'electron-updater';
import { Main } from '../main'; import { Main } from '../main';
import { IPC_CHANNELS } from '../utils/messages'; import { IPC_CHANNELS } from '../utils/messages';
export default function registerAutoUpdaterListeners(main: Main) { export default function registerAutoUpdaterListeners(main: Main) {
autoUpdater.autoDownload = true; autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = true; autoUpdater.autoInstallOnAppQuit = true;
autoUpdater.on('error', (error) => { autoUpdater.on('error', (error) => {
@ -13,5 +14,34 @@ export default function registerAutoUpdaterListeners(main: Main) {
} }
main.mainWindow!.webContents.send(IPC_CHANNELS.MAIN_PROCESS_ERROR, error); main.mainWindow!.webContents.send(IPC_CHANNELS.MAIN_PROCESS_ERROR, error);
dialog.showErrorBox(
'Update Error: ',
error == null ? 'unknown' : (error.stack || error).toString()
);
});
autoUpdater.on('update-available', async (info: UpdateInfo) => {
const currentVersion = app.getVersion();
const nextVersion = info.version;
const isCurrentBeta = currentVersion.includes('beta');
const isNextBeta = nextVersion.includes('beta');
let downloadUpdate = true;
if (!isCurrentBeta && isNextBeta) {
const option = await dialog.showMessageBox({
type: 'info',
title: `Update Frappe Books?`,
message: `Download version ${nextVersion}?`,
buttons: ['Yes', 'No'],
});
downloadUpdate = option.response === 0;
}
if (!downloadUpdate) {
return;
}
await autoUpdater.downloadUpdate();
}); });
} }

View File

@ -1,5 +1,5 @@
import { app, dialog, ipcMain } from 'electron'; import { app, dialog, ipcMain } from 'electron';
import { autoUpdater, UpdateInfo } from 'electron-updater'; import { autoUpdater } from 'electron-updater';
import fs from 'fs/promises'; import fs from 'fs/promises';
import path from 'path'; import path from 'path';
import databaseManager from '../backend/database/manager'; import databaseManager from '../backend/database/manager';
@ -15,40 +15,6 @@ import {
} from './helpers'; } from './helpers';
import { saveHtmlAsPdf } from './saveHtmlAsPdf'; import { saveHtmlAsPdf } from './saveHtmlAsPdf';
autoUpdater.autoDownload = false;
autoUpdater.on('error', (error) => {
dialog.showErrorBox(
'Update Error: ',
error == null ? 'unknown' : (error.stack || error).toString()
);
});
autoUpdater.on('update-available', async (info: UpdateInfo) => {
const currentVersion = app.getVersion();
const nextVersion = info.version;
const isCurrentBeta = currentVersion.includes('beta');
const isNextBeta = nextVersion.includes('beta');
let downloadUpdate = true;
if (!isCurrentBeta && isNextBeta) {
const option = await dialog.showMessageBox({
type: 'info',
title: `Update Frappe Books?`,
message: `Download version ${nextVersion}?`,
buttons: ['Yes', 'No'],
});
downloadUpdate = option.response === 0;
}
if (!downloadUpdate) {
return;
}
await autoUpdater.downloadUpdate();
});
export default function registerIpcMainActionListeners(main: Main) { export default function registerIpcMainActionListeners(main: Main) {
ipcMain.handle(IPC_ACTIONS.GET_OPEN_FILEPATH, async (event, options) => { ipcMain.handle(IPC_ACTIONS.GET_OPEN_FILEPATH, async (event, options) => {
return await dialog.showOpenDialog(main.mainWindow!, options); return await dialog.showOpenDialog(main.mainWindow!, options);

View File

@ -152,7 +152,7 @@ export abstract class Invoice extends Transactional {
} }
const tax = await this.getTax(item.tax!); const tax = await this.getTax(item.tax!);
for (const { account, rate } of tax.details as TaxDetail[]) { for (const { account, rate } of (tax.details ?? []) as TaxDetail[]) {
taxes[account] ??= { taxes[account] ??= {
account, account,
rate, rate,

View File

@ -228,8 +228,9 @@ export async function getExchangeRate({
} }
} catch (error) { } catch (error) {
console.error(error); console.error(error);
throw new Error( throw new NotFoundError(
`Could not fetch exchange rate for ${fromCurrency} -> ${toCurrency}` `Could not fetch exchange rate for ${fromCurrency} -> ${toCurrency}`,
false
); );
} }
} else { } else {

View File

@ -58,8 +58,8 @@
"electron-builder": "^23.0.3", "electron-builder": "^23.0.3",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"electron-notarize": "^1.1.1", "electron-notarize": "^1.1.1",
"electron-rebuild": "^3.2.7", "electron-rebuild": "^3.2.9",
"electron-updater": "^4.3.9", "electron-updater": "^5.2.1",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
@ -74,9 +74,12 @@
"tsconfig-paths": "^3.14.1", "tsconfig-paths": "^3.14.1",
"tslib": "^2.3.1", "tslib": "^2.3.1",
"typescript": "^4.6.2", "typescript": "^4.6.2",
"vue-cli-plugin-electron-builder": "^2.0.0", "vue-cli-plugin-electron-builder": "https://github.com/nklayman/vue-cli-plugin-electron-builder#ebb9183f4913f927d4e4f4eb1fbab61a960f7a09",
"webpack": "^5.66.0" "webpack": "^5.66.0"
}, },
"resolutions": {
"electron-builder": "^23.3.3"
},
"prettier": { "prettier": {
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,

View File

@ -461,6 +461,10 @@ function setValueMapOnAccountTreeNodes(
rangeGroupedMap: AccountNameValueMapMap rangeGroupedMap: AccountNameValueMapMap
) { ) {
for (const name of rangeGroupedMap.keys()) { for (const name of rangeGroupedMap.keys()) {
if (!accountTree[name]) {
continue;
}
const valueMap = rangeGroupedMap.get(name)!; const valueMap = rangeGroupedMap.get(name)!;
accountTree[name].valueMap = valueMap; accountTree[name].valueMap = valueMap;
accountTree[name].prune = false; accountTree[name].prune = false;
@ -547,7 +551,7 @@ function pruneAccountTree(accountTree: AccountTree) {
function getPrunedChildren(children: AccountTreeNode[]): AccountTreeNode[] { function getPrunedChildren(children: AccountTreeNode[]): AccountTreeNode[] {
return children.filter((child) => { return children.filter((child) => {
if (child.children) { if (child.children?.length) {
child.children = getPrunedChildren(child.children); child.children = getPrunedChildren(child.children);
} }

View File

@ -153,8 +153,11 @@ export default {
.map(({ item }) => item); .map(({ item }) => item);
}, },
setSuggestion(suggestion) { setSuggestion(suggestion) {
if (suggestion) {
this.linkValue = suggestion.label; this.linkValue = suggestion.label;
this.triggerChange(suggestion.value); this.triggerChange(suggestion.value);
}
this.toggleDropdown(false); this.toggleDropdown(false);
}, },
onFocus(e, toggleDropdown) { onFocus(e, toggleDropdown) {

View File

@ -188,7 +188,7 @@ export default {
return emptyMessage; return emptyMessage;
}, },
async selectItem(d) { async selectItem(d) {
if (!d.action) { if (!d?.action) {
return; return;
} }

View File

@ -149,7 +149,10 @@
<p> {{ t`Navigate` }}</p> <p> {{ t`Navigate` }}</p>
<p> {{ t`Select` }}</p> <p> {{ t`Select` }}</p>
<p><span class="tracking-tighter">esc</span> {{ t`Close` }}</p> <p><span class="tracking-tighter">esc</span> {{ t`Close` }}</p>
<button class="flex items-center hover:text-gray-800" @click="openDocs"> <button
class="flex items-center hover:text-gray-800"
@click="openDocs"
>
<feather-icon name="help-circle" class="w-4 h-4 mr-1" /> <feather-icon name="help-circle" class="w-4 h-4 mr-1" />
{{ t`Help` }} {{ t`Help` }}
</button> </button>
@ -284,7 +287,7 @@ export default {
}, },
open() { open() {
this.openModal = true; this.openModal = true;
this.searcher.updateKeywords(); this.searcher?.updateKeywords();
nextTick(() => { nextTick(() => {
this.$refs.input.focus(); this.$refs.input.focus();
}); });
@ -317,6 +320,10 @@ export default {
ref.scrollIntoView({ block: 'nearest' }); ref.scrollIntoView({ block: 'nearest' });
}, },
getGroupFilterButtonClass(g) { getGroupFilterButtonClass(g) {
if (!this.searcher) {
return '';
}
const isOn = this.searcher.filters.groupFilters[g]; const isOn = this.searcher.filters.groupFilters[g];
const color = this.groupColorMap[g]; const color = this.groupColorMap[g];
if (isOn) { if (isOn) {
@ -363,6 +370,10 @@ export default {
}, {}); }, {});
}, },
suggestions() { suggestions() {
if (!this.searcher) {
return [];
}
const suggestions = this.searcher.search(this.inputValue); const suggestions = this.searcher.search(this.inputValue);
if (this.limit === -1) { if (this.limit === -1) {
return suggestions; return suggestions;

View File

@ -26,11 +26,17 @@ async function reportError(errorLogObj: ErrorLog) {
error_name: errorLogObj.name, error_name: errorLogObj.name,
message: errorLogObj.message, message: errorLogObj.message,
stack: errorLogObj.stack, stack: errorLogObj.stack,
platform: fyo.store.platform,
version: fyo.store.appVersion,
language: fyo.store.language,
instance_id: fyo.store.instanceId,
open_count: fyo.store.openCount,
country_code: fyo.singles.SystemSettings?.countryCode,
more: stringifyCircular(errorLogObj.more ?? {}), more: stringifyCircular(errorLogObj.more ?? {}),
}; };
if (fyo.store.isDevelopment) { if (fyo.store.isDevelopment) {
console.log(body); console.log('reportError', body);
} }
await ipcRenderer.invoke(IPC_ACTIONS.SEND_ERROR, JSON.stringify(body)); await ipcRenderer.invoke(IPC_ACTIONS.SEND_ERROR, JSON.stringify(body));
@ -88,8 +94,11 @@ export async function handleErrorWithDialog(
const errorMessage = getErrorMessage(error, doc); const errorMessage = getErrorMessage(error, doc);
await handleError(false, error, { errorMessage, doc }); await handleError(false, error, { errorMessage, doc });
const name = error.name ?? t`Error`; const label = getErrorLabel(error);
const options: MessageDialogOptions = { message: name, detail: errorMessage }; const options: MessageDialogOptions = {
message: label,
detail: errorMessage,
};
if (reportError) { if (reportError) {
options.detail = truncate(options.detail, { length: 128 }); options.detail = truncate(options.detail, { length: 128 });
@ -100,7 +109,7 @@ export async function handleErrorWithDialog(
reportIssue(getErrorLogObject(error, { errorMessage })); reportIssue(getErrorLogObject(error, { errorMessage }));
}, },
}, },
{ label: t`OK`, action() {} }, { label: t`Cancel`, action() {} },
]; ];
} }
@ -190,3 +199,52 @@ export function reportIssue(errorLogObj?: ErrorLog) {
const urlQuery = getIssueUrlQuery(errorLogObj); const urlQuery = getIssueUrlQuery(errorLogObj);
ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, urlQuery); ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, urlQuery);
} }
function getErrorLabel(error: Error) {
const name = error.name;
if (!name) {
return t`Error`;
}
if (name === 'BaseError') {
return t`Error`;
}
if (name === 'ValidationError') {
return t`Validation Error`;
}
if (name === 'NotFoundError') {
return t`Not Found`;
}
if (name === 'ForbiddenError') {
return t`Forbidden Error`;
}
if (name === 'DuplicateEntryError') {
return t`Duplicate Entry`;
}
if (name === 'LinkValidationError') {
return t`Link Validation Error`;
}
if (name === 'MandatoryError') {
return t`Mandatory Error`;
}
if (name === 'DatabaseError') {
return t`Database Error`;
}
if (name === 'CannotCommitError') {
return t`Cannot Commit Error`;
}
if (name === 'NotImplemented') {
return t`Error`;
}
return t`Error`;
}

View File

@ -156,6 +156,10 @@ export default defineComponent({
this.pageEnd = end; this.pageEnd = end;
}, },
setUpdateListeners() { setUpdateListeners() {
if (!this.schemaName) {
return;
}
const listener = () => { const listener = () => {
this.updateData(); this.updateData();
}; };

View File

@ -4,9 +4,14 @@
import { t } from 'fyo'; import { t } from 'fyo';
import { Doc } from 'fyo/model/doc'; import { Doc } from 'fyo/model/doc';
import { isPesa } from 'fyo/utils'; import { isPesa } from 'fyo/utils';
import { DuplicateEntryError, LinkValidationError } from 'fyo/utils/errors'; import {
BaseError,
DuplicateEntryError,
LinkValidationError
} from 'fyo/utils/errors';
import { Money } from 'pesa'; import { Money } from 'pesa';
import { Field, FieldType, FieldTypeEnum } from 'schemas/types'; import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
import { fyo } from 'src/initFyo';
export function stringifyCircular( export function stringifyCircular(
obj: unknown, obj: unknown,
@ -24,7 +29,8 @@ export function stringifyCircular(
} }
if (cacheValue.includes(value)) { if (cacheValue.includes(value)) {
const circularKey = cacheKey[cacheValue.indexOf(value)] || '{self}'; const circularKey: string =
cacheKey[cacheValue.indexOf(value)] || '{self}';
return ignoreCircular ? undefined : `[Circular:${circularKey}]`; return ignoreCircular ? undefined : `[Circular:${circularKey}]`;
} }
@ -84,16 +90,23 @@ export function convertPesaValuesToFloat(obj: Record<string, unknown>) {
} }
export function getErrorMessage(e: Error, doc?: Doc): string { export function getErrorMessage(e: Error, doc?: Doc): string {
let errorMessage = e.message || t`An error occurred.`; const errorMessage = e.message || t`An error occurred.`;
const { schemaName, name }: { schemaName?: string; name?: string } = let { schemaName, name } = doc ?? {};
doc ?? {}; if (!doc) {
const canElaborate = !!(schemaName && name); schemaName = (e as BaseError).more?.schemaName as string | undefined;
name = (e as BaseError).more?.value as string | undefined;
}
if (e instanceof LinkValidationError && canElaborate) { if (!schemaName || !name) {
errorMessage = t`${schemaName} ${name} is linked with existing records.`; return errorMessage;
} else if (e instanceof DuplicateEntryError && canElaborate) { }
errorMessage = t`${schemaName} ${name} already exists.`;
const label = fyo.db.schemaMap[schemaName]?.label ?? schemaName;
if (e instanceof LinkValidationError) {
return t`${label} ${name} is linked with existing records.`;
} else if (e instanceof DuplicateEntryError) {
return t`${label} ${name} already exists.`;
} }
return errorMessage; return errorMessage;

View File

@ -418,7 +418,7 @@ export class Search {
const totalChildKeywords = Object.values(this.searchables) const totalChildKeywords = Object.values(this.searchables)
.filter((s) => s.isChild) .filter((s) => s.isChild)
.map((s) => this.keywords[s.schemaName]?.length ?? 0) .map((s) => this.keywords[s.schemaName]?.length ?? 0)
.reduce((a, b) => a + b); .reduce((a, b) => a + b, 0);
if (totalChildKeywords > 2_000) { if (totalChildKeywords > 2_000) {
this.set('skipTables', true); this.set('skipTables', true);
@ -523,7 +523,12 @@ export class Search {
keys.sort((a, b) => parseFloat(b) - parseFloat(a)); keys.sort((a, b) => parseFloat(b) - parseFloat(a));
const array: SearchItems = []; const array: SearchItems = [];
for (const key of keys) { for (const key of keys) {
this._pushDocSearchItems(groupedKeywords[key], array, input); const keywords = groupedKeywords[key];
if (!keywords?.length) {
continue;
}
this._pushDocSearchItems(keywords, array, input);
if (key === '0') { if (key === '0') {
this._pushNonDocSearchItems(array, input); this._pushNonDocSearchItems(array, input);
} }

806
yarn.lock

File diff suppressed because it is too large Load Diff