diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index c48a8f8341..8a52cdc511 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -82,7 +82,7 @@ jobs: - uses: pnpm/action-setup@v2.4.0 with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" @@ -125,7 +125,7 @@ jobs: - uses: pnpm/action-setup@v2.4.0 with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" @@ -161,7 +161,7 @@ jobs: - uses: pnpm/action-setup@v2.4.0 with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" @@ -192,7 +192,7 @@ jobs: if: steps.cache-react-build-assets.outputs.cache-hit != 'true' with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 if: steps.cache-react-build-assets.outputs.cache-hit != 'true' with: node-version: ${{ vars.NODE_VERSION }} @@ -246,7 +246,7 @@ jobs: - uses: pnpm/action-setup@v2.4.0 with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" @@ -447,7 +447,7 @@ jobs: with: version: ${{ vars.PNPM_VERSION }} - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" diff --git a/.github/workflows/push-main.yml b/.github/workflows/push-main.yml index f3910ce8ce..3b314089c2 100644 --- a/.github/workflows/push-main.yml +++ b/.github/workflows/push-main.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} # https://docs.github.com/en/free-pro-team@latest/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} - name: Configure AWS Credentials diff --git a/.github/workflows/push-production.yml b/.github/workflows/push-production.yml index 383358d256..a757fd612e 100644 --- a/.github/workflows/push-production.yml +++ b/.github/workflows/push-production.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} # https://docs.github.com/en/free-pro-team@latest/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action @@ -75,7 +75,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 1 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} - name: Configure AWS Credentials diff --git a/.github/workflows/regression-tests.yml b/.github/workflows/regression-tests.yml index ce8b0adee0..09c1804291 100644 --- a/.github/workflows/regression-tests.yml +++ b/.github/workflows/regression-tests.yml @@ -33,7 +33,7 @@ jobs: - uses: pnpm/action-setup@v2.4.0 with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" @@ -72,7 +72,7 @@ jobs: - uses: pnpm/action-setup@v2.4.0 with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" @@ -108,7 +108,7 @@ jobs: - uses: pnpm/action-setup@v2.4.0 with: version: ${{ vars.PNPM_VERSION }} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" @@ -141,7 +141,7 @@ jobs: with: version: ${{ vars.PNPM_VERSION }} - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ vars.NODE_VERSION }} cache: "pnpm" diff --git a/api.planx.uk/editor/publish.test.ts b/api.planx.uk/editor/publish.test.ts index 504ac123f5..af840293c7 100644 --- a/api.planx.uk/editor/publish.test.ts +++ b/api.planx.uk/editor/publish.test.ts @@ -3,8 +3,8 @@ import supertest from "supertest"; import { queryMock } from "../tests/graphqlQueryMock"; import { authHeader } from "../tests/mockJWT"; import app from "../server"; -import { Flow } from "../types"; import { flowWithInviteToPay } from "../tests/mocks/inviteToPayData"; +import { FlowGraph } from "@opensystemslab/planx-core/types"; beforeEach(() => { queryMock.mockQuery({ @@ -168,7 +168,7 @@ describe("sections validation on diff", () => { }); it("does not update if there are sections, but there is not a section in the first position", async () => { - const flowWithSections: Flow["data"] = { + const flowWithSections: FlowGraph = { _root: { edges: ["questionNode", "sectionNode"], }, @@ -205,7 +205,7 @@ describe("sections validation on diff", () => { describe("invite to pay validation on diff", () => { it("does not update if invite to pay is enabled, but there is not a Send component", async () => { - const { Send, ...invalidatedFlow } = flowWithInviteToPay; + const { Send: _Send, ...invalidatedFlow } = flowWithInviteToPay; invalidatedFlow["_root"].edges?.splice( invalidatedFlow["_root"].edges?.indexOf("Send"), ); @@ -266,7 +266,8 @@ describe("invite to pay validation on diff", () => { }); it("does not update if invite to pay is enabled, but there is not a FindProperty (`_address`) component", async () => { - const { FindProperty, ...invalidatedFlow } = flowWithInviteToPay; + const { FindProperty: _FindProperty, ...invalidatedFlow } = + flowWithInviteToPay; invalidatedFlow["_root"].edges?.splice( invalidatedFlow["_root"].edges?.indexOf("FindProperty"), ); @@ -326,9 +327,9 @@ describe("invite to pay validation on diff", () => { it("does not update if invite to pay is enabled, but there is not a Checklist that sets `proposal.projectType`", async () => { const { - Checklist, - ChecklistOptionOne, - ChecklistOptionTwo, + Checklist: _Checklist, + ChecklistOptionOne: _ChecklistOptionOne, + ChecklistOptionTwo: _ChecklistOptionTwo, ...invalidatedFlow } = flowWithInviteToPay; invalidatedFlow["_root"].edges?.splice( @@ -358,7 +359,7 @@ describe("invite to pay validation on diff", () => { }); }); -const mockFlowData: Flow["data"] = { +const mockFlowData: FlowGraph = { _root: { edges: [ "SectionOne", diff --git a/api.planx.uk/editor/publish.ts b/api.planx.uk/editor/publish.ts index 1c81377389..25efd4b81e 100644 --- a/api.planx.uk/editor/publish.ts +++ b/api.planx.uk/editor/publish.ts @@ -4,8 +4,13 @@ import { adminGraphQLClient as adminClient } from "../hasura"; import { dataMerged, getMostRecentPublishedFlow } from "../helpers"; import { gql } from "graphql-request"; import intersection from "lodash/intersection"; -import { ComponentType } from "@opensystemslab/planx-core/types"; +import { + ComponentType, + FlowGraph, + Node, +} from "@opensystemslab/planx-core/types"; import { userContext } from "../modules/auth/middleware"; +import type { Entry } from "type-fest"; const validateAndDiffFlow = async ( req: Request, @@ -139,9 +144,9 @@ type ValidationResponse = { description?: string; }; -const validateSections = (flow: Record): ValidationResponse => { - if (getSectionNodeIds(flow)?.length > 0) { - if (!sectionIsInFirstPosition(flow)) { +const validateSections = (flowGraph: FlowGraph): ValidationResponse => { + if (getSectionNodeIds(flowGraph)?.length > 0) { + if (!sectionIsInFirstPosition(flowGraph)) { return { isValid: false, message: "Cannot publish an invalid flow", @@ -149,7 +154,7 @@ const validateSections = (flow: Record): ValidationResponse => { }; } - if (!allSectionsOnRoot(flow)) { + if (!allSectionsOnRoot(flowGraph)) { return { isValid: false, message: "Cannot publish an invalid flow", @@ -165,34 +170,35 @@ const validateSections = (flow: Record): ValidationResponse => { }; }; -const getSectionNodeIds = (flow: Record): string[] => { - return Object.entries(flow) - .filter(([_nodeId, nodeData]) => nodeData?.type === ComponentType.Section) - ?.map(([nodeId, _nodeData]) => nodeId); +const getSectionNodeIds = (flowGraph: FlowGraph): string[] => { + const sectionNodes = Object.entries(flowGraph).filter((entry) => + isComponentType(entry, ComponentType.Section), + ); + return sectionNodes.map(([nodeId, _nodeData]) => nodeId); }; -const sectionIsInFirstPosition = (flow: Record): boolean => { - const firstNodeId = flow["_root"].edges[0]; - return flow[firstNodeId].type === ComponentType.Section; +const sectionIsInFirstPosition = (flowGraph: FlowGraph): boolean => { + const firstNodeId = flowGraph["_root"].edges[0]; + return flowGraph[firstNodeId].type === ComponentType.Section; }; -const allSectionsOnRoot = (flow: Record): boolean => { - const sectionTypeNodeIds = getSectionNodeIds(flow); +const allSectionsOnRoot = (flowData: FlowGraph): boolean => { + const sectionTypeNodeIds = getSectionNodeIds(flowData); const intersectingNodeIds = intersection( - flow["_root"].edges, + flowData["_root"].edges, sectionTypeNodeIds, ); return intersectingNodeIds.length === sectionTypeNodeIds.length; }; -const validateInviteToPay = (flow: Record): ValidationResponse => { +const validateInviteToPay = (flowGraph: FlowGraph): ValidationResponse => { const invalidResponseTemplate = { isValid: false, message: "Cannot publish an invalid flow", }; - if (inviteToPayEnabled(flow)) { - if (numberOfComponentType(flow, ComponentType.Pay) > 1) { + if (inviteToPayEnabled(flowGraph)) { + if (numberOfComponentType(flowGraph, ComponentType.Pay) > 1) { return { ...invalidResponseTemplate, description: @@ -200,14 +206,14 @@ const validateInviteToPay = (flow: Record): ValidationResponse => { }; } - if (!hasComponentType(flow, ComponentType.Send)) { + if (!hasComponentType(flowGraph, ComponentType.Send)) { return { ...invalidResponseTemplate, description: "When using Invite to Pay, your flow must have a Send", }; } - if (numberOfComponentType(flow, ComponentType.Send) > 1) { + if (numberOfComponentType(flowGraph, ComponentType.Send) > 1) { return { ...invalidResponseTemplate, description: @@ -215,7 +221,7 @@ const validateInviteToPay = (flow: Record): ValidationResponse => { }; } - if (!hasComponentType(flow, ComponentType.FindProperty)) { + if (!hasComponentType(flowGraph, ComponentType.FindProperty)) { return { ...invalidResponseTemplate, description: @@ -224,7 +230,11 @@ const validateInviteToPay = (flow: Record): ValidationResponse => { } if ( - !hasComponentType(flow, ComponentType.Checklist, "proposal.projectType") + !hasComponentType( + flowGraph, + ComponentType.Checklist, + "proposal.projectType", + ) ) { return { ...invalidResponseTemplate, @@ -241,27 +251,40 @@ const validateInviteToPay = (flow: Record): ValidationResponse => { }; }; -const inviteToPayEnabled = (flow: Record): boolean => { - const payNodeStatuses = Object.entries(flow) - .filter(([_nodeId, nodeData]) => nodeData?.type === ComponentType.Pay) - ?.map(([_nodeId, nodeData]) => nodeData?.data?.allowInviteToPay); +const inviteToPayEnabled = (flowGraph: FlowGraph): boolean => { + const payNodes = Object.entries(flowGraph).filter( + (entry): entry is [string, Node] => + isComponentType(entry, ComponentType.Pay), + ); + const payNodeStatuses = payNodes.map( + ([_nodeId, node]) => node?.data?.allowInviteToPay, + ); return ( payNodeStatuses.length > 0 && payNodeStatuses.every((status) => status === true) ); }; +const isComponentType = ( + entry: Entry, + type: ComponentType, +): entry is [string, Node] => { + const [nodeId, node] = entry; + if (nodeId === "_root") return false; + return Boolean(node?.type === type); +}; + const hasComponentType = ( - flow: Record, + flowGraph: FlowGraph, type: ComponentType, fn?: string, ): boolean => { - const nodeIds = Object.entries(flow).filter( - ([_nodeId, nodeData]) => nodeData?.type === type, + const nodeIds = Object.entries(flowGraph).filter( + (entry): entry is [string, Node] => isComponentType(entry, type), ); if (fn) { nodeIds - ?.filter(([_nodeId, nodeData]) => nodeData?.data.fn === fn) + ?.filter(([_nodeId, nodeData]) => nodeData?.data?.fn === fn) ?.map(([nodeId, _nodeData]) => nodeId); } else { nodeIds?.map(([nodeId, _nodeData]) => nodeId); @@ -270,16 +293,16 @@ const hasComponentType = ( }; const numberOfComponentType = ( - flow: Record, + flowGraph: FlowGraph, type: ComponentType, fn?: string, ): number => { - const nodeIds = Object.entries(flow).filter( - ([_nodeId, nodeData]) => nodeData?.type === type, + const nodeIds = Object.entries(flowGraph).filter( + (entry): entry is [string, Node] => isComponentType(entry, type), ); if (fn) { nodeIds - ?.filter(([_nodeId, nodeData]) => nodeData?.data.fn === fn) + ?.filter(([_nodeId, nodeData]) => nodeData?.data?.fn === fn) ?.map(([nodeId, _nodeData]) => nodeId); } else { nodeIds?.map(([nodeId, _nodeData]) => nodeId); diff --git a/api.planx.uk/gis/classifiedRoads.ts b/api.planx.uk/gis/classifiedRoads.ts index caf45846b3..ac8de8424c 100644 --- a/api.planx.uk/gis/classifiedRoads.ts +++ b/api.planx.uk/gis/classifiedRoads.ts @@ -14,7 +14,7 @@ type OSFeatures = { type: "Feature"; geometry: { type: string; - coordinates: any[]; + coordinates: number[]; }; properties: OSHighwayFeature; }[]; diff --git a/api.planx.uk/gis/local_authorities/metadata/braintree.js b/api.planx.uk/gis/local_authorities/metadata/braintree.js index d2ec5a6378..5cc21ab898 100644 --- a/api.planx.uk/gis/local_authorities/metadata/braintree.js +++ b/api.planx.uk/gis/local_authorities/metadata/braintree.js @@ -9,7 +9,7 @@ https://environment.data.gov.uk/arcgis/rest/services */ const braintreeDomain = "https://mapping.braintree.gov.uk/arcgis"; -const environmentDomain = "https://environment.data.gov.uk"; +const _environmentDomain = "https://environment.data.gov.uk"; const A4_KEY = "PARISH"; const planningConstraints = { @@ -20,7 +20,7 @@ const planningConstraints = { serverIndex: 0, fields: ["OBJECTID"], neg: "is not in, or within, a Listed Building", - pos: (data) => ({ + pos: (_data) => ({ text: "is, or is within, a Listed Building (Grade 1)", description: null, }), @@ -33,7 +33,7 @@ const planningConstraints = { serverIndex: 1, fields: ["OBJECTID"], neg: "is not in, or within, a Listed Building", - pos: (data) => ({ + pos: (_data) => ({ text: "is, or is within, a Listed Building (Grade 2)", description: null, }), @@ -46,7 +46,7 @@ const planningConstraints = { serverIndex: 2, fields: ["OBJECTID"], neg: "is not in, or within, a Listed Building", - pos: (data) => ({ + pos: (_data) => ({ text: "is, or is within, a Listed Building (Grade 2*)", description: null, }), @@ -59,7 +59,7 @@ const planningConstraints = { serverIndex: 3, fields: ["OBJECTID", "DESIGNATED"], neg: "is not in a Conservation Area", - pos: (data) => ({ + pos: (_data) => ({ text: "is in a Conservation Area", description: null, }), @@ -98,7 +98,7 @@ const planningConstraints = { serverIndex: 4, fields: ["OBJECTID"], neg: "is not in a TPO (Tree Preservation Order) Zone", - pos: (data) => ({ + pos: (_data) => ({ text: "is in a TPO (Tree Preservation Order) Zone", description: null, }), @@ -111,7 +111,7 @@ const planningConstraints = { serverIndex: 5, fields: ["OBJECTID", "AREAS"], neg: "is not in a TPO (Tree Preservation Order) Zone", - pos: (data) => ({ + pos: (_data) => ({ text: "is in a TPO (Tree Preservation Order) Zone", description: null, }), @@ -124,7 +124,7 @@ const planningConstraints = { serverIndex: 6, fields: ["OBJECTID", "SPECIES", "REFERENCE_"], neg: "is not in a TPO (Tree Preservation Order) Zone", - pos: (data) => ({ + pos: (_data) => ({ text: "is in a TPO (Tree Preservation Order) Zone", description: null, }), @@ -137,7 +137,7 @@ const planningConstraints = { serverIndex: 7, fields: ["OBJECTID", "GROUPS", "SPECIES"], neg: "is not in a TPO (Tree Preservation Order) Zone", - pos: (data) => ({ + pos: (_data) => ({ text: "is in a TPO (Tree Preservation Order) Zone", description: null, }), diff --git a/api.planx.uk/hasura/metadata/index.ts b/api.planx.uk/hasura/metadata/index.ts index 14829616b7..dc4937d45d 100644 --- a/api.planx.uk/hasura/metadata/index.ts +++ b/api.planx.uk/hasura/metadata/index.ts @@ -16,7 +16,7 @@ interface ScheduledEventArgs { }; webhook: string; schedule_at: Date; - payload: Record; + payload: Record; comment: string; } diff --git a/api.planx.uk/hasura/schema/index.ts b/api.planx.uk/hasura/schema/index.ts index 964190c9e0..15f2052aca 100644 --- a/api.planx.uk/hasura/schema/index.ts +++ b/api.planx.uk/hasura/schema/index.ts @@ -14,9 +14,9 @@ interface SchemaAPIQuery { * POST a request to the Hasura Schema API * https://hasura.io/docs/latest/api-reference/schema-api/index/ */ -const postToSchemaAPI = async ( +const postToSchemaAPI = async ( query: SchemaAPIQuery, -): Promise> => { +): Promise> => { try { return await Axios.post( process.env.HASURA_SCHEMA_URL!, @@ -35,13 +35,20 @@ const postToSchemaAPI = async ( } }; +/** + * https://hasura.io/docs/latest/api-reference/schema-api/run-sql/#response + */ +interface RunSQLResponse { + result?: string[][]; +} + /** * Run custom SQL via Hasura Schema API * https://hasura.io/docs/latest/api-reference/schema-api/run-sql/ */ export const runSQL = async (sql: string) => { try { - const response = await postToSchemaAPI({ + const response = await postToSchemaAPI({ type: "run_sql", args: { source: "default", diff --git a/api.planx.uk/helpers.test.ts b/api.planx.uk/helpers.test.ts index 653cec6185..2d10a91201 100644 --- a/api.planx.uk/helpers.test.ts +++ b/api.planx.uk/helpers.test.ts @@ -159,7 +159,9 @@ describe("dataMerged() function", () => { }); it("handles multiple external portal nodes", async () => { const result = await dataMerged("parent-id"); - const nodeTypes = Object.values(result).map((node) => node.type); + const nodeTypes = Object.values(result).map((node) => + "type" in node ? node.type : undefined, + ); const areAllPortalsFlattened = !nodeTypes.includes( ComponentType.ExternalPortal, ); diff --git a/api.planx.uk/helpers.ts b/api.planx.uk/helpers.ts index f0894a1098..0ab9bd860c 100644 --- a/api.planx.uk/helpers.ts +++ b/api.planx.uk/helpers.ts @@ -2,7 +2,7 @@ import { gql } from "graphql-request"; import { capitalize } from "lodash"; import { adminGraphQLClient as adminClient } from "./hasura"; import { Flow, Node } from "./types"; -import { ComponentType } from "@opensystemslab/planx-core/types"; +import { ComponentType, FlowGraph } from "@opensystemslab/planx-core/types"; // Get a flow's data (unflattened, without external portal nodes) const getFlowData = async (id: string): Promise => { @@ -135,7 +135,10 @@ const getPublishedFlowByDate = async (id: string, created_at: string) => { // Flatten a flow's data to include main content & portals in a single JSON representation // XXX: getFlowData & dataMerged are currently repeated in ../editor.planx.uk/src/lib/dataMergedHotfix.ts // in order to load frontend /preview routes for flows that are not published -const dataMerged = async (id: string, ob: Record = {}) => { +const dataMerged = async ( + id: string, + ob: { [key: string]: Node } = {}, +): Promise => { // get the primary flow data const { slug, data } = await getFlowData(id); @@ -172,7 +175,8 @@ const dataMerged = async (id: string, ob: Record = {}) => { else ob[nodeId] = node; } - return ob; + // TODO: Don't cast here once types updated across API + return ob as FlowGraph; }; /** diff --git a/api.planx.uk/inviteToPay/sendPaymentEmail.test.ts b/api.planx.uk/inviteToPay/sendPaymentEmail.test.ts index cc73b3cffc..32ec62f4c1 100644 --- a/api.planx.uk/inviteToPay/sendPaymentEmail.test.ts +++ b/api.planx.uk/inviteToPay/sendPaymentEmail.test.ts @@ -135,7 +135,7 @@ describe("Send email endpoint for invite to pay templates", () => { }); describe("'Payment Expiry' templates", () => { - const templates = ["payment-expiry", "payment-expiry-agent"]; + const _templates = ["payment-expiry", "payment-expiry-agent"]; it.todo( "soft deletes the payment_request when a payment expiry email is sent", ); diff --git a/api.planx.uk/modules/analytics/controller.ts b/api.planx.uk/modules/analytics/controller.ts index d47ec08e60..ebc618516f 100644 --- a/api.planx.uk/modules/analytics/controller.ts +++ b/api.planx.uk/modules/analytics/controller.ts @@ -4,7 +4,7 @@ import { ValidatedRequestHandler } from "../../shared/middleware/validate"; export const logAnalyticsSchema = z.object({ query: z.object({ - analyticsLogId: z.string(), + analyticsLogId: z.string().pipe(z.coerce.number()), }), }); @@ -13,14 +13,14 @@ export type LogAnalytics = ValidatedRequestHandler< Record >; -export const logUserExitController: LogAnalytics = async (req, res) => { - const { analyticsLogId } = req.query; - trackAnalyticsLogExit({ id: Number(analyticsLogId), isUserExit: true }); +export const logUserExitController: LogAnalytics = async (_req, res) => { + const { analyticsLogId } = res.locals.parsedReq.query; + trackAnalyticsLogExit({ id: analyticsLogId, isUserExit: true }); res.status(204).send(); }; -export const logUserResumeController: LogAnalytics = async (req, res) => { - const { analyticsLogId } = req.query; - trackAnalyticsLogExit({ id: Number(analyticsLogId), isUserExit: false }); +export const logUserResumeController: LogAnalytics = async (_req, res) => { + const { analyticsLogId } = res.locals.parsedReq.query; + trackAnalyticsLogExit({ id: analyticsLogId, isUserExit: false }); res.status(204).send(); }; diff --git a/api.planx.uk/modules/team/controller.ts b/api.planx.uk/modules/team/controller.ts index 4c12cf6e07..55ff9f1400 100644 --- a/api.planx.uk/modules/team/controller.ts +++ b/api.planx.uk/modules/team/controller.ts @@ -36,9 +36,9 @@ export type RemoveMember = ValidatedRequestHandler< TeamMemberResponse >; -export const addMember: UpsertMember = async (req, res, next) => { - const { teamSlug } = req.params; - const { userEmail, role } = req.body; +export const addMember: UpsertMember = async (_req, res, next) => { + const { teamSlug } = res.locals.parsedReq.params; + const { userEmail, role } = res.locals.parsedReq.body; try { await Service.addMember({ userEmail, teamSlug, role }); @@ -53,9 +53,9 @@ export const addMember: UpsertMember = async (req, res, next) => { } }; -export const changeMemberRole: UpsertMember = async (req, res, next) => { - const { teamSlug } = req.params; - const { userEmail, role } = req.body; +export const changeMemberRole: UpsertMember = async (_req, res, next) => { + const { teamSlug } = res.locals.parsedReq.params; + const { userEmail, role } = res.locals.parsedReq.body; try { await Service.changeMemberRole({ userEmail, teamSlug, role }); @@ -67,9 +67,9 @@ export const changeMemberRole: UpsertMember = async (req, res, next) => { } }; -export const removeMember: RemoveMember = async (req, res, next) => { - const { teamSlug } = req.params; - const { userEmail } = req.body; +export const removeMember: RemoveMember = async (_req, res, next) => { + const { teamSlug } = res.locals.parsedReq.params; + const { userEmail } = res.locals.parsedReq.body; try { await Service.removeMember({ userEmail, teamSlug }); diff --git a/api.planx.uk/modules/user/controller.ts b/api.planx.uk/modules/user/controller.ts index 9a3355c2d6..c61f3c7107 100644 --- a/api.planx.uk/modules/user/controller.ts +++ b/api.planx.uk/modules/user/controller.ts @@ -21,10 +21,11 @@ export type CreateUser = ValidatedRequestHandler< UserResponse >; -export const createUser: CreateUser = async (req, res, next) => { +export const createUser: CreateUser = async (_req, res, next) => { try { + const newUser = res.locals.parsedReq.body; const $client = getClient(); - await $client.user.create(req.body); + await $client.user.create(newUser); return res.send({ message: "Successfully created user" }); } catch (error) { return next( @@ -44,9 +45,9 @@ export type DeleteUser = ValidatedRequestHandler< UserResponse >; -export const deleteUser: DeleteUser = async (req, res, next) => { +export const deleteUser: DeleteUser = async (_req, res, next) => { try { - const { email } = req.params; + const { email } = res.locals.parsedReq.params; const $client = getClient(); const user = await $client.user.getByEmail(email); diff --git a/api.planx.uk/modules/webhooks/controller.ts b/api.planx.uk/modules/webhooks/controller.ts index 1621159778..edb43ac4eb 100644 --- a/api.planx.uk/modules/webhooks/controller.ts +++ b/api.planx.uk/modules/webhooks/controller.ts @@ -16,7 +16,7 @@ import { SanitiseApplicationData } from "./service/sanitiseApplicationData/types import { sanitiseApplicationData } from "./service/sanitiseApplicationData"; export const sendSlackNotificationController: SendSlackNotification = async ( - req, + _req, res, next, ) => { @@ -27,8 +27,9 @@ export const sendSlackNotificationController: SendSlackNotification = async ( }); } - const eventData = req.body.event.data.new; - const eventType = req.query.type; + const { body, query } = res.locals.parsedReq; + const eventData = body.event.data.new; + const eventType = query.type; try { const data = await sendSlackNotification(eventData, eventType); @@ -44,9 +45,11 @@ export const sendSlackNotificationController: SendSlackNotification = async ( }; export const createPaymentInvitationEventsController: CreatePaymentEventController = - async (req, res, next) => { + async (_req, res, next) => { try { - const response = await createPaymentInvitationEvents(req.body); + const response = await createPaymentInvitationEvents( + res.locals.parsedReq.body, + ); res.json(response); } catch (error) { return next( @@ -59,9 +62,11 @@ export const createPaymentInvitationEventsController: CreatePaymentEventControll }; export const createPaymentReminderEventsController: CreatePaymentEventController = - async (req, res, next) => { + async (_req, res, next) => { try { - const response = await createPaymentReminderEvents(req.body); + const response = await createPaymentReminderEvents( + res.locals.parsedReq.body, + ); res.json(response); } catch (error) { return next( @@ -74,9 +79,11 @@ export const createPaymentReminderEventsController: CreatePaymentEventController }; export const createPaymentExpiryEventsController: CreatePaymentEventController = - async (req, res, next) => { + async (_req, res, next) => { try { - const response = await createPaymentExpiryEvents(req.body); + const response = await createPaymentExpiryEvents( + res.locals.parsedReq.body, + ); res.json(response); } catch (error) { return next( @@ -89,9 +96,11 @@ export const createPaymentExpiryEventsController: CreatePaymentEventController = }; export const createSessionReminderEventController: CreateSessionEventController = - async (req, res, next) => { + async (_req, res, next) => { try { - const response = await createSessionReminderEvent(req.body); + const response = await createSessionReminderEvent( + res.locals.parsedReq.body, + ); res.json(response); } catch (error) { return next( @@ -104,9 +113,11 @@ export const createSessionReminderEventController: CreateSessionEventController }; export const createSessionExpiryEventController: CreateSessionEventController = - async (req, res, next) => { + async (_req, res, next) => { try { - const response = await createSessionExpiryEvent(req.body); + const response = await createSessionExpiryEvent( + res.locals.parsedReq.body, + ); res.json(response); } catch (error) { return next( diff --git a/api.planx.uk/modules/webhooks/docs.yaml b/api.planx.uk/modules/webhooks/docs.yaml index dc0d2af8d2..309976e562 100644 --- a/api.planx.uk/modules/webhooks/docs.yaml +++ b/api.planx.uk/modules/webhooks/docs.yaml @@ -123,6 +123,9 @@ components: properties: sessionId: type: string + email: + type: string + format: email responses: SlackNotificationSuccessMessage: content: diff --git a/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/index.test.ts b/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/index.test.ts index 8f557a4686..303310aa3b 100644 --- a/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/index.test.ts +++ b/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/index.test.ts @@ -32,8 +32,23 @@ describe("Create reminder event webhook", () => { it("returns a 400 if a required value is missing", async () => { const missingCreatedAt = { createdAt: null, payload: {} }; const missingPayload = { createdAt: new Date(), payload: null }; - - for (const body of [missingCreatedAt, missingPayload]) { + const missingEmail = { + createdAt: new Date(), + payload: { sessionId: "abc123" }, + }; + const missingSessionId = { + createdAt: new Date(), + payload: { email: "test@example.com" }, + }; + + const invalidRequestBodies = [ + missingCreatedAt, + missingPayload, + missingEmail, + missingSessionId, + ]; + + for (const body of invalidRequestBodies) { await post(ENDPOINT) .set({ Authorization: process.env.HASURA_PLANX_API_KEY }) .send(body) @@ -46,7 +61,10 @@ describe("Create reminder event webhook", () => { }); it("returns a 200 on successful event setup", async () => { - const body = { createdAt: new Date(), payload: { sessionId: "123" } }; + const body = { + createdAt: new Date(), + payload: { sessionId: "123", email: "test@example.com" }, + }; mockedCreateScheduledEvent.mockResolvedValue(mockScheduledEventResponse); await post(ENDPOINT) @@ -64,7 +82,10 @@ describe("Create reminder event webhook", () => { }); it("passes the correct arguments along to createScheduledEvent", async () => { - const body = { createdAt: new Date(), payload: { sessionId: "123" } }; + const body = { + createdAt: new Date(), + payload: { sessionId: "123", email: "test@example.com" }, + }; mockedCreateScheduledEvent.mockResolvedValue(mockScheduledEventResponse); await post(ENDPOINT) @@ -96,7 +117,10 @@ describe("Create reminder event webhook", () => { }); it("returns a 500 on event setup failure", async () => { - const body = { createdAt: new Date(), payload: { sessionId: "123" } }; + const body = { + createdAt: new Date(), + payload: { sessionId: "123", email: "test@example.com" }, + }; mockedCreateScheduledEvent.mockRejectedValue(new Error("Failed!")); await post(ENDPOINT) @@ -143,7 +167,10 @@ describe("Create expiry event webhook", () => { }); it("returns a 200 on successful event setup", async () => { - const body = { createdAt: new Date(), payload: { sessionId: "123" } }; + const body = { + createdAt: new Date(), + payload: { sessionId: "123", email: "test@example.com" }, + }; mockedCreateScheduledEvent.mockResolvedValue(mockScheduledEventResponse); await post(ENDPOINT) @@ -151,12 +178,16 @@ describe("Create expiry event webhook", () => { .send(body) .expect(200) .then((response) => { + // expect(mockedCreateScheduledEvent).toHaveBeenCalledWith(body.p) expect(response.body).toStrictEqual([mockScheduledEventResponse]); }); }); it("passes the correct arguments along to createScheduledEvent", async () => { - const body = { createdAt: new Date(), payload: { sessionId: "123" } }; + const body = { + createdAt: new Date(), + payload: { sessionId: "123", email: "test@example.com" }, + }; mockedCreateScheduledEvent.mockResolvedValue(mockScheduledEventResponse); await post(ENDPOINT) @@ -173,7 +204,10 @@ describe("Create expiry event webhook", () => { }); it("returns a 500 on event setup failure", async () => { - const body = { createdAt: new Date(), payload: { sessionId: "123" } }; + const body = { + createdAt: new Date(), + payload: { sessionId: "123", email: "test@example.com" }, + }; mockedCreateScheduledEvent.mockRejectedValue(new Error("Failed!")); await post(ENDPOINT) diff --git a/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/schema.ts b/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/schema.ts index 2f35683b0e..f8b17aa24f 100644 --- a/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/schema.ts +++ b/api.planx.uk/modules/webhooks/service/lowcalSessionEvents/schema.ts @@ -7,6 +7,7 @@ export const createSessionEventSchema = z.object({ createdAt: z.string().pipe(z.coerce.date()), payload: z.object({ sessionId: z.string(), + email: z.string().email(), }), }), }); diff --git a/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/index.ts b/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/index.ts index af9cecf7c3..5c730e9241 100644 --- a/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/index.ts +++ b/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/index.ts @@ -1,5 +1,4 @@ import SlackNotify from "slack-notify"; -import { Request, Response, NextFunction } from "express"; import { getOperations, operationHandler } from "./operations"; import { OperationResult } from "./types"; diff --git a/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/operations.ts b/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/operations.ts index 7e27194681..31d29ed578 100644 --- a/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/operations.ts +++ b/api.planx.uk/modules/webhooks/service/sanitiseApplicationData/operations.ts @@ -258,7 +258,7 @@ export const deleteHasuraEventLogs: Operation = async () => { AND created_at < now() - interval '6 months' RETURNING id; `); - const [_column_name, ...ids] = response.result.flat(); + const [_column_name, ...ids] = response.result?.flat() || []; return ids; }; @@ -275,6 +275,6 @@ export const deleteHasuraScheduledEventsForSubmittedSessions: Operation = ) RETURNING hse.id; `); - const [_column_name, ...ids] = response.result.flat(); + const [_column_name, ...ids] = response?.result?.flat() || []; return ids; }; diff --git a/api.planx.uk/package.json b/api.planx.uk/package.json index 2a7eaa6d57..bca0df088c 100644 --- a/api.planx.uk/package.json +++ b/api.planx.uk/package.json @@ -4,11 +4,11 @@ "private": true, "dependencies": { "@airbrake/node": "^2.1.8", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#69c797c", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#c923d21", "@types/isomorphic-fetch": "^0.0.36", "adm-zip": "^0.5.10", "aws-sdk": "^2.1467.0", - "axios": "^1.4.0", + "axios": "^1.6.0", "body-parser": "^1.20.2", "cookie-parser": "^1.4.6", "cookie-session": "^1.4.0", @@ -42,6 +42,7 @@ "string-to-stream": "^3.0.1", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.0", + "type-fest": "^4.6.0", "zod": "^3.22.3" }, "scripts": { @@ -65,7 +66,7 @@ }, "devDependencies": { "@babel/core": "^7.22.10", - "@babel/preset-typescript": "^7.22.5", + "@babel/preset-typescript": "^7.23.2", "@types/adm-zip": "^0.5.0", "@types/body-parser": "^1.19.2", "@types/cookie-parser": "^1.4.3", @@ -93,13 +94,13 @@ "esbuild-jest": "^0.5.0", "eslint": "^8.47.0", "eslint-config-prettier": "^8.8.0", - "eslint-plugin-jest": "^27.2.2", + "eslint-plugin-jest": "^27.6.0", "graphql-query-test-mock": "^0.12.1", "jest": "^29.7.0", "json-stringify-pretty-compact": "^3.0.0", - "lint-staged": "^14.0.0", + "lint-staged": "^15.0.2", "nock": "^13.3.2", - "node-dev": "^7.4.3", + "node-dev": "^8.0.0", "prettier": "^3.0.2", "rimraf": "^3.0.2", "supertest": "^6.3.3", diff --git a/api.planx.uk/pnpm-lock.yaml b/api.planx.uk/pnpm-lock.yaml index 14a6d16250..96776332d9 100644 --- a/api.planx.uk/pnpm-lock.yaml +++ b/api.planx.uk/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^2.1.8 version: 2.1.8 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#69c797c - version: github.com/theopensystemslab/planx-core/69c797c + specifier: git+https://github.com/theopensystemslab/planx-core#c923d21 + version: github.com/theopensystemslab/planx-core/c923d21 '@types/isomorphic-fetch': specifier: ^0.0.36 version: 0.0.36 @@ -21,8 +21,8 @@ dependencies: specifier: ^2.1467.0 version: 2.1467.0 axios: - specifier: ^1.4.0 - version: 1.4.0 + specifier: ^1.6.0 + version: 1.6.0 body-parser: specifier: ^1.20.2 version: 1.20.2 @@ -122,6 +122,9 @@ dependencies: swagger-ui-express: specifier: ^5.0.0 version: 5.0.0(express@4.18.2) + type-fest: + specifier: ^4.6.0 + version: 4.6.0 zod: specifier: ^3.22.3 version: 3.22.3 @@ -131,8 +134,8 @@ devDependencies: specifier: ^7.22.10 version: 7.22.10 '@babel/preset-typescript': - specifier: ^7.22.5 - version: 7.22.5(@babel/core@7.22.10) + specifier: ^7.23.2 + version: 7.23.2(@babel/core@7.22.10) '@types/adm-zip': specifier: ^0.5.0 version: 0.5.0 @@ -215,8 +218,8 @@ devDependencies: specifier: ^8.8.0 version: 8.8.0(eslint@8.47.0) eslint-plugin-jest: - specifier: ^27.2.2 - version: 27.2.2(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.47.0)(jest@29.7.0)(typescript@5.1.6) + specifier: ^27.6.0 + version: 27.6.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.47.0)(jest@29.7.0)(typescript@5.1.6) graphql-query-test-mock: specifier: ^0.12.1 version: 0.12.1(nock@13.3.2) @@ -227,14 +230,14 @@ devDependencies: specifier: ^3.0.0 version: 3.0.0 lint-staged: - specifier: ^14.0.0 - version: 14.0.0 + specifier: ^15.0.2 + version: 15.0.2 nock: specifier: ^13.3.2 version: 13.3.2 node-dev: - specifier: ^7.4.3 - version: 7.4.3 + specifier: ^8.0.0 + version: 8.0.0 prettier: specifier: ^3.0.2 version: 3.0.2 @@ -334,6 +337,7 @@ packages: dependencies: '@babel/highlight': 7.22.10 chalk: 2.4.2 + dev: true /@babel/code-frame@7.22.13: resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} @@ -341,7 +345,6 @@ packages: dependencies: '@babel/highlight': 7.22.20 chalk: 2.4.2 - dev: true /@babel/compat-data@7.22.9: resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} @@ -395,7 +398,7 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@babel/helper-compilation-targets@7.22.10: @@ -409,24 +412,22 @@ packages: semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.22.6(@babel/core@7.22.10): - resolution: {integrity: sha512-iwdzgtSiBxF6ni6mzVnZCF3xt5qE6cEA0J7nFt8QOAWZ0zjCFceEgpn3vtb2V7WFR6QzP2jmIFOHMTRo7eNJjQ==} + /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.22.10): + resolution: {integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.22.10 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 - '@babel/helper-member-expression-to-functions': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-function-name': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.5 + '@babel/helper-replace-supers': 7.22.20(@babel/core@7.22.10) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@nicolo-ribaudo/semver-v6': 6.3.3 - transitivePeerDependencies: - - supports-color + semver: 6.3.1 dev: true /@babel/helper-environment-visitor@7.22.20: @@ -439,14 +440,6 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.22.10 - dev: true - /@babel/helper-function-name@7.23.0: resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} @@ -462,54 +455,59 @@ packages: '@babel/types': 7.23.0 dev: true - /@babel/helper-member-expression-to-functions@7.22.5: - resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==} + /@babel/helper-member-expression-to-functions@7.23.0: + resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true + /@babel/helper-module-imports@7.22.15: + resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + /@babel/helper-module-imports@7.22.5: resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 + dev: true - /@babel/helper-module-transforms@7.22.5: - resolution: {integrity: sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==} + /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.10): + resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 dependencies: + '@babel/core': 7.22.10 '@babel/helper-environment-visitor': 7.22.5 '@babel/helper-module-imports': 7.22.5 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 '@babel/helper-validator-identifier': 7.22.5 - '@babel/template': 7.22.5 - '@babel/traverse': 7.23.2 - '@babel/types': 7.22.10 - transitivePeerDependencies: - - supports-color dev: true - /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.10): - resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} + /@babel/helper-module-transforms@7.23.0(@babel/core@7.22.10): + resolution: {integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 dev: true /@babel/helper-optimise-call-expression@7.22.5: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@babel/helper-plugin-utils@7.22.5: @@ -517,32 +515,30 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-replace-supers@7.22.5: - resolution: {integrity: sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==} + /@babel/helper-replace-supers@7.22.20(@babel/core@7.22.10): + resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 dependencies: - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-member-expression-to-functions': 7.22.5 + '@babel/core': 7.22.10 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.23.0 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/template': 7.22.5 - '@babel/traverse': 7.23.2 - '@babel/types': 7.22.10 - transitivePeerDependencies: - - supports-color dev: true /@babel/helper-simple-access@7.22.5: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@babel/helper-split-export-declaration@7.22.6: @@ -559,11 +555,16 @@ packages: /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-identifier@7.22.5: resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.22.15: + resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==} + engines: {node: '>=6.9.0'} + dev: true /@babel/helper-validator-option@7.22.5: resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} @@ -585,9 +586,10 @@ packages: resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 + dev: true /@babel/highlight@7.22.20: resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} @@ -596,7 +598,6 @@ packages: '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 - dev: true /@babel/parser@7.22.10: resolution: {integrity: sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==} @@ -750,42 +751,48 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.22.10 - '@babel/helper-module-transforms': 7.22.5 + '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.10) + '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-simple-access': 7.22.5 + dev: true + + /@babel/plugin-transform-modules-commonjs@7.23.0(@babel/core@7.22.10): + resolution: {integrity: sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.22.10 + '@babel/helper-module-transforms': 7.23.0(@babel/core@7.22.10) '@babel/helper-plugin-utils': 7.22.5 '@babel/helper-simple-access': 7.22.5 - transitivePeerDependencies: - - supports-color dev: true - /@babel/plugin-transform-typescript@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-SMubA9S7Cb5sGSFFUlqxyClTA9zWJ8qGQrppNUm05LtFuN1ELRFNndkix4zUJrC9F+YivWwa1dHMSyo0e0N9dA==} + /@babel/plugin-transform-typescript@7.22.15(@babel/core@7.22.10): + resolution: {integrity: sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.22.10 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.6(@babel/core@7.22.10) + '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.22.10) '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10) - transitivePeerDependencies: - - supports-color dev: true - /@babel/preset-typescript@7.22.5(@babel/core@7.22.10): - resolution: {integrity: sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==} + /@babel/preset-typescript@7.23.2(@babel/core@7.22.10): + resolution: {integrity: sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.22.10 '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.5 + '@babel/helper-validator-option': 7.22.15 '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-modules-commonjs': 7.22.5(@babel/core@7.22.10) - '@babel/plugin-transform-typescript': 7.22.5(@babel/core@7.22.10) - transitivePeerDependencies: - - supports-color + '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.22.10) + '@babel/plugin-transform-typescript': 7.22.15(@babel/core@7.22.10) dev: true /@babel/runtime@7.22.6: @@ -845,6 +852,7 @@ packages: '@babel/helper-string-parser': 7.22.5 '@babel/helper-validator-identifier': 7.22.5 to-fast-properties: 2.0.0 + dev: true /@babel/types@7.23.0: resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} @@ -853,7 +861,6 @@ packages: '@babel/helper-string-parser': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - dev: true /@bcherny/json-schema-ref-parser@10.0.5-fork: resolution: {integrity: sha512-E/jKbPoca1tfUPj3iSbitDZTGnq6FUFjkH6L8U2oDwSuwK1WhnnVtCG7oFOTg/DDnyoXbQYUiUiGOibHqaGVnw==} @@ -888,7 +895,7 @@ packages: /@emotion/babel-plugin@11.11.0: resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} dependencies: - '@babel/helper-module-imports': 7.22.5 + '@babel/helper-module-imports': 7.22.15 '@babel/runtime': 7.23.2 '@emotion/hash': 0.9.1 '@emotion/memoize': 0.8.1 @@ -1839,11 +1846,6 @@ packages: react-is: 18.2.0 dev: false - /@nicolo-ribaudo/semver-v6@6.3.3: - resolution: {integrity: sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==} - hasBin: true - dev: true - /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1907,8 +1909,8 @@ packages: /@types/babel__core@7.20.1: resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==} dependencies: - '@babel/parser': 7.22.10 - '@babel/types': 7.22.10 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.20.1 @@ -1917,20 +1919,20 @@ packages: /@types/babel__generator@7.6.4: resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.22.10 - '@babel/types': 7.22.10 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 dev: true /@types/babel__traverse@7.20.1: resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==} dependencies: - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 dev: true /@types/body-parser@1.19.2: @@ -1994,8 +1996,8 @@ packages: '@types/qs': 6.9.7 '@types/serve-static': 1.15.2 - /@types/geojson@7946.0.11: - resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} + /@types/geojson@7946.0.12: + resolution: {integrity: sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==} dev: false /@types/glob@7.2.0: @@ -2640,8 +2642,8 @@ packages: - debug dev: false - /axios@1.4.0: - resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==} + /axios@1.6.0: + resolution: {integrity: sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==} dependencies: follow-redirects: 1.15.2 form-data: 4.0.0 @@ -2704,8 +2706,8 @@ packages: resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==} engines: {node: '>= 10.14.2'} dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.22.10 + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 '@types/babel__core': 7.20.1 '@types/babel__traverse': 7.20.1 dev: true @@ -2714,8 +2716,8 @@ packages: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.22.10 + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 '@types/babel__core': 7.20.1 '@types/babel__traverse': 7.20.1 dev: true @@ -3151,8 +3153,8 @@ packages: dependencies: delayed-stream: 1.0.0 - /commander@11.0.0: - resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} + /commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} dev: true @@ -3716,11 +3718,11 @@ packages: eslint: 8.47.0 dev: true - /eslint-plugin-jest@27.2.2(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.47.0)(jest@29.7.0)(typescript@5.1.6): - resolution: {integrity: sha512-euzbp06F934Z7UDl5ZUaRPLAc9MKjh0rMPERrHT7UhlCEwgb25kBj37TvMgWeHZVkR5I9CayswrpoaqZU1RImw==} + /eslint-plugin-jest@27.6.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint@8.47.0)(jest@29.7.0)(typescript@5.1.6): + resolution: {integrity: sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: - '@typescript-eslint/eslint-plugin': ^5.0.0 + '@typescript-eslint/eslint-plugin': ^5.0.0 || ^6.0.0 eslint: ^7.0.0 || ^8.0.0 jest: '*' peerDependenciesMeta: @@ -3955,18 +3957,18 @@ packages: strip-final-newline: 2.0.0 dev: true - /execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} dependencies: cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 + 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: 3.0.7 + signal-exit: 4.1.0 strip-final-newline: 3.0.0 dev: true @@ -4382,6 +4384,11 @@ packages: engines: {node: '>=10'} dev: true + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + /get-value@2.0.6: resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} @@ -4669,9 +4676,9 @@ packages: engines: {node: '>=10.17.0'} dev: true - /human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} dev: true /husky@8.0.3: @@ -4998,7 +5005,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.22.10 - '@babel/parser': 7.22.10 + '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.1 @@ -5011,7 +5018,7 @@ packages: engines: {node: '>=10'} dependencies: '@babel/core': 7.22.10 - '@babel/parser': 7.22.10 + '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 7.5.4 @@ -5304,7 +5311,7 @@ packages: resolution: {integrity: sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/code-frame': 7.22.10 + '@babel/code-frame': 7.22.13 '@jest/types': 29.6.1 '@types/stack-utils': 2.0.1 chalk: 4.1.2 @@ -5319,7 +5326,7 @@ packages: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/code-frame': 7.22.10 + '@babel/code-frame': 7.22.13 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 @@ -5461,7 +5468,7 @@ packages: '@babel/generator': 7.22.10 '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10) - '@babel/types': 7.22.10 + '@babel/types': 7.23.0 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 @@ -5770,34 +5777,28 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - /lint-staged@14.0.0: - resolution: {integrity: sha512-0tLf0pqZYkar/wu3nTctk4rVIG+d7PanDYv4/IQR4qwdqfQkTDziLRFnqMcLuLBTuUqmcLwsHPD2EjQ18d/oaA==} - engines: {node: ^16.14.0 || >=18.0.0} + /lint-staged@15.0.2: + resolution: {integrity: sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==} + engines: {node: '>=18.12.0'} hasBin: true dependencies: chalk: 5.3.0 - commander: 11.0.0 + commander: 11.1.0 debug: 4.3.4 - execa: 7.2.0 + execa: 8.0.1 lilconfig: 2.1.0 - listr2: 6.6.1 + listr2: 7.0.2 micromatch: 4.0.5 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.3.1 + yaml: 2.3.3 transitivePeerDependencies: - - enquirer - supports-color dev: true - /listr2@6.6.1: - resolution: {integrity: sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==} + /listr2@7.0.2: + resolution: {integrity: sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==} engines: {node: '>=16.0.0'} - peerDependencies: - enquirer: '>= 2.3.0 < 3' - peerDependenciesMeta: - enquirer: - optional: true dependencies: cli-truncate: 3.1.0 colorette: 2.0.20 @@ -6173,9 +6174,9 @@ packages: - supports-color dev: true - /node-dev@7.4.3: - resolution: {integrity: sha512-o8aYipN28xY+WEunMHHiNc3hpPSkGG8ulHyYBapNbkg4dQxohmhx6jiRbiFhTF6zy+5IwljUGv1EcuxsaWI4Bw==} - engines: {node: '>=12'} + /node-dev@8.0.0: + resolution: {integrity: sha512-GXc0KxmBXfQxMPdymOui40yvC5W/RXFwmuUDT65wvTAO/o9wAsddYC8q4EHKxq3Qqt+uLS/g7XKdgVcsjyk9lw==} + engines: {node: '>=14'} hasBin: true dependencies: dateformat: 3.0.3 @@ -6426,7 +6427,7 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} dependencies: - '@babel/code-frame': 7.22.10 + '@babel/code-frame': 7.22.13 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -7180,6 +7181,11 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true @@ -7782,8 +7788,8 @@ packages: engines: {node: '>=10'} dev: true - /type-fest@4.4.0: - resolution: {integrity: sha512-HT3RRs7sTfY22KuPQJkD/XjbTbxgP2Je5HPt6H6JEGvcjHd5Lqru75EbrP3tb4FYjNJ+DjLp+MNQTFQU0mhXNw==} + /type-fest@4.6.0: + resolution: {integrity: sha512-rLjWJzQFOq4xw7MgJrCZ6T1jIOvvYElXT12r+y0CC6u67hegDHaxcPqb2fZHOGlqxugGQPNB1EnTezjBetkwkw==} engines: {node: '>=16'} dev: false @@ -8078,8 +8084,8 @@ packages: engines: {node: '>= 6'} dev: false - /yaml@2.3.1: - resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + /yaml@2.3.3: + resolution: {integrity: sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==} engines: {node: '>= 14'} dev: true @@ -8148,8 +8154,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/69c797c: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/69c797c} + github.com/theopensystemslab/planx-core/c923d21: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/c923d21} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -8158,7 +8164,7 @@ packages: '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) '@mui/material': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.11 + '@types/geojson': 7946.0.12 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) copyfiles: 2.4.1 @@ -8182,7 +8188,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) striptags: 3.2.0 - type-fest: 4.4.0 + type-fest: 4.6.0 uuid: 9.0.1 zod: 3.22.4 transitivePeerDependencies: diff --git a/api.planx.uk/saveAndReturn/utils.ts b/api.planx.uk/saveAndReturn/utils.ts index b9409058e0..03fc21adad 100644 --- a/api.planx.uk/saveAndReturn/utils.ts +++ b/api.planx.uk/saveAndReturn/utils.ts @@ -136,7 +136,7 @@ const validateSingleSessionRequest = async ( interface SessionDetails { hasUserSaved: boolean; - address: any; + address: string; projectType: string; id: string; expiryDate: string; diff --git a/api.planx.uk/shared/middleware/validate.ts b/api.planx.uk/shared/middleware/validate.ts index b2b740df00..037ed02a31 100644 --- a/api.planx.uk/shared/middleware/validate.ts +++ b/api.planx.uk/shared/middleware/validate.ts @@ -19,11 +19,9 @@ export const validate = query: req.query, }); - // Assign parsed values to the request object + // Assign parsed values to the response object // Required for schemas to transform or coerce raw requests - req.params = parsedReq.params; - req.body = parsedReq.body; - req.query = parsedReq.query; + res.locals.parsedReq = parsedReq; return next(); } catch (error) { @@ -40,8 +38,9 @@ export type ValidatedRequestHandler< TSchema extends ZodSchema, TResponse, > = RequestHandler< - z.infer["params"], + Request["params"], TResponse, - z.infer["body"], - z.infer["query"] + Request["body"], + Request["query"], + { parsedReq: z.infer } >; diff --git a/api.planx.uk/types.ts b/api.planx.uk/types.ts index b7e17693f8..f1be34cf00 100644 --- a/api.planx.uk/types.ts +++ b/api.planx.uk/types.ts @@ -1,6 +1,9 @@ import { PaymentRequest } from "@opensystemslab/planx-core/dist/types"; import { GovUKPayment } from "@opensystemslab/planx-core/types"; +/** + * @deprecated Migrating to Node from planx-core + */ export interface Node { id?: string; data?: Record; @@ -8,6 +11,9 @@ export interface Node { type?: number; } +/** + * @deprecated Migrating to Flow and FlowGraph from planx-core + */ export interface Flow { id: string; slug: string; @@ -94,7 +100,7 @@ type MinimumNotifyPersonalisation = { interface SaveAndReturnNotifyPersonalisation extends MinimumNotifyPersonalisation { - address?: any; + address?: string; projectType?: string; resumeLink?: string; teamName: string; diff --git a/e2e/package.json b/e2e/package.json index 3dbd4eb380..0165f90eef 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -24,9 +24,9 @@ "@types/node": "18.16.1", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", - "eslint": "^8.50.0", + "eslint": "^8.52.0", "husky": "^8.0.3", - "lint-staged": "^14.0.1", + "lint-staged": "^15.0.2", "prettier": "^3.0.2", "typescript": "^5.2.2" } diff --git a/e2e/pnpm-lock.yaml b/e2e/pnpm-lock.yaml index f2085a4d85..56712cbb69 100644 --- a/e2e/pnpm-lock.yaml +++ b/e2e/pnpm-lock.yaml @@ -10,19 +10,19 @@ devDependencies: version: 18.16.1 '@typescript-eslint/eslint-plugin': specifier: ^5.62.0 - version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.50.0)(typescript@5.2.2) + version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.52.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: ^5.62.0 - version: 5.62.0(eslint@8.50.0)(typescript@5.2.2) + version: 5.62.0(eslint@8.52.0)(typescript@5.2.2) eslint: - specifier: ^8.50.0 - version: 8.50.0 + specifier: ^8.52.0 + version: 8.52.0 husky: specifier: ^8.0.3 version: 8.0.3 lint-staged: - specifier: ^14.0.1 - version: 14.0.1 + specifier: ^15.0.2 + version: 15.0.2 prettier: specifier: ^3.0.2 version: 3.0.2 @@ -37,13 +37,13 @@ packages: engines: {node: '>=0.10.0'} dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.52.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: - eslint: 8.50.0 + eslint: 8.52.0 eslint-visitor-keys: 3.4.3 dev: true @@ -74,16 +74,16 @@ packages: - supports-color dev: true - /@eslint/js@8.50.0: - resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==} + /@eslint/js@8.52.0: + resolution: {integrity: sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@humanwhocodes/config-array@0.11.11: - resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 1.2.1 + '@humanwhocodes/object-schema': 2.0.1 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -95,8 +95,8 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true /@nodelib/fs.scandir@2.1.5: @@ -132,7 +132,7 @@ packages: resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} dev: true - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.52.0)(typescript@5.2.2): resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -144,12 +144,12 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/parser': 5.62.0(eslint@8.52.0)(typescript@5.2.2) '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/type-utils': 5.62.0(eslint@8.52.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.52.0)(typescript@5.2.2) debug: 4.3.4 - eslint: 8.50.0 + eslint: 8.52.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare-lite: 1.4.0 @@ -160,7 +160,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@5.62.0(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/parser@5.62.0(eslint@8.52.0)(typescript@5.2.2): resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -174,7 +174,7 @@ packages: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) debug: 4.3.4 - eslint: 8.50.0 + eslint: 8.52.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -188,7 +188,7 @@ packages: '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/type-utils@5.62.0(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/type-utils@5.62.0(eslint@8.52.0)(typescript@5.2.2): resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -199,9 +199,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) - '@typescript-eslint/utils': 5.62.0(eslint@8.50.0)(typescript@5.2.2) + '@typescript-eslint/utils': 5.62.0(eslint@8.52.0)(typescript@5.2.2) debug: 4.3.4 - eslint: 8.50.0 + eslint: 8.52.0 tsutils: 3.21.0(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: @@ -234,19 +234,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.50.0)(typescript@5.2.2): + /@typescript-eslint/utils@5.62.0(eslint@8.52.0)(typescript@5.2.2): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.0 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2) - eslint: 8.50.0 + eslint: 8.52.0 eslint-scope: 5.1.1 semver: 7.5.3 transitivePeerDependencies: @@ -262,6 +262,10 @@ packages: eslint-visitor-keys: 3.4.3 dev: true + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + /acorn-jsx@5.3.2(acorn@8.10.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -389,8 +393,8 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true - /commander@11.0.0: - resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} + /commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} dev: true @@ -471,18 +475,19 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.50.0: - resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==} + /eslint@8.52.0: + resolution: {integrity: sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.52.0) '@eslint-community/regexpp': 4.6.2 '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.50.0 - '@humanwhocodes/config-array': 0.11.11 + '@eslint/js': 8.52.0 + '@humanwhocodes/config-array': 0.11.13 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 @@ -559,18 +564,18 @@ packages: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} dev: true - /execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} dependencies: cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 + 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: 3.0.7 + signal-exit: 4.1.0 strip-final-newline: 3.0.0 dev: true @@ -641,9 +646,9 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} dev: true /glob-parent@5.1.2: @@ -699,9 +704,9 @@ packages: engines: {node: '>=8'} dev: true - /human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} dev: true /husky@8.0.3: @@ -803,34 +808,28 @@ packages: engines: {node: '>=10'} dev: true - /lint-staged@14.0.1: - resolution: {integrity: sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==} - engines: {node: ^16.14.0 || >=18.0.0} + /lint-staged@15.0.2: + resolution: {integrity: sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==} + engines: {node: '>=18.12.0'} hasBin: true dependencies: chalk: 5.3.0 - commander: 11.0.0 + commander: 11.1.0 debug: 4.3.4 - execa: 7.2.0 + execa: 8.0.1 lilconfig: 2.1.0 - listr2: 6.6.1 + listr2: 7.0.2 micromatch: 4.0.5 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.3.1 + yaml: 2.3.3 transitivePeerDependencies: - - enquirer - supports-color dev: true - /listr2@6.6.1: - resolution: {integrity: sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==} + /listr2@7.0.2: + resolution: {integrity: sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==} engines: {node: '>=16.0.0'} - peerDependencies: - enquirer: '>= 2.3.0 < 3' - peerDependenciesMeta: - enquirer: - optional: true dependencies: cli-truncate: 3.1.0 colorette: 2.0.20 @@ -1089,6 +1088,11 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -1226,8 +1230,8 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yaml@2.3.1: - resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} + /yaml@2.3.3: + resolution: {integrity: sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==} engines: {node: '>= 14'} dev: true diff --git a/e2e/tests/api-driven/package.json b/e2e/tests/api-driven/package.json index d9240d2846..af557fb73c 100644 --- a/e2e/tests/api-driven/package.json +++ b/e2e/tests/api-driven/package.json @@ -6,7 +6,7 @@ }, "dependencies": { "@cucumber/cucumber": "^9.3.0", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#69c797c", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#c923d21", "axios": "^1.4.0", "dotenv": "^16.3.1", "dotenv-expand": "^10.0.0", diff --git a/e2e/tests/api-driven/pnpm-lock.yaml b/e2e/tests/api-driven/pnpm-lock.yaml index b0852544da..2ad27a0b85 100644 --- a/e2e/tests/api-driven/pnpm-lock.yaml +++ b/e2e/tests/api-driven/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#69c797c - version: github.com/theopensystemslab/planx-core/69c797c + specifier: git+https://github.com/theopensystemslab/planx-core#c923d21 + version: github.com/theopensystemslab/planx-core/c923d21 axios: specifier: ^1.4.0 version: 1.4.0 @@ -694,8 +694,8 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true - /@types/geojson@7946.0.11: - resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} + /@types/geojson@7946.0.12: + resolution: {integrity: sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==} dev: false /@types/glob@7.2.0: @@ -2711,8 +2711,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/69c797c: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/69c797c} + github.com/theopensystemslab/planx-core/c923d21: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/c923d21} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2721,7 +2721,7 @@ packages: '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) '@mui/material': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.11 + '@types/geojson': 7946.0.12 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) copyfiles: 2.4.1 diff --git a/e2e/tests/api-driven/src/globalHelpers.ts b/e2e/tests/api-driven/src/globalHelpers.ts index 9894013afd..252cbb1e90 100644 --- a/e2e/tests/api-driven/src/globalHelpers.ts +++ b/e2e/tests/api-driven/src/globalHelpers.ts @@ -1,4 +1,4 @@ -import { TEST_EMAIL } from "../../ui-driven/src/helpers"; +import { TEST_EMAIL } from "../../ui-driven/src/globalHelpers"; import { $admin } from "./client"; export function createTeam( diff --git a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts index 343ccb0872..bb906a1656 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts @@ -12,7 +12,7 @@ import { mockPassport, } from "./mocks"; import { $admin } from "../client"; -import { TEST_EMAIL } from "../../../ui-driven/src/helpers"; +import { TEST_EMAIL } from "../../../ui-driven/src/globalHelpers"; import { createTeam, createUser } from "../globalHelpers"; export async function setUpMocks() { diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index e409d89dcd..dcc7a25d75 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -8,7 +8,7 @@ "postinstall": "./install-dependencies.sh" }, "dependencies": { - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#69c797c", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#c923d21", "axios": "^1.4.0", "dotenv": "^16.3.1", "eslint": "^8.44.0", diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index da1e6d7790..f9c90cf0a1 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#69c797c - version: github.com/theopensystemslab/planx-core/69c797c + specifier: git+https://github.com/theopensystemslab/planx-core#c923d21 + version: github.com/theopensystemslab/planx-core/c923d21 axios: specifier: ^1.4.0 version: 1.4.0 @@ -527,8 +527,8 @@ packages: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false - /@types/geojson@7946.0.11: - resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} + /@types/geojson@7946.0.12: + resolution: {integrity: sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==} dev: false /@types/glob@7.2.0: @@ -2494,8 +2494,8 @@ packages: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} dev: false - github.com/theopensystemslab/planx-core/69c797c: - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/69c797c} + github.com/theopensystemslab/planx-core/c923d21: + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/c923d21} name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -2504,7 +2504,7 @@ packages: '@emotion/react': 11.11.1(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(react@18.2.0) '@mui/material': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.11 + '@types/geojson': 7946.0.12 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) copyfiles: 2.4.1 diff --git a/e2e/tests/ui-driven/src/context.ts b/e2e/tests/ui-driven/src/context.ts index 88972b80dd..8dffe08045 100644 --- a/e2e/tests/ui-driven/src/context.ts +++ b/e2e/tests/ui-driven/src/context.ts @@ -1,5 +1,5 @@ import assert from "node:assert"; -import { log } from "./helpers"; +import { log } from "./globalHelpers"; import { sign } from "jsonwebtoken"; import { CoreDomainClient } from "@opensystemslab/planx-core"; import { GraphQLClient, gql } from "graphql-request"; diff --git a/e2e/tests/ui-driven/src/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts similarity index 84% rename from e2e/tests/ui-driven/src/create-flow.spec.ts rename to e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 4c522859c6..bb8b3aba97 100644 --- a/e2e/tests/ui-driven/src/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -3,14 +3,14 @@ import { contextDefaults, setUpTestContext, tearDownTestContext, -} from "./context"; +} from "../context"; import { - getTeamPage, createAuthenticatedSession, answerQuestion, clickContinue, -} from "./helpers"; -import type { Context } from "./context"; +} from "../globalHelpers"; +import type { Context } from "../context"; +import { getTeamPage, isGetUserRequest } from "./helpers"; test.describe("Navigation", () => { let context: Context = { @@ -43,32 +43,26 @@ test.describe("Navigation", () => { userId: context.user!.id!, }); - let getUserRequestCount = 0; - page.on("request", (req) => { - const isHasuraRequest = req.url().includes("/graphql"); - const isGetUserRequest = - isHasuraRequest && req.postData()?.toString().includes("GetUserById"); + const initialRequest = page.waitForRequest(isGetUserRequest); - if (isGetUserRequest) getUserRequestCount++; - }); + Promise.all([await page.goto("/"), await initialRequest]); - await page.goto("/"); - await page.waitForLoadState("networkidle"); + const team = page.locator("h2", { hasText: context.team.name }); - // Get user data on initial page load - expect(getUserRequestCount).toBe(1); + let isRepeatedRequestMade = false; + page.on( + "request", + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + ); - const team = page.locator("h2", { hasText: context.team.name }); - team.click(); - await page.waitForLoadState("networkidle"); + Promise.all([ + await team.click(), + expect(isRepeatedRequestMade).toBe(false), + ]); - // User data not refetched on navigation to a new page - expect(getUserRequestCount).toBe(1); + const reloadRequest = page.waitForRequest(isGetUserRequest); - // User data is refetched when page reloaded - await page.reload(); - await page.waitForLoadState("networkidle"); - expect(getUserRequestCount).toBe(2); + Promise.all([await page.reload(), await reloadRequest]); }); test("team data persists on page refresh @regression", async ({ diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts new file mode 100644 index 0000000000..2bc95b7eaf --- /dev/null +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -0,0 +1,37 @@ +import { Browser, Page, Request } from "@playwright/test"; +import { createAuthenticatedSession } from "../globalHelpers"; + +export const isGetUserRequest = (req: Request) => { + const isHasuraRequest = req.url().includes("/graphql"); + const isGetUserOperation = req.postData()?.toString().includes("GetUserById"); + return Boolean(isHasuraRequest && isGetUserOperation); +}; + +export async function getAdminPage({ + browser, + userId, +}: { + browser: Browser; + userId: number; +}): Promise { + const page = await createAuthenticatedSession({ browser, userId }); + await page.goto("/"); + await page.waitForResponse((response) => { + return response.url().includes("/graphql"); + }); + return page; +} + +export async function getTeamPage({ + browser, + userId, + teamName, +}: { + browser: Browser; + userId: number; + teamName: string; +}): Promise { + const page = await getAdminPage({ browser, userId }); + await page.locator("h2", { hasText: teamName }).click(); + return page; +} diff --git a/e2e/tests/ui-driven/src/helpers.ts b/e2e/tests/ui-driven/src/globalHelpers.ts similarity index 94% rename from e2e/tests/ui-driven/src/helpers.ts rename to e2e/tests/ui-driven/src/globalHelpers.ts index 63722b46f6..ee9055fbd2 100644 --- a/e2e/tests/ui-driven/src/helpers.ts +++ b/e2e/tests/ui-driven/src/globalHelpers.ts @@ -66,35 +66,6 @@ export async function createAuthenticatedSession({ return page; } -export async function getAdminPage({ - browser, - userId, -}: { - browser: Browser; - userId: number; -}): Promise { - const page = await createAuthenticatedSession({ browser, userId }); - await page.goto("/"); - await page.waitForResponse((response) => { - return response.url().includes("/graphql"); - }); - return page; -} - -export async function getTeamPage({ - browser, - userId, - teamName, -}: { - browser: Browser; - userId: number; - teamName: string; -}): Promise { - const page = await getAdminPage({ browser, userId }); - await page.locator("h2", { hasText: teamName }).click(); - return page; -} - export async function saveSession({ page, context, diff --git a/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts b/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts index 5aed324a94..2d78f35348 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts @@ -1,5 +1,5 @@ import { test, expect, Page, BrowserContext } from "@playwright/test"; -import { addSessionToContext, modifyFlow } from "../helpers"; +import { addSessionToContext, modifyFlow } from "../globalHelpers"; import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; import { Context, @@ -15,9 +15,7 @@ import { navigateToPayComponent, } from "./helpers"; import { mockPaymentRequest, modifiedInviteToPayFlow } from "./mocks"; -import { saveSession } from "../helpers"; -import { returnToSession } from "../helpers"; -import { clickContinue } from "../helpers"; +import { saveSession, returnToSession, clickContinue } from "../globalHelpers"; let context: Context = { ...contextDefaults, diff --git a/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts b/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts index f73de05143..7b84115020 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts @@ -5,10 +5,10 @@ import { answerContactInput, addSessionToContext, TEST_EMAIL, -} from "../helpers"; +} from "../globalHelpers"; import type { Page } from "@playwright/test"; import { gql, GraphQLClient } from "graphql-request"; -import { fillInEmail } from "../helpers"; +import { fillInEmail } from "../globalHelpers"; import { PaymentRequest } from "@opensystemslab/planx-core/dist/types"; import { Context } from "../context"; diff --git a/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts b/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts index 5d87a5e95d..bf68efb3a3 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts @@ -5,7 +5,7 @@ import { SessionData, } from "@opensystemslab/planx-core/types"; import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; -import { TEST_EMAIL } from "../helpers"; +import { TEST_EMAIL } from "../globalHelpers"; export const mockPaymentRequest: Partial = { payeeEmail: TEST_EMAIL, diff --git a/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts b/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts index 39e353095e..d200fc05bb 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts @@ -1,6 +1,6 @@ import { test, expect, Page, APIRequestContext } from "@playwright/test"; import { v4 as uuidV4 } from "uuid"; -import { fillGovUkCardDetails, cards } from "../helpers"; +import { fillGovUkCardDetails, cards } from "../globalHelpers"; import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; import { Context, diff --git a/e2e/tests/ui-driven/src/login.spec.ts b/e2e/tests/ui-driven/src/login.spec.ts index 75920f3fb9..b3d6a176ad 100644 --- a/e2e/tests/ui-driven/src/login.spec.ts +++ b/e2e/tests/ui-driven/src/login.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from "@playwright/test"; -import { createAuthenticatedSession } from "./helpers"; +import { createAuthenticatedSession } from "./globalHelpers"; import { contextDefaults, setUpTestContext, diff --git a/e2e/tests/ui-driven/src/pay.spec.ts b/e2e/tests/ui-driven/src/pay.spec.ts index 221c2f859a..dde9ea19df 100644 --- a/e2e/tests/ui-driven/src/pay.spec.ts +++ b/e2e/tests/ui-driven/src/pay.spec.ts @@ -5,7 +5,7 @@ import { getSessionId, log, waitForPaymentResponse, -} from "./helpers"; +} from "./globalHelpers"; import type { Page } from "@playwright/test"; import payFlow from "./mocks/flows/pay-flow.json"; import { gql, GraphQLClient } from "graphql-request"; diff --git a/e2e/tests/ui-driven/src/save-and-return.spec.ts b/e2e/tests/ui-driven/src/save-and-return.spec.ts index 496d87bfed..c0b13d3613 100644 --- a/e2e/tests/ui-driven/src/save-and-return.spec.ts +++ b/e2e/tests/ui-driven/src/save-and-return.spec.ts @@ -16,7 +16,7 @@ import { returnToSession, saveSession, modifyFlow, -} from "./helpers"; +} from "./globalHelpers"; import type { Context } from "./context"; test.describe("Save and return", () => { diff --git a/e2e/tests/ui-driven/src/sections.spec.ts b/e2e/tests/ui-driven/src/sections.spec.ts index 1cf605e263..fa0999cd5c 100644 --- a/e2e/tests/ui-driven/src/sections.spec.ts +++ b/e2e/tests/ui-driven/src/sections.spec.ts @@ -17,7 +17,7 @@ import { expectConfirmation, saveSession, returnToSession, -} from "./helpers"; +} from "./globalHelpers"; import { gql } from "graphql-request"; import type { Context } from "./context"; import type { FlowGraph } from "@opensystemslab/planx-core/types"; diff --git a/editor.planx.uk/package.json b/editor.planx.uk/package.json index 326a421c10..9d53596ed0 100644 --- a/editor.planx.uk/package.json +++ b/editor.planx.uk/package.json @@ -14,7 +14,7 @@ "@mui/styles": "^5.14.5", "@mui/utils": "^5.14.5", "@opensystemslab/map": "^0.7.5", - "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#69c797c", + "@opensystemslab/planx-core": "git+https://github.com/theopensystemslab/planx-core#c923d21", "@tiptap/core": "^2.0.3", "@tiptap/extension-bold": "^2.0.3", "@tiptap/extension-bubble-menu": "^2.1.6", @@ -39,7 +39,8 @@ "@turf/buffer": "^6.5.0", "@turf/helpers": "^6.5.0", "array-move": "^4.0.0", - "axios": "0.27.2", + "axios": "^1.6.0", + "bowser": "^2.11.0", "camelcase-keys": "^9.0.0", "classnames": "^2.3.2", "core-js": "^3.31.0", @@ -109,7 +110,7 @@ "@storybook/addons": "^7.3.2", "@storybook/node-logger": "^7.1.1", "@storybook/preset-create-react-app": "^7.3.2", - "@storybook/react": "^7.1.1", + "@storybook/react": "^7.5.2", "@storybook/react-webpack5": "^7.1.1", "@storybook/testing-library": "^0.2.0", "@storybook/theming": "^7.1.1", diff --git a/editor.planx.uk/pnpm-lock.yaml b/editor.planx.uk/pnpm-lock.yaml index 093c67f9df..43454db214 100644 --- a/editor.planx.uk/pnpm-lock.yaml +++ b/editor.planx.uk/pnpm-lock.yaml @@ -46,8 +46,8 @@ dependencies: specifier: ^0.7.5 version: 0.7.5 '@opensystemslab/planx-core': - specifier: git+https://github.com/theopensystemslab/planx-core#69c797c - version: github.com/theopensystemslab/planx-core/69c797c(@types/react@18.2.20) + specifier: git+https://github.com/theopensystemslab/planx-core#c923d21 + version: github.com/theopensystemslab/planx-core/c923d21(@types/react@18.2.20) '@tiptap/core': specifier: ^2.0.3 version: 2.0.3(@tiptap/pm@2.0.3) @@ -121,8 +121,11 @@ dependencies: specifier: ^4.0.0 version: 4.0.0 axios: - specifier: 0.27.2 - version: 0.27.2 + specifier: ^1.6.0 + version: 1.6.0 + bowser: + specifier: ^2.11.0 + version: 2.11.0 camelcase-keys: specifier: ^9.0.0 version: 9.0.0 @@ -304,7 +307,7 @@ devDependencies: version: 6.4.5(@swc/core@1.3.71)(@types/node@17.0.45)(react-scripts@5.0.1)(typescript@4.9.5) '@react-theming/storybook-addon': specifier: ^1.1.10 - version: 1.1.10(@storybook/addons@7.3.2)(@storybook/react@7.1.1)(@storybook/theming@7.1.1)(react-dom@18.2.0)(react@18.2.0) + version: 1.1.10(@storybook/addons@7.3.2)(@storybook/react@7.5.2)(@storybook/theming@7.1.1)(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-a11y': specifier: ^7.1.1 version: 7.1.1(react-dom@18.2.0)(react@18.2.0) @@ -327,8 +330,8 @@ devDependencies: specifier: ^7.3.2 version: 7.3.2(@babel/core@7.22.8)(react-refresh@0.14.0)(react-scripts@5.0.1)(typescript@4.9.5)(webpack@5.88.1) '@storybook/react': - specifier: ^7.1.1 - version: 7.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) + specifier: ^7.5.2 + version: 7.5.2(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) '@storybook/react-webpack5': specifier: ^7.1.1 version: 7.1.1(@babel/core@7.22.8)(@swc/core@1.3.71)(esbuild@0.14.54)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) @@ -469,7 +472,7 @@ devDependencies: version: 7.1.1 storybook-addon-material-ui: specifier: ^0.9.0-alpha.24 - version: 0.9.0-alpha.24(@material-ui/core@4.12.4)(@storybook/addons@7.3.2)(@storybook/react@7.1.1)(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) + version: 0.9.0-alpha.24(@material-ui/core@4.12.4)(@storybook/addons@7.3.2)(@storybook/react@7.5.2)(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) stream-browserify: specifier: ^3.0.0 version: 3.0.0 @@ -624,7 +627,7 @@ packages: '@babel/helper-compilation-targets': 7.22.9(@babel/core@7.22.9) '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.9) '@babel/helpers': 7.22.6 - '@babel/parser': 7.22.7 + '@babel/parser': 7.23.0 '@babel/template': 7.22.5 '@babel/traverse': 7.23.2 '@babel/types': 7.23.0 @@ -5263,8 +5266,8 @@ packages: dependencies: '@babel/runtime': 7.23.2 '@emotion/is-prop-valid': 1.2.1 - '@mui/types': 7.2.6(@types/react@18.2.20) - '@mui/utils': 5.14.13(@types/react@18.2.20)(react@18.2.0) + '@mui/types': 7.2.7(@types/react@18.2.20) + '@mui/utils': 5.14.15(@types/react@18.2.20)(react@18.2.0) '@popperjs/core': 2.11.8 '@types/react': 18.2.20 clsx: 2.0.0 @@ -5394,23 +5397,6 @@ packages: react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) dev: false - /@mui/private-theming@5.14.13(@types/react@18.2.20)(react@18.2.0): - resolution: {integrity: sha512-5EFqk4tqiSwPguj4NW/6bUf4u1qoUWXy9lrKfNh9H6oAohM+Ijv/7qSxFjnxPGBctj469/Sc5aKAR35ILBKZLQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@mui/utils': 5.14.15(@types/react@18.2.20)(react@18.2.0) - '@types/react': 18.2.20 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - /@mui/private-theming@5.14.15(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-V2Xh+Tu6A07NoSpup0P9m29GwvNMYl5DegsGWqlOTJyAV7cuuVjmVPqxgvL8xBng4R85xqIQJRMjtYYktoPNuQ==} engines: {node: '>=12.0.0'} @@ -5439,34 +5425,12 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.2 - '@mui/utils': 5.14.13(@types/react@18.2.20)(react@18.2.0) + '@mui/utils': 5.14.15(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 prop-types: 15.8.1 react: 18.2.0 dev: false - /@mui/styled-engine@5.14.13(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): - resolution: {integrity: sha512-1ff/egFQl26hiwcUtCMKAkp4Sgqpm3qIewmXq+GN27fb44lDIACquehMFBuadOjceOFmbIXbayzbA46ZyqFYzA==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.4.1 - '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@emotion/cache': 11.11.0 - '@emotion/react': 11.11.1(@types/react@18.2.20)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.20)(react@18.2.0) - csstype: 3.1.2 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - /@mui/styled-engine@5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0): resolution: {integrity: sha512-mbOjRf867BysNpexe5Z/P8s3bWzDPNowmKhi7gtNDP/LPEeqAfiDSuC4WPTXmtvse1dCl30Nl755OLUYuoi7Mw==} engines: {node: '>=12.0.0'} @@ -5569,10 +5533,10 @@ packages: '@babel/runtime': 7.23.2 '@emotion/react': 11.11.1(@types/react@18.2.20)(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.20)(react@18.2.0) - '@mui/private-theming': 5.14.13(@types/react@18.2.20)(react@18.2.0) - '@mui/styled-engine': 5.14.13(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) - '@mui/types': 7.2.6(@types/react@18.2.20) - '@mui/utils': 5.14.13(@types/react@18.2.20)(react@18.2.0) + '@mui/private-theming': 5.14.15(@types/react@18.2.20)(react@18.2.0) + '@mui/styled-engine': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0) + '@mui/types': 7.2.7(@types/react@18.2.20) + '@mui/utils': 5.14.15(@types/react@18.2.20)(react@18.2.0) '@types/react': 18.2.20 clsx: 2.0.0 csstype: 3.1.2 @@ -5591,17 +5555,6 @@ packages: '@types/react': 18.2.20 dev: false - /@mui/types@7.2.6(@types/react@18.2.20): - resolution: {integrity: sha512-7sjLQrUmBwufm/M7jw/quNiPK/oor2+pGUQP2CULRcFCArYTq78oJ3D5esTaL0UMkXKJvDqXn6Ike69yAOBQng==} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 18.2.20 - dev: false - /@mui/types@7.2.7(@types/react@18.2.20): resolution: {integrity: sha512-sofpWmcBqOlTzRbr1cLQuUDKaUYVZTw8ENQrtL39TECRNENEzwgnNPh6WMfqMZlMvf1Aj9DLg74XPjnLr0izUQ==} peerDependencies: @@ -5613,24 +5566,6 @@ packages: '@types/react': 18.2.20 dev: false - /@mui/utils@5.14.13(@types/react@18.2.20)(react@18.2.0): - resolution: {integrity: sha512-2AFpyXWw7uDCIqRu7eU2i/EplZtks5LAMzQvIhC79sPV9IhOZU2qwOWVnPtdctRXiQJOAaXulg+A37pfhEueQw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.2 - '@types/prop-types': 15.7.8 - '@types/react': 18.2.20 - prop-types: 15.8.1 - react: 18.2.0 - react-is: 18.2.0 - dev: false - /@mui/utils@5.14.15(@types/react@18.2.20)(react@18.2.0): resolution: {integrity: sha512-QBfHovAvTa0J1jXuYDaXGk+Yyp7+Fm8GSqx6nK2JbezGqzCFfirNdop/+bL9Flh/OQ/64PeXcW4HGDdOge+n3A==} engines: {node: '>=12.0.0'} @@ -5903,7 +5838,7 @@ packages: rgb-hex: 3.0.0 dev: true - /@react-theming/storybook-addon@1.1.10(@storybook/addons@7.3.2)(@storybook/react@7.1.1)(@storybook/theming@7.1.1)(react-dom@18.2.0)(react@18.2.0): + /@react-theming/storybook-addon@1.1.10(@storybook/addons@7.3.2)(@storybook/react@7.5.2)(@storybook/theming@7.1.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-7DHwzPIY4nerTqkUgyQ70IqPSHMycnYgJsYQMAVkT8dsQPuQUUKz2UVjqD0HkPMw3JA+wQuSvY9BpJap5z7fqQ==} peerDependencies: '@storybook/react': '*' @@ -5915,8 +5850,8 @@ packages: '@react-theming/flatten': 0.1.1 '@react-theming/theme-name': 1.0.3 '@react-theming/theme-swatch': 1.0.0(react@18.2.0) - '@storybook/addon-devkit': 1.4.2(@storybook/addons@7.3.2)(@storybook/react@7.1.1)(react-dom@18.2.0)(react@18.2.0) - '@storybook/react': 7.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) + '@storybook/addon-devkit': 1.4.2(@storybook/addons@7.3.2)(@storybook/react@7.5.2)(react-dom@18.2.0)(react@18.2.0) + '@storybook/react': 7.5.2(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) '@storybook/theming': 7.1.1(react-dom@18.2.0)(react@18.2.0) '@usulpro/react-json-view': 2.0.1(react-dom@18.2.0)(react@18.2.0) color-string: 1.9.1 @@ -6160,7 +6095,7 @@ packages: - supports-color dev: true - /@storybook/addon-devkit@1.4.2(@storybook/addons@7.3.2)(@storybook/react@7.1.1)(react-dom@18.2.0)(react@18.2.0): + /@storybook/addon-devkit@1.4.2(@storybook/addons@7.3.2)(@storybook/react@7.5.2)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-ggy34eCzmiKOwgV7xYNjlPClGpmtnYODPJv0vkCKiDyPeVLHocq2UZ7ZkOhQ5GO7TM7aLeeC1JBS00tZId9oLA==} peerDependencies: '@storybook/addons': '*' @@ -6171,7 +6106,7 @@ packages: '@reach/rect': 0.2.1(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) '@storybook/addons': 7.3.2(react-dom@18.2.0)(react@18.2.0) '@storybook/core-events': 6.5.16 - '@storybook/react': 7.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) + '@storybook/react': 7.5.2(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) '@storybook/theming': 6.5.16(react-dom@18.2.0)(react@18.2.0) deep-equal: 2.2.1 prop-types: 15.8.1 @@ -6561,6 +6496,17 @@ packages: tiny-invariant: 1.3.1 dev: true + /@storybook/channels@7.5.2: + resolution: {integrity: sha512-3SgqWq9NS0XX1QxK3riuaOLrReHWwVhI63u6q1ryDD3SttpmAezZETibOAtzDuk2FKgsyHTmAlmcGQf4ZxhOJA==} + dependencies: + '@storybook/client-logger': 7.5.2 + '@storybook/core-events': 7.5.2 + '@storybook/global': 5.0.0 + qs: 6.11.2 + telejson: 7.2.0 + tiny-invariant: 1.3.1 + dev: true + /@storybook/cli@7.1.1: resolution: {integrity: sha512-xQU0GBIRQpwlvTnzOvDo05H5aH660DaZ9JlXd8ThPkEicoTvhkH0oQVEMYaWKChp5Ok7Wu8+kB7fzgUSOGzj+Q==} hasBin: true @@ -6638,6 +6584,12 @@ packages: '@storybook/global': 5.0.0 dev: true + /@storybook/client-logger@7.5.2: + resolution: {integrity: sha512-7YgLItlmiYDzWYexTaRNuHhtFarh9krsI+8l7Yjn9ryoHSTJUcTWx+yPJm1II+PQR8v/x5UgsxzultjgEurfRQ==} + dependencies: + '@storybook/global': 5.0.0 + dev: true + /@storybook/codemod@7.1.1: resolution: {integrity: sha512-QB4MoeFXA4QsX0LuwjHoTVqsX7krRXmqfwSWIQMB8/qsAfyBp/jiG2xWmwa2agKwtlYvZzkvGdCjAOmK4SUSHQ==} dependencies: @@ -6684,6 +6636,13 @@ packages: '@storybook/preview-api': 7.1.1 dev: true + /@storybook/core-client@7.5.2: + resolution: {integrity: sha512-mMDSBxc7esMCu0FOkama9XYHzIHYGhBj8roX+XaTaLDYXaw/UajcCuzcO7fFBHNn3Vdqh2ufIxlI7359v3IqPw==} + dependencies: + '@storybook/client-logger': 7.5.2 + '@storybook/preview-api': 7.5.2 + dev: true + /@storybook/core-common@7.1.1: resolution: {integrity: sha512-DO7ZS6YDITykvqMHeOWSmnsPYk2w7gka9GtO2LPbEm0f6p5kG2nohBO5+nsI3PuXpKiHXOB7vKJjwfQqxvPj5A==} dependencies: @@ -6714,6 +6673,37 @@ packages: - supports-color dev: true + /@storybook/core-common@7.5.2: + resolution: {integrity: sha512-js7fIH4wHS08dBuIVsr3JnwMtKn5O1Izc/Zor4t6PntLWkGGX4X/GxbOkasGX5SkCT1qUtB9RpdPd1sUkLhIgw==} + dependencies: + '@storybook/core-events': 7.5.2 + '@storybook/node-logger': 7.5.2 + '@storybook/types': 7.5.2 + '@types/find-cache-dir': 3.2.1 + '@types/node': 18.18.8 + '@types/node-fetch': 2.6.4 + '@types/pretty-hrtime': 1.0.1 + chalk: 4.1.2 + esbuild: 0.18.17 + esbuild-register: 3.5.0(esbuild@0.18.17) + file-system-cache: 2.3.0 + find-cache-dir: 3.3.2 + find-up: 5.0.0 + fs-extra: 11.1.1 + glob: 10.3.3 + handlebars: 4.7.7 + lazy-universal-dotenv: 4.0.0 + node-fetch: 2.6.12 + picomatch: 2.3.1 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/core-events@6.5.16: resolution: {integrity: sha512-qMZQwmvzpH5F2uwNUllTPg6eZXr2OaYZQRRN8VZJiuorZzDNdAFmiVWMWdkThwmyLEJuQKXxqCL8lMj/7PPM+g==} dependencies: @@ -6728,6 +6718,12 @@ packages: resolution: {integrity: sha512-DCrM3s+sxLKS8vl0zB+1tZEtcl5XQTOGl46XgRRV/SIBabFbsC0l5pQPswWkTUsIqdREtiT0YUHcXB1+YDyFvA==} dev: true + /@storybook/core-events@7.5.2: + resolution: {integrity: sha512-DV8bFEFVKDEvaH87KYPXDE0YEV+Y9yjFv2xxmC9pF8l+MWCtVW72RBLhB+gU5NM1bkHrRDNb0lOJfVGKlhxOog==} + dependencies: + ts-dedent: 2.2.0 + dev: true + /@storybook/core-server@7.1.1: resolution: {integrity: sha512-IfrkdcYwVoP4bltBTx8Yr1e++UAfICV8IYCgW8VFW26Uvl22biCVWwliE35iTYpUmHJgn+U489hCnEdGpr2CWw==} dependencies: @@ -6806,7 +6802,7 @@ packages: resolution: {integrity: sha512-IdDW+NsTIxqv7BjeFaTonvX0Ac5HzzNiKvGkhydXrpaz7kJX4g0T96xpR+RhbEtPfQ0AcpiHnW0kMPx9YLJRew==} dependencies: '@babel/generator': 7.22.9 - '@babel/parser': 7.22.7 + '@babel/parser': 7.23.0 '@babel/traverse': 7.23.2 '@babel/types': 7.23.0 '@storybook/csf': 0.1.1 @@ -6842,6 +6838,20 @@ packages: - supports-color dev: true + /@storybook/docs-tools@7.5.2: + resolution: {integrity: sha512-mBiZFhzMA2ub7wX0ho3UqKqKXO+xUi/rqb4KV4PihLKlhThEdzKyYrIZO4W90NOmlp1yUJJcjG8D8SUPuHQoTw==} + dependencies: + '@storybook/core-common': 7.5.2 + '@storybook/preview-api': 7.5.2 + '@storybook/types': 7.5.2 + '@types/doctrine': 0.0.3 + doctrine: 3.0.0 + lodash: 4.17.21 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/global@5.0.0: resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} dev: true @@ -6908,6 +6918,10 @@ packages: resolution: {integrity: sha512-gnAuNM+wNoOcGnUM6hLsYV0lwUgRI39Ep/Pp3VF1oXZAthEyrQRm7ImbeAdt93ObPc9DZgqTx9OI8QnErZuJiA==} dev: true + /@storybook/node-logger@7.5.2: + resolution: {integrity: sha512-VIBuwPJOylu8vJofk1VfmqxlhXgbBgV0pCTo/UzdQAbc3w5y+qNRemf8goWxYEY+L9p6oUXqm/i9+bNGyX7/Mw==} + dev: true + /@storybook/postinstall@7.1.1: resolution: {integrity: sha512-qpe6BiFLVs9YYFQVGgRT0dJxPOKBtGLIAsnVEpXKUPrltEWQpTxQEqqOSJlut+FLoWB5MTxrwiJ/7891h4a5pw==} dev: true @@ -7028,6 +7042,25 @@ packages: util-deprecate: 1.0.2 dev: true + /@storybook/preview-api@7.5.2: + resolution: {integrity: sha512-rpmHR/09UBSnorDBTcE7JgHUQjZLO146NCI+vbI7Pqfb4QX/8lhwkFr4cuHRAR16mv6DAJbDVoPETO0Z/CH9aw==} + dependencies: + '@storybook/channels': 7.5.2 + '@storybook/client-logger': 7.5.2 + '@storybook/core-events': 7.5.2 + '@storybook/csf': 0.1.1 + '@storybook/global': 5.0.0 + '@storybook/types': 7.5.2 + '@types/qs': 6.9.7 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.11.2 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + /@storybook/preview@7.1.1: resolution: {integrity: sha512-F3ikRKzwmT9MlptYXxYOQmaSwmJckPag0k9lM0LvI0xYplLbyWJ5rfs2gLKl++wX+ag2A+1K4gId5Xaz4SKnxQ==} dev: true @@ -7061,6 +7094,16 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true + /@storybook/react-dom-shim@7.5.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-x7h3TTLRLs8mrsCBKXbvjBRFms73XrNlm0Lo5Tu/Tf//+pwOFq+2sGBkqbRkYd54jNHhpqNF7+UUdzA93ESnbQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: true + /@storybook/react-webpack5@7.1.1(@babel/core@7.22.8)(@swc/core@1.3.71)(esbuild@0.14.54)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5): resolution: {integrity: sha512-iTliWdmqSXw5wz/iHefr7yKhI7rko8oN5JUfkYlZafqk7M3mXy0wamLgFcrOncnBcY2UNPX1oEAiLJBKSy9ulA==} engines: {node: '>=16.0.0'} @@ -7139,6 +7182,46 @@ packages: - supports-color dev: true + /@storybook/react@7.5.2(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5): + resolution: {integrity: sha512-7X8GtqvRjWmVS112ifChJMxfD15rMVg5m3t6apZqi0uui1S/DImAveHwz8M4FhsElW6MIHs5xK0uJhR9rVQgTA==} + engines: {node: '>=16.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/client-logger': 7.5.2 + '@storybook/core-client': 7.5.2 + '@storybook/docs-tools': 7.5.2 + '@storybook/global': 5.0.0 + '@storybook/preview-api': 7.5.2 + '@storybook/react-dom-shim': 7.5.2(react-dom@18.2.0)(react@18.2.0) + '@storybook/types': 7.5.2 + '@types/escodegen': 0.0.6 + '@types/estree': 0.0.51 + '@types/node': 18.18.8 + acorn: 7.4.1 + acorn-jsx: 5.3.2(acorn@7.4.1) + acorn-walk: 7.2.0 + escodegen: 2.1.0 + html-tags: 3.3.1 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0) + ts-dedent: 2.2.0 + type-fest: 2.19.0 + typescript: 4.9.5 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@storybook/router@7.1.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-GRYYWVsqAtDm7DHxnGXuaAmr3PQfj+tonYsP8/L3gC5sOdQNF3yaBmvv1pu+bqezwXVowq0ew+iVYECiaGoB3Q==} peerDependencies: @@ -7256,6 +7339,15 @@ packages: file-system-cache: 2.3.0 dev: true + /@storybook/types@7.5.2: + resolution: {integrity: sha512-RDKHo6WUES+4nt7uZMfankjxdpYX2EI2GpJ2n2RPcnhzmb/ub1huNTjbzDEYMqY24SppljZeIN57m3Ar6L6f9A==} + dependencies: + '@storybook/channels': 7.5.2 + '@types/babel__core': 7.20.1 + '@types/express': 4.17.17 + file-system-cache: 2.3.0 + dev: true + /@surma/rollup-plugin-off-main-thread@2.2.3: resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} dependencies: @@ -7856,7 +7948,7 @@ packages: /@types/babel__core@7.20.1: resolution: {integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==} dependencies: - '@babel/parser': 7.22.7 + '@babel/parser': 7.23.0 '@babel/types': 7.23.0 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 @@ -7870,7 +7962,7 @@ packages: /@types/babel__template@7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.22.7 + '@babel/parser': 7.23.0 '@babel/types': 7.23.0 /@types/babel__traverse@7.20.1: @@ -7985,8 +8077,8 @@ packages: resolution: {integrity: sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==} dev: true - /@types/geojson@7946.0.11: - resolution: {integrity: sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==} + /@types/geojson@7946.0.12: + resolution: {integrity: sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==} dev: false /@types/glob@7.2.0: @@ -8115,6 +8207,12 @@ packages: /@types/node@17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + /@types/node@18.18.8: + resolution: {integrity: sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ==} + dependencies: + undici-types: 5.26.5 + dev: true + /@types/node@20.4.2: resolution: {integrity: sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==} dev: false @@ -9085,11 +9183,12 @@ packages: resolution: {integrity: sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==} engines: {node: '>=4'} - /axios@0.27.2: - resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + /axios@1.6.0: + resolution: {integrity: sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==} dependencies: follow-redirects: 1.15.2 form-data: 4.0.0 + proxy-from-env: 1.1.0 transitivePeerDependencies: - debug dev: false @@ -9572,6 +9671,10 @@ packages: /boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + /bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + dev: false + /bplist-parser@0.2.0: resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} engines: {node: '>= 5.10.0'} @@ -11417,6 +11520,17 @@ packages: - supports-color dev: true + /esbuild-register@3.5.0(esbuild@0.18.17): + resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==} + peerDependencies: + esbuild: '>=0.12 <1' + dependencies: + debug: 4.3.4 + esbuild: 0.18.17 + transitivePeerDependencies: + - supports-color + dev: true + /esbuild-sunos-64@0.14.54: resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} engines: {node: '>=12'} @@ -13762,7 +13876,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.22.9 - '@babel/parser': 7.22.7 + '@babel/parser': 7.23.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 7.5.3 @@ -14498,7 +14612,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.22.9 - '@babel/parser': 7.22.7 + '@babel/parser': 7.23.0 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.9) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.9) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.9) @@ -17302,7 +17416,6 @@ packages: /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: true /pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} @@ -19102,7 +19215,7 @@ packages: resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==} dev: true - /storybook-addon-material-ui@0.9.0-alpha.24(@material-ui/core@4.12.4)(@storybook/addons@7.3.2)(@storybook/react@7.1.1)(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): + /storybook-addon-material-ui@0.9.0-alpha.24(@material-ui/core@4.12.4)(@storybook/addons@7.3.2)(@storybook/react@7.5.2)(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-Z9S06K/x2lppPofINl/ZM6a1TzeGdy8NZfWwjzyQRXzVf4/ABanhv6Zib2i6ptCxa5AWahZ1HxBqOSQZS4YIHg==} peerDependencies: '@material-ui/core': ^1.0.0 || ^3.0.0 || ^4.0.0 @@ -19116,7 +19229,7 @@ packages: '@emotion/styled': 10.3.0(@emotion/core@10.3.1)(react@18.2.0) '@material-ui/core': 4.12.4(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) '@storybook/addons': 7.3.2(react-dom@18.2.0)(react@18.2.0) - '@storybook/react': 7.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) + '@storybook/react': 7.5.2(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5) '@usulpro/color-picker': 1.1.4(react@18.2.0) global: 4.4.0 js-beautify: 1.14.8 @@ -19537,6 +19650,12 @@ packages: memoizerific: 1.11.3 dev: true + /telejson@7.2.0: + resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==} + dependencies: + memoizerific: 1.11.3 + dev: true + /temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -20002,6 +20121,10 @@ packages: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + /unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -21012,9 +21135,9 @@ packages: use-sync-external-store: 1.2.0(react@18.2.0) dev: false - github.com/theopensystemslab/planx-core/69c797c(@types/react@18.2.20): - resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/69c797c} - id: github.com/theopensystemslab/planx-core/69c797c + github.com/theopensystemslab/planx-core/c923d21(@types/react@18.2.20): + resolution: {tarball: https://codeload.github.com/theopensystemslab/planx-core/tar.gz/c923d21} + id: github.com/theopensystemslab/planx-core/c923d21 name: '@opensystemslab/planx-core' version: 1.0.0 prepare: true @@ -21023,7 +21146,7 @@ packages: '@emotion/react': 11.11.1(@types/react@18.2.20)(react@18.2.0) '@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.20)(react@18.2.0) '@mui/material': 5.14.15(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0) - '@types/geojson': 7946.0.11 + '@types/geojson': 7946.0.12 ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) copyfiles: 2.4.1 diff --git a/editor.planx.uk/src/@planx/components/Content/Public.tsx b/editor.planx.uk/src/@planx/components/Content/Public.tsx index fd53d2112f..ef490d50ad 100644 --- a/editor.planx.uk/src/@planx/components/Content/Public.tsx +++ b/editor.planx.uk/src/@planx/components/Content/Public.tsx @@ -12,7 +12,7 @@ export type Props = PublicProps; const Content = styled(Box, { shouldForwardProp: (prop) => prop !== "color", -})(({ theme, color }) => ({ +})<{ color?: string }>(({ theme, color }) => ({ padding: theme.spacing(2), backgroundColor: color, color: @@ -28,7 +28,7 @@ const Content = styled(Box, { const ContentComponent: React.FC = (props) => { return ( - +

An interactive map centred on your address, with a red pointer - to draw your property boundary. Click to place points and connect the - lines to make your site. Once you've closed the site shape, - click and drag the lines to modify it. + to draw your property boundary. Click to place points and + connect the lines to make your site. Once you've closed the site + shape, click and drag the lines to modify it.

{!props.hideFileUpload && (

diff --git a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx index b346d906c9..b37bd17869 100644 --- a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx +++ b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx @@ -16,7 +16,6 @@ import Select, { SelectChangeEvent, SelectProps } from "@mui/material/Select"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; import capitalize from "lodash/capitalize"; -import merge from "lodash/merge"; import React, { useEffect, useState } from "react"; import { usePrevious } from "react-use"; import ErrorWrapper from "ui/ErrorWrapper"; @@ -28,7 +27,6 @@ import { FileList, getTagsForSlot, removeSlots, - resetAllSlots, } from "./model"; import { fileLabelSchema } from "./schema"; @@ -172,23 +170,22 @@ const SelectMultiple = (props: SelectMultipleProps) => { previousTags: string[] | undefined, tags: string[], ) => { - let updatedFileList: FileList = merge(fileList); const updatedTags = tags.filter((tag) => !previousTags?.includes(tag)); const removedTags = previousTags?.filter((tag) => !tags?.includes(tag)); if (updatedTags.length > 0) { - updatedFileList = addOrAppendSlots(updatedTags, uploadedFile, fileList); + const updatedFileList = addOrAppendSlots( + updatedTags, + uploadedFile, + fileList, + ); + setFileList(updatedFileList); } if (removedTags && removedTags.length > 0) { - updatedFileList = removeSlots(removedTags, uploadedFile, fileList); + const updatedFileList = removeSlots(removedTags, uploadedFile, fileList); + setFileList(updatedFileList); } - - if (tags.length === 0 && previousTags) { - updatedFileList = resetAllSlots(fileList); - } - - setFileList(updatedFileList); }; useEffect(() => { diff --git a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.tsx b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.tsx index 6ef543884e..87decf9a4e 100644 --- a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.tsx +++ b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Public.tsx @@ -94,6 +94,10 @@ function Component(props: Props) { const previousSlotCount = usePrevious(slots.length); useEffect(() => { if (previousSlotCount === undefined) return; + + // Show most recent upload at the top + if (slots.length) setSlots(slots.reverse()); + // Only stop modal opening on initial return to node if (isUserReturningToNode) return setIsUserReturningToNode(false); if (slots.length && dropzoneError) setDropzoneError(undefined); diff --git a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/model.ts b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/model.ts index 5b24dea4e9..a818f0e802 100644 --- a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/model.ts +++ b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/model.ts @@ -1,5 +1,4 @@ import cloneDeep from "lodash/cloneDeep"; -import merge from "lodash/merge"; import sortBy from "lodash/sortBy"; import uniqBy from "lodash/uniqBy"; import { Store } from "pages/FlowEditor/lib/store"; @@ -331,7 +330,7 @@ export const addOrAppendSlots = ( uploadedFile: FileUploadSlot, fileList: FileList, ): FileList => { - const updatedFileList: FileList = merge(fileList); + const updatedFileList: FileList = cloneDeep(fileList); const categories = Object.keys(updatedFileList) as Array< keyof typeof updatedFileList >; @@ -368,7 +367,7 @@ export const removeSlots = ( uploadedFile: FileUploadSlot, fileList: FileList, ): FileList => { - const updatedFileList: FileList = merge(fileList); + const updatedFileList: FileList = cloneDeep(fileList); const categories = Object.keys(updatedFileList) as Array< keyof typeof updatedFileList >; @@ -396,7 +395,7 @@ export const removeSlots = ( }; export const resetAllSlots = (fileList: FileList): FileList => { - const updatedFileList: FileList = merge(fileList); + const updatedFileList: FileList = cloneDeep(fileList); const categories = Object.keys(updatedFileList) as Array< keyof typeof updatedFileList >; diff --git a/editor.planx.uk/src/@planx/components/Notice/Public.tsx b/editor.planx.uk/src/@planx/components/Notice/Public.tsx index 4fd28a55ee..b7a1dc6195 100644 --- a/editor.planx.uk/src/@planx/components/Notice/Public.tsx +++ b/editor.planx.uk/src/@planx/components/Notice/Public.tsx @@ -76,10 +76,10 @@ const NoticeComponent: React.FC = (props) => { ? () => props.handleSubmit?.() : undefined; - const { trackResetFlow } = useAnalyticsTracking(); + const { trackFlowDirectionChange } = useAnalyticsTracking(); const handleNoticeResetClick = () => { - trackResetFlow(); + trackFlowDirectionChange("reset"); props.resetPreview && props.resetPreview(); }; diff --git a/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx b/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx index c1fb00292f..0fd9e20269 100644 --- a/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx +++ b/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx @@ -58,7 +58,8 @@ function Component(props: Props) { // Configure which planx teams should query Digital Land (or continue to use custom GIS) and set URL params accordingly // In future, Digital Land will theoretically support any UK address and this list won't be necessary, but data collection still limited to select councils! const digitalLandOrganisations: string[] = [ - "opensystemslab", + "opensystemslab", // for UK-wide testing, subject to data availability + "barnet", "buckinghamshire", "canterbury", "camden", diff --git a/editor.planx.uk/src/@planx/components/Send/Public.test.tsx b/editor.planx.uk/src/@planx/components/Send/Public.test.tsx index 70013e1f92..0dee982b21 100644 --- a/editor.planx.uk/src/@planx/components/Send/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/Send/Public.test.tsx @@ -1,4 +1,4 @@ -import * as axios from "axios"; +import Axios from "axios"; import React from "react"; import { axe, setup } from "testUtils"; @@ -6,7 +6,10 @@ import hasuraEventsResponseMock from "./mocks/hasuraEventsResponseMock"; import { Destination } from "./model"; import SendComponent from "./Public"; -jest.spyOn(axios, "default").mockImplementation((url: any) => { +jest.mock("axios"); +const mockAxios = Axios as jest.Mocked; + +mockAxios.post.mockImplementation((url: any) => { return { value: url()?.startsWith( `${process.env.REACT_APP_API_URL}/create-send-events/`, diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx index be64a1ae4d..efd7878976 100644 --- a/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx @@ -2,6 +2,7 @@ import Box from "@mui/material/Box"; import Link from "@mui/material/Link"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; +import { useAnalyticsTracking } from "pages/FlowEditor/lib/analyticsProvider"; import { useStore } from "pages/FlowEditor/lib/store"; import React from "react"; import { ApplicationPath } from "types"; @@ -16,10 +17,18 @@ const InnerContainer = styled(Box)(({ theme }) => ({ const SaveResumeButton: React.FC = () => { const saveToEmail = useStore((state) => state.saveToEmail); - const onClick = () => - useStore.setState({ - path: saveToEmail ? ApplicationPath.Save : ApplicationPath.Resume, - }); + const { trackFlowDirectionChange } = useAnalyticsTracking(); + + const handleClick = () => { + if (saveToEmail) { + trackFlowDirectionChange("save"); + useStore.setState({ path: ApplicationPath.Save }); + } else { + useStore.setState({ path: ApplicationPath.Resume }); + } + }; + + const onClick = () => handleClick(); return ( diff --git a/editor.planx.uk/src/api/upload.ts b/editor.planx.uk/src/api/upload.ts index 333f7ba8d9..1f3834b1da 100644 --- a/editor.planx.uk/src/api/upload.ts +++ b/editor.planx.uk/src/api/upload.ts @@ -47,7 +47,7 @@ function handleUpload( "Content-Type": "multipart/form-data", }, onUploadProgress: ({ loaded, total }) => { - if (onProgress) { + if (onProgress && total) { onProgress(loaded / total); } }, diff --git a/editor.planx.uk/src/components/Header.tsx b/editor.planx.uk/src/components/Header.tsx index 0c5b233672..05a2af7dce 100644 --- a/editor.planx.uk/src/components/Header.tsx +++ b/editor.planx.uk/src/components/Header.tsx @@ -320,7 +320,7 @@ const PublicToolbar: React.FC<{ theme.breakpoints.up("md"), ); - const { trackResetFlow } = useAnalyticsTracking(); + const { trackFlowDirectionChange } = useAnalyticsTracking(); const handleRestart = async () => { if ( @@ -328,7 +328,7 @@ const PublicToolbar: React.FC<{ "Are you sure you want to restart? This will delete your previous answers", ) ) { - trackResetFlow(); + trackFlowDirectionChange("reset"); if (path === ApplicationPath.SingleSession) { clearLocalFlow(id); window.location.reload(); diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx b/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx index f2ba6ed71f..d0c252e729 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/lib/analyticsProvider.tsx @@ -5,13 +5,19 @@ import { FlagSet, } from "@opensystemslab/planx-core/types"; import { TYPES } from "@planx/components/types"; +import Bowser from "bowser"; import { publicClient } from "lib/graphql"; import React, { createContext, useContext, useEffect, useState } from "react"; import { Store, useStore } from "./store"; export type AnalyticsType = "init" | "resume"; -type AnalyticsLogDirection = AnalyticsType | "forwards" | "backwards" | "reset"; +type AnalyticsLogDirection = + | AnalyticsType + | "forwards" + | "backwards" + | "reset" + | "save"; export type HelpClickMetadata = Record; export type SelectedUrlsMetadata = Record<"selectedUrls", string[]>; @@ -31,13 +37,15 @@ const analyticsContext = createContext<{ createAnalytics: (type: AnalyticsType) => Promise; trackHelpClick: (metadata?: HelpClickMetadata) => Promise; trackNextStepsLinkClick: (metadata?: SelectedUrlsMetadata) => Promise; - trackResetFlow: () => Promise; + trackFlowDirectionChange: ( + flowDirection: AnalyticsLogDirection, + ) => Promise; node: Store.node | null; }>({ createAnalytics: () => Promise.resolve(), trackHelpClick: () => Promise.resolve(), trackNextStepsLinkClick: () => Promise.resolve(), - trackResetFlow: () => Promise.resolve(), + trackFlowDirectionChange: () => Promise.resolve(), node: null, }); const { Provider } = analyticsContext; @@ -119,7 +127,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ createAnalytics, trackHelpClick, trackNextStepsLinkClick, - trackResetFlow, + trackFlowDirectionChange, node, }} > @@ -129,7 +137,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ async function track(direction: AnalyticsLogDirection, analyticsId: number) { const metadata = getNodeMetadata(); - const node_title = + const nodeTitle = node?.type === TYPES.Content ? getContentTitle(node) : node?.data?.title ?? node?.data?.text ?? node?.data?.flagSet; @@ -138,7 +146,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ direction, analyticsId, metadata, - node_title, + nodeTitle, ); const id = result?.data.insert_analytics_logs_one?.id; const newLogCreatedAt = result?.data.insert_analytics_logs_one?.created_at; @@ -156,7 +164,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ direction: AnalyticsLogDirection, analyticsId: number, metadata: NodeMetadata, - node_title: string, + nodeTitle: string, ) { const result = await publicClient.mutate({ mutation: gql` @@ -166,6 +174,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ $metadata: jsonb $node_type: Int $node_title: String + $user_agent: jsonb ) { insert_analytics_logs_one( object: { @@ -187,7 +196,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ analytics_id: analyticsId, metadata: metadata, node_type: node?.type, - node_title: node_title, + node_title: nodeTitle, }, }); return result; @@ -264,11 +273,13 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ } } - async function trackResetFlow() { + async function trackFlowDirectionChange( + flowDirection: AnalyticsLogDirection, + ) { if (shouldTrackAnalytics && lastAnalyticsLogId) { await publicClient.mutate({ mutation: gql` - mutation UpdateHasResetFlow($id: bigint!, $flow_direction: String) { + mutation UpdateFlowDirection($id: bigint!, $flow_direction: String) { update_analytics_logs_by_pk( pk_columns: { id: $id } _set: { flow_direction: $flow_direction } @@ -279,7 +290,7 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ `, variables: { id: lastAnalyticsLogId, - flow_direction: "reset", + flow_direction: flowDirection, }, }); } @@ -287,10 +298,25 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ async function createAnalytics(type: AnalyticsType) { if (shouldTrackAnalytics) { + const userAgent = Bowser.parse(window.navigator.userAgent); + const referrer = document.referrer || null; + const response = await publicClient.mutate({ mutation: gql` - mutation InsertNewAnalytics($type: String, $flow_id: uuid) { - insert_analytics_one(object: { type: $type, flow_id: $flow_id }) { + mutation InsertNewAnalytics( + $type: String + $flow_id: uuid + $user_agent: jsonb + $referrer: String + ) { + insert_analytics_one( + object: { + type: $type + flow_id: $flow_id + user_agent: $user_agent + referrer: $referrer + } + ) { id } } @@ -298,6 +324,8 @@ export const AnalyticsProvider: React.FC<{ children: React.ReactNode }> = ({ variables: { type, flow_id: flowId, + user_agent: userAgent, + referrer, }, }); const id = response.data.insert_analytics_one.id; diff --git a/hasura.planx.uk/metadata/tables.yaml b/hasura.planx.uk/metadata/tables.yaml index f4d1793f57..e3fb973ab0 100644 --- a/hasura.planx.uk/metadata/tables.yaml +++ b/hasura.planx.uk/metadata/tables.yaml @@ -9,7 +9,9 @@ - created_at - flow_id - id + - referrer - type + - user_agent select_permissions: - role: public permission: diff --git a/hasura.planx.uk/migrations/1698350858952_alter_table_public_analytics_logs_add_column_user_agent/down.sql b/hasura.planx.uk/migrations/1698350858952_alter_table_public_analytics_logs_add_column_user_agent/down.sql new file mode 100644 index 0000000000..deae24b750 --- /dev/null +++ b/hasura.planx.uk/migrations/1698350858952_alter_table_public_analytics_logs_add_column_user_agent/down.sql @@ -0,0 +1,3 @@ +ALTER TABLE "public"."analytics" DROP COLUMN "referrer" + +ALTER TABLE "public"."analytics" DROP COLUMN "user_agent" \ No newline at end of file diff --git a/hasura.planx.uk/migrations/1698350858952_alter_table_public_analytics_logs_add_column_user_agent/up.sql b/hasura.planx.uk/migrations/1698350858952_alter_table_public_analytics_logs_add_column_user_agent/up.sql new file mode 100644 index 0000000000..a5a449ae17 --- /dev/null +++ b/hasura.planx.uk/migrations/1698350858952_alter_table_public_analytics_logs_add_column_user_agent/up.sql @@ -0,0 +1,5 @@ +alter table "public"."analytics" add column "user_agent" jsonb + null default '{}'; + +alter table "public"."analytics" add column "referrer" text + null; \ No newline at end of file