From 95f5844a44aead711a4725cadc33120db909ce6f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 12 Jun 2024 15:02:16 +0100 Subject: [PATCH 001/116] Get in-memory searching into the search tests. --- .../src/api/routes/tests/search.spec.ts | 72 ++++++++++--------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index aac43874a0..51349e7647 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -22,30 +22,27 @@ import { import _ from "lodash" import tk from "timekeeper" import { encodeJSBinding } from "@budibase/string-templates" +import { dataFilters } from "@budibase/shared-core" describe.each([ - ["lucene", undefined], - ["sqs", undefined], - [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], - [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], - [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], - [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], -])("/api/:sourceId/search (%s)", (name, dsProvider) => { + ["in-memory", undefined], + // ["lucene", undefined], + // ["sqs", undefined], + // [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], + // [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], + // [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], + // [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], +])("search (%s)", (name, dsProvider) => { const isSqs = name === "sqs" const isLucene = name === "lucene" + const isInMemory = name === "in-memory" const isInternal = isSqs || isLucene const config = setup.getConfig() let envCleanup: (() => void) | undefined let datasource: Datasource | undefined let table: Table - - const snippets = [ - { - name: "WeeksAgo", - code: `return function (weeks) {\n const currentTime = new Date(${Date.now()});\n currentTime.setDate(currentTime.getDate()-(7 * (weeks || 1)));\n return currentTime.toISOString();\n}`, - }, - ] + let rows: Row[] beforeAll(async () => { if (isSqs) { @@ -55,7 +52,12 @@ describe.each([ if (config.app?.appId) { config.app = await config.api.application.update(config.app?.appId, { - snippets, + snippets: [ + { + name: "WeeksAgo", + code: `return function (weeks) {\n const currentTime = new Date(${Date.now()});\n currentTime.setDate(currentTime.getDate()-(7 * (weeks || 1)));\n return currentTime.toISOString();\n}`, + }, + ], }) } @@ -79,14 +81,30 @@ describe.each([ ) } - async function createRows(rows: Record[]) { + async function createRows(arr: Record[]) { // Shuffling to avoid false positives given a fixed order - await config.api.row.bulkImport(table._id!, { rows: _.shuffle(rows) }) + await config.api.row.bulkImport(table._id!, { + rows: _.shuffle(arr), + }) + rows = await config.api.row.fetch(table._id!) } class SearchAssertion { constructor(private readonly query: RowSearchParams) {} + private async performSearch(): Promise { + if (isInMemory) { + return dataFilters.runQuery(rows, this.query.query) + } else { + return ( + await config.api.row.search(table._id!, { + ...this.query, + tableId: table._id!, + }) + ).rows + } + } + // We originally used _.isMatch to compare rows, but found that when // comparing arrays it would return true if the source array was a subset of // the target array. This would sometimes create false matches. This @@ -157,10 +175,7 @@ describe.each([ // different to the one passed in will cause the assertion to fail. Extra // rows returned by the query will also cause the assertion to fail. async toMatchExactly(expectedRows: any[]) { - const { rows: foundRows } = await config.api.row.search(table._id!, { - ...this.query, - tableId: table._id!, - }) + const foundRows = await this.performSearch() // eslint-disable-next-line jest/no-standalone-expect expect(foundRows).toHaveLength(expectedRows.length) @@ -176,10 +191,7 @@ describe.each([ // passed in. The order of the rows is not important, but extra rows will // cause the assertion to fail. async toContainExactly(expectedRows: any[]) { - const { rows: foundRows } = await config.api.row.search(table._id!, { - ...this.query, - tableId: table._id!, - }) + const foundRows = await this.performSearch() // eslint-disable-next-line jest/no-standalone-expect expect(foundRows).toHaveLength(expectedRows.length) @@ -197,10 +209,7 @@ describe.each([ // The order of the rows is not important. Extra rows will not cause the // assertion to fail. async toContain(expectedRows: any[]) { - const { rows: foundRows } = await config.api.row.search(table._id!, { - ...this.query, - tableId: table._id!, - }) + const foundRows = await this.performSearch() // eslint-disable-next-line jest/no-standalone-expect expect([...foundRows]).toEqual( @@ -217,10 +226,7 @@ describe.each([ } async toHaveLength(length: number) { - const { rows: foundRows } = await config.api.row.search(table._id!, { - ...this.query, - tableId: table._id!, - }) + const foundRows = await this.performSearch() // eslint-disable-next-line jest/no-standalone-expect expect(foundRows).toHaveLength(length) From 7e4f571eb36092efccbc0c4ac7b5d3a6ce9fe54b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 12 Jun 2024 15:23:35 +0100 Subject: [PATCH 002/116] wip --- packages/server/src/api/routes/tests/search.spec.ts | 13 ++++++++++++- packages/shared-core/src/filters.ts | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 51349e7647..d6e764192d 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -94,7 +94,18 @@ describe.each([ private async performSearch(): Promise { if (isInMemory) { - return dataFilters.runQuery(rows, this.query.query) + let result = dataFilters.runQuery(rows, this.query.query) + if (this.query.sort) { + result = dataFilters.sort( + result, + this.query.sort, + this.query.sortOrder || SortOrder.ASCENDING + ) + } + if (this.query.limit) { + result = dataFilters.limit(result, this.query.limit.toString()) + } + return result } else { return ( await config.api.row.search(table._id!, { diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index f714f1cef6..862c2ea9bd 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -7,9 +7,9 @@ import { SearchFilters, SearchQueryFields, SearchFilterOperator, - SortDirection, SortType, FieldConstraints, + SortOrder, } from "@budibase/types" import dayjs from "dayjs" import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants" @@ -454,7 +454,7 @@ export const runQuery = (docs: any[], query?: SearchFilters) => { export const sort = ( docs: any[], sort: string, - sortOrder: SortDirection, + sortOrder: SortOrder, sortType = SortType.STRING ) => { if (!sort || !sortOrder || !sortType) { From ae6539161fb966abe2fc4d8b30d0967a3ae84261 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 12 Jun 2024 17:28:03 +0100 Subject: [PATCH 003/116] Get rid of negation in predicate. --- .../src/api/routes/tests/search.spec.ts | 2 +- packages/shared-core/src/filters.ts | 213 ++++++++++-------- 2 files changed, 117 insertions(+), 98 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index d6e764192d..3cafec1b9a 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1706,7 +1706,7 @@ describe.each([ }) describe("contains", () => { - it("successfully finds a row", () => + it.only("successfully finds a row", () => expectQuery({ contains: { users: [user1._id] } }).toContainExactly([ { users: [{ _id: user1._id }] }, { users: [{ _id: user1._id }, { _id: user2._id }] }, diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 862c2ea9bd..e95015d340 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -264,7 +264,10 @@ export const buildQuery = (filter: SearchFilter[]) => { * @param docs the data * @param query the JSON query */ -export const runQuery = (docs: any[], query?: SearchFilters) => { +export const runQuery = ( + docs: Record[], + query?: SearchFilters +) => { if (!docs || !Array.isArray(docs)) { return [] } @@ -272,50 +275,48 @@ export const runQuery = (docs: any[], query?: SearchFilters) => { return docs } - // Make query consistent first query = cleanupQuery(query) - // Iterates over a set of filters and evaluates a fail function against a doc const match = ( type: SearchFilterOperator, - failFn: (docValue: any, testValue: any) => boolean + test: (docValue: any, testValue: any) => boolean ) => - (doc: any) => { - const filters = Object.entries(query![type] || {}) - for (let i = 0; i < filters.length; i++) { - const [key, testValue] = filters[i] - const docValue = deepGet(doc, removeKeyNumbering(key)) - if (failFn(docValue, testValue)) { + (doc: Record) => { + for (const [key, testValue] of Object.entries(query[type] || {})) { + if (!test(deepGet(doc, removeKeyNumbering(key)), testValue)) { return false } } return true } - // Process a string match (fails if the value does not start with the string) const stringMatch = match( SearchFilterOperator.STRING, - (docValue: string, testValue: string) => { - return ( - !docValue || - !docValue?.toLowerCase().startsWith(testValue?.toLowerCase()) - ) + (docValue: any, testValue: any) => { + if (!(typeof docValue === "string")) { + return false + } + if (!(typeof testValue === "string")) { + return false + } + return docValue.toLowerCase().startsWith(testValue.toLowerCase()) } ) - // Process a fuzzy match (treat the same as starts with when running locally) const fuzzyMatch = match( SearchFilterOperator.FUZZY, - (docValue: string, testValue: string) => { - return ( - !docValue || - !docValue?.toLowerCase().startsWith(testValue?.toLowerCase()) - ) + (docValue: any, testValue: any) => { + if (!(typeof docValue === "string")) { + return false + } + if (!(typeof testValue === "string")) { + return false + } + return docValue.toLowerCase().includes(testValue.toLowerCase()) } ) - // Process a range match const rangeMatch = match( SearchFilterOperator.RANGE, ( @@ -323,54 +324,47 @@ export const runQuery = (docs: any[], query?: SearchFilters) => { testValue: { low: number; high: number } ) => { if (docValue == null || docValue === "") { - return true + return false } if (!isNaN(+docValue)) { - return +docValue < testValue.low || +docValue > testValue.high + return +docValue >= testValue.low || +docValue <= testValue.high } if (dayjs(docValue).isValid()) { return ( - new Date(docValue).getTime() < new Date(testValue.low).getTime() || - new Date(docValue).getTime() > new Date(testValue.high).getTime() + new Date(docValue).getTime() >= new Date(testValue.low).getTime() || + new Date(docValue).getTime() <= new Date(testValue.high).getTime() ) } return false } ) - // Process an equal match (fails if the value is different) - const equalMatch = match( - SearchFilterOperator.EQUAL, - (docValue: any, testValue: string | null) => { - return testValue != null && testValue !== "" && docValue !== testValue - } - ) + const not = + (f: (...args: T) => boolean) => + (...args: T): boolean => + !f(...args) - // Process a not-equal match (fails if the value is the same) - const notEqualMatch = match( - SearchFilterOperator.NOT_EQUAL, - (docValue: any, testValue: string | null) => { - return testValue != null && testValue !== "" && docValue === testValue - } - ) + const _equal = (docValue: any, testValue: any) => docValue === testValue - // Process an empty match (fails if the value is not empty) - const emptyMatch = match( - SearchFilterOperator.EMPTY, - (docValue: string | null) => { - return docValue != null && docValue !== "" - } - ) + const equalMatch = match(SearchFilterOperator.EQUAL, _equal) + const notEqualMatch = match(SearchFilterOperator.NOT_EQUAL, not(_equal)) - // Process a not-empty match (fails is the value is empty) - const notEmptyMatch = match( - SearchFilterOperator.NOT_EMPTY, - (docValue: string | null) => { - return docValue == null || docValue === "" + const _empty = (docValue: any) => { + if (typeof docValue === "string") { + return docValue === "" } - ) + if (Array.isArray(docValue)) { + return docValue.length === 0 + } + if (typeof docValue === "object") { + return Object.keys(docValue).length === 0 + } + return docValue == null + } + + const emptyMatch = match(SearchFilterOperator.EMPTY, _empty) + const notEmptyMatch = match(SearchFilterOperator.NOT_EMPTY, not(_empty)) - // Process an includes match (fails if the value is not included) const oneOf = match( SearchFilterOperator.ONE_OF, (docValue: any, testValue: any) => { @@ -380,61 +374,86 @@ export const runQuery = (docs: any[], query?: SearchFilters) => { testValue = testValue.map((item: string) => parseFloat(item)) } } - return !testValue?.includes(docValue) + + if (!Array.isArray(testValue)) { + return false + } + + return testValue.includes(docValue) } ) const containsAny = match( SearchFilterOperator.CONTAINS_ANY, (docValue: any, testValue: any) => { - return !docValue?.includes(...testValue) - } - ) - - const contains = match( - SearchFilterOperator.CONTAINS, - (docValue: string | any[], testValue: any[]) => { - return !testValue?.every((item: any) => docValue?.includes(item)) - } - ) - - const notContains = match( - SearchFilterOperator.NOT_CONTAINS, - (docValue: string | any[], testValue: any[]) => { - return testValue?.every((item: any) => docValue?.includes(item)) - } - ) - - const docMatch = (doc: any) => { - const filterFunctions: Record boolean> = - { - string: stringMatch, - fuzzy: fuzzyMatch, - range: rangeMatch, - equal: equalMatch, - notEqual: notEqualMatch, - empty: emptyMatch, - notEmpty: notEmptyMatch, - oneOf: oneOf, - contains: contains, - containsAny: containsAny, - notContains: notContains, + if (!Array.isArray(docValue)) { + return false } - const activeFilterKeys: SearchFilterOperator[] = Object.entries(query || {}) + if (typeof testValue === "string") { + testValue = testValue.split(",") + if (typeof docValue[0] === "number") { + testValue = testValue.map((item: string) => parseFloat(item)) + } + } + + if (!Array.isArray(testValue)) { + return false + } + + return testValue.some(item => docValue.includes(item)) + } + ) + + const _contains = (docValue: any, testValue: any) => { + if (!Array.isArray(docValue)) { + return false + } + + if (typeof testValue === "string") { + testValue = testValue.split(",") + if (typeof docValue[0] === "number") { + testValue = testValue.map((item: string) => parseFloat(item)) + } + } + + if (!Array.isArray(testValue)) { + return false + } + + return testValue.every(item => docValue.includes(item)) + } + + const contains = match(SearchFilterOperator.CONTAINS, _contains) + const notContains = match(SearchFilterOperator.NOT_CONTAINS, not(_contains)) + + const docMatch = (doc: Record) => { + const filterFunctions = { + string: stringMatch, + fuzzy: fuzzyMatch, + range: rangeMatch, + equal: equalMatch, + notEqual: notEqualMatch, + empty: emptyMatch, + notEmpty: notEmptyMatch, + oneOf: oneOf, + contains: contains, + containsAny: containsAny, + notContains: notContains, + } + + const results = Object.entries(query || {}) .filter( - ([key, value]: [string, any]) => + ([key, value]) => !["allOr", "onEmptyFilter"].includes(key) && value && - Object.keys(value as Record).length > 0 + Object.keys(value).length > 0 ) - .map(([key]) => key as any) + .map(([key]) => { + return filterFunctions[key as SearchFilterOperator]?.(doc) ?? false + }) - const results: boolean[] = activeFilterKeys.map(filterKey => { - return filterFunctions[filterKey]?.(doc) ?? false - }) - - if (query!.allOr) { + if (query.allOr) { return results.some(result => result === true) } else { return results.every(result => result === true) From 22bf0d05ad3dfca3a1deec7975d14dab547421c8 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 12 Jun 2024 17:58:13 +0100 Subject: [PATCH 004/116] Making progress. --- .../src/api/routes/tests/search.spec.ts | 19 +++-- packages/shared-core/src/filters.ts | 70 +++++++++++-------- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 3cafec1b9a..8f4d96d5e8 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1706,7 +1706,7 @@ describe.each([ }) describe("contains", () => { - it.only("successfully finds a row", () => + it("successfully finds a row", () => expectQuery({ contains: { users: [user1._id] } }).toContainExactly([ { users: [{ _id: user1._id }] }, { users: [{ _id: user1._id }, { _id: user2._id }] }, @@ -1763,9 +1763,12 @@ describe.each([ // This will never work for Lucene. !isLucene && + // It also can't work for in-memory searching because the related table name + // isn't available. + !isInMemory && describe("relations", () => { let otherTable: Table - let rows: Row[] + let otherRows: Row[] beforeAll(async () => { otherTable = await createTable({ @@ -1785,7 +1788,7 @@ describe.each([ }, }) - rows = await Promise.all([ + otherRows = await Promise.all([ config.api.row.save(otherTable._id!, { one: "foo" }), config.api.row.save(otherTable._id!, { one: "bar" }), ]) @@ -1793,18 +1796,22 @@ describe.each([ await Promise.all([ config.api.row.save(table._id!, { two: "foo", - other: [rows[0]._id], + other: [otherRows[0]._id], }), config.api.row.save(table._id!, { two: "bar", - other: [rows[1]._id], + other: [otherRows[1]._id], }), ]) + + rows = await config.api.row.fetch(table._id!) }) it("can search through relations", () => expectQuery({ equal: { [`${otherTable.name}.one`]: "foo" }, - }).toContainExactly([{ two: "foo", other: [{ _id: rows[0]._id }] }])) + }).toContainExactly([ + { two: "foo", other: [{ _id: otherRows[0]._id }] }, + ])) }) }) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index e95015d340..4ccbc60641 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -14,6 +14,7 @@ import { import dayjs from "dayjs" import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants" import { deepGet, schema } from "./helpers" +import _ from "lodash" const HBS_REGEX = /{{([^{].*?)}}/g @@ -339,15 +340,36 @@ export const runQuery = ( } ) + // This function exists to check that either the docValue is equal to the + // testValue, or if the docValue is an object or array of objects, that the + // _id of the docValue is equal to the testValue. + const _valueMatches = (docValue: any, testValue: any) => { + if (Array.isArray(docValue)) { + for (const item of docValue) { + if (_valueMatches(item, testValue)) { + return true + } + } + return false + } + + if (typeof docValue === "object" && typeof testValue === "string") { + return docValue._id === testValue + } + + return docValue === testValue + } + const not = (f: (...args: T) => boolean) => (...args: T): boolean => !f(...args) - const _equal = (docValue: any, testValue: any) => docValue === testValue - - const equalMatch = match(SearchFilterOperator.EQUAL, _equal) - const notEqualMatch = match(SearchFilterOperator.NOT_EQUAL, not(_equal)) + const equalMatch = match(SearchFilterOperator.EQUAL, _valueMatches) + const notEqualMatch = match( + SearchFilterOperator.NOT_EQUAL, + not(_valueMatches) + ) const _empty = (docValue: any) => { if (typeof docValue === "string") { @@ -379,13 +401,12 @@ export const runQuery = ( return false } - return testValue.includes(docValue) + return testValue.some(item => _valueMatches(docValue, item)) } ) - const containsAny = match( - SearchFilterOperator.CONTAINS_ANY, - (docValue: any, testValue: any) => { + const _contains = + (f: "some" | "every") => (docValue: any, testValue: any) => { if (!Array.isArray(docValue)) { return false } @@ -401,31 +422,18 @@ export const runQuery = ( return false } - return testValue.some(item => docValue.includes(item)) + return testValue[f](item => _valueMatches(docValue, item)) } + + const contains = match(SearchFilterOperator.CONTAINS, _contains("every")) + const notContains = match( + SearchFilterOperator.NOT_CONTAINS, + not(_contains("every")) + ) + const containsAny = match( + SearchFilterOperator.CONTAINS_ANY, + _contains("some") ) - - const _contains = (docValue: any, testValue: any) => { - if (!Array.isArray(docValue)) { - return false - } - - if (typeof testValue === "string") { - testValue = testValue.split(",") - if (typeof docValue[0] === "number") { - testValue = testValue.map((item: string) => parseFloat(item)) - } - } - - if (!Array.isArray(testValue)) { - return false - } - - return testValue.every(item => docValue.includes(item)) - } - - const contains = match(SearchFilterOperator.CONTAINS, _contains) - const notContains = match(SearchFilterOperator.NOT_CONTAINS, not(_contains)) const docMatch = (doc: Record) => { const filterFunctions = { From 7e69f85e779bcb0e9155702205b0c9e9e93587cf Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 12 Jun 2024 18:07:46 +0100 Subject: [PATCH 005/116] More progress. --- .../src/api/routes/tests/search.spec.ts | 2 ++ packages/shared-core/src/filters.ts | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 8f4d96d5e8..f3353be3b2 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1279,6 +1279,8 @@ describe.each([ { numbers: ["three"] }, ])) + // Not sure if this is correct behaviour but changing it would be a + // breaking change. it("finds all with empty list", () => expectQuery({ notContains: { numbers: [] } }).toContainExactly([ { numbers: ["one", "two"] }, diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 4ccbc60641..2e7afa882b 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -425,10 +425,26 @@ export const runQuery = ( return testValue[f](item => _valueMatches(docValue, item)) } - const contains = match(SearchFilterOperator.CONTAINS, _contains("every")) + const contains = match( + SearchFilterOperator.CONTAINS, + (docValue: any, testValue: any) => { + if (Array.isArray(testValue) && testValue.length === 0) { + return true + } + return _contains("every")(docValue, testValue) + } + ) const notContains = match( SearchFilterOperator.NOT_CONTAINS, - not(_contains("every")) + (docValue: any, testValue: any) => { + // Not sure if this is logically correct, but at the time this code was + // written the search endpoint behaved this way and we wanted to make this + // local search match its behaviour, so we had to do this. + if (Array.isArray(testValue) && testValue.length === 0) { + return true + } + return not(_contains("every"))(docValue, testValue) + } ) const containsAny = match( SearchFilterOperator.CONTAINS_ANY, From 6a2b65b75bebd830d5181b61aa091c7ab8cc42f7 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 09:56:33 +0100 Subject: [PATCH 006/116] Down to 75 failures. Started at 91. --- packages/shared-core/src/filters.ts | 34 +++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 2e7afa882b..1699d28b5e 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -320,21 +320,33 @@ export const runQuery = ( const rangeMatch = match( SearchFilterOperator.RANGE, - ( - docValue: string | number | null, - testValue: { low: number; high: number } - ) => { + (docValue: any, testValue: any) => { if (docValue == null || docValue === "") { return false } + if (testValue.low == null && testValue.high == null) { + return false + } if (!isNaN(+docValue)) { - return +docValue >= testValue.low || +docValue <= testValue.high + if (!isNaN(+testValue.low) && !isNaN(+testValue.high)) { + return +docValue >= testValue.low && +docValue <= testValue.high + } else if (!isNaN(+testValue.low)) { + return +docValue >= testValue.low + } else if (!isNaN(+testValue.high)) { + return +docValue <= testValue.high + } } if (dayjs(docValue).isValid()) { - return ( - new Date(docValue).getTime() >= new Date(testValue.low).getTime() || - new Date(docValue).getTime() <= new Date(testValue.high).getTime() - ) + if (dayjs(testValue.low).isValid() && dayjs(testValue.high).isValid()) { + return ( + dayjs(docValue).isAfter(testValue.low) && + dayjs(docValue).isBefore(testValue.high) + ) + } else if (dayjs(testValue.low).isValid()) { + return dayjs(docValue).isAfter(testValue.low) + } else if (dayjs(testValue.high).isValid()) { + return dayjs(docValue).isBefore(testValue.high) + } } return false } @@ -422,6 +434,10 @@ export const runQuery = ( return false } + if (testValue.length === 0) { + return true + } + return testValue[f](item => _valueMatches(docValue, item)) } From cb6acd8c0b26801578c020b4bc234569e9ab1a6f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 12:24:27 +0100 Subject: [PATCH 007/116] Down to 71 failures. --- .../src/api/routes/tests/search.spec.ts | 12 +++++------ packages/shared-core/src/filters.ts | 21 ++++++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index f3353be3b2..ec6c2bf304 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1079,13 +1079,13 @@ describe.each([ !isInternal && describe("datetime - time only", () => { - const T_1000 = "10:00" - const T_1045 = "10:45" - const T_1200 = "12:00" - const T_1530 = "15:30" - const T_0000 = "00:00" + const T_1000 = "10:00:00" + const T_1045 = "10:45:00" + const T_1200 = "12:00:00" + const T_1530 = "15:30:00" + const T_0000 = "00:00:00" - const UNEXISTING_TIME = "10:01" + const UNEXISTING_TIME = "10:01:00" const NULL_TIME__ID = `null_time__id` diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 1699d28b5e..a773524ae4 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -519,18 +519,29 @@ export const sort = ( if (!sort || !sortOrder || !sortType) { return docs } - const parse = - sortType === "string" ? (x: any) => `${x}` : (x: string) => parseFloat(x) + + const parse = (x: any) => { + if (x == null) { + return x + } + if (sortType === "string") { + return `${x}` + } + return parseFloat(x) + } + return docs .slice() .sort((a: { [x: string]: any }, b: { [x: string]: any }) => { const colA = parse(a[sort]) const colB = parse(b[sort]) + + const result = colB == null || colA > colB ? 1 : -1 if (sortOrder.toLowerCase() === "descending") { - return colA > colB ? -1 : 1 - } else { - return colA > colB ? 1 : -1 + return result * -1 } + + return result }) } From 69ab1ce44f4e9b0320582d11524facf2c1adad24 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 12:30:36 +0100 Subject: [PATCH 008/116] Down to 66 failures. --- packages/shared-core/src/filters.ts | 32 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index a773524ae4..43c99c8374 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -324,9 +324,11 @@ export const runQuery = ( if (docValue == null || docValue === "") { return false } + if (testValue.low == null && testValue.high == null) { return false } + if (!isNaN(+docValue)) { if (!isNaN(+testValue.low) && !isNaN(+testValue.high)) { return +docValue >= testValue.low && +docValue <= testValue.high @@ -336,18 +338,32 @@ export const runQuery = ( return +docValue <= testValue.high } } - if (dayjs(docValue).isValid()) { - if (dayjs(testValue.low).isValid() && dayjs(testValue.high).isValid()) { + + const docDate = dayjs(docValue) + if (docDate.isValid()) { + const lowDate = dayjs(testValue.low) + const highDate = dayjs(testValue.high) + if (lowDate.isValid() && highDate.isValid()) { return ( - dayjs(docValue).isAfter(testValue.low) && - dayjs(docValue).isBefore(testValue.high) + (docDate.isAfter(lowDate) && docDate.isBefore(highDate)) || + docDate.isSame(lowDate) || + docDate.isSame(highDate) ) - } else if (dayjs(testValue.low).isValid()) { - return dayjs(docValue).isAfter(testValue.low) - } else if (dayjs(testValue.high).isValid()) { - return dayjs(docValue).isBefore(testValue.high) + } else if (lowDate.isValid()) { + return docDate.isAfter(lowDate) || docDate.isSame(lowDate) + } else if (highDate.isValid()) { + return docDate.isBefore(highDate) || docDate.isSame(highDate) } } + + if (testValue.low && testValue.high) { + return docValue >= testValue.low && docValue <= testValue.high + } else if (testValue.low) { + return docValue >= testValue.low + } else if (testValue.high) { + return docValue <= testValue.high + } + return false } ) From a82da51b30a79b2974d4b9943b4476be1c32a91d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 12:34:22 +0100 Subject: [PATCH 009/116] Down to 60 failures. --- packages/shared-core/src/filters.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 43c99c8374..0b452c8cb8 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -381,7 +381,11 @@ export const runQuery = ( return false } - if (typeof docValue === "object" && typeof testValue === "string") { + if ( + docValue && + typeof docValue === "object" && + typeof testValue === "string" + ) { return docValue._id === testValue } From 854347f9f5c57ee04f1be94b02386275ffd5f23d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 14:42:34 +0100 Subject: [PATCH 010/116] Down to 59 failures. --- packages/shared-core/src/filters.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 0b452c8cb8..ae6dd8e15b 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -341,8 +341,8 @@ export const runQuery = ( const docDate = dayjs(docValue) if (docDate.isValid()) { - const lowDate = dayjs(testValue.low) - const highDate = dayjs(testValue.high) + const lowDate = dayjs(testValue.low || "0000-00-00T00:00:00.000Z") + const highDate = dayjs(testValue.high || "9999-00-00T00:00:00.000Z") if (lowDate.isValid() && highDate.isValid()) { return ( (docDate.isAfter(lowDate) && docDate.isBefore(highDate)) || @@ -356,11 +356,11 @@ export const runQuery = ( } } - if (testValue.low && testValue.high) { + if (testValue.low != null && testValue.high != null) { return docValue >= testValue.low && docValue <= testValue.high - } else if (testValue.low) { + } else if (testValue.low != null) { return docValue >= testValue.low - } else if (testValue.high) { + } else if (testValue.high != null) { return docValue <= testValue.high } From 746ee711ae949fd4fbcce02f50689f02e54001eb Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 14:45:43 +0100 Subject: [PATCH 011/116] Down to 19 failures. --- packages/server/src/api/routes/tests/search.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index ec6c2bf304..1c0747ba3f 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -94,7 +94,7 @@ describe.each([ private async performSearch(): Promise { if (isInMemory) { - let result = dataFilters.runQuery(rows, this.query.query) + let result = dataFilters.runQuery(_.cloneDeep(rows), this.query.query) if (this.query.sort) { result = dataFilters.sort( result, From c01c2c7cc34be144654f313ac421862b6c347aab Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 16:23:16 +0100 Subject: [PATCH 012/116] Down to 4 failures. --- .../src/api/routes/tests/search.spec.ts | 586 +++++++++--------- packages/shared-core/src/filters.ts | 25 +- 2 files changed, 315 insertions(+), 296 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 1c0747ba3f..afc575b2c4 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -313,351 +313,359 @@ describe.each([ }) }) - // Ensure all bindings resolve and perform as expected - describe("bindings", () => { - let globalUsers: any = [] + // We've decided not to try and support binding for in-memory search just now. + !isInMemory && + describe("bindings", () => { + let globalUsers: any = [] - const serverTime = new Date() + const serverTime = new Date() - // In MariaDB and MySQL we only store dates to second precision, so we need - // to remove milliseconds from the server time to ensure searches work as - // expected. - serverTime.setMilliseconds(0) + // In MariaDB and MySQL we only store dates to second precision, so we need + // to remove milliseconds from the server time to ensure searches work as + // expected. + serverTime.setMilliseconds(0) - const future = new Date(serverTime.getTime() + 1000 * 60 * 60 * 24 * 30) + const future = new Date(serverTime.getTime() + 1000 * 60 * 60 * 24 * 30) - const rows = (currentUser: User) => { - return [ - { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, - { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, - { name: currentUser.firstName, appointment: future.toISOString() }, - { name: "serverDate", appointment: serverTime.toISOString() }, - { - name: "single user, session user", - single_user: JSON.stringify(currentUser), - }, - { - name: "single user", - single_user: JSON.stringify(globalUsers[0]), - }, - { - name: "deprecated single user, session user", - deprecated_single_user: JSON.stringify([currentUser]), - }, - { - name: "deprecated single user", - deprecated_single_user: JSON.stringify([globalUsers[0]]), - }, - { - name: "multi user", - multi_user: JSON.stringify(globalUsers), - }, - { - name: "multi user with session user", - multi_user: JSON.stringify([...globalUsers, currentUser]), - }, - { - name: "deprecated multi user", - deprecated_multi_user: JSON.stringify(globalUsers), - }, - { - name: "deprecated multi user with session user", - deprecated_multi_user: JSON.stringify([...globalUsers, currentUser]), - }, - ] - } - - beforeAll(async () => { - // Set up some global users - globalUsers = await Promise.all( - Array(2) - .fill(0) - .map(async () => { - const globalUser = await config.globalUser() - const userMedataId = globalUser._id - ? dbCore.generateUserMetadataID(globalUser._id) - : null - return { - _id: globalUser._id, - _meta: userMedataId, - } - }) - ) - - table = await createTable({ - name: { name: "name", type: FieldType.STRING }, - appointment: { name: "appointment", type: FieldType.DATETIME }, - single_user: { - name: "single_user", - type: FieldType.BB_REFERENCE_SINGLE, - subtype: BBReferenceFieldSubType.USER, - }, - deprecated_single_user: { - name: "deprecated_single_user", - type: FieldType.BB_REFERENCE, - subtype: BBReferenceFieldSubType.USER, - }, - multi_user: { - name: "multi_user", - type: FieldType.BB_REFERENCE, - subtype: BBReferenceFieldSubType.USER, - constraints: { - type: "array", + const rows = (currentUser: User) => { + return [ + { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, + { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, + { name: currentUser.firstName, appointment: future.toISOString() }, + { name: "serverDate", appointment: serverTime.toISOString() }, + { + name: "single user, session user", + single_user: JSON.stringify(currentUser), }, - }, - deprecated_multi_user: { - name: "deprecated_multi_user", - type: FieldType.BB_REFERENCE, - subtype: BBReferenceFieldSubType.USERS, - constraints: { - type: "array", + { + name: "single user", + single_user: JSON.stringify(globalUsers[0]), }, - }, - }) - await createRows(rows(config.getUser())) - }) + { + name: "deprecated single user, session user", + deprecated_single_user: JSON.stringify([currentUser]), + }, + { + name: "deprecated single user", + deprecated_single_user: JSON.stringify([globalUsers[0]]), + }, + { + name: "multi user", + multi_user: JSON.stringify(globalUsers), + }, + { + name: "multi user with session user", + multi_user: JSON.stringify([...globalUsers, currentUser]), + }, + { + name: "deprecated multi user", + deprecated_multi_user: JSON.stringify(globalUsers), + }, + { + name: "deprecated multi user with session user", + deprecated_multi_user: JSON.stringify([ + ...globalUsers, + currentUser, + ]), + }, + ] + } - // !! Current User is auto generated per run - it("should return all rows matching the session user firstname", async () => { - await expectQuery({ - equal: { name: "{{ [user].firstName }}" }, - }).toContainExactly([ - { - name: config.getUser().firstName, - appointment: future.toISOString(), - }, - ]) - }) + beforeAll(async () => { + // Set up some global users + globalUsers = await Promise.all( + Array(2) + .fill(0) + .map(async () => { + const globalUser = await config.globalUser() + const userMedataId = globalUser._id + ? dbCore.generateUserMetadataID(globalUser._id) + : null + return { + _id: globalUser._id, + _meta: userMedataId, + } + }) + ) - it("should parse the date binding and return all rows after the resolved value", async () => { - await tk.withFreeze(serverTime, async () => { - await expectQuery({ - range: { - appointment: { - low: "{{ [now] }}", - high: "9999-00-00T00:00:00.000Z", + table = await createTable({ + name: { name: "name", type: FieldType.STRING }, + appointment: { name: "appointment", type: FieldType.DATETIME }, + single_user: { + name: "single_user", + type: FieldType.BB_REFERENCE_SINGLE, + subtype: BBReferenceFieldSubType.USER, + }, + deprecated_single_user: { + name: "deprecated_single_user", + type: FieldType.BB_REFERENCE, + subtype: BBReferenceFieldSubType.USER, + }, + multi_user: { + name: "multi_user", + type: FieldType.BB_REFERENCE, + subtype: BBReferenceFieldSubType.USER, + constraints: { + type: "array", }, }, + deprecated_multi_user: { + name: "deprecated_multi_user", + type: FieldType.BB_REFERENCE, + subtype: BBReferenceFieldSubType.USERS, + constraints: { + type: "array", + }, + }, + }) + await createRows(rows(config.getUser())) + }) + + // !! Current User is auto generated per run + it("should return all rows matching the session user firstname", async () => { + await expectQuery({ + equal: { name: "{{ [user].firstName }}" }, }).toContainExactly([ { name: config.getUser().firstName, appointment: future.toISOString(), }, + ]) + }) + + it("should parse the date binding and return all rows after the resolved value", async () => { + await tk.withFreeze(serverTime, async () => { + await expectQuery({ + range: { + appointment: { + low: "{{ [now] }}", + high: "9999-00-00T00:00:00.000Z", + }, + }, + }).toContainExactly([ + { + name: config.getUser().firstName, + appointment: future.toISOString(), + }, + { name: "serverDate", appointment: serverTime.toISOString() }, + ]) + }) + }) + + it("should parse the date binding and return all rows before the resolved value", async () => { + await expectQuery({ + range: { + appointment: { + low: "0000-00-00T00:00:00.000Z", + high: "{{ [now] }}", + }, + }, + }).toContainExactly([ + { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, + { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, { name: "serverDate", appointment: serverTime.toISOString() }, ]) }) - }) - it("should parse the date binding and return all rows before the resolved value", async () => { - await expectQuery({ - range: { - appointment: { - low: "0000-00-00T00:00:00.000Z", - high: "{{ [now] }}", + it("should parse the encoded js snippet. Return rows with appointments up to 1 week in the past", async () => { + const jsBinding = "return snippets.WeeksAgo();" + const encodedBinding = encodeJSBinding(jsBinding) + + await expectQuery({ + range: { + appointment: { + low: "0000-00-00T00:00:00.000Z", + high: encodedBinding, + }, }, - }, - }).toContainExactly([ - { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, - { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, - { name: "serverDate", appointment: serverTime.toISOString() }, - ]) - }) + }).toContainExactly([ + { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, + { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, + ]) + }) - it("should parse the encoded js snippet. Return rows with appointments up to 1 week in the past", async () => { - const jsBinding = "return snippets.WeeksAgo();" - const encodedBinding = encodeJSBinding(jsBinding) + it("should parse the encoded js binding. Return rows with appointments 2 weeks in the past", async () => { + const jsBinding = `const currentTime = new Date(${Date.now()})\ncurrentTime.setDate(currentTime.getDate()-14);\nreturn currentTime.toISOString();` + const encodedBinding = encodeJSBinding(jsBinding) - await expectQuery({ - range: { - appointment: { - low: "0000-00-00T00:00:00.000Z", - high: encodedBinding, + await expectQuery({ + range: { + appointment: { + low: "0000-00-00T00:00:00.000Z", + high: encodedBinding, + }, }, - }, - }).toContainExactly([ - { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, - { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, - ]) - }) + }).toContainExactly([ + { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, + { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, + ]) + }) - it("should parse the encoded js binding. Return rows with appointments 2 weeks in the past", async () => { - const jsBinding = `const currentTime = new Date(${Date.now()})\ncurrentTime.setDate(currentTime.getDate()-14);\nreturn currentTime.toISOString();` - const encodedBinding = encodeJSBinding(jsBinding) - - await expectQuery({ - range: { - appointment: { - low: "0000-00-00T00:00:00.000Z", - high: encodedBinding, + it("should match a single user row by the session user id", async () => { + await expectQuery({ + equal: { single_user: "{{ [user]._id }}" }, + }).toContainExactly([ + { + name: "single user, session user", + single_user: { _id: config.getUser()._id }, }, - }, - }).toContainExactly([ - { name: "foo", appointment: "1982-01-05T00:00:00.000Z" }, - { name: "bar", appointment: "1995-05-06T00:00:00.000Z" }, - ]) - }) + ]) + }) - it("should match a single user row by the session user id", async () => { - await expectQuery({ - equal: { single_user: "{{ [user]._id }}" }, - }).toContainExactly([ - { - name: "single user, session user", - single_user: { _id: config.getUser()._id }, - }, - ]) - }) + it("should match a deprecated single user row by the session user id", async () => { + await expectQuery({ + equal: { deprecated_single_user: "{{ [user]._id }}" }, + }).toContainExactly([ + { + name: "deprecated single user, session user", + deprecated_single_user: [{ _id: config.getUser()._id }], + }, + ]) + }) - it("should match a deprecated single user row by the session user id", async () => { - await expectQuery({ - equal: { deprecated_single_user: "{{ [user]._id }}" }, - }).toContainExactly([ - { - name: "deprecated single user, session user", - deprecated_single_user: [{ _id: config.getUser()._id }], - }, - ]) - }) + // TODO(samwho): fix for SQS + !isSqs && + it("should match the session user id in a multi user field", async () => { + const allUsers = [...globalUsers, config.getUser()].map( + (user: any) => { + return { _id: user._id } + } + ) - // TODO(samwho): fix for SQS - !isSqs && - it("should match the session user id in a multi user field", async () => { - const allUsers = [...globalUsers, config.getUser()].map((user: any) => { - return { _id: user._id } + await expectQuery({ + contains: { multi_user: ["{{ [user]._id }}"] }, + }).toContainExactly([ + { + name: "multi user with session user", + multi_user: allUsers, + }, + ]) }) - await expectQuery({ - contains: { multi_user: ["{{ [user]._id }}"] }, - }).toContainExactly([ - { - name: "multi user with session user", - multi_user: allUsers, - }, - ]) - }) + // TODO(samwho): fix for SQS + !isSqs && + it("should match the session user id in a deprecated multi user field", async () => { + const allUsers = [...globalUsers, config.getUser()].map( + (user: any) => { + return { _id: user._id } + } + ) - // TODO(samwho): fix for SQS - !isSqs && - it("should match the session user id in a deprecated multi user field", async () => { - const allUsers = [...globalUsers, config.getUser()].map((user: any) => { - return { _id: user._id } + await expectQuery({ + contains: { deprecated_multi_user: ["{{ [user]._id }}"] }, + }).toContainExactly([ + { + name: "deprecated multi user with session user", + deprecated_multi_user: allUsers, + }, + ]) }) + // TODO(samwho): fix for SQS + !isSqs && + it("should not match the session user id in a multi user field", async () => { + await expectQuery({ + notContains: { multi_user: ["{{ [user]._id }}"] }, + notEmpty: { multi_user: true }, + }).toContainExactly([ + { + name: "multi user", + multi_user: globalUsers.map((user: any) => { + return { _id: user._id } + }), + }, + ]) + }) + + // TODO(samwho): fix for SQS + !isSqs && + it("should not match the session user id in a deprecated multi user field", async () => { + await expectQuery({ + notContains: { deprecated_multi_user: ["{{ [user]._id }}"] }, + notEmpty: { deprecated_multi_user: true }, + }).toContainExactly([ + { + name: "deprecated multi user", + deprecated_multi_user: globalUsers.map((user: any) => { + return { _id: user._id } + }), + }, + ]) + }) + + it("should match the session user id and a user table row id using helpers, user binding and a static user id.", async () => { await expectQuery({ - contains: { deprecated_multi_user: ["{{ [user]._id }}"] }, + oneOf: { + single_user: [ + "{{ default [user]._id '_empty_' }}", + globalUsers[0]._id, + ], + }, }).toContainExactly([ { - name: "deprecated multi user with session user", - deprecated_multi_user: allUsers, + name: "single user, session user", + single_user: { _id: config.getUser()._id }, + }, + { + name: "single user", + single_user: { _id: globalUsers[0]._id }, }, ]) }) - // TODO(samwho): fix for SQS - !isSqs && - it("should not match the session user id in a multi user field", async () => { + it("should match the session user id and a user table row id using helpers, user binding and a static user id. (deprecated single user)", async () => { await expectQuery({ - notContains: { multi_user: ["{{ [user]._id }}"] }, - notEmpty: { multi_user: true }, + oneOf: { + deprecated_single_user: [ + "{{ default [user]._id '_empty_' }}", + globalUsers[0]._id, + ], + }, }).toContainExactly([ { - name: "multi user", - multi_user: globalUsers.map((user: any) => { - return { _id: user._id } - }), + name: "deprecated single user, session user", + deprecated_single_user: [{ _id: config.getUser()._id }], + }, + { + name: "deprecated single user", + deprecated_single_user: [{ _id: globalUsers[0]._id }], }, ]) }) - // TODO(samwho): fix for SQS - !isSqs && - it("should not match the session user id in a deprecated multi user field", async () => { + it("should resolve 'default' helper to '_empty_' when binding resolves to nothing", async () => { await expectQuery({ - notContains: { deprecated_multi_user: ["{{ [user]._id }}"] }, - notEmpty: { deprecated_multi_user: true }, + oneOf: { + single_user: [ + "{{ default [user]._idx '_empty_' }}", + globalUsers[0]._id, + ], + }, }).toContainExactly([ { - name: "deprecated multi user", - deprecated_multi_user: globalUsers.map((user: any) => { - return { _id: user._id } - }), + name: "single user", + single_user: { _id: globalUsers[0]._id }, }, ]) }) - it("should match the session user id and a user table row id using helpers, user binding and a static user id.", async () => { - await expectQuery({ - oneOf: { - single_user: [ - "{{ default [user]._id '_empty_' }}", - globalUsers[0]._id, - ], - }, - }).toContainExactly([ - { - name: "single user, session user", - single_user: { _id: config.getUser()._id }, - }, - { - name: "single user", - single_user: { _id: globalUsers[0]._id }, - }, - ]) + it("should resolve 'default' helper to '_empty_' when binding resolves to nothing (deprecated single user)", async () => { + await expectQuery({ + oneOf: { + deprecated_single_user: [ + "{{ default [user]._idx '_empty_' }}", + globalUsers[0]._id, + ], + }, + }).toContainExactly([ + { + name: "deprecated single user", + deprecated_single_user: [{ _id: globalUsers[0]._id }], + }, + ]) + }) }) - it("should match the session user id and a user table row id using helpers, user binding and a static user id. (deprecated single user)", async () => { - await expectQuery({ - oneOf: { - deprecated_single_user: [ - "{{ default [user]._id '_empty_' }}", - globalUsers[0]._id, - ], - }, - }).toContainExactly([ - { - name: "deprecated single user, session user", - deprecated_single_user: [{ _id: config.getUser()._id }], - }, - { - name: "deprecated single user", - deprecated_single_user: [{ _id: globalUsers[0]._id }], - }, - ]) - }) - - it("should resolve 'default' helper to '_empty_' when binding resolves to nothing", async () => { - await expectQuery({ - oneOf: { - single_user: [ - "{{ default [user]._idx '_empty_' }}", - globalUsers[0]._id, - ], - }, - }).toContainExactly([ - { - name: "single user", - single_user: { _id: globalUsers[0]._id }, - }, - ]) - }) - - it("should resolve 'default' helper to '_empty_' when binding resolves to nothing (deprecated single user)", async () => { - await expectQuery({ - oneOf: { - deprecated_single_user: [ - "{{ default [user]._idx '_empty_' }}", - globalUsers[0]._id, - ], - }, - }).toContainExactly([ - { - name: "deprecated single user", - deprecated_single_user: [{ _id: globalUsers[0]._id }], - }, - ]) - }) - }) - describe.each([FieldType.STRING, FieldType.LONGFORM])("%s", () => { beforeAll(async () => { table = await createTable({ diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index ae6dd8e15b..391b9a795f 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -325,17 +325,28 @@ export const runQuery = ( return false } + if (_.isObject(testValue.low) && _.isEmpty(testValue.low)) { + testValue.low = undefined + } + + if (_.isObject(testValue.high) && _.isEmpty(testValue.high)) { + testValue.high = undefined + } + if (testValue.low == null && testValue.high == null) { return false } - if (!isNaN(+docValue)) { - if (!isNaN(+testValue.low) && !isNaN(+testValue.high)) { - return +docValue >= testValue.low && +docValue <= testValue.high - } else if (!isNaN(+testValue.low)) { - return +docValue >= testValue.low - } else if (!isNaN(+testValue.high)) { - return +docValue <= testValue.high + const docNum = +docValue + if (!isNaN(docNum)) { + const lowNum = +testValue.low + const highNum = +testValue.high + if (!isNaN(lowNum) && !isNaN(highNum)) { + return docNum >= lowNum && docNum <= highNum + } else if (!isNaN(lowNum)) { + return docNum >= lowNum + } else if (!isNaN(highNum)) { + return docNum <= highNum } } From f909cdee43e3a880537006d0a0af5dbaf4e77eb5 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 17:05:02 +0100 Subject: [PATCH 013/116] Down to 2 failures. --- packages/shared-core/src/filters.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 391b9a795f..d63975f8fc 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -285,7 +285,10 @@ export const runQuery = ( ) => (doc: Record) => { for (const [key, testValue] of Object.entries(query[type] || {})) { - if (!test(deepGet(doc, removeKeyNumbering(key)), testValue)) { + const result = test(deepGet(doc, removeKeyNumbering(key)), testValue) + if (query.allOr && result) { + return true + } else if (!query.allOr && !result) { return false } } From 1e34411d667e6ff9f69812e2ec834005071d25f8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 13 Jun 2024 17:29:22 +0100 Subject: [PATCH 014/116] Adding the correct link for the migration pages. --- .../pages/builder/maintenance/index.svelte | 3 ++- yarn.lock | 24 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/builder/src/pages/builder/maintenance/index.svelte b/packages/builder/src/pages/builder/maintenance/index.svelte index e4c379885a..f7eb16ab81 100644 --- a/packages/builder/src/pages/builder/maintenance/index.svelte +++ b/packages/builder/src/pages/builder/maintenance/index.svelte @@ -33,7 +33,8 @@ {/if} diff --git a/yarn.lock b/yarn.lock index 426fa2275d..d71dd4da78 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3495,10 +3495,10 @@ dependencies: lodash "^4.17.21" -"@koa/cors@^3.1.0": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.4.3.tgz#d669ee6e8d6e4f0ec4a7a7b0a17e7a3ed3752ebb" - integrity sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw== +"@koa/cors@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-5.0.0.tgz#0029b5f057fa0d0ae0e37dd2c89ece315a0daffd" + integrity sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw== dependencies: vary "^1.1.2" @@ -5817,10 +5817,10 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/koa__cors@^3.1.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-3.3.1.tgz#0ec7543c4c620fd23451bfdd3e21b9a6aadedccd" - integrity sha512-aFGYhTFW7651KhmZZ05VG0QZJre7QxBxDj2LF1lf6GA/wSXEfKVAJxiQQWzRV4ZoMzQIO8vJBXKsUcRuvYK9qw== +"@types/koa__cors@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-5.0.0.tgz#74567a045b599266e2cd3940cef96cedecc2ef1f" + integrity sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g== dependencies: "@types/koa" "*" @@ -16343,10 +16343,10 @@ node-source-walk@^5.0.0: dependencies: "@babel/parser" "^7.0.0" -nodemailer@6.7.2: - version "6.7.2" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.2.tgz#44b2ad5f7ed71b7067f7a21c4fedabaec62b85e0" - integrity sha512-Dz7zVwlef4k5R71fdmxwR8Q39fiboGbu3xgswkzGwczUfjp873rVxt1O46+Fh0j1ORnAC6L9+heI8uUpO6DT7Q== +nodemailer@6.9.13: + version "6.9.13" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.13.tgz#5b292bf1e92645f4852ca872c56a6ba6c4a3d3d6" + integrity sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA== nodemailer@6.9.9: version "6.9.9" From 1161c185e21b3eb48cd357920382914b5b8fe17f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 13 Jun 2024 17:46:03 +0100 Subject: [PATCH 015/116] Down to 0 failures. --- .../src/api/routes/tests/search.spec.ts | 25 ++++++------------- packages/shared-core/src/filters.ts | 20 +++++++++++++++ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index afc575b2c4..22971c9c1f 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -26,12 +26,12 @@ import { dataFilters } from "@budibase/shared-core" describe.each([ ["in-memory", undefined], - // ["lucene", undefined], - // ["sqs", undefined], - // [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], - // [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], - // [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], - // [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], + ["lucene", undefined], + ["sqs", undefined], + [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], + [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], + [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], + [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], ])("search (%s)", (name, dsProvider) => { const isSqs = name === "sqs" const isLucene = name === "lucene" @@ -94,18 +94,7 @@ describe.each([ private async performSearch(): Promise { if (isInMemory) { - let result = dataFilters.runQuery(_.cloneDeep(rows), this.query.query) - if (this.query.sort) { - result = dataFilters.sort( - result, - this.query.sort, - this.query.sortOrder || SortOrder.ASCENDING - ) - } - if (this.query.limit) { - result = dataFilters.limit(result, this.query.limit.toString()) - } - return result + return dataFilters.search(_.cloneDeep(rows), this.query) } else { return ( await config.api.row.search(table._id!, { diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index d63975f8fc..14b9f004d3 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -10,6 +10,8 @@ import { SortType, FieldConstraints, SortOrder, + RowSearchParams, + EmptyFilterOption, } from "@budibase/types" import dayjs from "dayjs" import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants" @@ -260,6 +262,17 @@ export const buildQuery = (filter: SearchFilter[]) => { return query } +export const search = (docs: Record[], query: RowSearchParams) => { + let result = runQuery(docs, query.query) + if (query.sort) { + result = sort(result, query.sort, query.sortOrder || SortOrder.ASCENDING) + } + if (query.limit) { + result = limit(result, query.limit.toString()) + } + return result +} + /** * Performs a client-side search on an array of data * @param docs the data @@ -278,6 +291,13 @@ export const runQuery = ( query = cleanupQuery(query) + if ( + !hasFilters(query) && + query.onEmptyFilter === EmptyFilterOption.RETURN_NONE + ) { + return [] + } + const match = ( type: SearchFilterOperator, From 8970705b399d86e01285d61167ea704a55233dad Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 13 Jun 2024 17:49:41 +0100 Subject: [PATCH 016/116] Adding a minimum time to the app migration screen and adding a link to documentation. --- .../frontend-core/src/components/Updating.svelte | 15 ++++++++++++--- .../src/appMigrations/appMigrationMetadata.ts | 9 ++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/frontend-core/src/components/Updating.svelte b/packages/frontend-core/src/components/Updating.svelte index 7d4a101fee..7d14e57aba 100644 --- a/packages/frontend-core/src/components/Updating.svelte +++ b/packages/frontend-core/src/components/Updating.svelte @@ -1,18 +1,22 @@
- - + + {#if messages.includes(messageConstants.stringAsNumber)} {/if} @@ -84,7 +75,7 @@ {#if detailsModalSubject !== subjects.none} - import { - Line, - InfoWord, - DocumentationLink, - Text, - Period, - } from "../typography" + import { Line, InfoWord, DocumentationLink, Text } from "../typography" + import { FieldType } from "@budibase/types" + import { FIELDS } from "constants/backend" import subjects from "../subjects" - export let columnName - export let columnIcon - export let columnType + export let schema + export let name export let tableHref export let setExplanationSubject + const getTypeName = schema => { + const fieldDefinition = Object.values(FIELDS).find( + f => f.type === schema?.type + ) + + if (schema?.type === "jsonarray") { + return "JSON Array" + } + if (schema?.type === "options") { + return "Options" + } + + return fieldDefinition?.name || schema?.type || "Unknown" + } + + const getTypeIcon = schema => { + const fieldDefinition = Object.values(FIELDS).find( + f => f.type === schema?.type + ) + + if (schema?.type === "jsonarray") { + return "BracketsSquare" + } + + return fieldDefinition?.icon || "Circle" + } + const getDocLink = columnType => { - if (columnType === "Number") { + if (columnType === FieldType.NUMBER) { return "https://docs.budibase.com/docs/number" } - if (columnType === "Text") { + if (columnType === FieldType.STRING) { return "https://docs.budibase.com/docs/text" } - if (columnType === "Attachment") { + if (columnType === FieldType.LONGFORM) { + return "https://docs.budibase.com/docs/text" + } + if (columnType === FieldType.ATTACHMENT_SINGLE) { return "https://docs.budibase.com/docs/attachments" } - if (columnType === "Multi-select") { + if (columnType === FieldType.ATTACHMENTS) { + // No distinct multi attachment docs, link to attachment instead + return "https://docs.budibase.com/docs/attachments" + } + if (columnType === FieldType.ARRAY) { return "https://docs.budibase.com/docs/multi-select" } - if (columnType === "JSON") { + if (columnType === FieldType.JSON) { return "https://docs.budibase.com/docs/json" } - if (columnType === "Date/Time") { + if (columnType === "jsonarray") { + return "https://docs.budibase.com/docs/json" + } + if (columnType === FieldType.DATETIME) { return "https://docs.budibase.com/docs/datetime" } - if (columnType === "User") { - return "https://docs.budibase.com/docs/user" + if (columnType === FieldType.BB_REFERENCE_SINGLE) { + return "https://docs.budibase.com/docs/users-1" } - if (columnType === "QR") { + if (columnType === FieldType.BB_REFERENCE) { + return "https://docs.budibase.com/docs/users-1" + } + if (columnType === FieldType.BARCODEQR) { return "https://docs.budibase.com/docs/barcodeqr" } - if (columnType === "Relationship") { + if (columnType === FieldType.LINK) { return "https://docs.budibase.com/docs/relationships" } - if (columnType === "Formula") { + if (columnType === FieldType.FORMULA) { return "https://docs.budibase.com/docs/formula" } - if (columnType === "Options") { + if (columnType === FieldType.OPTIONS) { return "https://docs.budibase.com/docs/options" } - if (columnType === "BigInt") { - // No BigInt docs - return null - } - if (columnType === "Boolean") { + if (columnType === FieldType.BOOLEAN) { return "https://docs.budibase.com/docs/boolean-truefalse" } - if (columnType === "Signature") { + if (columnType === FieldType.SIGNATURE_SINGLE) { // No Signature docs return null } + if (columnType === FieldType.BIGINT) { + // No BigInt docs + return null + } return null } - $: docLink = getDocLink(columnType) + // NOTE The correct indefinite article is based on the pronounciation of the word it precedes, not the spelling. So simply checking if the word begins with a vowel is not sufficient. + + // e.g., `an honor`, `a user` + const getIndefiniteArticle = schema => { + const anTypes = [ + FieldType.OPTIONS, + null, // `null` gets parsed as "unknown" + undefined, // `undefined` gets parsed as "unknown" + ] + + if (anTypes.includes(schema?.type)) { + return "an" + } + + return "a" + } + + $: columnTypeName = getTypeName(schema) + $: columnIcon = getTypeIcon(schema) + $: docLink = getDocLink(schema?.type) + $: indefiniteArticle = getIndefiniteArticle(schema) @@ -71,14 +126,14 @@ on:mouseenter={() => setExplanationSubject(subjects.column)} on:mouseleave={() => setExplanationSubject(subjects.none)} href={tableHref} - text={columnName} + text={name} /> - + - + diff --git a/packages/builder/src/components/design/settings/controls/Explanation/lines/Support.svelte b/packages/builder/src/components/design/settings/controls/Explanation/lines/Support.svelte index 848ab208fb..ccb33798d7 100644 --- a/packages/builder/src/components/design/settings/controls/Explanation/lines/Support.svelte +++ b/packages/builder/src/components/design/settings/controls/Explanation/lines/Support.svelte @@ -2,9 +2,16 @@ import { Line, InfoWord, DocumentationLink, Text } from "../typography" import subjects from "../subjects" import * as explanation from "../explanation" + import { componentStore } from "stores/builder" export let setExplanationSubject export let support + export let componentName + + const getComponentDefinition = componentName => { + const components = $componentStore.components || {} + return components[componentName] || null + } const getIcon = support => { if (support === explanation.support.unsupported) { @@ -39,21 +46,24 @@ $: icon = getIcon(support) $: color = getColor(support) $: text = getText(support) + $: componentDefinition = getComponentDefinition(componentName) - - setExplanationSubject(subjects.support)} - on:mouseleave={() => setExplanationSubject(subjects.none)} - {icon} - {color} - {text} - /> - - - - +{#if componentDefinition} + + setExplanationSubject(subjects.support)} + on:mouseleave={() => setExplanationSubject(subjects.none)} + {icon} + {color} + {text} + /> + + + + +{/if} diff --git a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte index 15b67ded18..aab7eb60a5 100644 --- a/packages/builder/src/components/design/settings/controls/FieldSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldSelect.svelte @@ -6,8 +6,6 @@ import { Explanation } from "./Explanation" import { debounce } from "lodash" import { params } from "@roxi/routify" - import { Constants } from "@budibase/frontend-core" - import { FIELDS } from "constants/backend" export let componentInstance = {} export let value = "" @@ -60,35 +58,6 @@ const onOptionMouseleave = e => { updateTooltip(e, null) } - const getOptionIcon = optionKey => { - const option = schema[optionKey] - if (!option) return "" - - if (option.autocolumn) { - return "MagicWand" - } - const { type, subtype } = option - - const result = - typeof Constants.TypeIconMap[type] === "object" && subtype - ? Constants.TypeIconMap[type][subtype] - : Constants.TypeIconMap[type] - - return result || "Text" - } - - const getOptionIconTooltip = optionKey => { - const option = schema[optionKey] - - const type = option?.type - const field = Object.values(FIELDS).find(f => f.type === type) - - if (field) { - return field.name - } - - return "" - } { if (e.key.toLowerCase() === "enter") { @@ -158,7 +158,32 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + position: relative; + padding: 5px; + right: 6px; + border: 1px solid transparent; + border-radius: 3px; + transition: 150ms background-color, 150ms border-color, 150ms color; } + + .input:hover, + .input:focus { + cursor: text; + background-color: var( + --spectrum-textfield-m-background-color, + var(--spectrum-global-color-gray-50) + ); + border: 1px solid white; + border-color: var( + --spectrum-textfield-m-border-color, + var(--spectrum-alias-border-color) + ); + color: var( + --spectrum-textfield-m-text-color, + var(--spectrum-alias-text-color) + ); + } + .panel-title-content { display: contents; } From caeca22d7942b3f801924b6a107fd0b20cd9121b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 14 Jun 2024 13:59:28 +0100 Subject: [PATCH 025/116] Update pro reference. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index e093f49a9d..bf30f47a28 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit e093f49a9d0f548fe8cf7b981d1e88eb0fcc394a +Subproject commit bf30f47a28292d619cf0837f21d66790ff31c3a6 From 7491021ca0e0dfe4d4cee64a47e63d93483a0a48 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 14:35:14 +0100 Subject: [PATCH 026/116] Base implementation of counting (plumbing). --- .../server/src/sdk/app/rows/search/sqs.ts | 46 ++++++++++++++----- packages/server/src/sdk/app/rows/sqlAlias.ts | 21 ++++++++- packages/types/src/sdk/datasources.ts | 1 + yarn.lock | 24 +++++----- 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/packages/server/src/sdk/app/rows/search/sqs.ts b/packages/server/src/sdk/app/rows/search/sqs.ts index 87b905a29f..e45a7f94a7 100644 --- a/packages/server/src/sdk/app/rows/search/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/sqs.ts @@ -12,6 +12,7 @@ import { SortOrder, SortType, SqlClient, + SqlQuery, Table, } from "@budibase/types" import { @@ -101,12 +102,29 @@ function buildTableMap(tables: Table[]) { return tableMap } -async function runSqlQuery(json: QueryJson, tables: Table[]) { +function runSqlQuery(json: QueryJson, tables: Table[]): Promise +function runSqlQuery( + json: QueryJson, + tables: Table[], + opts: { countTotalRows: boolean } +): Promise +async function runSqlQuery( + json: QueryJson, + tables: Table[], + opts?: { countTotalRows?: boolean } +) { const alias = new AliasTables(tables.map(table => table.name)) - return await alias.queryWithAliasing(json, async json => { - const query = builder._query(json, { - disableReturning: true, - }) + const processSQLQuery = async (json: QueryJson) => { + let query: SqlQuery | SqlQuery[] + if (opts?.countTotalRows) { + query = builder._count(json, { + disableReturning: true, + }) + } else { + query = builder._query(json, { + disableReturning: true, + }) + } if (Array.isArray(query)) { throw new Error("SQS cannot currently handle multiple queries") @@ -125,7 +143,12 @@ async function runSqlQuery(json: QueryJson, tables: Table[]) { const db = context.getAppDB() return await db.sql(sql, bindings) - }) + } + if (opts?.countTotalRows) { + return await alias.countWithAliasing(json, processSQLQuery) + } else { + return await alias.queryWithAliasing(json, processSQLQuery) + } } export async function search( @@ -204,8 +227,11 @@ export async function search( ) // check for pagination final row - let nextRow: Row | undefined + let nextRow: Row | undefined, rowCount: number | undefined if (paginate && params.limit && processed.length > params.limit) { + // get the total count of rows + rowCount = await runSqlQuery(request, allTables, { countTotalRows: true }) + // remove the extra row that confirmed if there is another row to move to nextRow = processed.pop() } @@ -226,14 +252,10 @@ export async function search( const response: SearchResponse = { rows: finalRows, } - const prevLimit = request.paginate!.limit - request.paginate = { - limit: 1, - page: bookmark * prevLimit + 1, - } const hasNextPage = !!nextRow response.hasNextPage = hasNextPage if (hasNextPage) { + response.totalRows = rowCount response.bookmark = bookmark + 1 } return response diff --git a/packages/server/src/sdk/app/rows/sqlAlias.ts b/packages/server/src/sdk/app/rows/sqlAlias.ts index ab4f5d2844..1b470a6a02 100644 --- a/packages/server/src/sdk/app/rows/sqlAlias.ts +++ b/packages/server/src/sdk/app/rows/sqlAlias.ts @@ -65,7 +65,7 @@ export default class AliasTables { this.charSeq = new CharSequence() } - isAliasingEnabled(json: QueryJson, datasource: Datasource) { + isAliasingEnabled(json: QueryJson, datasource?: Datasource) { const operation = json.endpoint.operation const fieldLength = json.resource?.fields?.length if ( @@ -75,6 +75,10 @@ export default class AliasTables { ) { return false } + // SQS - doesn't have a datasource + if (!datasource) { + return true + } try { const sqlClient = getSQLClient(datasource) const isWrite = WRITE_OPERATIONS.includes(operation) @@ -173,7 +177,7 @@ export default class AliasTables { const isSqs = datasourceId === SQS_DATASOURCE_INTERNAL let aliasingEnabled: boolean, datasource: Datasource | undefined if (isSqs) { - aliasingEnabled = true + aliasingEnabled = this.isAliasingEnabled(json) } else { datasource = await datasources.get(datasourceId) aliasingEnabled = this.isAliasingEnabled(json, datasource) @@ -239,4 +243,17 @@ export default class AliasTables { return response } } + + // handles getting the count out of the query + async countWithAliasing( + json: QueryJson, + queryFn?: (json: QueryJson) => Promise + ): Promise { + let response = await this.queryWithAliasing(json, queryFn) + if (response && response.length === 1 && "total" in response[0]) { + return response[0].total + } else { + throw new Error("Unable to count rows in query - no count response") + } + } } diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts index 77e4877dfa..10a697671f 100644 --- a/packages/types/src/sdk/datasources.ts +++ b/packages/types/src/sdk/datasources.ts @@ -8,6 +8,7 @@ export enum Operation { READ = "READ", UPDATE = "UPDATE", DELETE = "DELETE", + COUNT = "COUNT", BULK_CREATE = "BULK_CREATE", CREATE_TABLE = "CREATE_TABLE", UPDATE_TABLE = "UPDATE_TABLE", diff --git a/yarn.lock b/yarn.lock index 426fa2275d..d71dd4da78 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3495,10 +3495,10 @@ dependencies: lodash "^4.17.21" -"@koa/cors@^3.1.0": - version "3.4.3" - resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-3.4.3.tgz#d669ee6e8d6e4f0ec4a7a7b0a17e7a3ed3752ebb" - integrity sha512-WPXQUaAeAMVaLTEFpoq3T2O1C+FstkjJnDQqy95Ck1UdILajsRhu6mhJ8H2f4NFPRBoCNN+qywTJfq/gGki5mw== +"@koa/cors@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-5.0.0.tgz#0029b5f057fa0d0ae0e37dd2c89ece315a0daffd" + integrity sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw== dependencies: vary "^1.1.2" @@ -5817,10 +5817,10 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/koa__cors@^3.1.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-3.3.1.tgz#0ec7543c4c620fd23451bfdd3e21b9a6aadedccd" - integrity sha512-aFGYhTFW7651KhmZZ05VG0QZJre7QxBxDj2LF1lf6GA/wSXEfKVAJxiQQWzRV4ZoMzQIO8vJBXKsUcRuvYK9qw== +"@types/koa__cors@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-5.0.0.tgz#74567a045b599266e2cd3940cef96cedecc2ef1f" + integrity sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g== dependencies: "@types/koa" "*" @@ -16343,10 +16343,10 @@ node-source-walk@^5.0.0: dependencies: "@babel/parser" "^7.0.0" -nodemailer@6.7.2: - version "6.7.2" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.2.tgz#44b2ad5f7ed71b7067f7a21c4fedabaec62b85e0" - integrity sha512-Dz7zVwlef4k5R71fdmxwR8Q39fiboGbu3xgswkzGwczUfjp873rVxt1O46+Fh0j1ORnAC6L9+heI8uUpO6DT7Q== +nodemailer@6.9.13: + version "6.9.13" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.13.tgz#5b292bf1e92645f4852ca872c56a6ba6c4a3d3d6" + integrity sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA== nodemailer@6.9.9: version "6.9.9" From 2c6262844bb719029b864b4d2b2a0ae24f2e915f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 14:35:35 +0100 Subject: [PATCH 027/116] Some work to limiting, changing how limiting works for pagination so that filtering on relationships doesn't cause problems. --- packages/backend-core/src/sql/sql.ts | 112 ++++++++++++++++++--------- 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 61d5849058..a48a102349 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -1,10 +1,10 @@ import { Knex, knex } from "knex" import * as dbCore from "../db" import { - isIsoDateString, - isValidFilter, getNativeSql, isExternalTable, + isIsoDateString, + isValidFilter, } from "./utils" import { SqlStatements } from "./sqlStatements" import SqlTableQueryBuilder from "./sqlTable" @@ -12,21 +12,21 @@ import { BBReferenceFieldMetadata, FieldSchema, FieldType, + INTERNAL_TABLE_SOURCE_ID, JsonFieldMetadata, + JsonTypes, Operation, + prefixed, QueryJson, - SqlQuery, + QueryOptions, RelationshipsJson, SearchFilters, SortDirection, + SqlClient, + SqlQuery, SqlQueryBinding, Table, TableSourceType, - INTERNAL_TABLE_SOURCE_ID, - SqlClient, - QueryOptions, - JsonTypes, - prefixed, } from "@budibase/types" import environment from "../environment" import { helpers } from "@budibase/shared-core" @@ -522,7 +522,7 @@ class InternalBuilder { }) } } - return query.limit(BASE_LIMIT) + return query } knexWithAlias( @@ -571,9 +571,15 @@ class InternalBuilder { return query.insert(parsedBody) } - read(knex: Knex, json: QueryJson, limit: number): Knex.QueryBuilder { + read( + knex: Knex, + json: QueryJson, + limit: number, + opts?: { counting?: boolean } + ): Knex.QueryBuilder { let { endpoint, resource, filters, paginate, relationships, tableAliases } = json + const counting = opts?.counting const tableName = endpoint.entityId // select all if not specified @@ -582,28 +588,16 @@ class InternalBuilder { } let selectStatement: string | (string | Knex.Raw)[] = "*" // handle select - if (resource.fields && resource.fields.length > 0) { + if (!counting && resource.fields && resource.fields.length > 0) { // select the resources as the format "table.columnName" - this is what is provided // by the resource builder further up selectStatement = generateSelectStatement(json, knex) } - let foundLimit = limit || BASE_LIMIT - // handle pagination - let foundOffset: number | null = null - if (paginate && paginate.page && paginate.limit) { - // @ts-ignore - const page = paginate.page <= 1 ? 0 : paginate.page - 1 - const offset = page * paginate.limit - foundLimit = paginate.limit - foundOffset = offset - } else if (paginate && paginate.limit) { - foundLimit = paginate.limit - } // start building the query let query = this.knexWithAlias(knex, endpoint, tableAliases) - query = query.limit(foundLimit) - if (foundOffset) { - query = query.offset(foundOffset) + // add a base query over all + if (!counting) { + query = query.limit(BASE_LIMIT) } query = this.addFilters(query, filters, json.meta.table, { aliases: tableAliases, @@ -614,7 +608,12 @@ class InternalBuilder { const alias = tableAliases?.[tableName] || tableName let preQuery = knex({ [alias]: query, - } as any).select(selectStatement) as any + } as any) + if (counting) { + preQuery = preQuery.count("* as total") + } else { + preQuery = preQuery.select(selectStatement) + } // have to add after as well (this breaks MS-SQL) if (this.client !== SqlClient.MS_SQL) { preQuery = this.addSorting(preQuery, json) @@ -627,6 +626,30 @@ class InternalBuilder { endpoint.schema, tableAliases ) + + let foundLimit = limit || BASE_LIMIT + // handle pagination + let foundOffset: number | null = null + if (paginate && paginate.page && paginate.limit) { + let page = + typeof paginate.page === "string" + ? parseInt(paginate.page) + : paginate.page + page = page <= 1 ? 0 : page - 1 + const offset = page * paginate.limit + foundLimit = paginate.limit + foundOffset = offset + } else if (paginate && paginate.limit) { + foundLimit = paginate.limit + } + if (!counting) { + query = query.limit(foundLimit) + } + // add overall pagination + if (!counting && foundOffset) { + query = query.offset(foundOffset) + } + return this.addFilters(query, filters, json.meta.table, { relationship: true, aliases: tableAliases, @@ -671,6 +694,19 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { this.limit = limit } + private convertToNative(query: Knex.QueryBuilder, opts: QueryOptions = {}) { + const sqlClient = this.getSqlClient() + if (opts?.disableBindings) { + return { sql: query.toString() } + } else { + let native = getNativeSql(query) + if (sqlClient === SqlClient.SQL_LITE) { + native = convertBooleans(native) + } + return native + } + } + /** * @param json The JSON query DSL which is to be converted to SQL. * @param opts extra options which are to be passed into the query builder, e.g. disableReturning @@ -713,15 +749,21 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { throw `Operation type is not supported by SQL query builder` } - if (opts?.disableBindings) { - return { sql: query.toString() } - } else { - let native = getNativeSql(query) - if (sqlClient === SqlClient.SQL_LITE) { - native = convertBooleans(native) - } - return native + return this.convertToNative(query, opts) + } + + _count(json: QueryJson, opts: QueryOptions = {}) { + const sqlClient = this.getSqlClient() + const config: Knex.Config = { + client: sqlClient, } + if (sqlClient === SqlClient.SQL_LITE) { + config.useNullAsDefault = true + } + const client = knex(config) + const builder = new InternalBuilder(sqlClient) + const query = builder.read(client, json, this.limit, { counting: true }) + return this.convertToNative(query, opts) } async getReturningRow(queryFn: QueryFunction, json: QueryJson) { From a54048e3089a0dfe096ed57400919f1487fbd89d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 14 Jun 2024 14:57:31 +0100 Subject: [PATCH 028/116] Solve all remaining TODOs in search.spec.ts. --- packages/backend-core/src/sql/sql.ts | 3 + .../src/api/routes/tests/search.spec.ts | 168 ++++++++---------- .../server/src/sdk/app/rows/search/sqs.ts | 25 +-- packages/types/src/sdk/search.ts | 1 + 4 files changed, 88 insertions(+), 109 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 7ab3e8ca80..9bc2092b83 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -596,6 +596,9 @@ class InternalBuilder { const offset = page * paginate.limit foundLimit = paginate.limit foundOffset = offset + } else if (paginate && paginate.offset && paginate.limit) { + foundLimit = paginate.limit + foundOffset = paginate.offset } else if (paginate && paginate.limit) { foundLimit = paginate.limit } diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 22971c9c1f..f651908c01 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -512,75 +512,63 @@ describe.each([ ]) }) - // TODO(samwho): fix for SQS - !isSqs && - it("should match the session user id in a multi user field", async () => { - const allUsers = [...globalUsers, config.getUser()].map( - (user: any) => { + it("should match the session user id in a multi user field", async () => { + const allUsers = [...globalUsers, config.getUser()].map((user: any) => { + return { _id: user._id } + }) + + await expectQuery({ + contains: { multi_user: ["{{ [user]._id }}"] }, + }).toContainExactly([ + { + name: "multi user with session user", + multi_user: allUsers, + }, + ]) + }) + + it("should match the session user id in a deprecated multi user field", async () => { + const allUsers = [...globalUsers, config.getUser()].map((user: any) => { + return { _id: user._id } + }) + + await expectQuery({ + contains: { deprecated_multi_user: ["{{ [user]._id }}"] }, + }).toContainExactly([ + { + name: "deprecated multi user with session user", + deprecated_multi_user: allUsers, + }, + ]) + }) + + it("should not match the session user id in a multi user field", async () => { + await expectQuery({ + notContains: { multi_user: ["{{ [user]._id }}"] }, + notEmpty: { multi_user: true }, + }).toContainExactly([ + { + name: "multi user", + multi_user: globalUsers.map((user: any) => { return { _id: user._id } - } - ) + }), + }, + ]) + }) - await expectQuery({ - contains: { multi_user: ["{{ [user]._id }}"] }, - }).toContainExactly([ - { - name: "multi user with session user", - multi_user: allUsers, - }, - ]) - }) - - // TODO(samwho): fix for SQS - !isSqs && - it("should match the session user id in a deprecated multi user field", async () => { - const allUsers = [...globalUsers, config.getUser()].map( - (user: any) => { + it("should not match the session user id in a deprecated multi user field", async () => { + await expectQuery({ + notContains: { deprecated_multi_user: ["{{ [user]._id }}"] }, + notEmpty: { deprecated_multi_user: true }, + }).toContainExactly([ + { + name: "deprecated multi user", + deprecated_multi_user: globalUsers.map((user: any) => { return { _id: user._id } - } - ) - - await expectQuery({ - contains: { deprecated_multi_user: ["{{ [user]._id }}"] }, - }).toContainExactly([ - { - name: "deprecated multi user with session user", - deprecated_multi_user: allUsers, - }, - ]) - }) - - // TODO(samwho): fix for SQS - !isSqs && - it("should not match the session user id in a multi user field", async () => { - await expectQuery({ - notContains: { multi_user: ["{{ [user]._id }}"] }, - notEmpty: { multi_user: true }, - }).toContainExactly([ - { - name: "multi user", - multi_user: globalUsers.map((user: any) => { - return { _id: user._id } - }), - }, - ]) - }) - - // TODO(samwho): fix for SQS - !isSqs && - it("should not match the session user id in a deprecated multi user field", async () => { - await expectQuery({ - notContains: { deprecated_multi_user: ["{{ [user]._id }}"] }, - notEmpty: { deprecated_multi_user: true }, - }).toContainExactly([ - { - name: "deprecated multi user", - deprecated_multi_user: globalUsers.map((user: any) => { - return { _id: user._id } - }), - }, - ]) - }) + }), + }, + ]) + }) it("should match the session user id and a user table row id using helpers, user binding and a static user id.", async () => { await expectQuery({ @@ -1552,38 +1540,34 @@ describe.each([ }) }) - // TODO(samwho): fix for SQS - !isSqs && - describe("pagination", () => { - it("should paginate through all rows", async () => { - // @ts-ignore - let bookmark: string | number = undefined - let rows: Row[] = [] + describe("pagination", () => { + it("should paginate through all rows", async () => { + // @ts-ignore + let bookmark: string | number = undefined + let rows: Row[] = [] - // eslint-disable-next-line no-constant-condition - while (true) { - const response = await config.api.row.search(table._id!, { - tableId: table._id!, - limit: 3, - query: {}, - bookmark, - paginate: true, - }) + // eslint-disable-next-line no-constant-condition + while (true) { + const response = await config.api.row.search(table._id!, { + tableId: table._id!, + limit: 3, + query: {}, + bookmark, + paginate: true, + }) - rows.push(...response.rows) + rows.push(...response.rows) - if (!response.bookmark || !response.hasNextPage) { - break - } - bookmark = response.bookmark + if (!response.bookmark || !response.hasNextPage) { + break } + bookmark = response.bookmark + } - expect(rows).toHaveLength(10) - expect(rows.map(row => row.auto)).toEqual( - expect.arrayContaining([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - ) - }) + const autoValues = rows.map(row => row.auto).sort((a, b) => a - b) + expect(autoValues).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) }) + }) }) describe("field name 1:name", () => { diff --git a/packages/server/src/sdk/app/rows/search/sqs.ts b/packages/server/src/sdk/app/rows/search/sqs.ts index 59a0afc0a4..98b4053931 100644 --- a/packages/server/src/sdk/app/rows/search/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/sqs.ts @@ -167,13 +167,9 @@ export async function search( const sortField = table.schema[params.sort] const sortType = sortField.type === FieldType.NUMBER ? SortType.NUMBER : SortType.STRING - const sortDirection = - params.sortOrder === SortOrder.ASCENDING - ? SortOrder.ASCENDING - : SortOrder.DESCENDING request.sort = { [sortField.name]: { - direction: sortDirection, + direction: params.sortOrder || SortOrder.DESCENDING, type: sortType as SortType, }, } @@ -182,14 +178,15 @@ export async function search( if (params.bookmark && typeof params.bookmark !== "number") { throw new Error("Unable to paginate with string based bookmarks") } - const bookmark: number = (params.bookmark as number) || 1 - const limit = params.limit + + const bookmark: number = (params.bookmark as number) || 0 if (paginate && params.limit) { request.paginate = { limit: params.limit + 1, - page: bookmark, + offset: bookmark * params.limit, } } + try { const rows = await runSqlQuery(request, allTables) @@ -221,18 +218,12 @@ export async function search( } // check for pagination - if (paginate && limit) { + if (paginate) { const response: SearchResponse = { rows: finalRows, } - const prevLimit = request.paginate!.limit - request.paginate = { - limit: 1, - page: bookmark * prevLimit + 1, - } - const hasNextPage = !!nextRow - response.hasNextPage = hasNextPage - if (hasNextPage) { + if (nextRow) { + response.hasNextPage = true response.bookmark = bookmark + 1 } return response diff --git a/packages/types/src/sdk/search.ts b/packages/types/src/sdk/search.ts index 00bb6e267d..e5cbccf5c1 100644 --- a/packages/types/src/sdk/search.ts +++ b/packages/types/src/sdk/search.ts @@ -85,6 +85,7 @@ export interface SortJson { export interface PaginationJson { limit: number page?: string | number + offset?: number } export interface RenameColumn { From c2320e4f5bac2419ea41e10a0b344fc6ca2d2910 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 14 Jun 2024 17:20:08 +0100 Subject: [PATCH 029/116] Convert postgres.spec.ts to use Knex instead of rawQuery. --- .../src/integration-test/postgres.spec.ts | 170 ++++++++---------- .../src/integrations/tests/utils/index.ts | 17 ++ .../src/integrations/tests/utils/mssql.ts | 15 ++ .../src/integrations/tests/utils/mysql.ts | 15 ++ .../src/integrations/tests/utils/postgres.ts | 15 ++ 5 files changed, 135 insertions(+), 97 deletions(-) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index cf4752978c..32eb91ca62 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -1,9 +1,3 @@ -import fetch from "node-fetch" -import { - generateMakeRequest, - MakeRequestResponse, -} from "../api/routes/public/tests/utils" - import * as setup from "../api/routes/tests/utilities" import { Datasource, FieldType } from "@budibase/types" import _ from "lodash" @@ -11,29 +5,22 @@ import { generator } from "@budibase/backend-core/tests" import { DatabaseName, getDatasource, - rawQuery, + knexClient, } from "../integrations/tests/utils" - -// @ts-ignore -fetch.mockSearch() +import { Knex } from "knex" const config = setup.getConfig()! -jest.mock("../websockets") - describe("postgres integrations", () => { - let makeRequest: MakeRequestResponse, - rawDatasource: Datasource, - datasource: Datasource + let rawDatasource: Datasource + let datasource: Datasource + let client: Knex beforeAll(async () => { await config.init() - const apiKey = await config.generateApiKey() - - makeRequest = generateMakeRequest(apiKey, true) - rawDatasource = await getDatasource(DatabaseName.POSTGRES) datasource = await config.api.datasource.create(rawDatasource) + client = await knexClient(rawDatasource) }) afterAll(config.end) @@ -46,11 +33,13 @@ describe("postgres integrations", () => { }) afterEach(async () => { - await rawQuery(rawDatasource, `DROP TABLE IF EXISTS "${tableName}"`) + await client.schema.dropTableIfExists(tableName) }) it("recognises when a table has no primary key", async () => { - await rawQuery(rawDatasource, `CREATE TABLE "${tableName}" (id SERIAL)`) + await client.schema.createTable(tableName, table => { + table.increments("id", { primaryKey: false }) + }) const response = await config.api.datasource.fetchSchema({ datasourceId: datasource._id!, @@ -62,10 +51,9 @@ describe("postgres integrations", () => { }) it("recognises when a table is using a reserved column name", async () => { - await rawQuery( - rawDatasource, - `CREATE TABLE "${tableName}" (_id SERIAL PRIMARY KEY) ` - ) + await client.schema.createTable(tableName, table => { + table.increments("_id").primary() + }) const response = await config.api.datasource.fetchSchema({ datasourceId: datasource._id!, @@ -81,20 +69,15 @@ describe("postgres integrations", () => { .guid() .replaceAll("-", "") .substring(0, 6)}` - const enumColumnName = "status" - await rawQuery( - rawDatasource, - ` - CREATE TYPE order_status AS ENUM ('pending', 'processing', 'shipped', 'delivered', 'cancelled'); - - CREATE TABLE ${tableName} ( - order_id SERIAL PRIMARY KEY, - customer_name VARCHAR(100) NOT NULL, - ${enumColumnName} order_status - ); - ` - ) + await client.schema.createTable(tableName, table => { + table.increments("order_id").primary() + table.string("customer_name").notNullable() + table.enum("status", ["pending", "processing", "shipped"], { + useNative: true, + enumName: `${tableName}_status`, + }) + }) const response = await config.api.datasource.fetchSchema({ datasourceId: datasource._id!, @@ -103,13 +86,14 @@ describe("postgres integrations", () => { const table = response.datasource.entities?.[tableName] expect(table).toBeDefined() - expect(table?.schema[enumColumnName].type).toEqual(FieldType.OPTIONS) + expect(table?.schema["status"].type).toEqual(FieldType.OPTIONS) }) }) describe("Integration compatibility with postgres search_path", () => { let rawDatasource: Datasource, datasource: Datasource, + client: Knex, schema1: string, schema2: string @@ -118,54 +102,55 @@ describe("postgres integrations", () => { schema2 = generator.guid().replaceAll("-", "") rawDatasource = await getDatasource(DatabaseName.POSTGRES) - const dbConfig = rawDatasource.config! + client = await knexClient(rawDatasource) - await rawQuery(rawDatasource, `CREATE SCHEMA "${schema1}";`) - await rawQuery(rawDatasource, `CREATE SCHEMA "${schema2}";`) + await client.schema.createSchema(schema1) + await client.schema.createSchema(schema2) - const pathConfig: any = { - ...rawDatasource, - config: { - ...dbConfig, - schema: `${schema1}, ${schema2}`, - }, - } - datasource = await config.api.datasource.create(pathConfig) + rawDatasource.config!.schema = `${schema1}, ${schema2}` + + client = await knexClient(rawDatasource) + datasource = await config.api.datasource.create(rawDatasource) }) afterEach(async () => { - await rawQuery(rawDatasource, `DROP SCHEMA "${schema1}" CASCADE;`) - await rawQuery(rawDatasource, `DROP SCHEMA "${schema2}" CASCADE;`) + await client.schema.dropSchema(schema1, true) + await client.schema.dropSchema(schema2, true) }) it("discovers tables from any schema in search path", async () => { - await rawQuery( - rawDatasource, - `CREATE TABLE "${schema1}".table1 (id1 SERIAL PRIMARY KEY);` - ) - await rawQuery( - rawDatasource, - `CREATE TABLE "${schema2}".table2 (id2 SERIAL PRIMARY KEY);` - ) - const response = await makeRequest("post", "/api/datasources/info", { - datasource: datasource, + await client.schema.createTable(`${schema1}.table1`, table => { + table.increments("id1").primary() }) - expect(response.status).toBe(200) - expect(response.body.tableNames).toBeDefined() - expect(response.body.tableNames).toEqual( + + await client.schema.createTable(`${schema2}.table2`, table => { + table.increments("id2").primary() + }) + + const response = await config.api.datasource.info(datasource) + expect(response.tableNames).toBeDefined() + expect(response.tableNames).toEqual( expect.arrayContaining(["table1", "table2"]) ) }) it("does not mix columns from different tables", async () => { const repeated_table_name = "table_same_name" - await rawQuery( - rawDatasource, - `CREATE TABLE "${schema1}".${repeated_table_name} (id SERIAL PRIMARY KEY, val1 TEXT);` + + await client.schema.createTable( + `${schema1}.${repeated_table_name}`, + table => { + table.increments("id").primary() + table.string("val1") + } ) - await rawQuery( - rawDatasource, - `CREATE TABLE "${schema2}".${repeated_table_name} (id2 SERIAL PRIMARY KEY, val2 TEXT);` + + await client.schema.createTable( + `${schema2}.${repeated_table_name}`, + table => { + table.increments("id2").primary() + table.string("val2") + } ) const response = await config.api.datasource.fetchSchema({ @@ -182,15 +167,11 @@ describe("postgres integrations", () => { describe("check custom column types", () => { beforeAll(async () => { - await rawQuery( - rawDatasource, - `CREATE TABLE binaryTable ( - id BYTEA PRIMARY KEY, - column1 TEXT, - column2 INT - ); - ` - ) + await client.schema.createTable("binaryTable", table => { + table.binary("id").primary() + table.string("column1") + table.integer("column2") + }) }) it("should handle binary columns", async () => { @@ -198,7 +179,7 @@ describe("postgres integrations", () => { datasourceId: datasource._id!, }) expect(response.datasource.entities).toBeDefined() - const table = response.datasource.entities?.["binarytable"] + const table = response.datasource.entities?.["binaryTable"] expect(table).toBeDefined() expect(table?.schema.id.externalType).toBe("bytea") const row = await config.api.row.save(table?._id!, { @@ -214,14 +195,10 @@ describe("postgres integrations", () => { describe("check fetching null/not null table", () => { beforeAll(async () => { - await rawQuery( - rawDatasource, - `CREATE TABLE nullableTable ( - order_id SERIAL PRIMARY KEY, - order_number INT NOT NULL - ); - ` - ) + await client.schema.createTable("nullableTable", table => { + table.increments("order_id").primary() + table.integer("order_number").notNullable() + }) }) it("should be able to change the table to allow nullable and refetch this", async () => { @@ -230,25 +207,24 @@ describe("postgres integrations", () => { }) const entities = response.datasource.entities expect(entities).toBeDefined() - const nullableTable = entities?.["nullabletable"] + const nullableTable = entities?.["nullableTable"] expect(nullableTable).toBeDefined() expect( nullableTable?.schema["order_number"].constraints?.presence ).toEqual(true) + // need to perform these calls raw to the DB so that the external state of the DB differs to what Budibase // is aware of - therefore we can try to fetch and make sure BB updates correctly - await rawQuery( - rawDatasource, - `ALTER TABLE nullableTable - ALTER COLUMN order_number DROP NOT NULL; - ` - ) + await client.schema.alterTable("nullableTable", table => { + table.setNullable("order_number") + }) + const responseAfter = await config.api.datasource.fetchSchema({ datasourceId: datasource._id!, }) const entitiesAfter = responseAfter.datasource.entities expect(entitiesAfter).toBeDefined() - const nullableTableAfter = entitiesAfter?.["nullabletable"] + const nullableTableAfter = entitiesAfter?.["nullableTable"] expect(nullableTableAfter).toBeDefined() expect( nullableTableAfter?.schema["order_number"].constraints?.presence diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index 64617461bb..fd7d2373bb 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -65,6 +65,23 @@ export async function rawQuery(ds: Datasource, sql: string): Promise { } } +export async function knexClient(ds: Datasource) { + switch (ds.source) { + case SourceName.POSTGRES: { + return postgres.knexClient(ds) + } + case SourceName.MYSQL: { + return mysql.knexClient(ds) + } + case SourceName.SQL_SERVER: { + return mssql.knexClient(ds) + } + default: { + throw new Error(`Unsupported source: ${ds.source}`) + } + } +} + export async function startContainer(container: GenericContainer) { const imageName = (container as any).imageName.string as string const key = imageName.replaceAll("/", "-").replaceAll(":", "-") diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index 57c5fe8049..3b3533eb7f 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -3,6 +3,7 @@ import { GenericContainer, Wait } from "testcontainers" import mssql from "mssql" import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { startContainer } from "." +import knex from "knex" let ports: Promise @@ -72,3 +73,17 @@ export async function rawQuery(ds: Datasource, sql: string) { await pool.close() } } + +export async function knexClient(ds: Datasource) { + if (!ds.config) { + throw new Error("Datasource config is missing") + } + if (ds.source !== SourceName.SQL_SERVER) { + throw new Error("Datasource source is not MSSQL") + } + + return knex({ + client: "mssql", + connection: ds.config, + }) +} diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index 560d6bb2d4..cb6667bc56 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -4,6 +4,7 @@ import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait- import mysql from "mysql2/promise" import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { startContainer } from "." +import knex from "knex" let ports: Promise @@ -77,3 +78,17 @@ export async function rawQuery(ds: Datasource, sql: string) { connection.end() } } + +export async function knexClient(ds: Datasource) { + if (!ds.config) { + throw new Error("Datasource config is missing") + } + if (ds.source !== SourceName.MYSQL) { + throw new Error("Datasource source is not MySQL") + } + + return knex({ + client: "mysql", + connection: ds.config, + }) +} diff --git a/packages/server/src/integrations/tests/utils/postgres.ts b/packages/server/src/integrations/tests/utils/postgres.ts index 8c0cd886e8..20b56331c1 100644 --- a/packages/server/src/integrations/tests/utils/postgres.ts +++ b/packages/server/src/integrations/tests/utils/postgres.ts @@ -3,6 +3,7 @@ import { GenericContainer, Wait } from "testcontainers" import pg from "pg" import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { startContainer } from "." +import knex from "knex" let ports: Promise @@ -66,3 +67,17 @@ export async function rawQuery(ds: Datasource, sql: string) { await client.end() } } + +export async function knexClient(ds: Datasource) { + if (!ds.config) { + throw new Error("Datasource config is missing") + } + if (ds.source !== SourceName.POSTGRES) { + throw new Error("Datasource source is not Postgres") + } + + return knex({ + client: "pg", + connection: ds.config, + }) +} From 77556820bf6c05c9d094ab1e982892b11157e53e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 18:12:36 +0100 Subject: [PATCH 030/116] Bit more work towards row counting, as well as moving external SQL to use row + 1 for working out pagination. --- packages/backend-core/src/sql/sql.ts | 24 ++------------ .../api/controllers/row/ExternalRequest.ts | 5 +-- .../server/src/integrations/base/query.ts | 7 +++-- .../src/sdk/app/rows/search/external.ts | 25 +++++++-------- .../server/src/sdk/app/rows/search/sqs.ts | 31 ++++++++++--------- packages/server/src/sdk/app/rows/sqlAlias.ts | 20 ++++++------ packages/types/src/api/web/app/rows.ts | 1 + packages/types/src/sdk/row.ts | 1 + 8 files changed, 49 insertions(+), 65 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index a48a102349..6c41b71993 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -571,15 +571,10 @@ class InternalBuilder { return query.insert(parsedBody) } - read( - knex: Knex, - json: QueryJson, - limit: number, - opts?: { counting?: boolean } - ): Knex.QueryBuilder { + read(knex: Knex, json: QueryJson, limit: number): Knex.QueryBuilder { let { endpoint, resource, filters, paginate, relationships, tableAliases } = json - const counting = opts?.counting + const counting = endpoint.operation === Operation.COUNT const tableName = endpoint.entityId // select all if not specified @@ -730,6 +725,7 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { query = builder.create(client, json, opts) break case Operation.READ: + case Operation.COUNT: query = builder.read(client, json, this.limit) break case Operation.UPDATE: @@ -752,20 +748,6 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { return this.convertToNative(query, opts) } - _count(json: QueryJson, opts: QueryOptions = {}) { - const sqlClient = this.getSqlClient() - const config: Knex.Config = { - client: sqlClient, - } - if (sqlClient === SqlClient.SQL_LITE) { - config.useNullAsDefault = true - } - const client = knex(config) - const builder = new InternalBuilder(sqlClient) - const query = builder.read(client, json, this.limit, { counting: true }) - return this.convertToNative(query, opts) - } - async getReturningRow(queryFn: QueryFunction, json: QueryJson) { if (!json.extra || !json.extra.idFilter) { return {} diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index b30c97e289..af27817411 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -39,6 +39,7 @@ import { cloneDeep } from "lodash/fp" import { db as dbCore } from "@budibase/backend-core" import sdk from "../../../sdk" import env from "../../../environment" +import { makeExternalQuery } from "../../../integrations/base/query" export interface ManyRelationship { tableId?: string @@ -517,7 +518,7 @@ export class ExternalRequest { // finally cleanup anything that needs to be removed for (let [colName, { isMany, rows, tableId }] of Object.entries(related)) { const table: Table | undefined = this.getTable(tableId) - // if its not the foreign key skip it, nothing to do + // if it's not the foreign key skip it, nothing to do if ( !table || (!isMany && table.primary && table.primary.indexOf(colName) !== -1) @@ -667,7 +668,7 @@ export class ExternalRequest { response = await getDatasourceAndQuery(json) } else { const aliasing = new sdk.rows.AliasTables(Object.keys(this.tables)) - response = await aliasing.queryWithAliasing(json) + response = await aliasing.queryWithAliasing(json, makeExternalQuery) } const responseRows = Array.isArray(response) ? response : [] diff --git a/packages/server/src/integrations/base/query.ts b/packages/server/src/integrations/base/query.ts index 371592bece..acef1c6f1e 100644 --- a/packages/server/src/integrations/base/query.ts +++ b/packages/server/src/integrations/base/query.ts @@ -8,8 +8,8 @@ import { getIntegration } from "../index" import sdk from "../../sdk" export async function makeExternalQuery( - datasource: Datasource, - json: QueryJson + json: QueryJson, + datasource?: Datasource ): Promise { const entityId = json.endpoint.entityId, tableName = json.meta.table.name, @@ -22,6 +22,9 @@ export async function makeExternalQuery( ) { throw new Error("Entity ID and table metadata do not align") } + if (!datasource) { + throw new Error("No datasource provided for external query") + } datasource = await sdk.datasources.enrich(datasource) const Integration = await getIntegration(datasource.source) // query is the opinionated function diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index 077f971903..c495613856 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -28,7 +28,7 @@ export async function search( table: Table ): Promise> { const { tableId } = options - const { paginate, query, ...params } = options + const { countRows, paginate, query, ...params } = options const { limit } = params let bookmark = (params.bookmark && parseInt(params.bookmark as string)) || undefined @@ -37,10 +37,14 @@ export async function search( } let paginateObj = {} - if (paginate) { + if (paginate && !limit) { + throw new Error("Cannot paginate query without a limit") + } + + if (paginate && limit) { paginateObj = { // add one so we can track if there is another page - limit: limit, + limit: limit + 1, page: bookmark, } } else if (params && limit) { @@ -76,17 +80,10 @@ export async function search( includeSqlRelationships: IncludeRelationship.INCLUDE, }) let hasNextPage = false - if (paginate && rows.length === limit) { - const nextRows = await handleRequest(Operation.READ, tableId, { - filters: query, - sort, - paginate: { - limit: 1, - page: bookmark! * limit + 1, - }, - includeSqlRelationships: IncludeRelationship.INCLUDE, - }) - hasNextPage = nextRows.length > 0 + // remove the extra row if it's there + if (paginate && limit && rows.length > limit) { + rows.pop() + hasNextPage = true } if (options.fields) { diff --git a/packages/server/src/sdk/app/rows/search/sqs.ts b/packages/server/src/sdk/app/rows/search/sqs.ts index e45a7f94a7..bab70134b3 100644 --- a/packages/server/src/sdk/app/rows/search/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/sqs.ts @@ -12,7 +12,6 @@ import { SortOrder, SortType, SqlClient, - SqlQuery, Table, } from "@budibase/types" import { @@ -114,17 +113,13 @@ async function runSqlQuery( opts?: { countTotalRows?: boolean } ) { const alias = new AliasTables(tables.map(table => table.name)) + if (opts?.countTotalRows) { + json.endpoint.operation = Operation.COUNT + } const processSQLQuery = async (json: QueryJson) => { - let query: SqlQuery | SqlQuery[] - if (opts?.countTotalRows) { - query = builder._count(json, { - disableReturning: true, - }) - } else { - query = builder._query(json, { - disableReturning: true, - }) - } + const query = builder._query(json, { + disableReturning: true, + }) if (Array.isArray(query)) { throw new Error("SQS cannot currently handle multiple queries") @@ -227,14 +222,18 @@ export async function search( ) // check for pagination final row - let nextRow: Row | undefined, rowCount: number | undefined + let nextRow: Row | undefined if (paginate && params.limit && processed.length > params.limit) { - // get the total count of rows - rowCount = await runSqlQuery(request, allTables, { countTotalRows: true }) // remove the extra row that confirmed if there is another row to move to nextRow = processed.pop() } + let rowCount: number | undefined + if (options.countRows) { + // get the total count of rows + rowCount = await runSqlQuery(request, allTables, { countTotalRows: true }) + } + // get the rows let finalRows = await outputProcessing(table, processed, { preserveLinks: true, @@ -255,9 +254,11 @@ export async function search( const hasNextPage = !!nextRow response.hasNextPage = hasNextPage if (hasNextPage) { - response.totalRows = rowCount response.bookmark = bookmark + 1 } + if (rowCount != null) { + response.totalRows = rowCount + } return response } else { return { diff --git a/packages/server/src/sdk/app/rows/sqlAlias.ts b/packages/server/src/sdk/app/rows/sqlAlias.ts index 1b470a6a02..52ec2472b2 100644 --- a/packages/server/src/sdk/app/rows/sqlAlias.ts +++ b/packages/server/src/sdk/app/rows/sqlAlias.ts @@ -11,7 +11,11 @@ import { SQS_DATASOURCE_INTERNAL } from "@budibase/backend-core" import { getSQLClient } from "./utils" import { cloneDeep } from "lodash" import datasources from "../datasources" -import { makeExternalQuery } from "../../../integrations/base/query" + +type PerformQueryFunction = ( + json: QueryJson, + datasource?: Datasource +) => Promise const WRITE_OPERATIONS: Operation[] = [ Operation.CREATE, @@ -171,7 +175,7 @@ export default class AliasTables { async queryWithAliasing( json: QueryJson, - queryFn?: (json: QueryJson) => Promise + queryFn: PerformQueryFunction ): Promise { const datasourceId = json.endpoint.datasourceId const isSqs = datasourceId === SQS_DATASOURCE_INTERNAL @@ -229,14 +233,7 @@ export default class AliasTables { json.tableAliases = invertedTableAliases } - let response: DatasourcePlusQueryResponse - if (datasource && !isSqs) { - response = await makeExternalQuery(datasource, json) - } else if (queryFn) { - response = await queryFn(json) - } else { - throw new Error("No supplied method to perform aliased query") - } + let response: DatasourcePlusQueryResponse = await queryFn(json, datasource) if (Array.isArray(response) && aliasingEnabled) { return this.reverse(response) } else { @@ -247,8 +244,9 @@ export default class AliasTables { // handles getting the count out of the query async countWithAliasing( json: QueryJson, - queryFn?: (json: QueryJson) => Promise + queryFn: PerformQueryFunction ): Promise { + json.endpoint.operation = Operation.COUNT let response = await this.queryWithAliasing(json, queryFn) if (response && response.length === 1 && "total" in response[0]) { return response[0].total diff --git a/packages/types/src/api/web/app/rows.ts b/packages/types/src/api/web/app/rows.ts index 5d49f01bfc..c120af0628 100644 --- a/packages/types/src/api/web/app/rows.ts +++ b/packages/types/src/api/web/app/rows.ts @@ -25,6 +25,7 @@ export interface SearchViewRowRequest | "bookmark" | "paginate" | "query" + | "countRows" > {} export interface SearchRowResponse { diff --git a/packages/types/src/sdk/row.ts b/packages/types/src/sdk/row.ts index 7f3fc1f391..b0b137034b 100644 --- a/packages/types/src/sdk/row.ts +++ b/packages/types/src/sdk/row.ts @@ -17,6 +17,7 @@ export interface SearchParams { fields?: string[] indexer?: () => Promise rows?: Row[] + countRows?: boolean } // when searching for rows we want a more extensive search type that requires certain properties From 908b77fd9be23ad3f5114467482e692418ee6e15 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 18:27:23 +0100 Subject: [PATCH 031/116] Fixing some issues with using offsets. --- .../server/src/sdk/app/rows/search/external.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index a32a036846..e90ec049d7 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -33,9 +33,9 @@ export async function search( let bookmark = (params.bookmark && parseInt(params.bookmark as string)) || undefined if (paginate && !bookmark) { - bookmark = 1 + bookmark = 0 } - let paginateObj = {} + let paginateObj: PaginationJson | undefined if (paginate && !limit) { throw new Error("Cannot paginate query without a limit") @@ -45,7 +45,9 @@ export async function search( paginateObj = { // add one so we can track if there is another page limit: limit + 1, - page: bookmark, + } + if (bookmark) { + paginateObj.offset = limit * bookmark } } else if (params && limit) { paginateObj = { @@ -97,7 +99,11 @@ export async function search( }) // need wrapper object for bookmarks etc when paginating - return { rows, hasNextPage, bookmark: bookmark && bookmark + 1 } + const response: SearchResponse = { rows, hasNextPage } + if (hasNextPage && bookmark != null) { + response.bookmark = bookmark + 1 + } + return response } catch (err: any) { if (err.message && err.message.includes("does not exist")) { throw new Error( From 1b36d8af5196300014847044a8c436fce13a43cf Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 19:00:59 +0100 Subject: [PATCH 032/116] Getting counting flow working correctly for external datasources. --- packages/backend-core/src/sql/sql.ts | 6 ++-- .../api/controllers/row/ExternalRequest.ts | 28 +++++++++++++------ .../api/controllers/table/ExternalRequest.ts | 2 +- .../src/sdk/app/rows/search/external.ts | 22 ++++++++++----- packages/server/src/sdk/app/rows/sqlAlias.ts | 3 +- packages/server/src/sdk/app/rows/utils.ts | 2 +- packages/types/src/sdk/datasources.ts | 1 + 7 files changed, 44 insertions(+), 20 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index dd350c0da8..1dba4a515e 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -599,7 +599,9 @@ class InternalBuilder { aliases: tableAliases, }) // add sorting to pre-query - query = this.addSorting(query, json) + if (!counting) { + query = this.addSorting(query, json) + } const alias = tableAliases?.[tableName] || tableName let preQuery = knex({ [alias]: query, @@ -610,7 +612,7 @@ class InternalBuilder { preQuery = preQuery.select(selectStatement) } // have to add after as well (this breaks MS-SQL) - if (this.client !== SqlClient.MS_SQL) { + if (this.client !== SqlClient.MS_SQL && !counting) { preQuery = this.addSorting(preQuery, json) } // handle joins diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index af27817411..98bd5412d4 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -7,6 +7,7 @@ import { FieldType, FilterType, IncludeRelationship, + isManyToOne, OneToManyRelationshipFieldMetadata, Operation, PaginationJson, @@ -16,22 +17,21 @@ import { SortJson, SortType, Table, - isManyToOne, } from "@budibase/types" import { breakExternalTableId, breakRowIdField, convertRowId, + generateRowIdField, isRowId, isSQL, - generateRowIdField, } from "../../../integrations/utils" import { buildExternalRelationships, buildSqlFieldList, generateIdForRow, - sqlOutputProcessing, isManyToMany, + sqlOutputProcessing, } from "./utils" import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils" import { processObjectSync } from "@budibase/string-templates" @@ -61,6 +61,13 @@ export interface RunConfig { includeSqlRelationships?: IncludeRelationship } +export type ExternalRequestReturnType = + T extends Operation.READ + ? Row[] + : T extends Operation.COUNT + ? number + : { row: Row; table: Table } + function buildFilters( id: string | undefined | string[], filters: SearchFilters, @@ -224,9 +231,6 @@ function isEditableColumn(column: FieldSchema) { return !(isExternalAutoColumn || isFormula) } -export type ExternalRequestReturnType = - T extends Operation.READ ? Row[] : { row: Row; table: Table } - export class ExternalRequest { private readonly operation: T private readonly tableId: string @@ -429,7 +433,10 @@ export class ExternalRequest { }) // this is the response from knex if no rows found const rows: Row[] = - !Array.isArray(response) || response?.[0].read ? [] : response + !Array.isArray(response) || + (response.length === 1 && "read" in response[0]) + ? [] + : response const storeTo = isManyToMany(field) ? field.throughFrom || linkPrimaryKey : fieldName @@ -664,10 +671,15 @@ export class ExternalRequest { // aliasing can be disabled fully if desired let response + const aliasing = new sdk.rows.AliasTables(Object.keys(this.tables)) if (env.SQL_ALIASING_DISABLE) { response = await getDatasourceAndQuery(json) + } else if (this.operation === Operation.COUNT) { + return (await aliasing.countWithAliasing( + json, + makeExternalQuery + )) as ExternalRequestReturnType } else { - const aliasing = new sdk.rows.AliasTables(Object.keys(this.tables)) response = await aliasing.queryWithAliasing(json, makeExternalQuery) } diff --git a/packages/server/src/api/controllers/table/ExternalRequest.ts b/packages/server/src/api/controllers/table/ExternalRequest.ts index 1e57ea3294..9661e56729 100644 --- a/packages/server/src/api/controllers/table/ExternalRequest.ts +++ b/packages/server/src/api/controllers/table/ExternalRequest.ts @@ -33,5 +33,5 @@ export async function makeTableRequest( if (renamed) { json.meta!.renamed = renamed } - return makeExternalQuery(datasource, json) + return makeExternalQuery(json, datasource) } diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index e90ec049d7..366bf46156 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -1,14 +1,14 @@ import { - SortJson, + IncludeRelationship, Operation, PaginationJson, - IncludeRelationship, Row, - SearchFilters, RowSearchParams, + SearchFilters, SearchResponse, - Table, + SortJson, SortOrder, + Table, } from "@budibase/types" import * as exporters from "../../../../api/controllers/view/exporters" import { handleRequest } from "../../../../api/controllers/row/external" @@ -18,7 +18,7 @@ import { } from "../../../../integrations/utils" import { utils } from "@budibase/shared-core" import { ExportRowsParams, ExportRowsResult } from "./types" -import { HTTPError, db } from "@budibase/backend-core" +import { db, HTTPError } from "@budibase/backend-core" import pick from "lodash/pick" import { outputProcessing } from "../../../../utilities/rowProcessor" import sdk from "../../../" @@ -75,12 +75,17 @@ export async function search( } try { - let rows = await handleRequest(Operation.READ, tableId, { + const parameters = { filters: query, sort, paginate: paginateObj as PaginationJson, includeSqlRelationships: IncludeRelationship.INCLUDE, - }) + } + let rows = await handleRequest(Operation.READ, tableId, parameters) + let totalRows: number | undefined + if (true) { + totalRows = await handleRequest(Operation.COUNT, tableId, parameters) + } let hasNextPage = false // remove the extra row if it's there if (paginate && limit && rows.length > limit) { @@ -103,6 +108,9 @@ export async function search( if (hasNextPage && bookmark != null) { response.bookmark = bookmark + 1 } + if (totalRows != null) { + response.totalRows = totalRows + } return response } catch (err: any) { if (err.message && err.message.includes("does not exist")) { diff --git a/packages/server/src/sdk/app/rows/sqlAlias.ts b/packages/server/src/sdk/app/rows/sqlAlias.ts index 52ec2472b2..ab47f98a85 100644 --- a/packages/server/src/sdk/app/rows/sqlAlias.ts +++ b/packages/server/src/sdk/app/rows/sqlAlias.ts @@ -249,7 +249,8 @@ export default class AliasTables { json.endpoint.operation = Operation.COUNT let response = await this.queryWithAliasing(json, queryFn) if (response && response.length === 1 && "total" in response[0]) { - return response[0].total + const total = response[0].total + return typeof total === "number" ? total : parseInt(total) } else { throw new Error("Unable to count rows in query - no count response") } diff --git a/packages/server/src/sdk/app/rows/utils.ts b/packages/server/src/sdk/app/rows/utils.ts index bb37fd99f3..da0132c8fa 100644 --- a/packages/server/src/sdk/app/rows/utils.ts +++ b/packages/server/src/sdk/app/rows/utils.ts @@ -61,7 +61,7 @@ export async function getDatasourceAndQuery( table, } } - return makeExternalQuery(datasource, json) + return makeExternalQuery(json, datasource) } export function cleanExportRows( diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts index d6c2bf2bf2..1a9c329153 100644 --- a/packages/types/src/sdk/datasources.ts +++ b/packages/types/src/sdk/datasources.ts @@ -197,6 +197,7 @@ enum DSPlusOperation { export type DatasourcePlusQueryResponse = | Row[] | Record[] + | { total: number }[] | void export interface DatasourcePlus extends IntegrationBase { From f3ca1d0b1e7bee5d40caf72e0ece146bba2a34a5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 19:01:20 +0100 Subject: [PATCH 033/116] Adding countRows parameter to external API for counting. --- packages/server/src/sdk/app/rows/search/external.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index 366bf46156..5e2074f56d 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -83,7 +83,7 @@ export async function search( } let rows = await handleRequest(Operation.READ, tableId, parameters) let totalRows: number | undefined - if (true) { + if (countRows) { totalRows = await handleRequest(Operation.COUNT, tableId, parameters) } let hasNextPage = false From cd1e7c0bad0b03f2c5defe7a0cf7f9adcbf1e52b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 19:04:02 +0100 Subject: [PATCH 034/116] Small re-jig make things easier to read. --- .../server/src/api/controllers/row/ExternalRequest.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 98bd5412d4..1679892d03 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -672,15 +672,16 @@ export class ExternalRequest { // aliasing can be disabled fully if desired let response const aliasing = new sdk.rows.AliasTables(Object.keys(this.tables)) - if (env.SQL_ALIASING_DISABLE) { - response = await getDatasourceAndQuery(json) - } else if (this.operation === Operation.COUNT) { + // if it's a counting operation there will be no more processing, just return the number + if (this.operation === Operation.COUNT) { return (await aliasing.countWithAliasing( json, makeExternalQuery )) as ExternalRequestReturnType } else { - response = await aliasing.queryWithAliasing(json, makeExternalQuery) + response = env.SQL_ALIASING_DISABLE + ? await getDatasourceAndQuery(json) + : await aliasing.queryWithAliasing(json, makeExternalQuery) } const responseRows = Array.isArray(response) ? response : [] From 654a417d66237df845aa3e1c3943b1905cc5a06f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 19:07:50 +0100 Subject: [PATCH 035/116] Type checking. --- packages/server/src/api/controllers/row/utils/utils.ts | 2 +- packages/server/src/api/controllers/row/views.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/row/utils/utils.ts b/packages/server/src/api/controllers/row/utils/utils.ts index c2d62e0204..bf45a85178 100644 --- a/packages/server/src/api/controllers/row/utils/utils.ts +++ b/packages/server/src/api/controllers/row/utils/utils.ts @@ -137,7 +137,7 @@ export async function sqlOutputProcessing( relationships: RelationshipsJson[], opts?: { sqs?: boolean } ): Promise { - if (!Array.isArray(rows) || rows.length === 0 || rows[0].read === true) { + if (!Array.isArray(rows) || rows.length === 0 || "read" in rows[0]) { return [] } let finalRows: { [key: string]: Row } = {} diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index 80aa97d8c0..800cc29ceb 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -60,7 +60,7 @@ export async function searchView( user: sdk.users.getUserContextBindings(ctx.user), }) - const searchOptions: RequiredKeys & + const searchOptions: RequiredKeys> & RequiredKeys> = { tableId: view.tableId, query: enrichedQuery, From c34c219e8fd88c58cc44769391a1adbef9e663cd Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 14 Jun 2024 19:10:52 +0100 Subject: [PATCH 036/116] Tidying up one of the weirder things knex can do. --- .../server/src/api/controllers/row/ExternalRequest.ts | 4 ++-- .../server/src/api/controllers/row/utils/sqlUtils.ts | 10 ++++++++++ packages/server/src/api/controllers/row/utils/utils.ts | 4 ++-- packages/types/src/sdk/datasources.ts | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 1679892d03..4e14ce2799 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -30,6 +30,7 @@ import { buildExternalRelationships, buildSqlFieldList, generateIdForRow, + isKnexNoRowReadResponse, isManyToMany, sqlOutputProcessing, } from "./utils" @@ -433,8 +434,7 @@ export class ExternalRequest { }) // this is the response from knex if no rows found const rows: Row[] = - !Array.isArray(response) || - (response.length === 1 && "read" in response[0]) + !Array.isArray(response) || isKnexNoRowReadResponse(response) ? [] : response const storeTo = isManyToMany(field) diff --git a/packages/server/src/api/controllers/row/utils/sqlUtils.ts b/packages/server/src/api/controllers/row/utils/sqlUtils.ts index 372b8394ff..b236578485 100644 --- a/packages/server/src/api/controllers/row/utils/sqlUtils.ts +++ b/packages/server/src/api/controllers/row/utils/sqlUtils.ts @@ -1,4 +1,6 @@ import { + DatasourcePlusQueryResponse, + DSPlusOperation, FieldType, ManyToManyRelationshipFieldMetadata, RelationshipFieldMetadata, @@ -192,3 +194,11 @@ export function buildSqlFieldList( } return fields } + +export function isKnexNoRowReadResponse(resp: DatasourcePlusQueryResponse) { + return ( + !Array.isArray(resp) || + resp.length === 0 || + (DSPlusOperation.READ in resp[0] && resp[0].read === true) + ) +} diff --git a/packages/server/src/api/controllers/row/utils/utils.ts b/packages/server/src/api/controllers/row/utils/utils.ts index bf45a85178..a607a01f16 100644 --- a/packages/server/src/api/controllers/row/utils/utils.ts +++ b/packages/server/src/api/controllers/row/utils/utils.ts @@ -14,7 +14,7 @@ import { processDates, processFormulas, } from "../../../../utilities/rowProcessor" -import { updateRelationshipColumns } from "./sqlUtils" +import { isKnexNoRowReadResponse, updateRelationshipColumns } from "./sqlUtils" import { basicProcessing, generateIdForRow, @@ -137,7 +137,7 @@ export async function sqlOutputProcessing( relationships: RelationshipsJson[], opts?: { sqs?: boolean } ): Promise { - if (!Array.isArray(rows) || rows.length === 0 || "read" in rows[0]) { + if (isKnexNoRowReadResponse(rows)) { return [] } let finalRows: { [key: string]: Row } = {} diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts index 1a9c329153..ba9b1e5f45 100644 --- a/packages/types/src/sdk/datasources.ts +++ b/packages/types/src/sdk/datasources.ts @@ -187,7 +187,7 @@ export interface Schema { } // return these when an operation occurred but we got no response -enum DSPlusOperation { +export enum DSPlusOperation { CREATE = "create", READ = "read", UPDATE = "update", From 92f9501d26cb3dcde5a747b6889a231e36c2aaad Mon Sep 17 00:00:00 2001 From: Conor Webb <126772285+ConorWebb96@users.noreply.github.com> Date: Mon, 17 Jun 2024 08:21:26 +0100 Subject: [PATCH 037/116] Add function to close side panel based on the browser's history. (#13944) * Add function to close side panel based on the browser's history. * Switched to hashchange, removed from SidePanel, added to ClientApp.svelte * Removed unused onMount, fixed lint issue * Removed unnecessary if statement. --- packages/client/src/components/ClientApp.svelte | 11 +++++++++++ packages/client/src/components/app/SidePanel.svelte | 4 ---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index b790ecd0d4..c1bdc92ac4 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -19,6 +19,7 @@ devToolsStore, devToolsEnabled, environmentStore, + sidePanelStore, } from "stores" import NotificationDisplay from "components/overlay/NotificationDisplay.svelte" import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte" @@ -102,6 +103,16 @@ embedded: !!$appStore.embedded, }) } + const handleHashChange = () => { + const { open } = $sidePanelStore + if (open) { + sidePanelStore.actions.close() + } + } + window.addEventListener("hashchange", handleHashChange) + return () => { + window.removeEventListener("hashchange", handleHashChange) + } }) $: { diff --git a/packages/client/src/components/app/SidePanel.svelte b/packages/client/src/components/app/SidePanel.svelte index bff5a78837..d49ab33c40 100644 --- a/packages/client/src/components/app/SidePanel.svelte +++ b/packages/client/src/components/app/SidePanel.svelte @@ -29,10 +29,6 @@ } } - // $: { - - // } - // Derive visibility $: open = $sidePanelStore.contentId === $component.id From 7601cd0ce29d7022fd9aad06b2bf949b39a99262 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 17 Jun 2024 09:11:47 +0000 Subject: [PATCH 038/116] Bump version to 2.28.7 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 70d6a683e0..abce1679c8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.28.6", + "version": "2.28.7", "npmClient": "yarn", "packages": [ "packages/*", From 9ecf82e0f6e034df559c35331027367111952bb6 Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Mon, 17 Jun 2024 10:22:44 +0100 Subject: [PATCH 039/116] Add endpoint to GET tenant info from tenant global DB. (#13923) * Add GET tenant info endpoint * Add GET global tenant to endpoint list. * Use correct not found code * Fix unit test * Code review comments * Ignore account portal check types * remove account-portal-ui from type check ignore * Remove ignore account-portal-ui from type check --- .github/workflows/budibase_ci.yml | 4 ++-- package.json | 2 +- packages/backend-core/src/tenancy/db.ts | 7 ++++++- packages/types/src/documents/global/tenantInfo.ts | 2 ++ .../worker/src/api/controllers/global/tenant.ts | 4 ++++ packages/worker/src/api/index.ts | 4 ++++ packages/worker/src/api/routes/global/tenant.ts | 15 +++++++++------ .../src/api/routes/global/tests/tenant.spec.ts | 3 ++- 8 files changed, 30 insertions(+), 11 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index eb11627758..b7dbcae771 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -73,9 +73,9 @@ jobs: - name: Check types run: | if ${{ env.USE_NX_AFFECTED }}; then - yarn check:types --since=${{ env.NX_BASE_BRANCH }} + yarn check:types --since=${{ env.NX_BASE_BRANCH }} --ignore @budibase/account-portal-server else - yarn check:types + yarn check:types --ignore @budibase/account-portal-server fi helm-lint: diff --git a/package.json b/package.json index 5377dfc5a1..e05eb795bc 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "build:oss": "NODE_OPTIONS=--max-old-space-size=1500 lerna run build --stream --ignore @budibase/account-portal-server --ignore @budibase/account-portal-ui", "build:account-portal": "NODE_OPTIONS=--max-old-space-size=1500 lerna run build --stream --scope @budibase/account-portal-server --scope @budibase/account-portal-ui", "build:dev": "lerna run --stream prebuild && yarn nx run-many --target=build --output-style=dynamic --watch --preserveWatchOutput", - "check:types": "lerna run --concurrency 2 check:types", + "check:types": "lerna run --concurrency 2 check:types --ignore @budibase/account-portal-server", "build:sdk": "lerna run --stream build:sdk", "deps:circular": "madge packages/server/dist/index.js packages/worker/src/index.ts packages/backend-core/dist/src/index.js packages/cli/src/index.js --circular", "release": "lerna publish from-package --yes --force-publish --no-git-tag-version --no-push --no-git-reset", diff --git a/packages/backend-core/src/tenancy/db.ts b/packages/backend-core/src/tenancy/db.ts index f2e4705fa8..d430296a5c 100644 --- a/packages/backend-core/src/tenancy/db.ts +++ b/packages/backend-core/src/tenancy/db.ts @@ -9,8 +9,13 @@ export function getTenantDB(tenantId: string) { export async function saveTenantInfo(tenantInfo: TenantInfo) { const db = getTenantDB(tenantInfo.tenantId) // save the tenant info to db - return await db.put({ + return db.put({ _id: "tenant_info", ...tenantInfo, }) } + +export async function getTenantInfo(tenantId: string): Promise { + const db = getTenantDB(tenantId) + return db.get("tenant_info") +} diff --git a/packages/types/src/documents/global/tenantInfo.ts b/packages/types/src/documents/global/tenantInfo.ts index 2fa8f4ad96..4c8837cf2a 100644 --- a/packages/types/src/documents/global/tenantInfo.ts +++ b/packages/types/src/documents/global/tenantInfo.ts @@ -1,3 +1,4 @@ +import { Hosting } from "../../sdk" import { Document } from "../document" export interface TenantInfo extends Document { @@ -10,4 +11,5 @@ export interface TenantInfo extends Document { budibaseUserId?: string } tenantId: string + hosting: Hosting } diff --git a/packages/worker/src/api/controllers/global/tenant.ts b/packages/worker/src/api/controllers/global/tenant.ts index 1e86fb8246..8b5ae6d528 100644 --- a/packages/worker/src/api/controllers/global/tenant.ts +++ b/packages/worker/src/api/controllers/global/tenant.ts @@ -8,3 +8,7 @@ export const save = async (ctx: Ctx) => { _rev: response.rev, } } + +export const get = async (ctx: Ctx) => { + ctx.body = await tenancy.getTenantInfo(ctx.params.id) +} diff --git a/packages/worker/src/api/index.ts b/packages/worker/src/api/index.ts index feec8b4de1..08c65b98d4 100644 --- a/packages/worker/src/api/index.ts +++ b/packages/worker/src/api/index.ts @@ -129,6 +129,10 @@ const NO_TENANCY_ENDPOINTS = [ route: "/api/global/tenant", method: "POST", }, + { + route: "/api/global/tenant/:id", + method: "GET", + }, ] // most public endpoints are gets, but some are posts diff --git a/packages/worker/src/api/routes/global/tenant.ts b/packages/worker/src/api/routes/global/tenant.ts index 7179532cde..18b7058c34 100644 --- a/packages/worker/src/api/routes/global/tenant.ts +++ b/packages/worker/src/api/routes/global/tenant.ts @@ -18,16 +18,19 @@ function buildTenantInfoValidation() { familyName: OPTIONAL_STRING, budibaseUserId: OPTIONAL_STRING, }).required(), + hosting: Joi.string().required(), tenantId: Joi.string().required(), }).required() ) } -router.post( - "/api/global/tenant", - cloudRestricted, - buildTenantInfoValidation(), - controller.save -) +router + .post( + "/api/global/tenant", + cloudRestricted, + buildTenantInfoValidation(), + controller.save + ) + .get("/api/global/tenant/:id", controller.get) export default router diff --git a/packages/worker/src/api/routes/global/tests/tenant.spec.ts b/packages/worker/src/api/routes/global/tests/tenant.spec.ts index 36036eceee..e039b4139b 100644 --- a/packages/worker/src/api/routes/global/tests/tenant.spec.ts +++ b/packages/worker/src/api/routes/global/tests/tenant.spec.ts @@ -1,4 +1,4 @@ -import { TenantInfo } from "@budibase/types" +import { Hosting, TenantInfo } from "@budibase/types" import { TestConfiguration } from "../../../../tests" import { tenancy as _tenancy } from "@budibase/backend-core" @@ -36,6 +36,7 @@ describe("/api/global/tenant", () => { budibaseUserId: "USER_ID", }, tenantId: "tenant123", + hosting: Hosting.CLOUD, } const response = await config.api.tenants.saveTenantInfo(tenantInfo) From c8c0dfb84d6746af82a09ed19ee294920384c292 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:23:30 +0000 Subject: [PATCH 040/116] Bump tar from 6.1.15 to 6.2.1 in /packages/server Bumps [tar](https://github.com/isaacs/node-tar) from 6.1.15 to 6.2.1. - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v6.1.15...v6.2.1) --- updated-dependencies: - dependency-name: tar dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- packages/server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/package.json b/packages/server/package.json index b3beac7ffb..28b1e876c2 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -110,7 +110,7 @@ "server-destroy": "1.0.1", "snowflake-promise": "^4.5.0", "socket.io": "4.6.1", - "tar": "6.1.15", + "tar": "6.2.1", "to-json-schema": "0.2.5", "uuid": "^8.3.2", "validate.js": "0.13.1", From 84118f9e8cf69e5f9fe6d39c02abf11f81103a4b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 17 Jun 2024 14:39:45 +0100 Subject: [PATCH 041/116] Remove makeRequest from mysql.spec.ts --- .../server/src/integration-test/mysql.spec.ts | 61 ++++++------------- .../src/integration-test/postgres.spec.ts | 2 +- 2 files changed, 21 insertions(+), 42 deletions(-) diff --git a/packages/server/src/integration-test/mysql.spec.ts b/packages/server/src/integration-test/mysql.spec.ts index 4dff40b61d..dc4f79bba9 100644 --- a/packages/server/src/integration-test/mysql.spec.ts +++ b/packages/server/src/integration-test/mysql.spec.ts @@ -1,8 +1,4 @@ import fetch from "node-fetch" -import { - generateMakeRequest, - MakeRequestResponse, -} from "../api/routes/public/tests/utils" import * as setup from "../api/routes/tests/utilities" import { Datasource, FieldType } from "@budibase/types" import { @@ -12,6 +8,7 @@ import { } from "../integrations/tests/utils" import { generator } from "@budibase/backend-core/tests" import { tableForDatasource } from "../../src/tests/utilities/structures" +import { Knex } from "knex" // @ts-ignore fetch.mockSearch() @@ -40,15 +37,12 @@ jest.mock("../websockets", () => ({ })) describe("mysql integrations", () => { - let makeRequest: MakeRequestResponse, - rawDatasource: Datasource, - datasource: Datasource + let rawDatasource: Datasource + let datasource: Datasource + let client: Knex beforeAll(async () => { await config.init() - const apiKey = await config.generateApiKey() - - makeRequest = generateMakeRequest(apiKey, true) rawDatasource = await getDatasource(DatabaseName.MYSQL) datasource = await config.api.datasource.create(rawDatasource) @@ -59,11 +53,8 @@ describe("mysql integrations", () => { it("validate table schema", async () => { // Creating a table so that `entities` is populated. await config.api.table.save(tableForDatasource(datasource)) - - const res = await makeRequest("get", `/api/datasources/${datasource._id}`) - - expect(res.status).toBe(200) - expect(res.body).toEqual({ + const res = await config.api.datasource.get(datasource._id!) + expect(res).toEqual({ config: { database: expect.any(String), host: datasource.config!.host, @@ -114,14 +105,9 @@ describe("mysql integrations", () => { rawDatasource, `CREATE TABLE \`${database}\`.table1 (id1 SERIAL PRIMARY KEY);` ) - const response = await makeRequest("post", "/api/datasources/info", { - datasource: datasource, - }) - expect(response.status).toBe(200) - expect(response.body.tableNames).toBeDefined() - expect(response.body.tableNames).toEqual( - expect.arrayContaining(["table1"]) - ) + const res = await config.api.datasource.info(datasource) + expect(res.tableNames).toBeDefined() + expect(res.tableNames).toEqual(expect.arrayContaining(["table1"])) }) it("does not mix columns from different tables", async () => { @@ -134,19 +120,13 @@ describe("mysql integrations", () => { rawDatasource, `CREATE TABLE \`${database2}\`.${repeated_table_name} (id2 SERIAL PRIMARY KEY, val2 TEXT);` ) - const response = await makeRequest( - "post", - `/api/datasources/${datasource._id}/schema`, - { - tablesFilter: [repeated_table_name], - } - ) - expect(response.status).toBe(200) - expect( - response.body.datasource.entities[repeated_table_name].schema - ).toBeDefined() - const schema = - response.body.datasource.entities[repeated_table_name].schema + + const res = await config.api.datasource.fetchSchema({ + datasourceId: datasource._id!, + tablesFilter: [repeated_table_name], + }) + expect(res.datasource.entities![repeated_table_name].schema).toBeDefined() + const schema = res.datasource.entities![repeated_table_name].schema expect(Object.keys(schema).sort()).toEqual(["id", "val1"]) }) }) @@ -175,12 +155,11 @@ describe("mysql integrations", () => { await rawQuery(rawDatasource, createTableQuery) - const response = await makeRequest( - "post", - `/api/datasources/${datasource._id}/schema` - ) + const res = await config.api.datasource.fetchSchema({ + datasourceId: datasource._id!, + }) - const table = response.body.datasource.entities[tableName] + const table = res.datasource.entities![tableName] expect(table).toBeDefined() expect(table.schema[enumColumnName].type).toEqual(FieldType.OPTIONS) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 32eb91ca62..9dd6a951b5 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -14,7 +14,7 @@ const config = setup.getConfig()! describe("postgres integrations", () => { let rawDatasource: Datasource let datasource: Datasource - let client: Knex + let client: Knex beforeAll(async () => { await config.init() From 16cacb3de777b56736d183b9daa388af4321cea0 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 17 Jun 2024 15:48:58 +0100 Subject: [PATCH 042/116] Convert mysql.spec.ts away from rawQuery. --- .../server/src/integration-test/mysql.spec.ts | 115 ++++++------------ .../src/integration-test/postgres.spec.ts | 2 +- .../src/integrations/tests/utils/mssql.ts | 2 +- .../src/integrations/tests/utils/mysql.ts | 2 +- 4 files changed, 40 insertions(+), 81 deletions(-) diff --git a/packages/server/src/integration-test/mysql.spec.ts b/packages/server/src/integration-test/mysql.spec.ts index dc4f79bba9..eb81b8920f 100644 --- a/packages/server/src/integration-test/mysql.spec.ts +++ b/packages/server/src/integration-test/mysql.spec.ts @@ -1,16 +1,12 @@ -import fetch from "node-fetch" import * as setup from "../api/routes/tests/utilities" import { Datasource, FieldType } from "@budibase/types" import { DatabaseName, getDatasource, - rawQuery, + knexClient, } from "../integrations/tests/utils" import { generator } from "@budibase/backend-core/tests" -import { tableForDatasource } from "../../src/tests/utilities/structures" import { Knex } from "knex" -// @ts-ignore -fetch.mockSearch() function uniqueTableName(length?: number): string { return generator @@ -21,21 +17,6 @@ function uniqueTableName(length?: number): string { const config = setup.getConfig()! -jest.mock("../websockets", () => ({ - clientAppSocket: jest.fn(), - gridAppSocket: jest.fn(), - initialise: jest.fn(), - builderSocket: { - emitTableUpdate: jest.fn(), - emitTableDeletion: jest.fn(), - emitDatasourceUpdate: jest.fn(), - emitDatasourceDeletion: jest.fn(), - emitScreenUpdate: jest.fn(), - emitAppMetadataUpdate: jest.fn(), - emitAppPublish: jest.fn(), - }, -})) - describe("mysql integrations", () => { let rawDatasource: Datasource let datasource: Datasource @@ -43,68 +24,40 @@ describe("mysql integrations", () => { beforeAll(async () => { await config.init() - rawDatasource = await getDatasource(DatabaseName.MYSQL) datasource = await config.api.datasource.create(rawDatasource) + client = await knexClient(rawDatasource) }) afterAll(config.end) - it("validate table schema", async () => { - // Creating a table so that `entities` is populated. - await config.api.table.save(tableForDatasource(datasource)) - const res = await config.api.datasource.get(datasource._id!) - expect(res).toEqual({ - config: { - database: expect.any(String), - host: datasource.config!.host, - password: "--secret-value--", - port: datasource.config!.port, - user: "root", - }, - plus: true, - source: "MYSQL", - type: "datasource_plus", - isSQL: true, - _id: expect.any(String), - _rev: expect.any(String), - createdAt: expect.any(String), - updatedAt: expect.any(String), - entities: expect.any(Object), - }) - }) - describe("Integration compatibility with mysql search_path", () => { - let datasource: Datasource, rawDatasource: Datasource + let datasource: Datasource + let rawDatasource: Datasource + let client: Knex const database = generator.guid() const database2 = generator.guid() beforeAll(async () => { rawDatasource = await getDatasource(DatabaseName.MYSQL) + client = await knexClient(rawDatasource) - await rawQuery(rawDatasource, `CREATE DATABASE \`${database}\`;`) - await rawQuery(rawDatasource, `CREATE DATABASE \`${database2}\`;`) + await client.raw(`CREATE DATABASE \`${database}\`;`) + await client.raw(`CREATE DATABASE \`${database2}\`;`) - const pathConfig: any = { - ...rawDatasource, - config: { - ...rawDatasource.config!, - database, - }, - } - datasource = await config.api.datasource.create(pathConfig) + rawDatasource.config!.database = database + datasource = await config.api.datasource.create(rawDatasource) }) afterAll(async () => { - await rawQuery(rawDatasource, `DROP DATABASE \`${database}\`;`) - await rawQuery(rawDatasource, `DROP DATABASE \`${database2}\`;`) + await client.raw(`DROP DATABASE \`${database}\`;`) + await client.raw(`DROP DATABASE \`${database2}\`;`) }) it("discovers tables from any schema in search path", async () => { - await rawQuery( - rawDatasource, - `CREATE TABLE \`${database}\`.table1 (id1 SERIAL PRIMARY KEY);` - ) + await client.schema.createTable(`${database}.table1`, table => { + table.increments("id1").primary() + }) const res = await config.api.datasource.info(datasource) expect(res.tableNames).toBeDefined() expect(res.tableNames).toEqual(expect.arrayContaining(["table1"])) @@ -112,13 +65,19 @@ describe("mysql integrations", () => { it("does not mix columns from different tables", async () => { const repeated_table_name = "table_same_name" - await rawQuery( - rawDatasource, - `CREATE TABLE \`${database}\`.${repeated_table_name} (id SERIAL PRIMARY KEY, val1 TEXT);` + await client.schema.createTable( + `${database}.${repeated_table_name}`, + table => { + table.increments("id").primary() + table.string("val1") + } ) - await rawQuery( - rawDatasource, - `CREATE TABLE \`${database2}\`.${repeated_table_name} (id2 SERIAL PRIMARY KEY, val2 TEXT);` + await client.schema.createTable( + `${database2}.${repeated_table_name}`, + table => { + table.increments("id2").primary() + table.string("val2") + } ) const res = await config.api.datasource.fetchSchema({ @@ -139,21 +98,21 @@ describe("mysql integrations", () => { }) afterEach(async () => { - await rawQuery(rawDatasource, `DROP TABLE IF EXISTS \`${tableName}\``) + await client.schema.dropTableIfExists(tableName) }) it("recognises enum columns as options", async () => { const enumColumnName = "status" - const createTableQuery = ` - CREATE TABLE \`${tableName}\` ( - \`order_id\` INT AUTO_INCREMENT PRIMARY KEY, - \`customer_name\` VARCHAR(100) NOT NULL, - \`${enumColumnName}\` ENUM('pending', 'processing', 'shipped', 'delivered', 'cancelled') - ); - ` - - await rawQuery(rawDatasource, createTableQuery) + await client.schema.createTable(tableName, table => { + table.increments("order_id").primary() + table.string("customer_name", 100).notNullable() + table.enum( + enumColumnName, + ["pending", "processing", "shipped", "delivered", "cancelled"], + { useNative: true, enumName: `${tableName}_${enumColumnName}` } + ) + }) const res = await config.api.datasource.fetchSchema({ datasourceId: datasource._id!, diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 9dd6a951b5..3e34ce097b 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -93,7 +93,7 @@ describe("postgres integrations", () => { describe("Integration compatibility with postgres search_path", () => { let rawDatasource: Datasource, datasource: Datasource, - client: Knex, + client: Knex, schema1: string, schema2: string diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index 3b3533eb7f..3f661f4914 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -7,7 +7,7 @@ import knex from "knex" let ports: Promise -export async function getDatasource(): Promise { +export async function GetDatasource(): Promise { if (!ports) { ports = startContainer( new GenericContainer("mcr.microsoft.com/mssql/server:2022-latest") diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index cb6667bc56..1975e07730 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -88,7 +88,7 @@ export async function knexClient(ds: Datasource) { } return knex({ - client: "mysql", + client: "mysql2", connection: ds.config, }) } From c107ab937d2a9602f9c4805274c821c6fde47494 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 17 Jun 2024 16:15:51 +0100 Subject: [PATCH 043/116] Remove rawQuery entirely. --- .../routes/tests/queries/generic-sql.spec.ts | 96 ++++++++----------- .../server/src/integration-test/mysql.spec.ts | 3 +- .../src/integration-test/postgres.spec.ts | 14 ++- .../src/integrations/tests/utils/index.ts | 17 ---- .../src/integrations/tests/utils/mariadb.ts | 5 +- .../src/integrations/tests/utils/mssql.ts | 24 +---- .../src/integrations/tests/utils/mysql.ts | 21 +--- .../src/integrations/tests/utils/postgres.ts | 22 +---- 8 files changed, 57 insertions(+), 145 deletions(-) diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index e8a38dcfaa..d44acb84d9 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -10,37 +10,11 @@ import * as setup from "../utilities" import { DatabaseName, getDatasource, - rawQuery, + knexClient, } from "../../../../integrations/tests/utils" import { Expectations } from "src/tests/utilities/api/base" import { events } from "@budibase/backend-core" - -const createTableSQL: Record = { - [SourceName.POSTGRES]: ` - CREATE TABLE test_table ( - id serial PRIMARY KEY, - name VARCHAR ( 50 ) NOT NULL, - birthday TIMESTAMP, - number INT - );`, - [SourceName.MYSQL]: ` - CREATE TABLE test_table ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(50) NOT NULL, - birthday TIMESTAMP, - number INT - );`, - [SourceName.SQL_SERVER]: ` - CREATE TABLE test_table ( - id INT IDENTITY(1,1) PRIMARY KEY, - name NVARCHAR(50) NOT NULL, - birthday DATETIME, - number INT - );`, -} - -const insertSQL = `INSERT INTO test_table (name) VALUES ('one'), ('two'), ('three'), ('four'), ('five')` -const dropTableSQL = `DROP TABLE test_table;` +import { Knex } from "knex" describe.each( [ @@ -53,6 +27,7 @@ describe.each( const config = setup.getConfig() let rawDatasource: Datasource let datasource: Datasource + let client: Knex async function createQuery( query: Partial, @@ -82,21 +57,34 @@ describe.each( rawDatasource = await dsProvider datasource = await config.api.datasource.create(rawDatasource) - // The Datasource API does not return the password, but we need - // it later to connect to the underlying database, so we fill it - // back in here. + // The Datasource API doesn ot return the password, but we need it later to + // connect to the underlying database, so we fill it back in here. datasource.config!.password = rawDatasource.config!.password - await rawQuery(datasource, createTableSQL[datasource.source]) - await rawQuery(datasource, insertSQL) + client = await knexClient(rawDatasource) + + await client.schema.createTable("test_table", table => { + table.increments("id").primary() + table.string("name") + table.timestamp("birthday") + table.integer("number") + }) + + await client("test_table").insert([ + { name: "one" }, + { name: "two" }, + { name: "three" }, + { name: "four" }, + { name: "five" }, + ]) jest.clearAllMocks() }) afterEach(async () => { const ds = await config.api.datasource.get(datasource._id!) - config.api.datasource.delete(ds) - await rawQuery(datasource, dropTableSQL) + await config.api.datasource.delete(ds) + await client.schema.dropTable("test_table") }) afterAll(async () => { @@ -207,7 +195,7 @@ describe.each( }, }) - await config.publish() + await config.api.application.publish(config.getAppId()) const prodQuery = await config.api.query.getProd(query._id!) expect(prodQuery._id).toEqual(query._id) @@ -429,11 +417,11 @@ describe.each( }, ]) - const rows = await rawQuery( - datasource, - "SELECT * FROM test_table WHERE name = 'baz'" - ) + const rows = await client("test_table").where({ name: "baz" }).select() expect(rows).toHaveLength(1) + for (const row of rows) { + expect(row).toMatchObject({ name: "baz" }) + } }) it("should not allow handlebars as parameters", async () => { @@ -490,11 +478,14 @@ describe.each( expect(result.data).toEqual([{ created: true }]) - const rows = await rawQuery( - datasource, - `SELECT * FROM test_table WHERE birthday = '${date.toISOString()}'` - ) + const rows = await client("test_table") + .where({ birthday: datetimeStr }) + .select() expect(rows).toHaveLength(1) + + for (const row of rows) { + expect(new Date(row.birthday)).toEqual(date) + } } ) @@ -522,10 +513,9 @@ describe.each( expect(result.data).toEqual([{ created: true }]) - const rows = await rawQuery( - datasource, - `SELECT * FROM test_table WHERE name = '${notDateStr}'` - ) + const rows = await client("test_table") + .where({ name: notDateStr }) + .select() expect(rows).toHaveLength(1) } ) @@ -660,10 +650,7 @@ describe.each( }, ]) - const rows = await rawQuery( - datasource, - "SELECT * FROM test_table WHERE id = 1" - ) + const rows = await client("test_table").where({ id: 1 }).select() expect(rows).toEqual([ { id: 1, name: "foo", birthday: null, number: null }, ]) @@ -731,10 +718,7 @@ describe.each( }, ]) - const rows = await rawQuery( - datasource, - "SELECT * FROM test_table WHERE id = 1" - ) + const rows = await client("test_table").where({ id: 1 }).select() expect(rows).toHaveLength(0) }) }) diff --git a/packages/server/src/integration-test/mysql.spec.ts b/packages/server/src/integration-test/mysql.spec.ts index eb81b8920f..eb6c840abc 100644 --- a/packages/server/src/integration-test/mysql.spec.ts +++ b/packages/server/src/integration-test/mysql.spec.ts @@ -18,13 +18,12 @@ function uniqueTableName(length?: number): string { const config = setup.getConfig()! describe("mysql integrations", () => { - let rawDatasource: Datasource let datasource: Datasource let client: Knex beforeAll(async () => { await config.init() - rawDatasource = await getDatasource(DatabaseName.MYSQL) + const rawDatasource = await getDatasource(DatabaseName.MYSQL) datasource = await config.api.datasource.create(rawDatasource) client = await knexClient(rawDatasource) }) diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index 3e34ce097b..f2e0382deb 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -12,13 +12,12 @@ import { Knex } from "knex" const config = setup.getConfig()! describe("postgres integrations", () => { - let rawDatasource: Datasource let datasource: Datasource let client: Knex beforeAll(async () => { await config.init() - rawDatasource = await getDatasource(DatabaseName.POSTGRES) + const rawDatasource = await getDatasource(DatabaseName.POSTGRES) datasource = await config.api.datasource.create(rawDatasource) client = await knexClient(rawDatasource) }) @@ -91,17 +90,16 @@ describe("postgres integrations", () => { }) describe("Integration compatibility with postgres search_path", () => { - let rawDatasource: Datasource, - datasource: Datasource, - client: Knex, - schema1: string, - schema2: string + let datasource: Datasource + let client: Knex + let schema1: string + let schema2: string beforeEach(async () => { schema1 = generator.guid().replaceAll("-", "") schema2 = generator.guid().replaceAll("-", "") - rawDatasource = await getDatasource(DatabaseName.POSTGRES) + const rawDatasource = await getDatasource(DatabaseName.POSTGRES) client = await knexClient(rawDatasource) await client.schema.createSchema(schema1) diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index fd7d2373bb..b888f1adc1 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -48,23 +48,6 @@ export async function getDatasources( return Promise.all(sourceNames.map(sourceName => providers[sourceName]())) } -export async function rawQuery(ds: Datasource, sql: string): Promise { - switch (ds.source) { - case SourceName.POSTGRES: { - return postgres.rawQuery(ds, sql) - } - case SourceName.MYSQL: { - return mysql.rawQuery(ds, sql) - } - case SourceName.SQL_SERVER: { - return mssql.rawQuery(ds, sql) - } - default: { - throw new Error(`Unsupported source: ${ds.source}`) - } - } -} - export async function knexClient(ds: Datasource) { switch (ds.source) { case SourceName.POSTGRES: { diff --git a/packages/server/src/integrations/tests/utils/mariadb.ts b/packages/server/src/integrations/tests/utils/mariadb.ts index c4dd4cf43b..3a90b554ee 100644 --- a/packages/server/src/integrations/tests/utils/mariadb.ts +++ b/packages/server/src/integrations/tests/utils/mariadb.ts @@ -1,9 +1,9 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" -import { rawQuery } from "./mysql" import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { startContainer } from "." +import { knexClient } from "./mysql" let ports: Promise @@ -55,7 +55,8 @@ export async function getDatasource(): Promise { } const database = generator.guid().replaceAll("-", "") - await rawQuery(datasource, `CREATE DATABASE \`${database}\``) + const client = await knexClient(datasource) + await client.raw(`CREATE DATABASE \`${database}\``) datasource.config.database = database return datasource } diff --git a/packages/server/src/integrations/tests/utils/mssql.ts b/packages/server/src/integrations/tests/utils/mssql.ts index 3f661f4914..a4bd5448f4 100644 --- a/packages/server/src/integrations/tests/utils/mssql.ts +++ b/packages/server/src/integrations/tests/utils/mssql.ts @@ -1,13 +1,12 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" -import mssql from "mssql" import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { startContainer } from "." import knex from "knex" let ports: Promise -export async function GetDatasource(): Promise { +export async function getDatasource(): Promise { if (!ports) { ports = startContainer( new GenericContainer("mcr.microsoft.com/mssql/server:2022-latest") @@ -50,30 +49,13 @@ export async function GetDatasource(): Promise { } const database = generator.guid().replaceAll("-", "") - await rawQuery(datasource, `CREATE DATABASE "${database}"`) + const client = await knexClient(datasource) + await client.raw(`CREATE DATABASE "${database}"`) datasource.config!.database = database return datasource } -export async function rawQuery(ds: Datasource, sql: string) { - if (!ds.config) { - throw new Error("Datasource config is missing") - } - if (ds.source !== SourceName.SQL_SERVER) { - throw new Error("Datasource source is not SQL Server") - } - - const pool = new mssql.ConnectionPool(ds.config! as mssql.config) - const client = await pool.connect() - try { - const { recordset } = await client.query(sql) - return recordset - } finally { - await pool.close() - } -} - export async function knexClient(ds: Datasource) { if (!ds.config) { throw new Error("Datasource config is missing") diff --git a/packages/server/src/integrations/tests/utils/mysql.ts b/packages/server/src/integrations/tests/utils/mysql.ts index 1975e07730..c35be0689e 100644 --- a/packages/server/src/integrations/tests/utils/mysql.ts +++ b/packages/server/src/integrations/tests/utils/mysql.ts @@ -1,7 +1,6 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" -import mysql from "mysql2/promise" import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { startContainer } from "." import knex from "knex" @@ -57,28 +56,12 @@ export async function getDatasource(): Promise { } const database = generator.guid().replaceAll("-", "") - await rawQuery(datasource, `CREATE DATABASE \`${database}\``) + const client = await knexClient(datasource) + await client.raw(`CREATE DATABASE \`${database}\``) datasource.config!.database = database return datasource } -export async function rawQuery(ds: Datasource, sql: string) { - if (!ds.config) { - throw new Error("Datasource config is missing") - } - if (ds.source !== SourceName.MYSQL) { - throw new Error("Datasource source is not MySQL") - } - - const connection = await mysql.createConnection(ds.config) - try { - const [rows] = await connection.query(sql) - return rows - } finally { - connection.end() - } -} - export async function knexClient(ds: Datasource) { if (!ds.config) { throw new Error("Datasource config is missing") diff --git a/packages/server/src/integrations/tests/utils/postgres.ts b/packages/server/src/integrations/tests/utils/postgres.ts index 20b56331c1..74f5722737 100644 --- a/packages/server/src/integrations/tests/utils/postgres.ts +++ b/packages/server/src/integrations/tests/utils/postgres.ts @@ -1,6 +1,5 @@ import { Datasource, SourceName } from "@budibase/types" import { GenericContainer, Wait } from "testcontainers" -import pg from "pg" import { generator, testContainerUtils } from "@budibase/backend-core/tests" import { startContainer } from "." import knex from "knex" @@ -44,30 +43,13 @@ export async function getDatasource(): Promise { } const database = generator.guid().replaceAll("-", "") - await rawQuery(datasource, `CREATE DATABASE "${database}"`) + const client = await knexClient(datasource) + await client.raw(`CREATE DATABASE "${database}"`) datasource.config!.database = database return datasource } -export async function rawQuery(ds: Datasource, sql: string) { - if (!ds.config) { - throw new Error("Datasource config is missing") - } - if (ds.source !== SourceName.POSTGRES) { - throw new Error("Datasource source is not Postgres") - } - - const client = new pg.Client(ds.config) - await client.connect() - try { - const { rows } = await client.query(sql) - return rows - } finally { - await client.end() - } -} - export async function knexClient(ds: Datasource) { if (!ds.config) { throw new Error("Datasource config is missing") From 903c3cf84d49f0001a0ba1a49b43de02eae624d5 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 17 Jun 2024 16:17:00 +0100 Subject: [PATCH 044/116] Fix flake in generic-sql.spec.ts. --- .../server/src/api/routes/tests/queries/generic-sql.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts index d44acb84d9..b060a099d8 100644 --- a/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts +++ b/packages/server/src/api/routes/tests/queries/generic-sql.spec.ts @@ -63,6 +63,7 @@ describe.each( client = await knexClient(rawDatasource) + await client.schema.dropTableIfExists("test_table") await client.schema.createTable("test_table", table => { table.increments("id").primary() table.string("name") @@ -84,7 +85,6 @@ describe.each( afterEach(async () => { const ds = await config.api.datasource.get(datasource._id!) await config.api.datasource.delete(ds) - await client.schema.dropTable("test_table") }) afterAll(async () => { From 9bcb5a3500694eaa04ee2394893bfaf31d407488 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:51:45 +0000 Subject: [PATCH 045/116] Bump ws from 7.5.9 to 7.5.10 Bumps [ws](https://github.com/websockets/ws) from 7.5.9 to 7.5.10. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 1249 ++++------------------------------------------------- 1 file changed, 78 insertions(+), 1171 deletions(-) diff --git a/yarn.lock b/yarn.lock index d71dd4da78..ea5f08a788 100644 --- a/yarn.lock +++ b/yarn.lock @@ -625,13 +625,6 @@ dependencies: tslib "^2.5.0" -"@aws/dynamodb-auto-marshaller@^0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@aws/dynamodb-auto-marshaller/-/dynamodb-auto-marshaller-0.7.1.tgz#70676c056e4ecb798c08ec2e398a3d93e703858d" - integrity sha512-LeURlf6/avrfFo9+4Yht9J3CUTJ72yoBpm1FOUmlexuHNW4Ka61tG30w3ZDCXXXmCO2rG0k3ywAgNJEo3WPbyw== - dependencies: - tslib "^1.8.1" - "@azure/abort-controller@^1.0.0", "@azure/abort-controller@^1.0.4": version "1.1.0" resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.1.0.tgz#788ee78457a55af8a1ad342acb182383d2119249" @@ -1992,13 +1985,6 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.13.10": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" - integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -2126,7 +2112,7 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" -"@budibase/pro@npm:@budibase/pro@latest": +"@budibase/pro@0.0.0", "@budibase/pro@npm:@budibase/pro@latest": version "2.23.12" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.23.12.tgz#b2e813c547a5ed22b5bd86b1158159fe4b918260" integrity sha512-DMtfkrJDSIF9V7AL6brpuWWw7Ot5XxO4YQ32ggmr0264uU9KYsTFvlFXFP3MSF2H+247ZYUouSJU76+XeC13qQ== @@ -2192,32 +2178,6 @@ dependencies: "@bull-board/api" "5.10.2" -"@camunda8/sdk@^8.5.3": - version "8.6.2" - resolved "https://registry.yarnpkg.com/@camunda8/sdk/-/sdk-8.6.2.tgz#7f1ed90dfb5ad50ac22e5f984e92739c4e54f216" - integrity sha512-QdpuU3qsbJVKYDuIIYIgryl9HbnOoUqmeUcCU4YZPBhoWVkbCjnP0GD4Q3485SE3WzpbbAMoLtYCHi7hJwnAcA== - dependencies: - "@grpc/grpc-js" "1.10.9" - "@grpc/proto-loader" "0.7.13" - chalk "^2.4.2" - console-stamp "^3.0.2" - dayjs "^1.8.15" - debug "^4.3.4" - fast-xml-parser "^4.1.3" - got "^11.8.6" - jwt-decode "^4.0.0" - lodash.mergewith "^4.6.2" - long "^4.0.0" - lossless-json "^4.0.1" - neon-env "^0.1.3" - promise-retry "^1.1.1" - reflect-metadata "^0.2.1" - stack-trace "0.0.10" - typed-duration "^1.0.12" - uuid "^7.0.3" - optionalDependencies: - win-ca "3.5.1" - "@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.7.1": version "6.7.1" resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.7.1.tgz#3364799b78dff70fb8f81615536c52ea53ce40b2" @@ -2401,231 +2361,116 @@ find-up "^5.0.0" strip-json-comments "^3.1.1" -"@esbuild/aix-ppc64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" - integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== - "@esbuild/android-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== -"@esbuild/android-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" - integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== - "@esbuild/android-arm@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== -"@esbuild/android-arm@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" - integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== - "@esbuild/android-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== -"@esbuild/android-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" - integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== - "@esbuild/darwin-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== -"@esbuild/darwin-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" - integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== - "@esbuild/darwin-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== -"@esbuild/darwin-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" - integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== - "@esbuild/freebsd-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== -"@esbuild/freebsd-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" - integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== - "@esbuild/freebsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== -"@esbuild/freebsd-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" - integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== - "@esbuild/linux-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== -"@esbuild/linux-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" - integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== - "@esbuild/linux-arm@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== -"@esbuild/linux-arm@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" - integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== - "@esbuild/linux-ia32@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== -"@esbuild/linux-ia32@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" - integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== - "@esbuild/linux-loong64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== -"@esbuild/linux-loong64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" - integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== - "@esbuild/linux-mips64el@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== -"@esbuild/linux-mips64el@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" - integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== - "@esbuild/linux-ppc64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== -"@esbuild/linux-ppc64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" - integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== - "@esbuild/linux-riscv64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== -"@esbuild/linux-riscv64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" - integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== - "@esbuild/linux-s390x@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== -"@esbuild/linux-s390x@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" - integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== - "@esbuild/linux-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== -"@esbuild/linux-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" - integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== - "@esbuild/netbsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== -"@esbuild/netbsd-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" - integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== - "@esbuild/openbsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== -"@esbuild/openbsd-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" - integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== - "@esbuild/sunos-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== -"@esbuild/sunos-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" - integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== - "@esbuild/win32-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== -"@esbuild/win32-arm64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" - integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== - "@esbuild/win32-ia32@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== -"@esbuild/win32-ia32@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" - integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== - "@esbuild/win32-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== -"@esbuild/win32-x64@0.20.2": - version "0.20.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" - integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== - "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -2779,14 +2624,6 @@ oauth "^0.10.0" passport-strategy "^1.0.0" -"@grpc/grpc-js@1.10.9": - version "1.10.9" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.10.9.tgz#468cc1549a3fe37b760a16745fb7685d91f4f10c" - integrity sha512-5tcgUctCG0qoNyfChZifz2tJqbRbXVO9J7X6duFcOjY3HUNCxg5D0ZCK7EP9vIcZ0zRpLU9bWkyCqVCLZ46IbQ== - dependencies: - "@grpc/proto-loader" "^0.7.13" - "@js-sdsl/ordered-map" "^4.4.2" - "@grpc/grpc-js@~1.8.0": version "1.8.22" resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.8.22.tgz#847930c9af46e14df05b57fc12325db140ceff1d" @@ -2795,7 +2632,7 @@ "@grpc/proto-loader" "^0.7.0" "@types/node" ">=12.12.47" -"@grpc/proto-loader@0.7.13", "@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.13": +"@grpc/proto-loader@^0.7.0": version "0.7.13" resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== @@ -2805,32 +2642,18 @@ protobufjs "^7.2.5" yargs "^17.7.2" -"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": +"@hapi/hoek@^9.0.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== -"@hapi/topo@^5.0.0", "@hapi/topo@^5.1.0": +"@hapi/topo@^5.0.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== dependencies: "@hapi/hoek" "^9.0.0" -"@hubspot/api-client@7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@hubspot/api-client/-/api-client-7.1.2.tgz#a405b0a18b8caa27f129fd510b2555e5a5cc2708" - integrity sha512-JVQqh0fdHf97ePk0Hg/7BJsiXNlS9HQRPiM/CLgvVWt5CIviSLQ/kHLZXREmZqTWu7BisjCgHxnSx/d7gRdr2g== - dependencies: - bluebird "^3.7.2" - bottleneck "^2.19.5" - btoa "^1.2.1" - es6-promise "^4.2.4" - form-data "^2.5.0" - lodash "^4.17.21" - node-fetch "^2.6.0" - url-parse "^1.4.3" - "@humanwhocodes/config-array@^0.11.13": version "0.11.13" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" @@ -3478,11 +3301,6 @@ resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-5.6.1.tgz#03e2453d877b61c3f593cf031fd18b375bd548b6" integrity sha512-Xla/d7ZMMR6+zRd6lTio0wRZECfcfFJP7GGe9A9L4tDOlD5CX4YcZ4YZle9w58bBYzssojVapI84RraKWDQZRg== -"@js-sdsl/ordered-map@^4.4.2": - version "4.4.2" - resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" - integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== - "@jsdevtools/ono@^7.1.3": version "7.1.3" resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" @@ -3495,13 +3313,6 @@ dependencies: lodash "^4.17.21" -"@koa/cors@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@koa/cors/-/cors-5.0.0.tgz#0029b5f057fa0d0ae0e37dd2c89ece315a0daffd" - integrity sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw== - dependencies: - vary "^1.1.2" - "@koa/router@8.0.8": version "8.0.8" resolved "https://registry.yarnpkg.com/@koa/router/-/router-8.0.8.tgz#95f32d11373d03d89dcb63fabe9ac6f471095236" @@ -4042,7 +3853,7 @@ dependencies: slash "^4.0.0" -"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0": +"@rollup/plugin-commonjs@^16.0.0": version "16.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== @@ -4128,22 +3939,6 @@ is-module "^1.0.0" resolve "^1.22.1" -"@rollup/plugin-replace@^2.4.2": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz#a2d539314fbc77c244858faa523012825068510a" - integrity sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg== - dependencies: - "@rollup/pluginutils" "^3.1.0" - magic-string "^0.25.7" - -"@rollup/plugin-replace@^5.0.2": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.7.tgz#150c9ee9db8031d9e4580a61a0edeaaed3d37687" - integrity sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ== - dependencies: - "@rollup/pluginutils" "^5.0.1" - magic-string "^0.30.3" - "@rollup/plugin-replace@^5.0.3": version "5.0.5" resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.5.tgz#33d5653dce6d03cb24ef98bef7f6d25b57faefdf" @@ -4205,11 +4000,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz#1a32112822660ee104c5dd3a7c595e26100d4c2d" integrity sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ== -"@rollup/rollup-android-arm-eabi@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27" - integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ== - "@rollup/rollup-android-arm64@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz#3822e929f415627609e53b11cec9a4be806de0e2" @@ -4220,11 +4010,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz#5aeef206d65ff4db423f3a93f71af91b28662c5b" integrity sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw== -"@rollup/rollup-android-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203" - integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA== - "@rollup/rollup-darwin-arm64@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz#6c082de71f481f57df6cfa3701ab2a7afde96f69" @@ -4235,11 +4020,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz#6b66aaf003c70454c292cd5f0236ebdc6ffbdf1a" integrity sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw== -"@rollup/rollup-darwin-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096" - integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w== - "@rollup/rollup-darwin-x64@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz#c34ca0d31f3c46a22c9afa0e944403eea0edcfd8" @@ -4250,11 +4030,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz#f64fc51ed12b19f883131ccbcea59fc68cbd6c0b" integrity sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ== -"@rollup/rollup-darwin-x64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c" - integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA== - "@rollup/rollup-linux-arm-gnueabihf@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz#48e899c1e438629c072889b824a98787a7c2362d" @@ -4265,21 +4040,11 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz#1a7641111be67c10111f7122d1e375d1226cbf14" integrity sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A== -"@rollup/rollup-linux-arm-gnueabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8" - integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA== - "@rollup/rollup-linux-arm-musleabihf@4.17.2": version "4.17.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz#c93fd632923e0fee25aacd2ae414288d0b7455bb" integrity sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg== -"@rollup/rollup-linux-arm-musleabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549" - integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A== - "@rollup/rollup-linux-arm64-gnu@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz#788c2698a119dc229062d40da6ada8a090a73a68" @@ -4290,11 +4055,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz#fa531425dd21d058a630947527b4612d9d0b4a4a" integrity sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A== -"@rollup/rollup-linux-arm64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577" - integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw== - "@rollup/rollup-linux-arm64-musl@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz#3882a4e3a564af9e55804beeb67076857b035ab7" @@ -4305,21 +4065,11 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz#8acc16f095ceea5854caf7b07e73f7d1802ac5af" integrity sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA== -"@rollup/rollup-linux-arm64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c" - integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ== - "@rollup/rollup-linux-powerpc64le-gnu@4.17.2": version "4.17.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz#94e69a8499b5cf368911b83a44bb230782aeb571" integrity sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ== -"@rollup/rollup-linux-powerpc64le-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf" - integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA== - "@rollup/rollup-linux-riscv64-gnu@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz#0c6ad792e1195c12bfae634425a3d2aa0fe93ab7" @@ -4330,21 +4080,11 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz#7ef1c781c7e59e85a6ce261cc95d7f1e0b56db0f" integrity sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg== -"@rollup/rollup-linux-riscv64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9" - integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg== - "@rollup/rollup-linux-s390x-gnu@4.17.2": version "4.17.2" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz#f15775841c3232fca9b78cd25a7a0512c694b354" integrity sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g== -"@rollup/rollup-linux-s390x-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec" - integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg== - "@rollup/rollup-linux-x64-gnu@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz#9d62485ea0f18d8674033b57aa14fb758f6ec6e3" @@ -4355,11 +4095,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz#b521d271798d037ad70c9f85dd97d25f8a52e811" integrity sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ== -"@rollup/rollup-linux-x64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942" - integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w== - "@rollup/rollup-linux-x64-musl@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz#50e8167e28b33c977c1f813def2b2074d1435e05" @@ -4370,11 +4105,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz#9254019cc4baac35800991315d133cc9fd1bf385" integrity sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q== -"@rollup/rollup-linux-x64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d" - integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg== - "@rollup/rollup-win32-arm64-msvc@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz#68d233272a2004429124494121a42c4aebdc5b8e" @@ -4385,11 +4115,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz#27f65a89f6f52ee9426ec11e3571038e4671790f" integrity sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA== -"@rollup/rollup-win32-arm64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf" - integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA== - "@rollup/rollup-win32-ia32-msvc@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz#366ca62221d1689e3b55a03f4ae12ae9ba595d40" @@ -4400,11 +4125,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz#a2fbf8246ed0bb014f078ca34ae6b377a90cb411" integrity sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ== -"@rollup/rollup-win32-ia32-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54" - integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg== - "@rollup/rollup-win32-x64-msvc@4.12.0": version "4.12.0" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz#9ffdf9ed133a7464f4ae187eb9e1294413fab235" @@ -4415,28 +4135,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503" integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w== -"@rollup/rollup-win32-x64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4" - integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== - -"@roxi/routify@2.18.0": - version "2.18.0" - resolved "https://registry.yarnpkg.com/@roxi/routify/-/routify-2.18.0.tgz#8f88bedd936312d0dbe44cbc11ab179b1f938ec2" - integrity sha512-MVB50HN+VQWLzfjLplcBjsSBvwOiExKOmht2DuWR3WQ60JxQi9pSejkB06tFVkFKNXz2X5iYtKDqKBTdae/gRg== - dependencies: - "@roxi/ssr" "^0.2.1" - "@types/node" ">=4.2.0 < 13" - chalk "^4.0.0" - cheap-watch "^1.0.2" - commander "^7.1.0" - configent "^2.1.4" - esm "^3.2.25" - fs-extra "^9.0.1" - log-symbols "^3.0.0" - picomatch "^2.2.2" - rollup-pluginutils "^2.8.2" - "@roxi/routify@2.18.12": version "2.18.12" resolved "https://registry.yarnpkg.com/@roxi/routify/-/routify-2.18.12.tgz#901ca95b96f274ddddaefbf18424557ee1ae3fae" @@ -4479,14 +4177,7 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@sideway/address@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5" - integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q== - dependencies: - "@hapi/hoek" "^9.0.0" - -"@sideway/formula@^3.0.0", "@sideway/formula@^3.0.1": +"@sideway/formula@^3.0.0": version "3.0.1" resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== @@ -4525,11 +4216,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@sindresorhus/is@^4.0.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== - "@sinonjs/commons@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" @@ -5326,13 +5012,6 @@ dependencies: defer-to-connect "^1.0.1" -"@szmarczak/http-timer@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" - integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== - dependencies: - defer-to-connect "^2.0.0" - "@techpass/passport-openidconnect@0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@techpass/passport-openidconnect/-/passport-openidconnect-0.3.2.tgz#f8fd5d97256286665dbf26dac92431f977ab1e63" @@ -5344,17 +5023,6 @@ request "^2.88.0" webfinger "^0.4.2" -"@techpass/passport-openidconnect@^0.3.0": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@techpass/passport-openidconnect/-/passport-openidconnect-0.3.3.tgz#6c01c78bd8da0ca8917378dfbe18024702620352" - integrity sha512-i2X/CofjnGBqpTmw6b+Ex3Co/NrR2xjnIHvnOJk62XIlJJHNSTwmhJ1PkXoA5RGKlxZWchADFGjLTJnebvRj7A== - dependencies: - base64url "^3.0.1" - oauth "^0.9.15" - passport-strategy "^1.0.0" - request "^2.88.0" - webfinger "^0.4.2" - "@techteamer/ocsp@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@techteamer/ocsp/-/ocsp-1.0.0.tgz#7b82b02093fbe351e915bb37685ac1ac5a1233d3" @@ -5524,16 +5192,6 @@ "@types/connect" "*" "@types/node" "*" -"@types/cacheable-request@^6.0.1": - version "6.0.3" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" - integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "^3.1.4" - "@types/node" "*" - "@types/responselike" "^1.0.0" - "@types/caseless@*": version "0.12.2" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" @@ -5686,11 +5344,6 @@ resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.3.tgz#ef8e3d1a8d46c387f04ab0f2e8ab8cb0c5078661" integrity sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA== -"@types/http-cache-semantics@*": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" - integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== - "@types/http-errors@*": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" @@ -5752,13 +5405,6 @@ resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== -"@types/keyv@^3.1.4": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" - integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== - dependencies: - "@types/node" "*" - "@types/koa-compose@*": version "3.2.5" resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" @@ -5766,29 +5412,13 @@ dependencies: "@types/koa" "*" -"@types/koa-passport@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/koa-passport/-/koa-passport-4.0.3.tgz#063ec6310edee76cf854aadaa717b97f04b104fb" - integrity sha512-tNMYd/bcv0Zw7fc0CzEBYM9uUzVtn4XWzdUYfkTgSkEljP6nap7eI4E5x43ukrUQvztgXSYFkz3Uk+ujFeUzTg== - dependencies: - "@types/koa" "*" - "@types/passport" "*" - -"@types/koa-send@*", "@types/koa-send@^4.1.6": +"@types/koa-send@^4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/koa-send/-/koa-send-4.1.6.tgz#15d90e95e3ccce669a15b6a3c56c3a650a167cea" integrity sha512-vgnNGoOJkx7FrF0Jl6rbK1f8bBecqAchKpXtKuXzqIEdXTDO6dsSTjr+eZ5m7ltSjH4K/E7auNJEQCAd0McUPA== dependencies: "@types/koa" "*" -"@types/koa-static@^4.0.2": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/koa-static/-/koa-static-4.0.4.tgz#ce6f2a5d14cc7ef19f9bf6ee8e4f3eadfcc77323" - integrity sha512-j1AUzzl7eJYEk9g01hNTlhmipFh8RFbOQmaMNLvLcNNAkPw0bdTs3XTa3V045XFlrWN0QYnblbDJv2RzawTn6A== - dependencies: - "@types/koa" "*" - "@types/koa-send" "*" - "@types/koa@*": version "2.13.5" resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.5.tgz#64b3ca4d54e08c0062e89ec666c9f45443b21a61" @@ -5817,13 +5447,6 @@ "@types/koa-compose" "*" "@types/node" "*" -"@types/koa__cors@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/koa__cors/-/koa__cors-5.0.0.tgz#74567a045b599266e2cd3940cef96cedecc2ef1f" - integrity sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g== - dependencies: - "@types/koa" "*" - "@types/koa__router@8.0.8": version "8.0.8" resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-8.0.8.tgz#b1e0e9a512498777d3366bbdf0e853df27ec831c" @@ -5930,22 +5553,15 @@ dependencies: undici-types "~5.26.4" -"@types/node@>=4.2.0 < 13", "@types/node@^12.20.52": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== - "@types/node@>=8.0.0 <15": version "14.18.37" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.37.tgz#0bfcd173e8e1e328337473a8317e37b3b14fd30d" integrity sha512-7GgtHCs/QZrBrDzgIJnQtuSvhFSwhyYSI2uafSwZoNt1iOGhEN5fwNrQMjtONyHm9+/LoA4453jH0CMYcr06Pg== -"@types/node@>=8.1.0": - version "20.14.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" - integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== - dependencies: - undici-types "~5.26.4" +"@types/node@^12.20.52": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== "@types/node@^18.11.18": version "18.19.10" @@ -5954,25 +5570,11 @@ dependencies: undici-types "~5.26.4" -"@types/nodemailer@^6.4.4": - version "6.4.15" - resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.15.tgz#494be695e11c438f7f5df738fb4ab740312a6ed2" - integrity sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ== - dependencies: - "@types/node" "*" - "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== -"@types/oauth@*": - version "0.9.5" - resolved "https://registry.yarnpkg.com/@types/oauth/-/oauth-0.9.5.tgz#acc4209bfa1c8d7d3aaf2c9ad0b32216a29616c1" - integrity sha512-+oQ3C2Zx6ambINOcdIARF5Z3Tu3x//HipE889/fqo3sgpQZbe9c6ExdQFtN6qlhpR7p83lTZfPJt0tCAW29dog== - dependencies: - "@types/node" "*" - "@types/oracledb@5.2.2": version "5.2.2" resolved "https://registry.yarnpkg.com/@types/oracledb/-/oracledb-5.2.2.tgz#ae7ba795969e3bbd8d57ab141873a1aa012b86cd" @@ -5981,37 +5583,6 @@ "@types/node" "*" dotenv "^8.2.0" -"@types/passport-google-oauth@^1.0.42": - version "1.0.45" - resolved "https://registry.yarnpkg.com/@types/passport-google-oauth/-/passport-google-oauth-1.0.45.tgz#c986c787ec9706b4a596d2bae43342b50b54973d" - integrity sha512-O3Y3DDKnf9lR8+DSaUOCEGF6aFjVYdI8TLhQYtySZ3Sq75c5tGYJ0KJRDZw0GsyLD/Que0nqFkP/GnDVwZZL9w== - dependencies: - "@types/express" "*" - "@types/passport" "*" - -"@types/passport-microsoft@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/passport-microsoft/-/passport-microsoft-1.0.0.tgz#a2ddc2200843570d38c35c53f6388e33df915b58" - integrity sha512-vD9ajSUc9Sz/8gdCj0ODUbPYQDxcI/imIDdgMPh//c5yMK/PgV6SNUXFLBzJo89Y30LU6bYAfXKn40WJqtMBiA== - dependencies: - "@types/passport-oauth2" "*" - -"@types/passport-oauth2@*": - version "1.4.17" - resolved "https://registry.yarnpkg.com/@types/passport-oauth2/-/passport-oauth2-1.4.17.tgz#d5d54339d44f6883d03e69dc0cc0e2114067abb4" - integrity sha512-ODiAHvso6JcWJ6ZkHHroVp05EHGhqQN533PtFNBkg8Fy5mERDqsr030AX81M0D69ZcaMvhF92SRckEk2B0HYYg== - dependencies: - "@types/express" "*" - "@types/oauth" "*" - "@types/passport" "*" - -"@types/passport@*": - version "1.0.16" - resolved "https://registry.yarnpkg.com/@types/passport/-/passport-1.0.16.tgz#5a2918b180a16924c4d75c31254c31cdca5ce6cf" - integrity sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A== - dependencies: - "@types/express" "*" - "@types/pg@8.6.6": version "8.6.6" resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.6.tgz#21cdf873a3e345a6e78f394677e3b3b1b543cb80" @@ -6244,13 +5815,6 @@ resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== -"@types/responselike@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" - integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== - dependencies: - "@types/node" "*" - "@types/retry@*": version "0.12.5" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.5.tgz#f090ff4bd8d2e5b940ff270ab39fd5ca1834a07e" @@ -6289,13 +5853,6 @@ dependencies: "@types/node" "*" -"@types/server-destroy@^1.0.1": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@types/server-destroy/-/server-destroy-1.0.3.tgz#2460932ea3a02a70ec99669c8f40ff089a5b8a2b" - integrity sha512-Qq0fn70C7TLDG1W9FCblKufNWW1OckQ41dVKV2Dku5KdZF7bexezG4e2WBaBKhdwL3HZ+cYCEIKwg2BRgzrWmA== - dependencies: - "@types/node" "*" - "@types/ssh2-streams@*": version "0.1.12" resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.12.tgz#e68795ba2bf01c76b93f9c9809e1f42f0eaaec5f" @@ -6397,7 +5954,7 @@ dependencies: "@types/node" "*" -"@types/uuid@8.3.4", "@types/uuid@^8.3.4": +"@types/uuid@8.3.4": version "8.3.4" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== @@ -6666,15 +6223,6 @@ "@vitest/utils" "0.29.8" chai "^4.3.7" -"@vitest/expect@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.6.0.tgz#0b3ba0914f738508464983f4d811bc122b51fb30" - integrity sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ== - dependencies: - "@vitest/spy" "1.6.0" - "@vitest/utils" "1.6.0" - chai "^4.3.10" - "@vitest/runner@0.29.8": version "0.29.8" resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.29.8.tgz#ede8a7be8a074ea1180bc1d1595bd879ed15971c" @@ -6684,24 +6232,6 @@ p-limit "^4.0.0" pathe "^1.1.0" -"@vitest/runner@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.6.0.tgz#a6de49a96cb33b0e3ba0d9064a3e8d6ce2f08825" - integrity sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg== - dependencies: - "@vitest/utils" "1.6.0" - p-limit "^5.0.0" - pathe "^1.1.1" - -"@vitest/snapshot@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.6.0.tgz#deb7e4498a5299c1198136f56e6e0f692e6af470" - integrity sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ== - dependencies: - magic-string "^0.30.5" - pathe "^1.1.1" - pretty-format "^29.7.0" - "@vitest/spy@0.29.8": version "0.29.8" resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.29.8.tgz#2e0c3b30e04d317b2197e3356234448aa432e131" @@ -6709,13 +6239,6 @@ dependencies: tinyspy "^1.0.2" -"@vitest/spy@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.6.0.tgz#362cbd42ccdb03f1613798fde99799649516906d" - integrity sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw== - dependencies: - tinyspy "^2.2.0" - "@vitest/utils@0.29.8": version "0.29.8" resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.29.8.tgz#423da85fd0c6633f3ab496cf7d2fc0119b850df8" @@ -6726,16 +6249,6 @@ loupe "^2.3.6" pretty-format "^27.5.1" -"@vitest/utils@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.6.0.tgz#5c5675ca7d6f546a7b4337de9ae882e6c57896a1" - integrity sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw== - dependencies: - diff-sequences "^29.6.3" - estree-walker "^3.0.3" - loupe "^2.3.7" - pretty-format "^29.7.0" - "@yarnpkg/lockfile@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" @@ -6801,7 +6314,7 @@ abortcontroller-polyfill@^1.4.0: resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== -abstract-leveldown@^6.2.1, abstract-leveldown@^6.3.0: +abstract-leveldown@^6.2.1: version "6.3.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== @@ -6881,7 +6394,7 @@ acorn-walk@^8.0.2, acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn-walk@^8.2.0, acorn-walk@^8.3.2: +acorn-walk@^8.2.0: version "8.3.2" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== @@ -7382,13 +6895,6 @@ async-retry@^1.3.3: dependencies: retry "0.13.1" -async@^2.6.3: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== - dependencies: - lodash "^4.17.14" - async@^3.2.1, async@^3.2.3, async@^3.2.4: version "3.2.5" resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" @@ -7690,11 +7196,6 @@ big-integer@^1.6.43: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - bignumber.js@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-2.4.0.tgz#838a992da9f9d737e0f4b2db0be62bb09dd0c5e8" @@ -7779,11 +7280,6 @@ boolean@^3.0.1: resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== -bottleneck@^2.19.5: - version "2.19.5" - resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" - integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== - bowser@^2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" @@ -7932,11 +7428,6 @@ bson@^6.2.0: resolved "https://registry.yarnpkg.com/bson/-/bson-6.3.0.tgz#d47acba525ba7d7eb0e816c10538bce26a337fe0" integrity sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw== -btoa@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" - integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== - buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -8140,11 +7631,6 @@ cache-content-type@^1.0.0: mime-types "^2.1.18" ylru "^1.2.0" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" @@ -8171,19 +7657,6 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -cacheable-request@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" - integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^6.0.1" - responselike "^2.0.0" - call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" @@ -8251,7 +7724,7 @@ catharsis@^0.9.0: dependencies: lodash "^4.17.15" -chai@^4.3.10, chai@^4.3.7: +chai@^4.3.7: version "4.4.1" resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== @@ -8317,7 +7790,7 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== -cheap-watch@^1.0.2, cheap-watch@^1.0.4: +cheap-watch@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/cheap-watch/-/cheap-watch-1.0.4.tgz#0bcb4a3a8fbd9d5327936493f6b56baa668d8fef" integrity sha512-QR/9FrtRL5fjfUJBhAKCdi0lSRQ3rVRRum3GF9wDKp2TJbEIMGhUEr2yU8lORzm9Isdjx7/k9S0DFDx+z5VGtw== @@ -8650,7 +8123,7 @@ commander@^5.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== -commander@^7.1.0, commander@^7.2.0: +commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== @@ -8780,7 +8253,7 @@ config-chain@^1.1.13: ini "^1.3.4" proto-list "~1.2.1" -configent@^2.1.4, configent@^2.2.0: +configent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/configent/-/configent-2.2.0.tgz#2de230fc43f22c47cfd99016aa6962d6f9546994" integrity sha512-yIN6zfOWk2nycNJ2JFNiWEai0oiqAhISIht8+pbEBP8bdcpwoQ74AhCZPbUv9aRVJwo7wh1MbCBDUV44UJa7Kw== @@ -8809,14 +8282,6 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -console-stamp@^3.0.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/console-stamp/-/console-stamp-3.1.2.tgz#35dac393e16069a4d9d37b71ca6d5d13d7f3f8fd" - integrity sha512-ab66x3NxOTxPuq71dI6gXEiw2X6ql4Le5gZz0bm7FW3FSCB00eztra/oQUuCoCGlsyKOxtULnHwphzMrRtzMBg== - dependencies: - chalk "^4.1.2" - dateformat "^4.6.3" - consolidate@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" @@ -9337,7 +8802,7 @@ dateformat@^4.6.3: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== -dayjs@^1.10.8, dayjs@^1.8.15: +dayjs@^1.10.8: version "1.11.11" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== @@ -9565,11 +9030,6 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== -defer-to-connect@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== - deferred-leveldown@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-0.2.0.tgz#2cef1f111e1c57870d8bbb8af2650e587cd2f5b4" @@ -9613,11 +9073,6 @@ defined@^1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== -defined@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-0.0.0.tgz#f35eea7d705e933baf13b2f03b3f83d921403b3e" - integrity sha512-zpqiCT8bODLu3QSmLLic8xJnYWBFjOSu/fBCm189oAiTtPq/PSanNACKZDS7kgSyCJY7P+IcODzlIogBK/9RBg== - delay@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" @@ -9913,13 +9368,6 @@ docker-compose@0.24.0: dependencies: yaml "^1.10.2" -docker-compose@^0.23.6: - version "0.23.19" - resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.19.tgz#9947726e2fe67bdfa9e8efe1ff15aa0de2e10eb8" - integrity sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g== - dependencies: - yaml "^1.10.2" - docker-compose@^0.24.6: version "0.24.6" resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.24.6.tgz#d1f490a641bdb7ccc07c4d446b264f026f9a1f15" @@ -10118,24 +9566,6 @@ duplexify@^4.0.0, duplexify@^4.1.2: readable-stream "^3.1.1" stream-shift "^1.0.0" -dynalite@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/dynalite/-/dynalite-3.2.2.tgz#34b4f4dd69638f17c0f7551a867959972c892441" - integrity sha512-sx9ZjTgMs/D4gHnba4rnBkw29648dHwHmywJet132KAbiq1ZyWx9W1fMd/eP9cPwTKDXyCBuTYOChE0qMDjaXQ== - dependencies: - async "^2.6.3" - big.js "^5.2.2" - buffer-crc32 "^0.2.13" - lazy "^1.0.11" - levelup "^4.4.0" - lock "^1.1.0" - memdown "^5.1.0" - minimist "^1.2.5" - once "^1.4.0" - subleveldown "^5.0.1" - optionalDependencies: - leveldown "^5.6.0" - eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -10237,7 +9667,7 @@ encodeurl@^1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encoding-down@^6.2.0, encoding-down@^6.3.0: +encoding-down@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== @@ -10345,11 +9775,6 @@ envinfo@7.8.1: resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== -err-code@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" - integrity sha512-CJAN+O0/yA1CKfRn9SXOGctSpEM7DCon/r/5r2eXFMY2zCCJBasFhcM5I+1kh3Ap11FsQCX+vGHceNPvpWKhoA== - err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -10501,11 +9926,6 @@ es6-error@^4.0.1, es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -es6-promise@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - esbuild-node-externals@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/esbuild-node-externals/-/esbuild-node-externals-1.8.0.tgz#878fbe458d4e58337753c2eacfd7200dc1077bd1" @@ -10542,35 +9962,6 @@ esbuild@^0.18.10, esbuild@^0.18.17: "@esbuild/win32-ia32" "0.18.20" "@esbuild/win32-x64" "0.18.20" -esbuild@^0.20.1: - version "0.20.2" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" - integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== - optionalDependencies: - "@esbuild/aix-ppc64" "0.20.2" - "@esbuild/android-arm" "0.20.2" - "@esbuild/android-arm64" "0.20.2" - "@esbuild/android-x64" "0.20.2" - "@esbuild/darwin-arm64" "0.20.2" - "@esbuild/darwin-x64" "0.20.2" - "@esbuild/freebsd-arm64" "0.20.2" - "@esbuild/freebsd-x64" "0.20.2" - "@esbuild/linux-arm" "0.20.2" - "@esbuild/linux-arm64" "0.20.2" - "@esbuild/linux-ia32" "0.20.2" - "@esbuild/linux-loong64" "0.20.2" - "@esbuild/linux-mips64el" "0.20.2" - "@esbuild/linux-ppc64" "0.20.2" - "@esbuild/linux-riscv64" "0.20.2" - "@esbuild/linux-s390x" "0.20.2" - "@esbuild/linux-x64" "0.20.2" - "@esbuild/netbsd-x64" "0.20.2" - "@esbuild/openbsd-x64" "0.20.2" - "@esbuild/sunos-x64" "0.20.2" - "@esbuild/win32-arm64" "0.20.2" - "@esbuild/win32-ia32" "0.20.2" - "@esbuild/win32-x64" "0.20.2" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -11017,21 +10408,6 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" - integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^8.0.1" - human-signals "^5.0.0" - is-stream "^3.0.0" - merge-stream "^2.0.0" - npm-run-path "^5.1.0" - onetime "^6.0.0" - signal-exit "^4.1.0" - strip-final-newline "^3.0.0" - exif-parser@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" @@ -11197,13 +10573,6 @@ fast-xml-parser@4.2.5: dependencies: strnum "^1.0.5" -fast-xml-parser@^4.1.3: - version "4.4.0" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz#341cc98de71e9ba9e651a67f41f1752d1441a501" - integrity sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg== - dependencies: - strnum "^1.0.5" - fast-xml-parser@^4.2.2, fast-xml-parser@^4.2.5: version "4.3.6" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz#190f9d99097f0c8f2d3a0e681a10404afca052ff" @@ -11546,7 +10915,7 @@ fs-extra@^11.1.0, fs-extra@^11.1.1: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: +fs-extra@^9.0.0, fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -11575,7 +10944,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -11595,7 +10964,7 @@ function.prototype.name@^1.1.6: es-abstract "^1.22.1" functions-have-names "^1.2.3" -functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: +functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= @@ -11704,7 +11073,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0, get-func-name@^2.0.1, get-func-name@^2.0.2: +get-func-name@^2.0.0, get-func-name@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== @@ -11797,11 +11166,6 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -get-stream@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" - integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== - get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -11943,17 +11307,6 @@ glob@^10.0.0, glob@^10.2.2: minipass "^7.0.4" path-scurry "^1.10.2" -glob@^10.3.7: - version "10.4.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.1.tgz#0cfb01ab6a6b438177bfe6a58e2576f6efe909c2" - integrity sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - path-scurry "^1.11.1" - glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -12131,23 +11484,6 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -got@^11.8.6: - version "11.8.6" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" - integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - got@^8.3.1: version "8.3.2" resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" @@ -12538,14 +11874,6 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -12559,11 +11887,6 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -human-signals@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" - integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== - humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -12670,11 +11993,6 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== -immediate@~3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" - integrity sha512-RrGCXRm/fRVqMIhqXrGEX9rRADavPiDFSoMb/k64i9XMk8uH4r/Omi5Ctierj6XzNecwDbO4WuFbDD1zmpl3Tg== - import-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" @@ -13020,11 +12338,6 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-electron@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.2.tgz#3778902a2044d76de98036f5dc58089ac4d80bb9" - integrity sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg== - is-extendable@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -13276,11 +12589,6 @@ is-stream@^2.0.0, is-stream@^2.0.1: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" - integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -13522,15 +12830,6 @@ jackspeak@^2.3.6: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jackspeak@^3.1.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" - integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -13648,15 +12947,6 @@ jest-docblock@^29.7.0: dependencies: detect-newline "^3.0.0" -jest-dynalite@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/jest-dynalite/-/jest-dynalite-3.6.1.tgz#8bae305a3c33d9a8036f563827b173b54a323ca5" - integrity sha512-MERtTt8Pj39vFmbItMC3YuIaqLf1kh/pJIE0DRcjeP/2Fa8Nni9IxwN6XWIMgXNbFKtlOM6ppH+Bsy0rWIdPiw== - dependencies: - "@aws/dynamodb-auto-marshaller" "^0.7.1" - dynalite "^3.2.1" - setimmediate "^1.0.5" - jest-each@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" @@ -13981,17 +13271,6 @@ joi@17.6.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -joi@^17.13.1: - version "17.13.1" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.1.tgz#9c7b53dc3b44dd9ae200255cc3b398874918a6ca" - integrity sha512-vaBlIKCyo4FCUtCm7Eu4QZd/q02bWcxfUO6YSXAZOWF6gzcLBeba8kwotUdYJjDLW8Cz8RywsSOqiNJZW0mNvg== - dependencies: - "@hapi/hoek" "^9.3.0" - "@hapi/topo" "^5.1.0" - "@sideway/address" "^4.1.5" - "@sideway/formula" "^3.0.1" - "@sideway/pinpoint" "^2.0.0" - join-component@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" @@ -14027,11 +13306,6 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-tokens@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.0.tgz#0f893996d6f3ed46df7f0a3b12a03f5fd84223c1" - integrity sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ== - js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -14177,11 +13451,6 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - json-format-highlight@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/json-format-highlight/-/json-format-highlight-1.0.4.tgz#2e44277edabcec79a3d2c84e984c62e2258037b9" @@ -14334,11 +13603,6 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -jwt-decode@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b" - integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA== - keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" @@ -14360,13 +13624,6 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" -keyv@^4.0.0: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - kill-port@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/kill-port/-/kill-port-1.6.1.tgz#560fe79484583bdf3a5e908557dae614447618aa" @@ -14475,7 +13732,7 @@ koa-mount@^4.0.0: debug "^4.0.1" koa-compose "^4.1.0" -koa-passport@4.1.4, koa-passport@^4.1.4: +koa-passport@4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.4.tgz#5f1665c1c2a37ace79af9f970b770885ca30ccfa" integrity sha512-dJBCkl4X+zdYxbI2V2OtoGy0PUenpvp2ZLLWObc8UJhsId0iQpTFT8RVcuA0709AL2txGwRHnSPoT1bYNGa6Kg== @@ -14516,7 +13773,7 @@ koa-send@5.0.1, koa-send@^5.0.0: http-errors "^1.7.3" resolve-path "^1.4.0" -koa-session@5.13.1, koa-session@^5.12.0: +koa-session@5.13.1: version "5.13.1" resolved "https://registry.yarnpkg.com/koa-session/-/koa-session-5.13.1.tgz#a47e39015a4b464e21e3e1e2deeca48eb83916ee" integrity sha512-TfYiun6xiFosyfIJKnEw0aoG5XmLIwM+K3OVWfkz84qY0NP2gbk0F/olRn0/Hrxq0f14s8amHVXeWyKYH3Cx3Q== @@ -14534,7 +13791,7 @@ koa-static@5.0.0, koa-static@^5.0.0: debug "^3.1.0" koa-send "^5.0.0" -koa-useragent@4.1.0, koa-useragent@^4.1.0: +koa-useragent@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/koa-useragent/-/koa-useragent-4.1.0.tgz#d3f128b552c6da3e5e9e9e9c887b2922b16e4468" integrity sha512-x/HUDZ1zAmNNh5hA9hHbPm9p3UVg2prlpHzxCXQCzbibrNS0kmj7MkCResCbAbG7ZT6FVxNSMjR94ZGamdMwxA== @@ -14634,11 +13891,6 @@ latest-version@^5.1.0: dependencies: package-json "^6.3.0" -lazy@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690" - integrity sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA== - lazystream@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" @@ -14831,13 +14083,6 @@ level-js@^5.0.0: inherits "^2.0.3" ltgt "^2.1.2" -level-option-wrap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/level-option-wrap/-/level-option-wrap-1.1.0.tgz#ad20e68d9f3c22c8897531cc6aa7af596b1ed129" - integrity sha512-gQouC22iCqHuBLNl4BHxEZUxLvUKALAtT/Q0c6ziOxZQ8c02G/gyxHWNbLbxUzRNfMrRnbt6TZT3gNe8VBqQeg== - dependencies: - defined "~0.0.0" - level-packager@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" @@ -14886,7 +14131,7 @@ level@6.0.1: level-packager "^5.1.0" leveldown "^5.4.0" -leveldown@5.6.0, leveldown@^5.4.0, leveldown@^5.6.0: +leveldown@5.6.0, leveldown@^5.4.0: version "5.6.0" resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== @@ -14895,7 +14140,7 @@ leveldown@5.6.0, leveldown@^5.4.0, leveldown@^5.6.0: napi-macros "~2.0.0" node-gyp-build "~4.1.0" -levelup@4.4.0, levelup@^4.3.2, levelup@^4.4.0: +levelup@4.4.0, levelup@^4.3.2: version "4.4.0" resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== @@ -15040,14 +14285,6 @@ local-pkg@^0.4.2: resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== -local-pkg@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c" - integrity sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg== - dependencies: - mlly "^1.4.2" - pkg-types "^1.0.3" - locate-character@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-character/-/locate-character-3.0.0.tgz#0305c5b8744f61028ef5d01f444009e00779f974" @@ -15083,11 +14320,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lock@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/lock/-/lock-1.1.0.tgz#53157499d1653b136ca66451071fca615703fa55" - integrity sha512-NZQIJJL5Rb9lMJ0Yl1JoVr9GSdo4HTPsUEWsSFzB8dE8DSoiLCVavWZPi7Rnlv/o73u6I24S/XYc/NmG4l8EKA== - lodash-es@^4.17.15, lodash-es@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" @@ -15233,7 +14465,7 @@ lodash.xor@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.xor/-/lodash.xor-4.5.0.tgz#4d48ed7e98095b0632582ba714d3ff8ae8fb1db6" integrity sha512-sVN2zimthq7aZ5sPGXnSz32rZPuqcparVW50chJQe+mzTYV+IsxSsl/2gnkWWE2Of7K3myBQBqtLKOUEHJKRsQ== -lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0: +lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -15280,11 +14512,6 @@ lookpath@1.1.0: resolved "https://registry.yarnpkg.com/lookpath/-/lookpath-1.1.0.tgz#932d68371a2f0b4a5644f03d6a2b4728edba96d2" integrity sha512-B9NM7XpVfkyWqfOBI/UW0kVhGw7pJztsduch+1wkbYDi90mYK6/InFul3lG0hYko/VEcVMARVBJ5daFRc5aKCw== -lossless-json@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lossless-json/-/lossless-json-4.0.1.tgz#d45229e3abb213a0235812780ca894ea8c5b2c6b" - integrity sha512-l0L+ppmgPDnb+JGxNLndPtJZGNf6+ZmVaQzoxQm3u6TXmhdnsA+YtdVR8DjzZd/em58686CQhOFDPewfJ4l7MA== - loupe@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" @@ -15292,13 +14519,6 @@ loupe@^2.3.6: dependencies: get-func-name "^2.0.0" -loupe@^2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" - integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== - dependencies: - get-func-name "^2.0.1" - lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" @@ -15423,13 +14643,6 @@ magic-string@^0.30.3, magic-string@^0.30.4: dependencies: "@jridgewell/sourcemap-codec" "^1.4.15" -magic-string@^0.30.5: - version "0.30.10" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" - integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== - dependencies: - "@jridgewell/sourcemap-codec" "^1.4.15" - make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -15437,7 +14650,7 @@ make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-dir@^1.0.0, make-dir@^1.3.0: +make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== @@ -15611,18 +14824,6 @@ memdown@1.4.1: ltgt "~2.2.0" safe-buffer "~5.1.1" -memdown@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" - integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== - dependencies: - abstract-leveldown "~6.2.1" - functional-red-black-tree "~1.0.1" - immediate "~3.2.3" - inherits "~2.0.1" - ltgt "~2.2.0" - safe-buffer "~5.2.0" - memory-pager@^1.0.2: version "1.5.0" resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" @@ -15726,11 +14927,6 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-fn@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" - integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -15810,13 +15006,6 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.4: - version "9.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" - integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== - dependencies: - brace-expansion "^2.0.1" - minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -15911,11 +15100,6 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -15951,16 +15135,6 @@ mlly@^1.1.0, mlly@^1.7.0: pkg-types "^1.1.0" ufo "^1.5.3" -mlly@^1.4.2: - version "1.7.1" - resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.1.tgz#e0336429bb0731b6a8e887b438cbdae522c8f32f" - integrity sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA== - dependencies: - acorn "^8.11.3" - pathe "^1.1.2" - pkg-types "^1.1.1" - ufo "^1.5.3" - modify-values@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" @@ -16204,11 +15378,6 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -neon-env@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/neon-env/-/neon-env-0.1.3.tgz#071e86fde3c698e9314f057d209e0b79ddab16e9" - integrity sha512-Zo+L6Nm19gJrjyfhxn/ZDm8eIIDzr75o64ZhijBau4LNuhLzjEAteRg3gchIvgaN8XTo5BxN6iTNP5clZQ0agA== - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -16257,7 +15426,7 @@ node-fetch@2.6.0, node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-f dependencies: whatwg-url "^5.0.0" -node-forge@^1.2.1, node-forge@^1.3.1: +node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== @@ -16343,11 +15512,6 @@ node-source-walk@^5.0.0: dependencies: "@babel/parser" "^7.0.0" -nodemailer@6.9.13: - version "6.9.13" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.13.tgz#5b292bf1e92645f4852ca872c56a6ba6c4a3d3d6" - integrity sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA== - nodemailer@6.9.9: version "6.9.9" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.9.tgz#4549bfbf710cc6addec5064dd0f19874d24248d9" @@ -16561,13 +15725,6 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npm-run-path@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" - integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== - dependencies: - path-key "^4.0.0" - npmlog@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" @@ -16850,13 +16007,6 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -onetime@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" - integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== - dependencies: - mimic-fn "^4.0.0" - only@~0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" @@ -17011,11 +16161,6 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== -p-cancelable@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== - p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -17071,13 +16216,6 @@ p-limit@^4.0.0: dependencies: yocto-queue "^1.0.0" -p-limit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" - integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== - dependencies: - yocto-queue "^1.0.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -17335,7 +16473,7 @@ passport-google-oauth20@2.x.x: dependencies: passport-oauth2 "1.x.x" -passport-google-oauth@2.0.0, passport-google-oauth@^2.0.0: +passport-google-oauth@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/passport-google-oauth/-/passport-google-oauth-2.0.0.tgz#f6eb4bc96dd6c16ec0ecfdf4e05ec48ca54d4dae" integrity sha512-JKxZpBx6wBQXX1/a1s7VmdBgwOugohH+IxCy84aPTZNq/iIPX6u7Mqov1zY7MKRz3niFPol0KJz8zPLBoHKtYA== @@ -17350,14 +16488,6 @@ passport-local@1.0.0: dependencies: passport-strategy "1.x.x" -passport-microsoft@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/passport-microsoft/-/passport-microsoft-1.0.0.tgz#78954cf3201fdce61beeb6587a3b158f8e9db86c" - integrity sha512-L1JHeCbSObSZZXiG7jU2KoKie6nzZLwGt38HXz1GasKrsCQdOnf5kH8ltV4BWNUfBL2Pt1csWn1iuBSerprrcg== - dependencies: - passport-oauth2 "1.6.1" - pkginfo "0.4.x" - passport-oauth1@1.x.x: version "1.3.0" resolved "https://registry.yarnpkg.com/passport-oauth1/-/passport-oauth1-1.3.0.tgz#5d57f1415c8e28e46b461a12ec1b492934f7c354" @@ -17372,17 +16502,6 @@ passport-oauth2-refresh@^2.1.0: resolved "https://registry.yarnpkg.com/passport-oauth2-refresh/-/passport-oauth2-refresh-2.1.0.tgz#c31cd133826383f5539d16ad8ab4f35ca73ce4a4" integrity sha512-4ML7ooCESCqiTgdDBzNUFTBcPR8zQq9iM6eppEUGMMvLdsjqRL93jKwWm4Az3OJcI+Q2eIVyI8sVRcPFvxcF/A== -passport-oauth2@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.6.1.tgz#c5aee8f849ce8bd436c7f81d904a3cd1666f181b" - integrity sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ== - dependencies: - base64url "3.x.x" - oauth "0.9.x" - passport-strategy "1.x.x" - uid2 "0.0.x" - utils-merge "1.x.x" - passport-oauth2@1.x.x: version "1.7.0" resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.7.0.tgz#5c4766c8531ac45ffe9ec2c09de9809e2c841fc4" @@ -17433,11 +16552,6 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== - path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -17459,14 +16573,6 @@ path-scurry@^1.10.2, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-to-regexp@1.x: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -17501,7 +16607,7 @@ pathe@^1.1.0: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== -pathe@^1.1.1, pathe@^1.1.2: +pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== @@ -17773,7 +16879,7 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-types@^1.0.3, pkg-types@^1.1.0, pkg-types@^1.1.1: +pkg-types@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.1.1.tgz#07b626880749beb607b0c817af63aac1845a73f2" integrity sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ== @@ -17782,11 +16888,6 @@ pkg-types@^1.0.3, pkg-types@^1.1.0, pkg-types@^1.1.1: mlly "^1.7.0" pathe "^1.1.2" -pkginfo@0.4.x: - version "0.4.1" - resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" - integrity sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ== - pluralize@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" @@ -18099,15 +17200,6 @@ postcss@^8.1.7, postcss@^8.2.9, postcss@^8.3.11, postcss@^8.4.12, postcss@^8.4.2 picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.4.38: - version "8.4.38" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" - integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== - dependencies: - nanoid "^3.3.7" - picocolors "^1.0.0" - source-map-js "^1.2.0" - postgres-array@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" @@ -18138,14 +17230,6 @@ posthog-js@^1.118.0: fflate "^0.4.8" preact "^10.19.3" -posthog-js@^1.13.4: - version "1.139.1" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.139.1.tgz#25db31d94ce218357a2be43be4a55cfbb940f295" - integrity sha512-+JDu2S7z6sh9Q5kj0oh/W8PZJMQ1gSigWi7gbY4NwwCq2M3t0wNFjxlfHbAo1GncRWDxen+IC+3J7oJ8TJGnkA== - dependencies: - fflate "^0.4.8" - preact "^10.19.3" - posthog-node@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" @@ -18552,14 +17636,6 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== -promise-retry@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d" - integrity sha512-StEy2osPr28o17bIW776GtwO6+Q+M9zPiZkYfosciUUMYqjhU/ffwRAH0zN2+uvGyUsn8/YICIHRzLbPacpZGw== - dependencies: - err-code "^1.0.0" - retry "^0.10.0" - promise-retry@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" @@ -18772,7 +17848,7 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@^6.10.3, qs@^6.11.0, qs@^6.4.0: +qs@^6.11.0, qs@^6.4.0: version "6.12.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== @@ -18833,11 +17909,6 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - quote-unquote@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/quote-unquote/-/quote-unquote-1.0.0.tgz#67a9a77148effeaf81a4d428404a710baaac8a0b" @@ -18890,11 +17961,6 @@ rc@1.2.8, rc@^1.2.7, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -reachdown@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/reachdown/-/reachdown-1.1.0.tgz#c3b85b459dbd0fe2c79782233a0a38e66a9b5454" - integrity sha512-6LsdRe4cZyOjw4NnvbhUd/rGG7WQ9HMopPr+kyL018Uci4kijtxcGR5kVb5Ln13k4PEE+fEFQbjfOvNw7cnXmA== - react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -19133,11 +18199,6 @@ redlock@4.2.0: dependencies: bluebird "^3.7.2" -reflect-metadata@^0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" - integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== - regenerate-unicode-properties@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" @@ -19297,11 +18358,6 @@ requizzle@^0.2.3: dependencies: lodash "^4.17.21" -resolve-alpn@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" - integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -19353,13 +18409,6 @@ responselike@1.0.2, responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -responselike@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" - integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== - dependencies: - lowercase-keys "^2.0.0" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -19381,11 +18430,6 @@ retry@0.13.1, retry@^0.13.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" integrity "sha1-GFsVh6z2eRnWOzVzSeA1N7JIRlg= sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" -retry@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" - integrity sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ== - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -19415,13 +18459,6 @@ rimraf@^4.4.1: dependencies: glob "^9.2.0" -rimraf@^5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.7.tgz#27bddf202e7d89cb2e0381656380d1734a854a74" - integrity sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg== - dependencies: - glob "^10.3.7" - ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -19584,31 +18621,6 @@ rollup@^3.27.1: optionalDependencies: fsevents "~2.3.2" -rollup@^4.13.0: - version "4.18.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.18.0.tgz#497f60f0c5308e4602cf41136339fbf87d5f5dda" - integrity sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg== - dependencies: - "@types/estree" "1.0.5" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.18.0" - "@rollup/rollup-android-arm64" "4.18.0" - "@rollup/rollup-darwin-arm64" "4.18.0" - "@rollup/rollup-darwin-x64" "4.18.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.18.0" - "@rollup/rollup-linux-arm-musleabihf" "4.18.0" - "@rollup/rollup-linux-arm64-gnu" "4.18.0" - "@rollup/rollup-linux-arm64-musl" "4.18.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.18.0" - "@rollup/rollup-linux-riscv64-gnu" "4.18.0" - "@rollup/rollup-linux-s390x-gnu" "4.18.0" - "@rollup/rollup-linux-x64-gnu" "4.18.0" - "@rollup/rollup-linux-x64-musl" "4.18.0" - "@rollup/rollup-win32-arm64-msvc" "4.18.0" - "@rollup/rollup-win32-ia32-msvc" "4.18.0" - "@rollup/rollup-win32-x64-msvc" "4.18.0" - fsevents "~2.3.2" - rollup@^4.9.4: version "4.17.2" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.17.2.tgz#26d1785d0144122277fdb20ab3a24729ae68301f" @@ -19870,7 +18882,7 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -server-destroy@1.0.1, server-destroy@^1.0.1: +server-destroy@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" integrity sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ== @@ -19901,11 +18913,6 @@ set-function-name@^2.0.0, set-function-name@^2.0.1: functions-have-names "^1.2.3" has-property-descriptors "^1.0.0" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -20004,11 +19011,6 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967" integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q== -signal-exit@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - sigstore@^1.3.0, sigstore@^1.4.0: version "1.6.0" resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-1.6.0.tgz#887a4007c6ee83f3ef3fd844be1a0840e849c301" @@ -20217,11 +19219,6 @@ source-map-js@^1.0.1, source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-js@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -20412,7 +19409,7 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -stack-trace@0.0.10, stack-trace@0.0.x: +stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== @@ -20444,7 +19441,7 @@ statuses@2.0.1, statuses@^2.0.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -std-env@^3.3.1, std-env@^3.5.0: +std-env@^3.3.1: version "3.7.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== @@ -20528,7 +19525,16 @@ string-similarity@^4.0.4: resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -20618,7 +19624,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -20632,6 +19638,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" @@ -20673,11 +19686,6 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-final-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" - integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -20702,13 +19710,6 @@ strip-literal@^1.0.0: dependencies: acorn "^8.10.0" -strip-literal@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-2.1.0.tgz#6d82ade5e2e74f5c7e8739b6c84692bd65f0bd2a" - integrity sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw== - dependencies: - js-tokens "^9.0.0" - strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" @@ -20716,14 +19717,6 @@ strip-outer@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -stripe@9.16.0: - version "9.16.0" - resolved "https://registry.yarnpkg.com/stripe/-/stripe-9.16.0.tgz#94c24549c91fced457b9e3259e8a1a1bdb6dbd0e" - integrity sha512-Dn8K+jSoQcXjxCobRI4HXUdHjOXsiF/KszK49fJnkbeCFjZ3EZxLG2JiM/CX+Hcq27NBDtv/Sxhvy+HhTmvyaQ== - dependencies: - "@types/node" ">=8.1.0" - qs "^6.10.3" - striptags@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.2.0.tgz#cc74a137db2de8b0b9a370006334161f7dd67052" @@ -20792,18 +19785,6 @@ sublevel-pouchdb@7.2.2: ltgt "2.2.1" readable-stream "1.1.14" -subleveldown@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/subleveldown/-/subleveldown-5.0.1.tgz#aa2b4e4698a48d9a86856b2c4df1b6bce2d2ce53" - integrity sha512-cVqd/URpp7si1HWu5YqQ3vqQkjuolAwHypY1B4itPlS71/lsf6TQPZ2Y0ijT22EYVkvH5ove9JFJf4u7VGPuZw== - dependencies: - abstract-leveldown "^6.3.0" - encoding-down "^6.2.0" - inherits "^2.0.3" - level-option-wrap "^1.1.0" - levelup "^4.4.0" - reachdown "^1.1.0" - superagent@^8.0.5: version "8.1.2" resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" @@ -21102,7 +20083,7 @@ tar@6.1.15: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.1.11, tar@^6.1.2: +tar@6.2.1, tar@^6.1.11, tar@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -21282,11 +20263,6 @@ timekeeper@2.2.0: resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== -timekeeper@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.3.1.tgz#2deb6e0b95d93625fda84c18d47f84a99e4eba01" - integrity sha512-LeQRS7/4JcC0PgdSFnfUiStQEdiuySlCj/5SJ18D+T1n9BoY7PxKFfCwLulpHXoLUFr67HxBddQdEX47lDGx1g== - timm@^1.6.1: version "1.7.1" resolved "https://registry.yarnpkg.com/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f" @@ -21305,7 +20281,7 @@ tiny-queue@^0.2.0: resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" integrity sha512-EijGsv7kzd9I9g0ByCl6h42BWNGUZrlCSejfrb3AKeHC33SGbASu1VDf5O3rRiiUOhAC9CHdZxFPbZu0HmR70A== -tinybench@^2.3.1, tinybench@^2.5.1: +tinybench@^2.3.1: version "2.8.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.8.0.tgz#30e19ae3a27508ee18273ffed9ac7018949acd7b" integrity sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw== @@ -21320,21 +20296,11 @@ tinypool@^0.4.0: resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.4.0.tgz#3cf3ebd066717f9f837e8d7d31af3c127fdb5446" integrity sha512-2ksntHOKf893wSAH4z/+JbPpi92esw8Gn9N2deXX+B0EO92hexAVI9GIZZPx7P5aYo5KULfeOSt3kMOmSOy6uA== -tinypool@^0.8.3: - version "0.8.4" - resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.4.tgz#e217fe1270d941b39e98c625dcecebb1408c9aa8" - integrity sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ== - tinyspy@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-1.1.1.tgz#0cb91d5157892af38cb2d217f5c7e8507a5bf092" integrity sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g== -tinyspy@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.1.tgz#117b2342f1f38a0dbdcc73a50a454883adf861d1" - integrity sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A== - tlhunter-sorted-set@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/tlhunter-sorted-set/-/tlhunter-sorted-set-0.1.0.tgz#1c3eae28c0fa4dff97e9501d2e3c204b86406f4b" @@ -21546,7 +20512,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tsconfig-paths@^4.1.2, tsconfig-paths@^4.2.0: +tsconfig-paths@^4.1.2: version "4.2.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== @@ -21704,11 +20670,6 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typed-duration@^1.0.12: - version "1.0.13" - resolved "https://registry.yarnpkg.com/typed-duration/-/typed-duration-1.0.13.tgz#a40f9ba563b6e20674cac491e15ecbf6811d85a7" - integrity sha512-HLwA+hNq/2eXe03isJSfa7YJt6NikplBGdNKvlhyuR6WL5iZi2uXJIZv1SSOMEIukCZbeQ8QwIcQ801S0/Qulw== - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -21950,7 +20911,7 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" -update-dotenv@1.1.1, update-dotenv@^1.1.1: +update-dotenv@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/update-dotenv/-/update-dotenv-1.1.1.tgz#17146f302f216c3c92419d5a327a45be910050ca" integrity sha512-3cIC18In/t0X/yH793c00qqxcKD8jVCgNOPif/fGQkFpYMGecM9YAc+kaAKXuZsM2dE9I9wFI7KvAuNX22SGMQ== @@ -21994,7 +20955,7 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3, url-parse@^1.5.3: +url-parse@^1.5.3: version "1.5.10" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== @@ -22064,11 +21025,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -22151,17 +21107,6 @@ vite-node@0.29.8: picocolors "^1.0.0" vite "^3.0.0 || ^4.0.0" -vite-node@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.6.0.tgz#2c7e61129bfecc759478fa592754fd9704aaba7f" - integrity sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw== - dependencies: - cac "^6.7.14" - debug "^4.3.4" - pathe "^1.1.1" - picocolors "^1.0.0" - vite "^5.0.0" - vite-plugin-static-copy@^0.17.0: version "0.17.0" resolved "https://registry.yarnpkg.com/vite-plugin-static-copy/-/vite-plugin-static-copy-0.17.0.tgz#e45527da186c4a3818d09635797b6fc7cc9e035f" @@ -22194,17 +21139,6 @@ vite@^4.5.0: optionalDependencies: fsevents "~2.3.2" -vite@^5.0.0: - version "5.2.13" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.13.tgz#945ababcbe3d837ae2479c29f661cd20bc5e1a80" - integrity sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A== - dependencies: - esbuild "^0.20.1" - postcss "^8.4.38" - rollup "^4.13.0" - optionalDependencies: - fsevents "~2.3.3" - vitefu@^0.2.2: version "0.2.5" resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" @@ -22240,32 +21174,6 @@ vitest@^0.29.2: vite-node "0.29.8" why-is-node-running "^2.2.2" -vitest@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.6.0.tgz#9d5ad4752a3c451be919e412c597126cffb9892f" - integrity sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA== - dependencies: - "@vitest/expect" "1.6.0" - "@vitest/runner" "1.6.0" - "@vitest/snapshot" "1.6.0" - "@vitest/spy" "1.6.0" - "@vitest/utils" "1.6.0" - acorn-walk "^8.3.2" - chai "^4.3.10" - debug "^4.3.4" - execa "^8.0.1" - local-pkg "^0.5.0" - magic-string "^0.30.5" - pathe "^1.1.1" - picocolors "^1.0.0" - std-env "^3.5.0" - strip-literal "^2.0.0" - tinybench "^2.5.1" - tinypool "^0.8.3" - vite "^5.0.0" - vite-node "1.6.0" - why-is-node-running "^2.2.2" - vlq@^0.2.2: version "0.2.3" resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" @@ -22491,16 +21399,6 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -win-ca@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/win-ca/-/win-ca-3.5.1.tgz#2ef37ac24b0a1daa2714b4c5ef258c5242429e00" - integrity sha512-RNy9gpBS6cxWHjfbqwBA7odaHyT+YQNhtdpJZwYCFoxB/Dq22oeOZ9YCXMwjhLytKpo7JJMnKdJ/ve7N12zzfQ== - dependencies: - is-electron "^2.2.0" - make-dir "^1.3.0" - node-forge "^1.2.1" - split "^1.0.1" - winston-transport@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" @@ -22544,7 +21442,7 @@ worker-farm@1.7.0: dependencies: errno "~0.1.7" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -22562,6 +21460,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -22640,14 +21547,14 @@ write-stream@~0.4.3: readable-stream "~0.0.2" ws@^7.4.6: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== ws@~8.11.0: version "8.11.0" From 73013332aeb8a361692f116ca675c52d4e01d19c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 18 Jun 2024 11:16:21 +0100 Subject: [PATCH 046/116] Adding countRows to search validators. --- packages/server/src/api/controllers/row/views.ts | 3 ++- packages/server/src/api/routes/row.ts | 1 + packages/server/src/api/routes/utils/validators.ts | 1 + yarn.lock | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index 800cc29ceb..63ce12f0ab 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -60,7 +60,7 @@ export async function searchView( user: sdk.users.getUserContextBindings(ctx.user), }) - const searchOptions: RequiredKeys> & + const searchOptions: RequiredKeys & RequiredKeys> = { tableId: view.tableId, query: enrichedQuery, @@ -69,6 +69,7 @@ export async function searchView( limit: body.limit, bookmark: body.bookmark, paginate: body.paginate, + countRows: body.countRows, } const result = await sdk.rows.search(searchOptions) diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts index f1aa39a461..e443b2daeb 100644 --- a/packages/server/src/api/routes/row.ts +++ b/packages/server/src/api/routes/row.ts @@ -86,6 +86,7 @@ router router.post( "/api/v2/views/:viewId/search", + internalSearchValidator(), authorizedResource(PermissionType.VIEW, PermissionLevel.READ, "viewId"), rowController.views.searchView ) diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index a63b29fe5a..671ce95038 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -109,6 +109,7 @@ export function internalSearchValidator() { sortOrder: OPTIONAL_STRING, sortType: OPTIONAL_STRING, paginate: Joi.boolean(), + countRows: Joi.boolean(), bookmark: Joi.alternatives() .try(OPTIONAL_STRING, OPTIONAL_NUMBER) .optional(), diff --git a/yarn.lock b/yarn.lock index d71dd4da78..7f7def28b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21102,7 +21102,7 @@ tar@6.1.15: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.1.11, tar@^6.1.2: +tar@6.2.1, tar@^6.1.11, tar@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== From e88ffea1a4262579bfdb7e1fcf6f0e179542bcd4 Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Tue, 18 Jun 2024 11:18:05 +0100 Subject: [PATCH 047/116] Modal component (#13848) * wip * wip * wip * wip * wip * add note for illegalChildren reset behavior * on close working * wip * lint * wip * Fix potential remounting loop caused by spreading props and unnecessary component keying * theme * user prompt * dotted border for empty * PR Feedback * lint * fix modal background color * use bbui modal * lint * fix indicator and prevent closing modal in builder * pr feedback * pr feedback * fix fullscreen --------- Co-authored-by: deanhannigan Co-authored-by: Andrew Kingston --- packages/bbui/src/Modal/Modal.svelte | 2 +- .../actions/CloseModal.svelte | 8 + .../actions/OpenModal.svelte | 36 +++++ .../ButtonActionEditor/actions/index.js | 2 + .../controls/ButtonActionEditor/manifest.json | 12 ++ .../new/_components/NewComponentPanel.svelte | 9 +- .../new/_components/componentStructure.json | 2 +- .../builder/src/stores/builder/screens.js | 9 +- packages/client/manifest.json | 49 +++++- .../client/src/components/ClientApp.svelte | 10 +- .../client/src/components/app/Layout.svelte | 3 + .../client/src/components/app/Link.svelte | 9 +- .../client/src/components/app/Modal.svelte | 141 ++++++++++++++++++ .../app/blocks/form/FormBlock.svelte | 63 ++++---- .../app/blocks/form/InnerFormBlock.svelte | 16 +- packages/client/src/components/app/index.js | 1 + .../components/preview/IndicatorSet.svelte | 5 +- packages/client/src/sdk.js | 2 + packages/client/src/stores/index.js | 1 + packages/client/src/stores/modal.js | 32 ++++ packages/client/src/utils/buttonActions.js | 14 ++ packages/frontend-core/src/utils/utils.js | 3 + 22 files changed, 376 insertions(+), 53 deletions(-) create mode 100644 packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseModal.svelte create mode 100644 packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/OpenModal.svelte create mode 100644 packages/client/src/components/app/Modal.svelte create mode 100644 packages/client/src/stores/modal.js diff --git a/packages/bbui/src/Modal/Modal.svelte b/packages/bbui/src/Modal/Modal.svelte index 4656be69d1..dec1455d0c 100644 --- a/packages/bbui/src/Modal/Modal.svelte +++ b/packages/bbui/src/Modal/Modal.svelte @@ -162,6 +162,7 @@ max-height: 100%; } .modal-inner-wrapper { + padding: 40px; flex: 1 1 auto; display: flex; flex-direction: row; @@ -176,7 +177,6 @@ border: 2px solid var(--spectrum-global-color-gray-200); overflow: visible; max-height: none; - margin: 40px 0; transform: none; --spectrum-dialog-confirm-border-radius: var( --spectrum-global-dimension-size-100 diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseModal.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseModal.svelte new file mode 100644 index 0000000000..ed0ca2c72b --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/CloseModal.svelte @@ -0,0 +1,8 @@ +
This action doesn't require any settings.
+ + diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/OpenModal.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/OpenModal.svelte new file mode 100644 index 0000000000..8e61b8763f --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/OpenModal.svelte @@ -0,0 +1,36 @@ + + +
+ + + + + {/if}
diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte index 3b4a7c2d38..b6cdd663fd 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DuplicateRow.svelte @@ -83,6 +83,12 @@ placeholder="Are you sure you want to duplicate this row?" bind:value={parameters.confirmText} /> + + + + + + {/if} diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte index 54295d8b0f..43797f6369 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ExecuteQuery.svelte @@ -74,6 +74,18 @@ placeholder="Are you sure you want to execute this query?" bind:value={parameters.confirmText} /> + + + + {/if} {#if query?.parameters?.length > 0} diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte index d834e9aac9..aed2618778 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/SaveRow.svelte @@ -80,6 +80,12 @@ placeholder="Are you sure you want to save this row?" bind:value={parameters.confirmText} /> + + + + + + {/if} diff --git a/packages/client/src/components/overlay/ConfirmationDisplay.svelte b/packages/client/src/components/overlay/ConfirmationDisplay.svelte index e7a1046191..b96af502df 100644 --- a/packages/client/src/components/overlay/ConfirmationDisplay.svelte +++ b/packages/client/src/components/overlay/ConfirmationDisplay.svelte @@ -8,6 +8,8 @@ {$confirmationStore.text} diff --git a/packages/client/src/stores/confirmation.js b/packages/client/src/stores/confirmation.js index bb9a54386f..3fbf3d5deb 100644 --- a/packages/client/src/stores/confirmation.js +++ b/packages/client/src/stores/confirmation.js @@ -4,6 +4,8 @@ const initialState = { showConfirmation: false, title: null, text: null, + confirmButtonText: null, + cancelButtonText: null, onConfirm: null, onCancel: null, } @@ -11,11 +13,20 @@ const initialState = { const createConfirmationStore = () => { const store = writable(initialState) - const showConfirmation = (title, text, onConfirm, onCancel) => { + const showConfirmation = ( + title, + text, + onConfirm, + onCancel, + confirmButtonText, + cancelButtonText + ) => { store.set({ showConfirmation: true, title, text, + confirmButtonText, + cancelButtonText, onConfirm, onCancel, }) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index bd220b8e85..8f0cb575a7 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -522,6 +522,7 @@ const confirmTextMap = { ["Execute Query"]: "Are you sure you want to execute this query?", ["Trigger Automation"]: "Are you sure you want to trigger this automation?", ["Prompt User"]: "Are you sure you want to continue?", + ["Duplicate Row"]: "Are you sure you want to duplicate this row?", } /** @@ -582,6 +583,11 @@ export const enrichButtonActions = (actions, context) => { const defaultTitleText = action["##eventHandlerType"] const customTitleText = action.parameters?.customTitleText || defaultTitleText + const cancelButtonText = + action.parameters?.cancelButtonText || "Cancel" + const confirmButtonText = + action.parameters?.confirmButtonText || "Confirm" + confirmationStore.actions.showConfirmation( customTitleText, confirmText, @@ -612,7 +618,9 @@ export const enrichButtonActions = (actions, context) => { }, () => { resolve(false) - } + }, + confirmButtonText, + cancelButtonText ) }) } From a850ce6996ad14db04a9cf372c2ce0041a45abc0 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 21 Jun 2024 12:39:18 +0000 Subject: [PATCH 094/116] Bump version to 2.29.1 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 9d04750a0d..0efaf75283 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.29.0", + "version": "2.29.1", "npmClient": "yarn", "packages": [ "packages/*", From c9fecbaa776e69d2f25da4d21897fc12153e7f07 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 21 Jun 2024 15:12:02 +0100 Subject: [PATCH 095/116] Fix updating rows in external tables where the primary key is an autocolumn. --- .../api/controllers/row/ExternalRequest.ts | 8 +- .../src/api/controllers/table/external.ts | 8 +- .../server/src/api/controllers/table/utils.ts | 2 +- .../server/src/api/routes/tests/row.spec.ts | 87 +++++++++++++------ .../src/utilities/rowProcessor/index.ts | 1 + packages/server/src/utilities/schema.ts | 15 +++- 6 files changed, 89 insertions(+), 32 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 0ad55a78c3..c2f51decc2 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -289,7 +289,13 @@ export class ExternalRequest { manyRelationships: ManyRelationship[] = [] for (let [key, field] of Object.entries(table.schema)) { // if set already, or not set just skip it - if (row[key] === undefined || newRow[key] || !isEditableColumn(field)) { + if (row[key] === undefined || newRow[key]) { + continue + } + if ( + !(this.operation === Operation.BULK_UPSERT) && + !isEditableColumn(field) + ) { continue } // parse floats/numbers diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index db4343b405..15768d73bb 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -86,7 +86,11 @@ export async function bulkImport( const { rows, identifierFields } = ctx.request.body const schema = table.schema - if (identifierFields && !_.isEqual(identifierFields, table.primary)) { + if ( + identifierFields && + identifierFields.length > 0 && + !_.isEqual(identifierFields, table.primary) + ) { // This is becuse we make use of the ON CONFLICT functionality in SQL // databases, which only triggers when there's a conflict against a unique // index. The only unique index we can count on atm in Budibase is the @@ -102,7 +106,7 @@ export async function bulkImport( } const parsedRows = [] - for (const row of parse(rows, schema)) { + for (const row of parse(rows, table)) { const processed = await inputProcessing(ctx.user?._id, table, row, { noAutoRelationships: true, }) diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index a42cfc43c3..0e9a32b294 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -178,7 +178,7 @@ export async function handleDataImport( } const db = context.getAppDB() - const data = parse(importRows, schema) + const data = parse(importRows, table) let finalData: any = await importToRows(data, table, user) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 96eec95921..c5f9262861 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -315,13 +315,13 @@ describe.each([ // as quickly as possible. await Promise.all( sequence.map(async () => { - const attempts = 20 + const attempts = 30 for (let attempt = 0; attempt < attempts; attempt++) { try { await config.api.row.save(table._id!, {}) return } catch (e) { - await new Promise(r => setTimeout(r, Math.random() * 15)) + await new Promise(r => setTimeout(r, Math.random() * 50)) } } throw new Error(`Failed to create row after ${attempts} attempts`) @@ -919,32 +919,21 @@ describe.each([ await assertRowUsage(isInternal ? rowUsage - 1 : rowUsage) }) - it("Should ignore malformed/invalid delete requests", async () => { - const rowUsage = await getRowUsage() + it.each([{ not: "valid" }, { rows: 123 }, "invalid"])( + "Should ignore malformed/invalid delete request: %s", + async (request: any) => { + const rowUsage = await getRowUsage() - await config.api.row.delete(table._id!, { not: "valid" } as any, { - status: 400, - body: { - message: "Invalid delete rows request", - }, - }) + await config.api.row.delete(table._id!, request, { + status: 400, + body: { + message: "Invalid delete rows request", + }, + }) - await config.api.row.delete(table._id!, { rows: 123 } as any, { - status: 400, - body: { - message: "Invalid delete rows request", - }, - }) - - await config.api.row.delete(table._id!, "invalid" as any, { - status: 400, - body: { - message: "Invalid delete rows request", - }, - }) - - await assertRowUsage(rowUsage) - }) + await assertRowUsage(rowUsage) + } + ) }) describe("bulkImport", () => { @@ -1162,6 +1151,52 @@ describe.each([ expect(rows[2].name).toEqual("Row 3") expect(rows[2].description).toEqual("Row 3 description") }) + + // Upserting isn't yet supported in MSSQL, see: + // https://github.com/knex/knex/pull/6050 + !isMSSQL && + !isInternal && + it("should be able to update existing rows an autoID primary key", async () => { + const tableName = uuid.v4() + await client!.schema.createTable(tableName, table => { + table.increments("userId").primary() + table.string("name") + }) + + const resp = await config.api.datasource.fetchSchema({ + datasourceId: datasource!._id!, + }) + const table = resp.datasource.entities![tableName] + + const row1 = await config.api.row.save(table._id!, { + name: "Clare", + }) + + const row2 = await config.api.row.save(table._id!, { + name: "Jeff", + }) + + await config.api.row.bulkImport(table._id!, { + identifierFields: ["userId"], + rows: [ + { + userId: row1.userId, + name: "Clare updated", + }, + { + userId: row2.userId, + name: "Jeff updated", + }, + ], + }) + + const rows = await config.api.row.fetch(table._id!) + expect(rows.length).toEqual(2) + + rows.sort((a, b) => a.name.localeCompare(b.name)) + expect(rows[0].name).toEqual("Clare updated") + expect(rows[1].name).toEqual("Jeff updated") + }) }) describe("enrich", () => { diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 73176af6d8..97b5332dd4 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -9,6 +9,7 @@ import { Row, RowAttachment, Table, + TableSourceType, } from "@budibase/types" import { cloneDeep } from "lodash/fp" import { diff --git a/packages/server/src/utilities/schema.ts b/packages/server/src/utilities/schema.ts index 8e6cd34c7c..e473675633 100644 --- a/packages/server/src/utilities/schema.ts +++ b/packages/server/src/utilities/schema.ts @@ -4,6 +4,7 @@ import { TableSchema, FieldSchema, Row, + Table, } from "@budibase/types" import { ValidColumnNameRegex, helpers, utils } from "@budibase/shared-core" import { db } from "@budibase/backend-core" @@ -118,16 +119,26 @@ export function validate(rows: Rows, schema: TableSchema): ValidationResults { return results } -export function parse(rows: Rows, schema: TableSchema): Rows { +export function parse(rows: Rows, table: Table): Rows { return rows.map(row => { const parsedRow: Row = {} Object.entries(row).forEach(([columnName, columnData]) => { - if (!(columnName in schema) || schema[columnName]?.autocolumn) { + const schema = table.schema + if (!(columnName in schema)) { // Objects can be present in the row data but not in the schema, so make sure we don't proceed in such a case return } + if ( + schema[columnName].autocolumn && + !table.primary?.includes(columnName) + ) { + // Don't want the user specifying values for autocolumns unless they're updating + // a row through its primary key. + return + } + const columnSchema = schema[columnName] const { type: columnType } = columnSchema if (columnType === FieldType.NUMBER) { From d5481312d0e52ec73e569c8a44b724b499eb9c3a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 21 Jun 2024 15:31:34 +0100 Subject: [PATCH 096/116] Fix lint, add new update test. --- .../server/src/api/routes/tests/row.spec.ts | 33 ++++++++++++++++++- .../src/utilities/rowProcessor/index.ts | 1 - 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index c5f9262861..b6e3edf5ff 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -606,6 +606,35 @@ describe.each([ expect(res.name).toEqual("Updated Name") await assertRowUsage(rowUsage) }) + + !isInternal && + it("can update a row on an external table with a primary key", async () => { + const tableName = uuid.v4().substring(0, 10) + await client!.schema.createTable(tableName, table => { + table.increments("id").primary() + table.string("name") + }) + + const res = await config.api.datasource.fetchSchema({ + datasourceId: datasource!._id!, + }) + const table = res.datasource.entities![tableName] + + const row = await config.api.row.save(table._id!, { + id: 1, + name: "Row 1", + }) + + const updatedRow = await config.api.row.save(table._id!, { + _id: row._id!, + name: "Row 1 Updated", + }) + + expect(updatedRow.name).toEqual("Row 1 Updated") + + const rows = await config.api.row.fetch(table._id!) + expect(rows).toHaveLength(1) + }) }) describe("patch", () => { @@ -675,6 +704,7 @@ describe.each([ expect(event.oldRow.description).toEqual(beforeRow.description) expect(event.row.description).toEqual(beforeRow.description) }) + it("should throw an error when given improper types", async () => { const existing = await config.api.row.save(table._id!, {}) const rowUsage = await getRowUsage() @@ -766,7 +796,8 @@ describe.each([ }) !isInternal && - // TODO: SQL is having issues creating composite keys + // MSSQL needs a setting called IDENTITY_INSERT to be set to ON to allow writing + // to identity columns. This is not something Budibase does currently. providerType !== DatabaseName.SQL_SERVER && it("should support updating fields that are part of a composite key", async () => { const tableRequest = saveTableRequest({ diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 97b5332dd4..73176af6d8 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -9,7 +9,6 @@ import { Row, RowAttachment, Table, - TableSourceType, } from "@budibase/types" import { cloneDeep } from "lodash/fp" import { From e5c40c7ecdf00f654cbe133317a119f2ee1808d0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 21 Jun 2024 16:58:27 +0100 Subject: [PATCH 097/116] Moving some stuff around inside ExternalRequests to make it easier to access parts of the full context. --- .../api/controllers/row/ExternalRequest.ts | 238 ++++++++++-------- 1 file changed, 138 insertions(+), 100 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 619a1e9548..3209416544 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -72,92 +72,6 @@ export type ExternalRequestReturnType = ? number : { row: Row; table: Table } -function buildFilters( - id: string | undefined | string[], - filters: SearchFilters, - table: Table -) { - const primary = table.primary - // if passed in array need to copy for shifting etc - let idCopy: undefined | string | any[] = cloneDeep(id) - if (filters) { - // need to map over the filters and make sure the _id field isn't present - let prefix = 1 - for (let operator of Object.values(filters)) { - for (let field of Object.keys(operator || {})) { - if (dbCore.removeKeyNumbering(field) === "_id") { - if (primary) { - const parts = breakRowIdField(operator[field]) - for (let field of primary) { - operator[`${prefix}:${field}`] = parts.shift() - } - prefix++ - } - // make sure this field doesn't exist on any filter - delete operator[field] - } - } - } - } - // there is no id, just use the user provided filters - if (!idCopy || !table) { - return filters - } - // if used as URL parameter it will have been joined - if (!Array.isArray(idCopy)) { - idCopy = breakRowIdField(idCopy) - } - const equal: any = {} - if (primary && idCopy) { - for (let field of primary) { - // work through the ID and get the parts - equal[field] = idCopy.shift() - } - } - return { - equal, - } -} - -async function removeManyToManyRelationships( - rowId: string, - table: Table, - colName: string -) { - const tableId = table._id! - const filters = buildFilters(rowId, {}, table) - // safety check, if there are no filters on deletion bad things happen - if (Object.keys(filters).length !== 0) { - return getDatasourceAndQuery({ - endpoint: getEndpoint(tableId, Operation.DELETE), - body: { [colName]: null }, - filters, - meta: { - table, - }, - }) - } else { - return [] - } -} - -async function removeOneToManyRelationships(rowId: string, table: Table) { - const tableId = table._id! - const filters = buildFilters(rowId, {}, table) - // safety check, if there are no filters on deletion bad things happen - if (Object.keys(filters).length !== 0) { - return getDatasourceAndQuery({ - endpoint: getEndpoint(tableId, Operation.UPDATE), - filters, - meta: { - table, - }, - }) - } else { - return [] - } -} - /** * This function checks the incoming parameters to make sure all the inputs are * valid based on on the table schema. The main thing this is looking for is when a @@ -240,6 +154,7 @@ export class ExternalRequest { private readonly tableId: string private datasource?: Datasource private tables: { [key: string]: Table } = {} + private tableList: Table[] constructor(operation: T, tableId: string, datasource?: Datasource) { this.operation = operation @@ -248,6 +163,117 @@ export class ExternalRequest { if (datasource && datasource.entities) { this.tables = datasource.entities } + this.tableList = Object.values(this.tables) + } + + private prepareFilters( + id: string | undefined | string[], + filters: SearchFilters, + table: Table + ) { + const tables = this.tableList + // replace any relationship columns initially, table names and relationship column names are acceptable + const relationshipColumns = sdk.rows.filters.getRelationshipColumns(table) + filters = sdk.rows.filters.updateFilterKeys( + filters, + relationshipColumns + .map(({ name, definition }) => { + const { tableName } = breakExternalTableId(definition.tableId) + return { + original: name, + updated: tableName!, + } + }) + // don't update table names - include this for context incase a column would be replaced + .concat( + tables.map(table => { + const tableName = table.originalName || table.name + return { + original: tableName, + updated: tableName, + } + }) + ) + ) + const primary = table.primary + // if passed in array need to copy for shifting etc + let idCopy: undefined | string | any[] = cloneDeep(id) + if (filters) { + // need to map over the filters and make sure the _id field isn't present + let prefix = 1 + for (let operator of Object.values(filters)) { + for (let field of Object.keys(operator || {})) { + if (dbCore.removeKeyNumbering(field) === "_id") { + if (primary) { + const parts = breakRowIdField(operator[field]) + for (let field of primary) { + operator[`${prefix}:${field}`] = parts.shift() + } + prefix++ + } + // make sure this field doesn't exist on any filter + delete operator[field] + } + } + } + } + // there is no id, just use the user provided filters + if (!idCopy || !table) { + return filters + } + // if used as URL parameter it will have been joined + if (!Array.isArray(idCopy)) { + idCopy = breakRowIdField(idCopy) + } + const equal: any = {} + if (primary && idCopy) { + for (let field of primary) { + // work through the ID and get the parts + equal[field] = idCopy.shift() + } + } + return { + equal, + } + } + + private async removeManyToManyRelationships( + rowId: string, + table: Table, + colName: string + ) { + const tableId = table._id! + const filters = this.prepareFilters(rowId, {}, table) + // safety check, if there are no filters on deletion bad things happen + if (Object.keys(filters).length !== 0) { + return getDatasourceAndQuery({ + endpoint: getEndpoint(tableId, Operation.DELETE), + body: { [colName]: null }, + filters, + meta: { + table, + }, + }) + } else { + return [] + } + } + + private async removeOneToManyRelationships(rowId: string, table: Table) { + const tableId = table._id! + const filters = this.prepareFilters(rowId, {}, table) + // safety check, if there are no filters on deletion bad things happen + if (Object.keys(filters).length !== 0) { + return getDatasourceAndQuery({ + endpoint: getEndpoint(tableId, Operation.UPDATE), + filters, + meta: { + table, + }, + }) + } else { + return [] + } } getTable(tableId: string | undefined): Table | undefined { @@ -260,10 +286,22 @@ export class ExternalRequest { } } + // seeds the object with table and datasource information + async retrieveMetadata(datasourceId: string) { + if (!this.datasource) { + this.datasource = await sdk.datasources.get(datasourceId) + if (!this.datasource || !this.datasource.entities) { + throw "No tables found, fetch tables before query." + } + this.tables = this.datasource.entities + this.tableList = Object.values(this.tables) + } + } + async getRow(table: Table, rowId: string): Promise { const response = await getDatasourceAndQuery({ endpoint: getEndpoint(table._id!, Operation.READ), - filters: buildFilters(rowId, {}, table), + filters: this.prepareFilters(rowId, {}, table), meta: { table, }, @@ -514,7 +552,7 @@ export class ExternalRequest { endpoint: getEndpoint(tableId, operation), // if we're doing many relationships then we're writing, only one response body, - filters: buildFilters(id, {}, linkTable), + filters: this.prepareFilters(id, {}, linkTable), meta: { table: linkTable, }, @@ -538,8 +576,8 @@ export class ExternalRequest { for (let row of rows) { const rowId = generateIdForRow(row, table) const promise: Promise = isMany - ? removeManyToManyRelationships(rowId, table, colName) - : removeOneToManyRelationships(rowId, table) + ? this.removeManyToManyRelationships(rowId, table, colName) + : this.removeOneToManyRelationships(rowId, table) if (promise) { promises.push(promise) } @@ -562,12 +600,12 @@ export class ExternalRequest { rows.map(row => { const rowId = generateIdForRow(row, table) return isMany - ? removeManyToManyRelationships( + ? this.removeManyToManyRelationships( rowId, table, relationshipColumn.fieldName ) - : removeOneToManyRelationships(rowId, table) + : this.removeOneToManyRelationships(rowId, table) }) ) } @@ -580,14 +618,10 @@ export class ExternalRequest { throw "Unable to run without a table name" } if (!this.datasource) { - this.datasource = await sdk.datasources.get(datasourceId!) - if (!this.datasource || !this.datasource.entities) { - throw "No tables found, fetch tables before query." - } - this.tables = this.datasource.entities + await this.retrieveMetadata(datasourceId!) } const table = this.tables[tableName] - let isSql = isSQL(this.datasource) + let isSql = isSQL(this.datasource!) if (!table) { throw `Unable to process query, table "${tableName}" not defined.` } @@ -612,7 +646,7 @@ export class ExternalRequest { break } } - filters = buildFilters(id, filters || {}, table) + filters = this.prepareFilters(id, filters || {}, table) const relationships = buildExternalRelationships(table, this.tables) const incRelationships = @@ -660,7 +694,11 @@ export class ExternalRequest { body: row || rows, // pass an id filter into extra, purely for mysql/returning extra: { - idFilter: buildFilters(id || generateIdForRow(row, table), {}, table), + idFilter: this.prepareFilters( + id || generateIdForRow(row, table), + {}, + table + ), }, meta: { table, From 6812c2107696c05d35e87367088f3639f71c952a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 21 Jun 2024 16:58:40 +0100 Subject: [PATCH 098/116] Updating test cases. --- .../src/api/routes/tests/search.spec.ts | 71 +++++++++++-------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index cff966ab89..c92738479f 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -76,9 +76,9 @@ describe.each([ } }) - async function createTable(schema: TableSchema) { + async function createTable(schema: TableSchema, name?: string) { return await config.api.table.save( - tableForDatasource(datasource, { schema }) + tableForDatasource(datasource, { schema, name }) ) } @@ -1956,51 +1956,62 @@ describe.each([ // isn't available. !isInMemory && describe("relations", () => { - let otherTable: Table - let otherRows: Row[] + let productCategoryTable: Table, productCatRows: Row[] beforeAll(async () => { - otherTable = await createTable({ - one: { name: "one", type: FieldType.STRING }, - }) - table = await createTable({ - two: { name: "two", type: FieldType.STRING }, - other: { - type: FieldType.LINK, - relationshipType: RelationshipType.ONE_TO_MANY, - name: "other", - fieldName: "other", - tableId: otherTable._id!, - constraints: { - type: "array", + productCategoryTable = await createTable( + { + name: { name: "name", type: FieldType.STRING }, + }, + "productCategory" + ) + table = await createTable( + { + name: { name: "name", type: FieldType.STRING }, + productCat: { + type: FieldType.LINK, + relationshipType: RelationshipType.ONE_TO_MANY, + name: "productCat", + fieldName: "product", + tableId: productCategoryTable._id!, + constraints: { + type: "array", + }, }, }, - }) + "product" + ) - otherRows = await Promise.all([ - config.api.row.save(otherTable._id!, { one: "foo" }), - config.api.row.save(otherTable._id!, { one: "bar" }), + productCatRows = await Promise.all([ + config.api.row.save(productCategoryTable._id!, { name: "foo" }), + config.api.row.save(productCategoryTable._id!, { name: "bar" }), ]) await Promise.all([ config.api.row.save(table._id!, { - two: "foo", - other: [otherRows[0]._id], + name: "foo", + productCat: [productCatRows[0]._id], }), config.api.row.save(table._id!, { - two: "bar", - other: [otherRows[1]._id], + name: "bar", + productCat: [productCatRows[1]._id], }), ]) - - rows = await config.api.row.fetch(table._id!) }) - it("can search through relations", async () => { + it("should be able to filter by relationship using column name", async () => { await expectQuery({ - equal: { [`${otherTable.name}.one`]: "foo" }, + equal: { ["productCat.name"]: "foo" }, }).toContainExactly([ - { two: "foo", other: [{ _id: otherRows[0]._id }] }, + { name: "foo", productCat: [{ _id: productCatRows[0]._id }] }, + ]) + }) + + it("should be able to filter by relationship using table name", async () => { + await expectQuery({ + equal: { ["productCategory.name"]: "foo" }, + }).toContainExactly([ + { name: "foo", productCat: [{ _id: productCatRows[0]._id }] }, ]) }) }) From 28d0d627ce971e029018f2b77e9abaae2bdbccf3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 21 Jun 2024 17:00:12 +0100 Subject: [PATCH 099/116] Getting functions in place which make it easy to update pats of a filter list by their keys - getting this to work for SQS and external. --- packages/server/src/sdk/app/rows/index.ts | 2 + .../server/src/sdk/app/rows/search/filters.ts | 56 +++++++++++++++++ .../server/src/sdk/app/rows/search/sqs.ts | 60 ++++++++++--------- 3 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 packages/server/src/sdk/app/rows/search/filters.ts diff --git a/packages/server/src/sdk/app/rows/index.ts b/packages/server/src/sdk/app/rows/index.ts index c117941419..fb077509a9 100644 --- a/packages/server/src/sdk/app/rows/index.ts +++ b/packages/server/src/sdk/app/rows/index.ts @@ -3,12 +3,14 @@ import * as rows from "./rows" import * as search from "./search" import * as utils from "./utils" import * as external from "./external" +import * as filters from "./search/filters" import AliasTables from "./sqlAlias" export default { ...attachments, ...rows, ...search, + filters, utils, external, AliasTables, diff --git a/packages/server/src/sdk/app/rows/search/filters.ts b/packages/server/src/sdk/app/rows/search/filters.ts new file mode 100644 index 0000000000..3f8facc78e --- /dev/null +++ b/packages/server/src/sdk/app/rows/search/filters.ts @@ -0,0 +1,56 @@ +import { + FieldType, + RelationshipFieldMetadata, + SearchFilters, + Table, +} from "@budibase/types" + +export function getRelationshipColumns(table: Table): { + name: string + definition: RelationshipFieldMetadata +}[] { + return Object.entries(table.schema) + .filter(entry => entry[1].type === FieldType.LINK) + .map(entry => ({ + name: entry[0], + definition: entry[1] as RelationshipFieldMetadata, + })) +} + +export function getTableIDList( + tables: Table[] +): { name: string; id: string }[] { + return tables + .filter(table => table.originalName) + .map(table => ({ id: table._id!, name: table.originalName! })) +} + +export function updateFilterKeys( + filters: SearchFilters, + updates: { original: string; updated: string }[] +): SearchFilters { + // sort the updates by length first - this is necessary to avoid replacing sub-strings + updates = updates.sort((a, b) => b.original.length - a.original.length) + const makeFilterKeyRegex = (str: string) => + new RegExp(`^${str}.|:${str}.`, "g") + for (let filter of Object.values(filters)) { + if (typeof filter !== "object") { + continue + } + for (let [key, keyFilter] of Object.entries(filter)) { + if (keyFilter === "") { + delete filter[key] + } + const possibleKey = updates.find(({ original }) => + key.match(makeFilterKeyRegex(original)) + ) + if (possibleKey && possibleKey.original !== possibleKey.updated) { + // only replace the first, not replaceAll + filter[key.replace(possibleKey.original, possibleKey.updated)] = + filter[key] + delete filter[key] + } + } + } + return filters +} diff --git a/packages/server/src/sdk/app/rows/search/sqs.ts b/packages/server/src/sdk/app/rows/search/sqs.ts index bb1d62affc..174ecc0e38 100644 --- a/packages/server/src/sdk/app/rows/search/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/sqs.ts @@ -1,4 +1,5 @@ import { + Datasource, DocumentType, FieldType, Operation, @@ -12,7 +13,6 @@ import { SortType, SqlClient, Table, - Datasource, } from "@budibase/types" import { buildInternalRelationships, @@ -30,6 +30,11 @@ import AliasTables from "../sqlAlias" import { outputProcessing } from "../../../../utilities/rowProcessor" import pick from "lodash/pick" import { processRowCountResponse } from "../utils" +import { + updateFilterKeys, + getRelationshipColumns, + getTableIDList, +} from "./filters" const builder = new sql.Sql(SqlClient.SQL_LITE) @@ -60,34 +65,31 @@ function buildInternalFieldList( return fieldList } -function tableNameInFieldRegex(tableName: string) { - return new RegExp(`^${tableName}.|:${tableName}.`, "g") -} - -function cleanupFilters(filters: SearchFilters, tables: Table[]) { - for (let filter of Object.values(filters)) { - if (typeof filter !== "object") { - continue - } - for (let [key, keyFilter] of Object.entries(filter)) { - if (keyFilter === "") { - delete filter[key] - } - - // relationship, switch to table ID - const tableRelated = tables.find( - table => - table.originalName && - key.match(tableNameInFieldRegex(table.originalName)) +function cleanupFilters( + filters: SearchFilters, + table: Table, + allTables: Table[] +) { + // get a list of all relationship columns in the table for updating + const relationshipColumns = getRelationshipColumns(table) + // get table names to ID map for relationships + const tableNameToID = getTableIDList(allTables) + // all should be applied at once + filters = updateFilterKeys( + filters, + relationshipColumns + .map(({ name, definition }) => ({ + original: name, + updated: definition.tableId, + })) + .concat( + tableNameToID.map(({ name, id }) => ({ + original: name, + updated: id, + })) ) - if (tableRelated && tableRelated.originalName) { - // only replace the first, not replaceAll - filter[key.replace(tableRelated.originalName, tableRelated._id!)] = - filter[key] - delete filter[key] - } - } - } + ) + return filters } @@ -176,7 +178,7 @@ export async function search( operation: Operation.READ, }, filters: { - ...cleanupFilters(query, allTables), + ...cleanupFilters(query, table, allTables), documentType: DocumentType.ROW, }, table, From 337584f5b2d4f9d53c6c8ee2c22177ce54cb3c99 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 21 Jun 2024 17:51:02 +0100 Subject: [PATCH 100/116] Updating the regex to correctly find within the filter keys. --- packages/server/src/sdk/app/rows/search/filters.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/server/src/sdk/app/rows/search/filters.ts b/packages/server/src/sdk/app/rows/search/filters.ts index 3f8facc78e..32b8526697 100644 --- a/packages/server/src/sdk/app/rows/search/filters.ts +++ b/packages/server/src/sdk/app/rows/search/filters.ts @@ -29,10 +29,8 @@ export function updateFilterKeys( filters: SearchFilters, updates: { original: string; updated: string }[] ): SearchFilters { - // sort the updates by length first - this is necessary to avoid replacing sub-strings - updates = updates.sort((a, b) => b.original.length - a.original.length) const makeFilterKeyRegex = (str: string) => - new RegExp(`^${str}.|:${str}.`, "g") + new RegExp(`^${str}\\.|:${str}\\.`) for (let filter of Object.values(filters)) { if (typeof filter !== "object") { continue From fcf67f729743db26647a15bb7900c101afaf2f3c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 21 Jun 2024 19:29:30 +0100 Subject: [PATCH 101/116] Fixing an issue raised by Poirazis around empty relationships coming back as related to themselves. --- .../server/src/api/controllers/row/utils/basic.ts | 2 +- packages/server/src/api/routes/tests/search.spec.ts | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/row/utils/basic.ts b/packages/server/src/api/controllers/row/utils/basic.ts index afb98d0255..bca2494ac3 100644 --- a/packages/server/src/api/controllers/row/utils/basic.ts +++ b/packages/server/src/api/controllers/row/utils/basic.ts @@ -99,7 +99,7 @@ export function basicProcessing({ row, tableName: table._id!, fieldName: internalColumn, - isLinked: false, + isLinked, }) } } diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index c92738479f..b9c25b98aa 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1950,10 +1950,7 @@ describe.each([ }) }) - // This will never work for Lucene. !isLucene && - // It also can't work for in-memory searching because the related table name - // isn't available. !isInMemory && describe("relations", () => { let productCategoryTable: Table, productCatRows: Row[] @@ -1996,6 +1993,10 @@ describe.each([ name: "bar", productCat: [productCatRows[1]._id], }), + config.api.row.save(table._id!, { + name: "baz", + productCat: [], + }), ]) }) @@ -2014,6 +2015,12 @@ describe.each([ { name: "foo", productCat: [{ _id: productCatRows[0]._id }] }, ]) }) + + it("shouldn't return any relationship for last row", async () => { + await expectQuery({ + equal: { ["name"]: "baz" }, + }).toContainExactly([{ name: "baz", productCat: undefined }]) + }) }) // lucene can't count the total rows From 05ea231d203323b769474c6d3617ef2fef39e7eb Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Jun 2024 11:53:02 +0100 Subject: [PATCH 102/116] Adding back missing comments. --- packages/server/src/api/routes/tests/search.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index b9c25b98aa..8ff35e7232 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1950,8 +1950,10 @@ describe.each([ }) }) + // This will never work for Lucene. !isLucene && - !isInMemory && + // It also can't work for in-memory searching because the related table name + // isn't available. describe("relations", () => { let productCategoryTable: Table, productCatRows: Row[] From 965725d022b360fd8b5e1b80d6b269514ec7be23 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Jun 2024 12:43:26 +0100 Subject: [PATCH 103/116] First PR comments. --- .../api/controllers/row/ExternalRequest.ts | 25 ++++++------------- .../src/api/routes/tests/search.spec.ts | 1 + .../server/src/sdk/app/rows/search/filters.ts | 24 ++++++++++++------ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 3209416544..75d3da6b03 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -176,24 +176,13 @@ export class ExternalRequest { const relationshipColumns = sdk.rows.filters.getRelationshipColumns(table) filters = sdk.rows.filters.updateFilterKeys( filters, - relationshipColumns - .map(({ name, definition }) => { - const { tableName } = breakExternalTableId(definition.tableId) - return { - original: name, - updated: tableName!, - } - }) - // don't update table names - include this for context incase a column would be replaced - .concat( - tables.map(table => { - const tableName = table.originalName || table.name - return { - original: tableName, - updated: tableName, - } - }) - ) + relationshipColumns.map(({ name, definition }) => { + const { tableName } = breakExternalTableId(definition.tableId) + return { + original: name, + updated: tableName!, + } + }) ) const primary = table.primary // if passed in array need to copy for shifting etc diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 8ff35e7232..23c2ca819c 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1954,6 +1954,7 @@ describe.each([ !isLucene && // It also can't work for in-memory searching because the related table name // isn't available. + !isInMemory && describe("relations", () => { let productCategoryTable: Table, productCatRows: Row[] diff --git a/packages/server/src/sdk/app/rows/search/filters.ts b/packages/server/src/sdk/app/rows/search/filters.ts index 32b8526697..ccce0ab86a 100644 --- a/packages/server/src/sdk/app/rows/search/filters.ts +++ b/packages/server/src/sdk/app/rows/search/filters.ts @@ -4,24 +4,32 @@ import { SearchFilters, Table, } from "@budibase/types" +import { isPlainObject } from "lodash" export function getRelationshipColumns(table: Table): { name: string definition: RelationshipFieldMetadata }[] { - return Object.entries(table.schema) - .filter(entry => entry[1].type === FieldType.LINK) - .map(entry => ({ - name: entry[0], - definition: entry[1] as RelationshipFieldMetadata, - })) + // performing this with a for loop rather than an array filter improves + // type guarding, as no casts are required + const linkEntries: [string, RelationshipFieldMetadata][] = [] + for (let entry of Object.entries(table.schema)) { + if (entry[1].type === FieldType.LINK) { + const linkColumn: RelationshipFieldMetadata = entry[1] + linkEntries.push([entry[0], linkColumn]) + } + } + return linkEntries.map(entry => ({ + name: entry[0], + definition: entry[1], + })) } export function getTableIDList( tables: Table[] ): { name: string; id: string }[] { return tables - .filter(table => table.originalName) + .filter(table => table.originalName && table._id) .map(table => ({ id: table._id!, name: table.originalName! })) } @@ -32,7 +40,7 @@ export function updateFilterKeys( const makeFilterKeyRegex = (str: string) => new RegExp(`^${str}\\.|:${str}\\.`) for (let filter of Object.values(filters)) { - if (typeof filter !== "object") { + if (!isPlainObject(filter)) { continue } for (let [key, keyFilter] of Object.entries(filter)) { From 1402716f5cd9c37a5dc84830000822c083be4e4a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Jun 2024 13:10:30 +0100 Subject: [PATCH 104/116] Some type updates. --- packages/backend-core/src/sql/sqlTable.ts | 6 ++-- packages/backend-core/src/sql/utils.ts | 8 ++--- .../api/controllers/row/ExternalRequest.ts | 31 ++++++++----------- .../src/api/controllers/row/external.ts | 7 ++--- .../src/api/controllers/row/utils/sqlUtils.ts | 26 +++++++++------- .../src/api/controllers/table/external.ts | 4 +-- .../src/sdk/app/rows/search/external.ts | 10 +++--- packages/server/src/sdk/app/tables/getters.ts | 6 ++-- packages/server/src/sdk/app/views/external.ts | 24 +++++++------- 9 files changed, 60 insertions(+), 62 deletions(-) diff --git a/packages/backend-core/src/sql/sqlTable.ts b/packages/backend-core/src/sql/sqlTable.ts index 09f9908baa..bdc8a3dd69 100644 --- a/packages/backend-core/src/sql/sqlTable.ts +++ b/packages/backend-core/src/sql/sqlTable.ts @@ -109,8 +109,10 @@ function generateSchema( const { tableName } = breakExternalTableId(column.tableId) // @ts-ignore const relatedTable = tables[tableName] - if (!relatedTable) { - throw new Error("Referenced table doesn't exist") + if (!relatedTable || !relatedTable.primary) { + throw new Error( + "Referenced table doesn't exist or has no primary keys" + ) } const relatedPrimary = relatedTable.primary[0] const externalType = relatedTable.schema[relatedPrimary].externalType diff --git a/packages/backend-core/src/sql/utils.ts b/packages/backend-core/src/sql/utils.ts index 2d9b289417..45ab510948 100644 --- a/packages/backend-core/src/sql/utils.ts +++ b/packages/backend-core/src/sql/utils.ts @@ -55,10 +55,7 @@ export function buildExternalTableId(datasourceId: string, tableName: string) { return `${datasourceId}${DOUBLE_SEPARATOR}${tableName}` } -export function breakExternalTableId(tableId: string | undefined) { - if (!tableId) { - return {} - } +export function breakExternalTableId(tableId: string) { const parts = tableId.split(DOUBLE_SEPARATOR) let datasourceId = parts.shift() // if they need joined @@ -67,6 +64,9 @@ export function breakExternalTableId(tableId: string | undefined) { if (tableName.includes(ENCODED_SPACE)) { tableName = decodeURIComponent(tableName) } + if (!datasourceId || !tableName) { + throw new Error("Unable to get datasource/table name from table ID") + } return { datasourceId, tableName } } diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 75d3da6b03..7cbe023fd2 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -126,8 +126,8 @@ function getEndpoint(tableId: string | undefined, operation: string) { } const { datasourceId, tableName } = breakExternalTableId(tableId) return { - datasourceId: datasourceId!, - entityId: tableName!, + datasourceId: datasourceId, + entityId: tableName, operation: operation as Operation, } } @@ -180,7 +180,7 @@ export class ExternalRequest { const { tableName } = breakExternalTableId(definition.tableId) return { original: name, - updated: tableName!, + updated: tableName, } }) ) @@ -267,12 +267,10 @@ export class ExternalRequest { getTable(tableId: string | undefined): Table | undefined { if (!tableId) { - throw "Table ID is unknown, cannot find table" + throw new Error("Table ID is unknown, cannot find table") } const { tableName } = breakExternalTableId(tableId) - if (tableName) { - return this.tables[tableName] - } + return this.tables[tableName] } // seeds the object with table and datasource information @@ -323,9 +321,7 @@ export class ExternalRequest { if (field.type === FieldType.NUMBER && !isNaN(parseFloat(row[key]))) { newRow[key] = parseFloat(row[key]) } else if (field.type === FieldType.LINK) { - const { tableName: linkTableName } = breakExternalTableId( - field?.tableId - ) + const { tableName: linkTableName } = breakExternalTableId(field.tableId) // table has to exist for many to many if (!linkTableName || !this.tables[linkTableName]) { continue @@ -406,9 +402,6 @@ export class ExternalRequest { [key: string]: { rows: Row[]; isMany: boolean; tableId: string } } = {} const { tableName } = breakExternalTableId(tableId) - if (!tableName) { - return related - } const table = this.tables[tableName] // @ts-ignore const primaryKey = table.primary[0] @@ -602,17 +595,19 @@ export class ExternalRequest { async run(config: RunConfig): Promise> { const { operation, tableId } = this - let { datasourceId, tableName } = breakExternalTableId(tableId) - if (!tableName) { - throw "Unable to run without a table name" + if (!tableId) { + throw new Error("Unable to run without a table ID") } + let { datasourceId, tableName } = breakExternalTableId(tableId) if (!this.datasource) { - await this.retrieveMetadata(datasourceId!) + await this.retrieveMetadata(datasourceId) } const table = this.tables[tableName] let isSql = isSQL(this.datasource!) if (!table) { - throw `Unable to process query, table "${tableName}" not defined.` + throw new Error( + `Unable to process query, table "${tableName}" not defined.` + ) } // look for specific components of config which may not be considered acceptable let { id, row, filters, sort, paginate, rows } = cleanupConfig( diff --git a/packages/server/src/api/controllers/row/external.ts b/packages/server/src/api/controllers/row/external.ts index 5b12b5c207..126b11d0c1 100644 --- a/packages/server/src/api/controllers/row/external.ts +++ b/packages/server/src/api/controllers/row/external.ts @@ -136,10 +136,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) { const id = ctx.params.rowId const tableId = utils.getTableId(ctx) const { datasourceId, tableName } = breakExternalTableId(tableId) - const datasource: Datasource = await sdk.datasources.get(datasourceId!) - if (!tableName) { - ctx.throw(400, "Unable to find table.") - } + const datasource: Datasource = await sdk.datasources.get(datasourceId) if (!datasource || !datasource.entities) { ctx.throw(400, "Datasource has not been configured for plus API.") } @@ -163,7 +160,7 @@ export async function fetchEnrichedRow(ctx: UserCtx) { } const links = row[fieldName] const linkedTableId = field.tableId - const linkedTableName = breakExternalTableId(linkedTableId).tableName! + const linkedTableName = breakExternalTableId(linkedTableId).tableName const linkedTable = tables[linkedTableName] // don't support composite keys right now const linkedIds = links.map((link: Row) => breakRowIdField(link._id!)[0]) diff --git a/packages/server/src/api/controllers/row/utils/sqlUtils.ts b/packages/server/src/api/controllers/row/utils/sqlUtils.ts index 6f7bdc7335..767916616c 100644 --- a/packages/server/src/api/controllers/row/utils/sqlUtils.ts +++ b/packages/server/src/api/controllers/row/utils/sqlUtils.ts @@ -2,6 +2,8 @@ import { DatasourcePlusQueryResponse, DSPlusOperation, FieldType, + isManyToOne, + isOneToMany, ManyToManyRelationshipFieldMetadata, RelationshipFieldMetadata, RelationshipsJson, @@ -93,12 +95,12 @@ export function buildExternalRelationships( ): RelationshipsJson[] { const relationships = [] for (let [fieldName, field] of Object.entries(table.schema)) { - if (field.type !== FieldType.LINK) { + if (field.type !== FieldType.LINK || !field.tableId) { continue } const { tableName: linkTableName } = breakExternalTableId(field.tableId) // no table to link to, this is not a valid relationships - if (!linkTableName || !tables[linkTableName]) { + if (!tables[linkTableName]) { continue } const linkTable = tables[linkTableName] @@ -110,7 +112,7 @@ export function buildExternalRelationships( // need to specify where to put this back into column: fieldName, } - if (isManyToMany(field)) { + if (isManyToMany(field) && field.through) { const { tableName: throughTableName } = breakExternalTableId( field.through ) @@ -120,7 +122,7 @@ export function buildExternalRelationships( definition.to = field.throughFrom || linkTable.primary[0] definition.fromPrimary = table.primary[0] definition.toPrimary = linkTable.primary[0] - } else { + } else if (isManyToOne(field) || isOneToMany(field)) { // if no foreign key specified then use the name of the field in other table definition.from = field.foreignKey || table.primary[0] definition.to = field.fieldName @@ -180,16 +182,18 @@ export function buildSqlFieldList( } let fields = extractRealFields(table) for (let field of Object.values(table.schema)) { - if (field.type !== FieldType.LINK || !opts?.relationships) { + if ( + field.type !== FieldType.LINK || + !opts?.relationships || + !field.tableId + ) { continue } const { tableName: linkTableName } = breakExternalTableId(field.tableId) - if (linkTableName) { - const linkTable = tables[linkTableName] - if (linkTable) { - const linkedFields = extractRealFields(linkTable, fields) - fields = fields.concat(linkedFields) - } + const linkTable = tables[linkTableName] + if (linkTable) { + const linkedFields = extractRealFields(linkTable, fields) + fields = fields.concat(linkedFields) } } return fields diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index f1b186c233..fabc4d195a 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -18,8 +18,8 @@ import { builderSocket } from "../../../websockets" import { inputProcessing } from "../../../utilities/rowProcessor" function getDatasourceId(table: Table) { - if (!table) { - throw "No table supplied" + if (!table || !table._id) { + throw new Error("No table/table ID supplied") } if (table.sourceId) { return table.sourceId diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index 9fc3487f62..93c46d8cc3 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -145,6 +145,10 @@ export async function exportRows( delimiter, customHeaders, } = options + + if (!tableId) { + throw new HTTPError("No table ID for search provided.", 400) + } const { datasourceId, tableName } = breakExternalTableId(tableId) let requestQuery: SearchFilters = {} @@ -167,7 +171,7 @@ export async function exportRows( requestQuery = query || {} } - const datasource = await sdk.datasources.get(datasourceId!) + const datasource = await sdk.datasources.get(datasourceId) const table = await sdk.tables.getTable(tableId) if (!datasource || !datasource.entities) { throw new HTTPError("Datasource has not been configured for plus API.", 400) @@ -180,10 +184,6 @@ export async function exportRows( let rows: Row[] = [] let headers - if (!tableName) { - throw new HTTPError("Could not find table name.", 400) - } - // Filter data to only specified columns if required if (columns && columns.length) { for (let i = 0; i < result.rows.length; i++) { diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 355493579d..738e57eff8 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -90,10 +90,10 @@ export async function getExternalTable( export async function getTable(tableId: string): Promise { const db = context.getAppDB() let output: Table - if (isExternalTableID(tableId)) { + if (tableId && isExternalTableID(tableId)) { let { datasourceId, tableName } = breakExternalTableId(tableId) - const datasource = await datasources.get(datasourceId!) - const table = await getExternalTable(datasourceId!, tableName!) + const datasource = await datasources.get(datasourceId) + const table = await getExternalTable(datasourceId, tableName) output = { ...table, sql: isSQL(datasource) } } else { output = await db.get
(tableId) diff --git a/packages/server/src/sdk/app/views/external.ts b/packages/server/src/sdk/app/views/external.ts index 0f96bcc061..2b3e271597 100644 --- a/packages/server/src/sdk/app/views/external.ts +++ b/packages/server/src/sdk/app/views/external.ts @@ -10,9 +10,9 @@ export async function get(viewId: string): Promise { const { tableId } = utils.extractViewInfoFromID(viewId) const { datasourceId, tableName } = breakExternalTableId(tableId) - const ds = await sdk.datasources.get(datasourceId!) + const ds = await sdk.datasources.get(datasourceId) - const table = ds.entities![tableName!] + const table = ds.entities![tableName] const views = Object.values(table.views!).filter(isV2) const found = views.find(v => v.id === viewId) if (!found) { @@ -25,9 +25,9 @@ export async function getEnriched(viewId: string): Promise { const { tableId } = utils.extractViewInfoFromID(viewId) const { datasourceId, tableName } = breakExternalTableId(tableId) - const ds = await sdk.datasources.get(datasourceId!) + const ds = await sdk.datasources.get(datasourceId) - const table = ds.entities![tableName!] + const table = ds.entities![tableName] const views = Object.values(table.views!).filter(isV2) const found = views.find(v => v.id === viewId) if (!found) { @@ -49,9 +49,9 @@ export async function create( const db = context.getAppDB() const { datasourceId, tableName } = breakExternalTableId(tableId) - const ds = await sdk.datasources.get(datasourceId!) - ds.entities![tableName!].views ??= {} - ds.entities![tableName!].views![view.name] = view + const ds = await sdk.datasources.get(datasourceId) + ds.entities![tableName].views ??= {} + ds.entities![tableName].views![view.name] = view await db.put(ds) return view } @@ -60,9 +60,9 @@ export async function update(tableId: string, view: ViewV2): Promise { const db = context.getAppDB() const { datasourceId, tableName } = breakExternalTableId(tableId) - const ds = await sdk.datasources.get(datasourceId!) - ds.entities![tableName!].views ??= {} - const views = ds.entities![tableName!].views! + const ds = await sdk.datasources.get(datasourceId) + ds.entities![tableName].views ??= {} + const views = ds.entities![tableName].views! const existingView = Object.values(views).find( v => isV2(v) && v.id === view.id @@ -87,9 +87,9 @@ export async function remove(viewId: string): Promise { } const { datasourceId, tableName } = breakExternalTableId(view.tableId) - const ds = await sdk.datasources.get(datasourceId!) + const ds = await sdk.datasources.get(datasourceId) - delete ds.entities![tableName!].views![view?.name] + delete ds.entities![tableName].views![view?.name] await db.put(ds) return view } From 75cee3c4fda301ef9fe6a1b3b5953270c34b48e8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Jun 2024 13:28:13 +0100 Subject: [PATCH 105/116] Quick type improvement. --- .../src/api/controllers/row/ExternalRequest.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 7cbe023fd2..1ce8d29e5f 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -170,8 +170,7 @@ export class ExternalRequest { id: string | undefined | string[], filters: SearchFilters, table: Table - ) { - const tables = this.tableList + ): SearchFilters { // replace any relationship columns initially, table names and relationship column names are acceptable const relationshipColumns = sdk.rows.filters.getRelationshipColumns(table) filters = sdk.rows.filters.updateFilterKeys( @@ -214,7 +213,7 @@ export class ExternalRequest { if (!Array.isArray(idCopy)) { idCopy = breakRowIdField(idCopy) } - const equal: any = {} + const equal: SearchFilters["equal"] = {} if (primary && idCopy) { for (let field of primary) { // work through the ID and get the parts @@ -274,7 +273,9 @@ export class ExternalRequest { } // seeds the object with table and datasource information - async retrieveMetadata(datasourceId: string) { + async retrieveMetadata( + datasourceId: string + ): Promise<{ tables: Record; datasource: Datasource }> { if (!this.datasource) { this.datasource = await sdk.datasources.get(datasourceId) if (!this.datasource || !this.datasource.entities) { @@ -283,6 +284,7 @@ export class ExternalRequest { this.tables = this.datasource.entities this.tableList = Object.values(this.tables) } + return { tables: this.tables, datasource: this.datasource } } async getRow(table: Table, rowId: string): Promise { @@ -599,11 +601,13 @@ export class ExternalRequest { throw new Error("Unable to run without a table ID") } let { datasourceId, tableName } = breakExternalTableId(tableId) - if (!this.datasource) { - await this.retrieveMetadata(datasourceId) + let datasource = this.datasource + if (!datasource) { + const { datasource: ds } = await this.retrieveMetadata(datasourceId) + datasource = ds } const table = this.tables[tableName] - let isSql = isSQL(this.datasource!) + let isSql = isSQL(datasource) if (!table) { throw new Error( `Unable to process query, table "${tableName}" not defined.` From b597bd3dbec1e8a5f2ba35da348fd7eb21609dae Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Jun 2024 13:30:18 +0100 Subject: [PATCH 106/116] Fixing an issue detected by tests. --- packages/server/src/api/controllers/table/external.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index fabc4d195a..6ca8cdd82c 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -18,12 +18,15 @@ import { builderSocket } from "../../../websockets" import { inputProcessing } from "../../../utilities/rowProcessor" function getDatasourceId(table: Table) { - if (!table || !table._id) { - throw new Error("No table/table ID supplied") + if (!table) { + throw new Error("No table supplied") } if (table.sourceId) { return table.sourceId } + if (!table._id) { + throw new Error("No table ID supplied") + } return breakExternalTableId(table._id).datasourceId } From aefe46b253e02a2a7b456c38088e7a634a06ee93 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Mon, 24 Jun 2024 14:31:27 +0100 Subject: [PATCH 107/116] Adds _id and _rev back to internal datasource filter options (#13977) * Adds _id and _rev back to internal datasource filter options * add bb default datasource const into shared-core * re-export var from shared-core --- packages/backend-core/src/constants/db.ts | 2 +- .../src/components/FilterBuilder.svelte | 21 ++++++++++++++++--- packages/frontend-core/src/constants.js | 6 +++++- packages/shared-core/src/constants/index.ts | 2 ++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/backend-core/src/constants/db.ts b/packages/backend-core/src/constants/db.ts index 2fd713119b..3085b91ef1 100644 --- a/packages/backend-core/src/constants/db.ts +++ b/packages/backend-core/src/constants/db.ts @@ -72,4 +72,4 @@ export const DEFAULT_JOBS_TABLE_ID = "ta_bb_jobs" export const DEFAULT_INVENTORY_TABLE_ID = "ta_bb_inventory" export const DEFAULT_EXPENSES_TABLE_ID = "ta_bb_expenses" export const DEFAULT_EMPLOYEE_TABLE_ID = "ta_bb_employee" -export const DEFAULT_BB_DATASOURCE_ID = "datasource_internal_bb_default" +export { DEFAULT_BB_DATASOURCE_ID } from "@budibase/shared-core" diff --git a/packages/frontend-core/src/components/FilterBuilder.svelte b/packages/frontend-core/src/components/FilterBuilder.svelte index 0d254186f2..6d1e1fa502 100644 --- a/packages/frontend-core/src/components/FilterBuilder.svelte +++ b/packages/frontend-core/src/components/FilterBuilder.svelte @@ -18,7 +18,7 @@ import FilterUsers from "./FilterUsers.svelte" import { getFields } from "../utils/searchFields" - const { OperatorOptions } = Constants + const { OperatorOptions, DEFAULT_BB_DATASOURCE_ID } = Constants export let schemaFields export let filters = [] @@ -28,6 +28,23 @@ export let allowBindings = false export let filtersLabel = "Filters" + $: { + if ( + tables.find( + table => + table._id === datasource.tableId && + table.sourceId === DEFAULT_BB_DATASOURCE_ID + ) && + !schemaFields.some(field => field.name === "_id") + ) { + schemaFields = [ + ...schemaFields, + { name: "_id", type: "string" }, + { name: "_rev", type: "string" }, + ] + } + } + $: matchAny = filters?.find(filter => filter.operator === "allOr") != null $: onEmptyFilter = filters?.find(filter => filter.onEmptyFilter)?.onEmptyFilter ?? "all" @@ -35,7 +52,6 @@ $: fieldFilters = filters.filter( filter => filter.operator !== "allOr" && !filter.onEmptyFilter ) - const behaviourOptions = [ { value: "and", label: "Match all filters" }, { value: "or", label: "Match any filter" }, @@ -44,7 +60,6 @@ { value: "all", label: "Return all table rows" }, { value: "none", label: "Return no rows" }, ] - const context = getContext("context") $: fieldOptions = getFields(tables, schemaFields || [], { diff --git a/packages/frontend-core/src/constants.js b/packages/frontend-core/src/constants.js index 0d6261f5f8..e5869a3b98 100644 --- a/packages/frontend-core/src/constants.js +++ b/packages/frontend-core/src/constants.js @@ -1,7 +1,11 @@ /** * Operator options for lucene queries */ -export { OperatorOptions, SqlNumberTypeRangeMap } from "@budibase/shared-core" +export { + OperatorOptions, + SqlNumberTypeRangeMap, + DEFAULT_BB_DATASOURCE_ID, +} from "@budibase/shared-core" export { Feature as Features } from "@budibase/types" import { BpmCorrelationKey } from "@budibase/shared-core" import { FieldType, BBReferenceFieldSubType } from "@budibase/types" diff --git a/packages/shared-core/src/constants/index.ts b/packages/shared-core/src/constants/index.ts index 084bc8fe9e..c9d1a8fc8f 100644 --- a/packages/shared-core/src/constants/index.ts +++ b/packages/shared-core/src/constants/index.ts @@ -180,3 +180,5 @@ export enum BpmStatusValue { VERIFYING_EMAIL = "verifying_email", COMPLETED = "completed", } + +export const DEFAULT_BB_DATASOURCE_ID = "datasource_internal_bb_default" From 2458259093b37fc2c4bd4809c57694d5a58d99a0 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 24 Jun 2024 15:50:09 +0100 Subject: [PATCH 108/116] Respond to PR feedback. --- .../ExistingTableDataImport.svelte | 25 +++++++++++++------ .../integrations/mssql/data/Dockerfile | 2 +- .../src/api/controllers/table/external.ts | 4 +-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte index cd6b5aeb2e..80655d1099 100644 --- a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte @@ -1,9 +1,14 @@