diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index 69bf7009b2..6117f4b485 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -121,7 +121,7 @@ const identifyInstallationGroup = async ( const identifyTenantGroup = async ( tenantId: string, - account: Account | undefined, + hosting: Hosting, timestamp?: string | number ): Promise => { const id = await getEventTenantId(tenantId) @@ -129,26 +129,12 @@ const identifyTenantGroup = async ( const installationId = await getInstallationId() const environment = getDeploymentEnvironment() - let hosting: Hosting - let profession: string | undefined - let companySize: string | undefined - - if (account) { - profession = account.profession - companySize = account.size - hosting = account.hosting - } else { - hosting = getHostingFromEnv() - } - const group: TenantGroup = { id, type, hosting, environment, installationId, - profession, - companySize, } await identifyGroup(group, timestamp) diff --git a/packages/backend-core/src/features/features.ts b/packages/backend-core/src/features/features.ts index b3f016e88a..650254fcb2 100644 --- a/packages/backend-core/src/features/features.ts +++ b/packages/backend-core/src/features/features.ts @@ -266,12 +266,14 @@ export class FlagSet, T extends { [key: string]: V }> { // new flag, add it here and use the `fetch` and `get` functions to access it. // All of the machinery in this file is to make sure that flags have their // default values set correctly and their types flow through the system. -export const flags = new FlagSet({ +const flagsConfig: Record> = { [FeatureFlag.DEFAULT_VALUES]: Flag.boolean(true), [FeatureFlag.AUTOMATION_BRANCHING]: Flag.boolean(true), [FeatureFlag.AI_CUSTOM_CONFIGS]: Flag.boolean(true), [FeatureFlag.BUDIBASE_AI]: Flag.boolean(true), -}) + [FeatureFlag.USE_ZOD_VALIDATOR]: Flag.boolean(env.isDev()), +} +export const flags = new FlagSet(flagsConfig) type UnwrapPromise = T extends Promise ? U : T export type FeatureFlags = UnwrapPromise> diff --git a/packages/server/package.json b/packages/server/package.json index d2a51b4453..63d7ed3a9a 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -129,7 +129,8 @@ "uuid": "^8.3.2", "validate.js": "0.13.1", "worker-farm": "1.7.0", - "xml2js": "0.6.2" + "xml2js": "0.6.2", + "zod-validation-error": "^3.4.0" }, "devDependencies": { "@babel/core": "^7.22.5", @@ -175,7 +176,8 @@ "tsconfig-paths": "4.0.0", "typescript": "5.5.2", "update-dotenv": "1.1.1", - "yargs": "13.2.4" + "yargs": "^13.2.4", + "zod": "^3.23.8" }, "nx": { "targets": { diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index fe29d46700..cc5491e54f 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -19,6 +19,7 @@ import { isRelationshipField, PatchRowRequest, PatchRowResponse, + RequiredKeys, Row, RowAttachment, RowSearchParams, @@ -239,7 +240,8 @@ export async function search(ctx: Ctx) { await context.ensureSnippetContext(true) - let { query } = ctx.request.body + const searchRequest = ctx.request.body + let { query } = searchRequest if (query) { const allTables = await sdk.tables.getAllTables() query = replaceTableNamesInFilters(tableId, query, allTables) @@ -249,11 +251,22 @@ export async function search(ctx: Ctx) { user: sdk.users.getUserContextBindings(ctx.user), }) - const searchParams: RowSearchParams = { - ...ctx.request.body, + const searchParams: RequiredKeys = { query: enrichedQuery, tableId, viewId, + bookmark: searchRequest.bookmark ?? undefined, + paginate: searchRequest.paginate, + limit: searchRequest.limit, + sort: searchRequest.sort ?? undefined, + sortOrder: searchRequest.sortOrder, + sortType: searchRequest.sortType ?? undefined, + countRows: searchRequest.countRows, + version: searchRequest.version, + disableEscaping: searchRequest.disableEscaping, + fields: undefined, + indexer: undefined, + rows: undefined, } ctx.status = 200 diff --git a/packages/server/src/api/controllers/row/staticFormula.ts b/packages/server/src/api/controllers/row/staticFormula.ts index 31a6678012..4464b7f44a 100644 --- a/packages/server/src/api/controllers/row/staticFormula.ts +++ b/packages/server/src/api/controllers/row/staticFormula.ts @@ -15,10 +15,21 @@ import { } from "@budibase/types" import * as linkRows from "../../../db/linkedRows" import isEqual from "lodash/isEqual" -import { cloneDeep } from "lodash/fp" +import { cloneDeep, merge } from "lodash/fp" import sdk from "../../../sdk" import * as pro from "@budibase/pro" +function mergeRows(row1: Row, row2: Row) { + const merged = merge(row1, row2) + // make sure any specifically undefined fields are removed + for (const key of Object.keys(row2)) { + if (row2[key] === undefined) { + delete merged[key] + } + } + return merged +} + /** * This function runs through a list of enriched rows, looks at the rows which * are related and then checks if they need the state of their formulas @@ -162,9 +173,14 @@ export async function finaliseRow( }) } - const response = await db.put(row) - // for response, calculate the formulas for the enriched row - enrichedRow._rev = response.rev + await db.put(row) + const retrieved = await db.tryGet(row._id) + if (!retrieved) { + throw new Error(`Unable to retrieve row ${row._id} after saving.`) + } + + delete enrichedRow._rev + enrichedRow = mergeRows(retrieved, enrichedRow) enrichedRow = await processFormulas(table, enrichedRow, { dynamic: false, }) diff --git a/packages/server/src/api/controllers/row/utils/utils.ts b/packages/server/src/api/controllers/row/utils/utils.ts index 4188fcced3..5b60143792 100644 --- a/packages/server/src/api/controllers/row/utils/utils.ts +++ b/packages/server/src/api/controllers/row/utils/utils.ts @@ -175,7 +175,7 @@ export async function enrichArrayContext( } export async function enrichSearchContext( - fields: Record, + fields: Record | undefined, inputs = {}, helpers = true ): Promise> { diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index 02ac871de0..0655a3b38f 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -29,19 +29,20 @@ export async function searchView( await context.ensureSnippetContext(true) - const searchOptions: RequiredKeys & - RequiredKeys< - Pick - > = { + const searchOptions: RequiredKeys = { tableId: view.tableId, viewId: view.id, - query: body.query, + query: body.query || {}, fields: viewFields, ...getSortOptions(body, view), limit: body.limit, - bookmark: body.bookmark, + bookmark: body.bookmark ?? undefined, paginate: body.paginate, countRows: body.countRows, + version: undefined, + disableEscaping: undefined, + indexer: undefined, + rows: undefined, } const result = await sdk.rows.search(searchOptions, { @@ -56,7 +57,7 @@ function getSortOptions(request: SearchViewRowRequest, view: ViewV2) { return { sort: request.sort, sortOrder: request.sortOrder, - sortType: request.sortType, + sortType: request.sortType ?? undefined, } } if (view.sort) { diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts index e443b2daeb..61cdee0c2f 100644 --- a/packages/server/src/api/routes/row.ts +++ b/packages/server/src/api/routes/row.ts @@ -5,6 +5,8 @@ import { paramResource, paramSubResource } from "../../middleware/resourceId" import { permissions } from "@budibase/backend-core" import { internalSearchValidator } from "./utils/validators" import trimViewRowInfo from "../../middleware/trimViewRowInfo" +import { validateBody } from "../../middleware/zod-validator" +import { searchRowRequestValidator } from "@budibase/types" const { PermissionType, PermissionLevel } = permissions @@ -32,6 +34,7 @@ router .post( "/api/:sourceId/search", internalSearchValidator(), + validateBody(searchRowRequestValidator), paramResource("sourceId"), authorized(PermissionType.TABLE, PermissionLevel.READ), rowController.search @@ -87,6 +90,7 @@ router router.post( "/api/v2/views/:viewId/search", internalSearchValidator(), + validateBody(searchRowRequestValidator), authorizedResource(PermissionType.VIEW, PermissionLevel.READ, "viewId"), rowController.views.searchView ) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index b5fff83b43..1f4c4bc7cb 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2607,6 +2607,8 @@ if (descriptions.length) { name: "foo", description: "bar", tableId, + createdAt: isInternal ? new Date().toISOString() : undefined, + updatedAt: isInternal ? new Date().toISOString() : undefined, }) }) @@ -2628,6 +2630,8 @@ if (descriptions.length) { id: isInternal ? undefined : expect.any(Number), type: isInternal ? "row" : undefined, [`fk_${o2mTable.name}_fk_o2m`]: isInternal ? undefined : user.id, + createdAt: isInternal ? new Date().toISOString() : undefined, + updatedAt: isInternal ? new Date().toISOString() : undefined, }) }) @@ -2650,6 +2654,8 @@ if (descriptions.length) { _rev: expect.any(String), id: isInternal ? undefined : expect.any(Number), type: isInternal ? "row" : undefined, + createdAt: isInternal ? new Date().toISOString() : undefined, + updatedAt: isInternal ? new Date().toISOString() : undefined, }) }) @@ -2729,6 +2735,8 @@ if (descriptions.length) { id: isInternal ? undefined : expect.any(Number), type: isInternal ? "row" : undefined, [`fk_${o2mTable.name}_fk_o2m`]: isInternal ? undefined : user.id, + createdAt: isInternal ? new Date().toISOString() : undefined, + updatedAt: isInternal ? new Date().toISOString() : undefined, }) }) @@ -2745,15 +2753,8 @@ if (descriptions.length) { user: null, users: null, }) - expect(updatedRow).toEqual({ - name: "foo", - description: "bar", - tableId, - _id: row._id, - _rev: expect.any(String), - id: isInternal ? undefined : expect.any(Number), - type: isInternal ? "row" : undefined, - }) + expect(updatedRow.user).toBeUndefined() + expect(updatedRow.users).toBeUndefined() }) it("fetch all will populate the relationships", async () => { diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 5384444067..c66197334e 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -24,6 +24,7 @@ import { JsonFieldSubType, LogicalOperator, RelationshipType, + RequiredKeys, Row, RowSearchParams, SearchFilters, @@ -208,9 +209,25 @@ if (descriptions.length) { private async performSearch(): Promise> { if (isInMemory) { - return dataFilters.search(_.cloneDeep(rows), { - ...this.query, - }) + const inMemoryQuery: RequiredKeys< + Omit + > = { + sort: this.query.sort ?? undefined, + query: { ...this.query.query }, + paginate: this.query.paginate, + bookmark: this.query.bookmark ?? undefined, + limit: this.query.limit, + sortOrder: this.query.sortOrder, + sortType: this.query.sortType ?? undefined, + version: this.query.version, + disableEscaping: this.query.disableEscaping, + countRows: this.query.countRows, + viewId: undefined, + fields: undefined, + indexer: undefined, + rows: undefined, + } + return dataFilters.search(_.cloneDeep(rows), inMemoryQuery) } else { return config.api.row.search(tableOrViewId, this.query) } diff --git a/packages/server/src/automations/tests/scenarios/looping.spec.ts b/packages/server/src/automations/tests/scenarios/looping.spec.ts index 9c5313e9da..0baa69b3bc 100644 --- a/packages/server/src/automations/tests/scenarios/looping.spec.ts +++ b/packages/server/src/automations/tests/scenarios/looping.spec.ts @@ -152,6 +152,44 @@ describe("Loop automations", () => { ) }) + it("ensure the loop stops if the max iterations are reached", async () => { + const builder = createAutomationBuilder({ + name: "Test Loop max iterations", + }) + + const results = await builder + .appAction({ fields: {} }) + .loop({ + option: LoopStepType.ARRAY, + binding: ["test", "test2", "test3"], + iterations: 2, + }) + .serverLog({ text: "{{loop.currentItem}}" }) + .serverLog({ text: "{{steps.1.iterations}}" }) + .run() + + expect(results.steps[0].outputs.iterations).toBe(2) + }) + + it("should run an automation with loop and max iterations to ensure context correctness further down the tree", async () => { + const builder = createAutomationBuilder({ + name: "Test context down tree with Loop and max iterations", + }) + + const results = await builder + .appAction({ fields: {} }) + .loop({ + option: LoopStepType.ARRAY, + binding: ["test", "test2", "test3"], + iterations: 2, + }) + .serverLog({ text: "{{loop.currentItem}}" }) + .serverLog({ text: "{{steps.1.iterations}}" }) + .run() + + expect(results.steps[1].outputs.message).toContain("- 2") + }) + it("should run an automation where a loop is successfully run twice", async () => { const builder = createAutomationBuilder({ name: "Test Trigger with Loop and Create Row", diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 604a81cd9f..89e2f26516 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -137,7 +137,6 @@ export enum InvalidColumns { export enum AutomationErrors { INCORRECT_TYPE = "INCORRECT_TYPE", - MAX_ITERATIONS = "MAX_ITERATIONS_REACHED", FAILURE_CONDITION = "FAILURE_CONDITION_MET", } diff --git a/packages/server/src/integrations/tests/utils/index.ts b/packages/server/src/integrations/tests/utils/index.ts index dcdaece191..e5d2e1f229 100644 --- a/packages/server/src/integrations/tests/utils/index.ts +++ b/packages/server/src/integrations/tests/utils/index.ts @@ -102,6 +102,9 @@ function createDummyTest() { } export function datasourceDescribe(opts: DatasourceDescribeOpts) { + // tests that call this need a lot longer timeouts + jest.setTimeout(120000) + if (process.env.DATASOURCE === "none") { createDummyTest() } diff --git a/packages/server/src/integrations/tests/utils/oracle.ts b/packages/server/src/integrations/tests/utils/oracle.ts index 8e7fd6c900..2ec8da5902 100644 --- a/packages/server/src/integrations/tests/utils/oracle.ts +++ b/packages/server/src/integrations/tests/utils/oracle.ts @@ -25,7 +25,7 @@ export async function getDatasource(): Promise { }) .withWaitStrategy( Wait.forLogMessage("DATABASE IS READY TO USE!").withStartupTimeout( - 20000 + 60000 ) ) ) diff --git a/packages/server/src/middleware/zod-validator.ts b/packages/server/src/middleware/zod-validator.ts new file mode 100644 index 0000000000..e8cc2c470a --- /dev/null +++ b/packages/server/src/middleware/zod-validator.ts @@ -0,0 +1,43 @@ +import { features } from "@budibase/backend-core" +import { Ctx, FeatureFlag } from "@budibase/types" + +import { AnyZodObject } from "zod" +import { fromZodError } from "zod-validation-error" + +function validate(schema: AnyZodObject, property: "body" | "params") { + // Return a Koa middleware function + return async (ctx: Ctx, next: any) => { + if (!(await features.flags.isEnabled(FeatureFlag.USE_ZOD_VALIDATOR))) { + return next() + } + + if (!schema) { + return next() + } + let params = null + let setClean: ((data: any) => void) | undefined + if (ctx[property] != null) { + params = ctx[property] + setClean = data => (ctx[property] = data) + } else if (property === "body" && ctx.request[property] != null) { + params = ctx.request[property] + setClean = data => (ctx.request[property] = data) + } else if (property === "params") { + params = ctx.request.query + setClean = data => (ctx.request.query = data) + } + + const result = schema.safeParse(params) + if (!result.success) { + ctx.throw(400, fromZodError(result.error)) + } else { + setClean?.(result.data) + } + + return next() + } +} + +export function validateBody(schema: AnyZodObject) { + return validate(schema, "body") +} diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index a56c92492b..7f718cee2f 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -9,11 +9,12 @@ import { db as dbUtils, } from "@budibase/backend-core" import { - QuotaUsage, - CloudAccount, App, - TenantBackfillSucceededEvent, + CloudAccount, Event, + Hosting, + QuotaUsage, + TenantBackfillSucceededEvent, User, } from "@budibase/types" import env from "../../../environment" @@ -125,7 +126,7 @@ export const run = async (db: any) => { try { await events.identification.identifyTenantGroup( tenantId, - account, + env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD, timestamp ) } catch (e) { diff --git a/packages/server/src/threads/automation.ts b/packages/server/src/threads/automation.ts index 33e11a6ac0..7e8f947580 100644 --- a/packages/server/src/threads/automation.ts +++ b/packages/server/src/threads/automation.ts @@ -392,6 +392,7 @@ class Orchestrator { let iterationCount = 0 let shouldCleanup = true + let reachedMaxIterations = false for (let loopStepIndex = 0; loopStepIndex < iterations; loopStepIndex++) { try { @@ -419,19 +420,8 @@ class Orchestrator { loopStepIndex === env.AUTOMATION_MAX_ITERATIONS || (loopStep.inputs.iterations && loopStepIndex === maxIterations) ) { - this.updateContextAndOutput( - pathStepIdx + 1, - steps[stepToLoopIndex], - { - items: this.loopStepOutputs, - iterations: loopStepIndex, - }, - { - status: AutomationErrors.MAX_ITERATIONS, - success: true, - } - ) - shouldCleanup = false + reachedMaxIterations = true + shouldCleanup = true break } @@ -485,6 +475,10 @@ class Orchestrator { iterations: iterationCount, } + if (reachedMaxIterations && iterations !== 0) { + tempOutput.status = AutomationStepStatus.MAX_ITERATIONS + } + // Loop Step clean up this.executionOutput.steps.splice(pathStepIdx, 0, { id: steps[stepToLoopIndex].id, diff --git a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts index b02ea2ff60..6c8ecc3167 100644 --- a/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts +++ b/packages/server/src/utilities/rowProcessor/bbReferenceProcessor.ts @@ -153,10 +153,10 @@ export async function processOutputBBReference( } export async function processOutputBBReferences( - value: string | null | undefined, + value: string | string[] | null | undefined, subtype: BBReferenceFieldSubType ): Promise { - if (!value) { + if (!value || (Array.isArray(value) && value.length === 0)) { return undefined } const ids = diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 7d6d537302..14b524fd95 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -29,6 +29,8 @@ import { import { isExternalTableID } from "../../integrations/utils" import { helpers, + isExternalColumnName, + isInternalColumnName, PROTECTED_EXTERNAL_COLUMNS, PROTECTED_INTERNAL_COLUMNS, } from "@budibase/shared-core" @@ -200,14 +202,17 @@ export async function inputProcessing( const clonedRow = cloneDeep(row) const table = await getTableFromSource(source) - const dontCleanseKeys = ["type", "_id", "_rev", "tableId"] for (const [key, value] of Object.entries(clonedRow)) { const field = table.schema[key] + const isBuiltinColumn = isExternalTableID(table._id!) + ? isExternalColumnName(key) + : isInternalColumnName(key) // cleanse fields that aren't in the schema + if (!field && !isBuiltinColumn) { + delete clonedRow[key] + } + // field isn't found - might be a built-in column, skip over it if (!field) { - if (dontCleanseKeys.indexOf(key) === -1) { - delete clonedRow[key] - } continue } // remove any formula values, they are to be generated diff --git a/packages/shared-core/src/constants/rows.ts b/packages/shared-core/src/constants/rows.ts index 03663a4014..23c123aaf4 100644 --- a/packages/shared-core/src/constants/rows.ts +++ b/packages/shared-core/src/constants/rows.ts @@ -12,3 +12,7 @@ export const PROTECTED_EXTERNAL_COLUMNS = ["_id", "_rev", "tableId"] as const export function isInternalColumnName(name: string): boolean { return (PROTECTED_INTERNAL_COLUMNS as readonly string[]).includes(name) } + +export function isExternalColumnName(name: string): boolean { + return (PROTECTED_EXTERNAL_COLUMNS as readonly string[]).includes(name) +} diff --git a/packages/types/package.json b/packages/types/package.json index d132383081..ae99d31838 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -20,7 +20,8 @@ "@types/redlock": "4.0.7", "rimraf": "3.0.2", "typescript": "5.5.2", - "koa-useragent": "^4.1.0" + "koa-useragent": "^4.1.0", + "zod": "^3.23.8" }, "dependencies": { "scim-patch": "^0.8.1" diff --git a/packages/types/src/api/web/app/rows.ts b/packages/types/src/api/web/app/rows.ts deleted file mode 100644 index ce6f6f672d..0000000000 --- a/packages/types/src/api/web/app/rows.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { SearchFilters, RowSearchParams } from "../../../sdk" -import { Row } from "../../../documents" -import { PaginationResponse, SortOrder } from "../../../api" -import { ReadStream } from "fs" - -export interface SaveRowRequest extends Row {} - -export interface PatchRowRequest extends Row { - _id: string - _rev: string - tableId: string -} - -export interface PatchRowResponse extends Row {} - -export interface SearchRowRequest extends Omit {} - -export interface SearchViewRowRequest - extends Pick< - SearchRowRequest, - | "sort" - | "sortOrder" - | "sortType" - | "limit" - | "bookmark" - | "paginate" - | "query" - | "countRows" - > {} - -export interface SearchRowResponse { - rows: any[] -} - -export interface PaginatedSearchRowResponse - extends SearchRowResponse, - PaginationResponse {} - -export interface ExportRowsRequest { - rows?: string[] - columns?: string[] - query?: SearchFilters - sort?: string - sortOrder?: SortOrder - delimiter?: string - customHeaders?: { [key: string]: string } -} - -export type ExportRowsResponse = ReadStream diff --git a/packages/types/src/api/web/app/rows/index.ts b/packages/types/src/api/web/app/rows/index.ts new file mode 100644 index 0000000000..2642a8b04e --- /dev/null +++ b/packages/types/src/api/web/app/rows/index.ts @@ -0,0 +1,28 @@ +import { SearchFilters } from "../../../../sdk" +import { Row } from "../../../../documents" +import { SortOrder } from "../../../../api/web/pagination" +import { ReadStream } from "fs" + +export * from "./search" + +export interface SaveRowRequest extends Row {} + +export interface PatchRowRequest extends Row { + _id: string + _rev: string + tableId: string +} + +export interface PatchRowResponse extends Row {} + +export interface ExportRowsRequest { + rows?: string[] + columns?: string[] + query?: SearchFilters + sort?: string + sortOrder?: SortOrder + delimiter?: string + customHeaders?: { [key: string]: string } +} + +export type ExportRowsResponse = ReadStream diff --git a/packages/types/src/api/web/app/rows/search.ts b/packages/types/src/api/web/app/rows/search.ts new file mode 100644 index 0000000000..7ba23bceca --- /dev/null +++ b/packages/types/src/api/web/app/rows/search.ts @@ -0,0 +1,100 @@ +import { + ArrayOperator, + BasicOperator, + EmptyFilterOption, + InternalSearchFilterOperator, + LogicalOperator, + RangeOperator, + SearchFilterKey, +} from "../../../../sdk" +import { Row } from "../../../../documents" +import { + PaginationResponse, + SortOrder, + SortType, +} from "../../../../api/web/pagination" +import { z } from "zod" + +const fieldKey = z + .string() + .refine(s => s !== InternalSearchFilterOperator.COMPLEX_ID_OPERATOR, { + message: `Key '${InternalSearchFilterOperator.COMPLEX_ID_OPERATOR}' is not allowed`, + }) + +const stringBasicFilter = z.record(fieldKey, z.string()) +const basicFilter = z.record(fieldKey, z.any()) +const arrayFilter = z.record(fieldKey, z.union([z.any().array(), z.string()])) +const logicFilter = z.lazy(() => + z.object({ + conditions: z.array(z.object(queryFilterValidation)), + }) +) + +const stringOrNumber = z.union([z.string(), z.number()]) + +const queryFilterValidation: Record = { + [BasicOperator.STRING]: stringBasicFilter.optional(), + [BasicOperator.FUZZY]: stringBasicFilter.optional(), + [RangeOperator.RANGE]: z + .record( + fieldKey, + z.union([ + z.object({ high: stringOrNumber, low: stringOrNumber }), + z.object({ high: stringOrNumber }), + z.object({ low: stringOrNumber }), + ]) + ) + .optional(), + [BasicOperator.EQUAL]: basicFilter.optional(), + [BasicOperator.NOT_EQUAL]: basicFilter.optional(), + [BasicOperator.EMPTY]: basicFilter.optional(), + [BasicOperator.NOT_EMPTY]: basicFilter.optional(), + [ArrayOperator.ONE_OF]: arrayFilter.optional(), + [ArrayOperator.CONTAINS]: arrayFilter.optional(), + [ArrayOperator.NOT_CONTAINS]: arrayFilter.optional(), + [ArrayOperator.CONTAINS_ANY]: arrayFilter.optional(), + [LogicalOperator.AND]: logicFilter.optional(), + [LogicalOperator.OR]: logicFilter.optional(), +} + +const searchRowRequest = z.object({ + query: z + .object({ + allOr: z.boolean().optional(), + onEmptyFilter: z.nativeEnum(EmptyFilterOption).optional(), + ...queryFilterValidation, + }) + .optional(), + paginate: z.boolean().optional(), + bookmark: z.union([z.string(), z.number()]).nullish(), + limit: z.number().optional(), + sort: z.string().nullish(), + sortOrder: z.nativeEnum(SortOrder).optional(), + sortType: z.nativeEnum(SortType).nullish(), + version: z.string().optional(), + disableEscaping: z.boolean().optional(), + countRows: z.boolean().optional(), +}) + +export const searchRowRequestValidator = searchRowRequest + +export type SearchRowRequest = z.infer +export type SearchViewRowRequest = Pick< + SearchRowRequest, + | "sort" + | "sortOrder" + | "sortType" + | "limit" + | "bookmark" + | "paginate" + | "query" + | "countRows" +> + +export interface SearchRowResponse { + rows: Row[] +} + +export interface PaginatedSearchRowResponse + extends SearchRowResponse, + PaginationResponse {} diff --git a/packages/types/src/documents/app/automation/automation.ts b/packages/types/src/documents/app/automation/automation.ts index 1af892d8d1..71530c7939 100644 --- a/packages/types/src/documents/app/automation/automation.ts +++ b/packages/types/src/documents/app/automation/automation.ts @@ -174,6 +174,7 @@ export enum AutomationFeature { export enum AutomationStepStatus { NO_ITERATIONS = "no_iterations", + MAX_ITERATIONS = "max_iterations_reached", } export enum AutomationStatus { diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index 64a7362e9f..98e744324c 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -1,11 +1,9 @@ export enum FeatureFlag { - PER_CREATOR_PER_USER_PRICE = "PER_CREATOR_PER_USER_PRICE", - PER_CREATOR_PER_USER_PRICE_ALERT = "PER_CREATOR_PER_USER_PRICE_ALERT", AUTOMATION_BRANCHING = "AUTOMATION_BRANCHING", AI_CUSTOM_CONFIGS = "AI_CUSTOM_CONFIGS", DEFAULT_VALUES = "DEFAULT_VALUES", - BUDIBASE_AI = "BUDIBASE_AI", + USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR", } export interface TenantFeatureFlags { diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 2479a50d9e..e977d5ff5d 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -6,12 +6,12 @@ import { AddSSoUserRequest, BulkUserRequest, BulkUserResponse, - CloudAccount, CreateAdminUserRequest, CreateAdminUserResponse, Ctx, DeleteInviteUserRequest, DeleteInviteUsersRequest, + Hosting, InviteUserRequest, InviteUsersRequest, InviteUsersResponse, @@ -26,7 +26,6 @@ import { UserIdentifier, } from "@budibase/types" import { - accounts, users, cache, ErrorCode, @@ -192,12 +191,10 @@ export const adminUser = async ( lastName: familyName, }) - // events - let account: CloudAccount | undefined - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - account = await accounts.getAccountByTenantId(tenantId) - } - await events.identification.identifyTenantGroup(tenantId, account) + await events.identification.identifyTenantGroup( + tenantId, + env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD + ) ctx.body = { _id: finalUser._id!, diff --git a/yarn.lock b/yarn.lock index eb654ec4ff..e6bbff411e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2319,6 +2319,7 @@ version "0.0.0" dependencies: scim-patch "^0.8.1" + zod "^3.23.8" "@bull-board/api@5.10.2": version "5.10.2" @@ -8863,17 +8864,6 @@ cross-spawn@7.0.6: shebang-command "^2.0.0" which "^2.0.1" -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -10675,19 +10665,6 @@ execa@5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -11495,7 +11472,7 @@ get-stream@^2.2.0: object-assign "^4.0.1" pinkie-promise "^2.0.0" -get-stream@^4.0.0, get-stream@^4.1.0: +get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== @@ -12591,11 +12568,6 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - ioredis-mock@8.9.0: version "8.9.0" resolved "https://registry.yarnpkg.com/ioredis-mock/-/ioredis-mock-8.9.0.tgz#5d694c4b81d3835e4291e0b527f947e260981779" @@ -14287,13 +14259,6 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - leaflet@^1.7.1: version "1.9.3" resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.3.tgz#52ec436954964e2d3d39e0d433da4b2500d74414" @@ -15109,13 +15074,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -15157,15 +15115,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - memdown@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" @@ -15276,7 +15225,7 @@ mime@^1.3.4: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -15732,11 +15681,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== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - nock@13.5.4, nock@^13.5.4: version "13.5.4" resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" @@ -16074,13 +16018,6 @@ npm-registry-fetch@^14.0.0, npm-registry-fetch@^14.0.3, npm-registry-fetch@^14.0 npm-package-arg "^10.0.0" proc-log "^3.0.0" -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== - dependencies: - path-key "^2.0.0" - npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -16492,15 +16429,6 @@ oracledb@6.5.1: resolved "https://registry.yarnpkg.com/oracledb/-/oracledb-6.5.1.tgz#814d985035acdb1a6470b1152af0ca3767569ede" integrity sha512-JzoSGei1wnvmqgKnAZK1W650mzHTZXx+7hClV4mwsbY/ZjUtrpnojNJMYJ2jkOhj7XG5oJPfXc4GqDKaNzkxqg== -os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -16516,11 +16444,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-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== - p-event@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6" @@ -16538,11 +16461,6 @@ p-is-promise@^1.1.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" integrity sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg== -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -16903,11 +16821,6 @@ path-is-absolute@1.0.1, path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== - path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -18918,7 +18831,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@7.5.3, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@~2.3.1: +"semver@2 || 3 || 4 || 5", semver@7.5.3, semver@^5.6.0, semver@^5.7.1, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@~2.3.1: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== @@ -19013,13 +18926,6 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -19027,11 +18933,6 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" @@ -19746,11 +19647,6 @@ strip-dirs@^2.0.0: dependencies: is-natural-number "^4.0.1" -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -21424,7 +21320,7 @@ which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, gopd "^1.0.1" has-tostringtag "^1.0.2" -which@^1.2.14, which@^1.2.9: +which@^1.2.14: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -21775,7 +21671,7 @@ yargs-parser@21.1.1, yargs-parser@>=21.1.1, yargs-parser@^21.0.0, yargs-parser@^ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs-parser@^13.1.0: +yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -21788,23 +21684,6 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@13.2.4: - version "13.2.4" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" - integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.0" - yargs@16.2.0, yargs@^16.1.0, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" @@ -21818,6 +21697,22 @@ yargs@16.2.0, yargs@^16.1.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^13.2.4: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yargs@^17.3.1, yargs@^17.6.2, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" @@ -21925,6 +21820,11 @@ zip-stream@^6.0.1: compress-commons "^6.0.2" readable-stream "^4.0.0" +zod-validation-error@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== + zod@^3.23.8: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"