From b57842f9c8217867057a76c8c10766048bf1e6a2 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:17:06 +0100 Subject: [PATCH 001/108] feat: Initial version of `fdm-rvo` that that provides the logic for fdm-app to connect to RVO and sync fields with fdm-core --- .changeset/tangy-ways-win.md | 5 + fdm-rvo/.gitignore | 27 ++++ fdm-rvo/package.json | 72 +++++++++ fdm-rvo/rollup.config.js | 31 ++++ fdm-rvo/src/auth.ts | 29 ++++ fdm-rvo/src/compare.ts | 203 ++++++++++++++++++++++++++ fdm-rvo/src/data.ts | 26 ++++ fdm-rvo/src/index.ts | 5 + fdm-rvo/src/types.ts | 42 ++++++ fdm-rvo/tsconfig.build.json | 12 ++ fdm-rvo/tsconfig.json | 17 +++ package.json | 3 +- pnpm-lock.yaml | 273 ++++++++++++++++++++++++++++++++++- pnpm-workspace.yaml | 1 + 14 files changed, 740 insertions(+), 6 deletions(-) create mode 100644 .changeset/tangy-ways-win.md create mode 100644 fdm-rvo/.gitignore create mode 100644 fdm-rvo/package.json create mode 100644 fdm-rvo/rollup.config.js create mode 100644 fdm-rvo/src/auth.ts create mode 100644 fdm-rvo/src/compare.ts create mode 100644 fdm-rvo/src/data.ts create mode 100644 fdm-rvo/src/index.ts create mode 100644 fdm-rvo/src/types.ts create mode 100644 fdm-rvo/tsconfig.build.json create mode 100644 fdm-rvo/tsconfig.json diff --git a/.changeset/tangy-ways-win.md b/.changeset/tangy-ways-win.md new file mode 100644 index 000000000..367741b78 --- /dev/null +++ b/.changeset/tangy-ways-win.md @@ -0,0 +1,5 @@ +--- +"@svenvw/fdm-rvo": minor +--- + +Initial version of `fdm-rvo` that that provides the logic for fdm-app to connect to RVO and sync fields with fdm-core diff --git a/fdm-rvo/.gitignore b/fdm-rvo/.gitignore new file mode 100644 index 000000000..72375ed48 --- /dev/null +++ b/fdm-rvo/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +coverage +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.env \ No newline at end of file diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json new file mode 100644 index 000000000..2dd59ec94 --- /dev/null +++ b/fdm-rvo/package.json @@ -0,0 +1,72 @@ +{ + "name": "@svenvw/fdm-rvo", + "private": false, + "version": "0.1.0", + "description": "RVO Synchronization Logic for FDM", + "homepage": "https://svenvw.github.io/fdm/", + "repository": { + "type": "git", + "url": "git+https://github.com/SvenVw/fdm.git" + }, + "bugs": "https://github.com/SvenVw/fdm/issues/new", + "author": { + "name": "Sven Verweij", + "email": "37927107+SvenVw@users.noreply.github.com", + "url": "https://github.com/SvenVw" + }, + "license": "MIT", + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "./types": { + "import": { + "types": "./dist/types.d.ts", + "default": "./dist/types.js" + } + } + }, + "files": [ + "dist" + ], + "scripts": { + "coverage": "vitest run --coverage", + "document": "npx typedoc src --plugin typedoc-plugin-missing-exports", + "build": "rollup -c && tsc -p tsconfig.build.json", + "check-types": "tsc --noEmit", + "test": "vitest run", + "test-coverage": "vitest run --coverage" + }, + "dependencies": { + "@nmi-agro/rvo-connector": "0.1.0", + "@svenvw/fdm-core": "workspace:*", + "@turf/area": "^7.0.0", + "@turf/bbox": "^7.0.0", + "@turf/intersect": "^7.0.0", + "@turf/union": "^7.0.0", + "@turf/helpers": "^7.0.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "catalog:", + "@rollup/plugin-node-resolve": "catalog:", + "@types/node": "^20.14.9", + "@vitest/coverage-v8": "catalog:", + "rollup": "catalog:", + "rollup-plugin-esbuild": "catalog:", + "typedoc": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "packageManager": "pnpm@10.23.0", + "publishConfig": { + "registry": "https://npm.pkg.github.com" + } +} diff --git a/fdm-rvo/rollup.config.js b/fdm-rvo/rollup.config.js new file mode 100644 index 000000000..c0fbdf3b7 --- /dev/null +++ b/fdm-rvo/rollup.config.js @@ -0,0 +1,31 @@ +import commonjs from "@rollup/plugin-commonjs" +import resolve from "@rollup/plugin-node-resolve" +import esbuild from "rollup-plugin-esbuild" +import packageJson from "./package.json" with { type: "json" } + +const isProd = process.env.NODE_ENV === "production" + +const external = [ + ...Object.keys(packageJson.dependencies || {}), + ...Object.keys(packageJson.peerDependencies || {}), +] + +export default { + input: "src/index.ts", + output: { + dir: "dist", + format: "esm", + preserveModules: true, + entryFileNames: "[name].js", + sourcemap: isProd ? true : "inline", + }, + plugins: [ + resolve(), + commonjs(), + esbuild({ + minify: isProd, // Use esbuild's minifier in production + target: "node20", + }), + ], + external, +} diff --git a/fdm-rvo/src/auth.ts b/fdm-rvo/src/auth.ts new file mode 100644 index 000000000..550a042d9 --- /dev/null +++ b/fdm-rvo/src/auth.ts @@ -0,0 +1,29 @@ +import { RvoClient } from "@nmi-agro/rvo-connector" + +export const createRvoClient = ( + clientId: string, + clientName: string, + redirectUri: string, + pkioPrivateKey: string, + environment: "acceptance" | "production" = "production", +) => { + return new RvoClient({ + clientId, + clientName, + environment, + tvs: { + clientId, + redirectUri, + pkioPrivateKey, + }, + }) +} + +export const generateAuthUrl = (rvoClient: RvoClient, state: string) => { + return rvoClient.getAuthorizationUrl({ state }) +} + +export const exchangeToken = async (rvoClient: RvoClient, code: string) => { + const tokenResponse = await rvoClient.exchangeAuthCode(code) + return tokenResponse.accessToken +} diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts new file mode 100644 index 000000000..7fee0165a --- /dev/null +++ b/fdm-rvo/src/compare.ts @@ -0,0 +1,203 @@ +import bbox from "@turf/bbox" +import intersect from "@turf/intersect" +import union from "@turf/union" +import area from "@turf/area" +import { feature, featureCollection } from "@turf/helpers" +import type { Field } from "@svenvw/fdm-core" +import { + type RvoField, + ReconciliationStatus, + type ReconciliationItem, + type FieldDiff, +} from "./types" + +// Threshold for IoU (Intersection over Union) to consider fields "the same" spatially +const IOU_THRESHOLD = 0.9 + +/** + * Calculates Intersection over Union (IoU) for two geometries. + */ +function calculateIoU(geom1: any, geom2: any): number { + try { + // Cast to Polygon or MultiPolygon because Turf expects specific geometry types for intersect/union + const f1 = feature(geom1) + const f2 = feature(geom2) + + // Turf v7 intersect takes a FeatureCollection of polygons + const intResult = intersect(featureCollection([f1, f2])) + if (!intResult) return 0 + + // Union also takes a FeatureCollection in some versions or strict types + const unionResult = union(featureCollection([f1, f2])) + if (!unionResult) return 0 + + const areaInt = area(intResult) + const areaUnion = area(unionResult) + + if (areaUnion === 0) return 0 + return areaInt / areaUnion + } catch (e) { + console.error("Error calculating IoU", e) + return 0 + } +} + +/** + * Checks if two bounding boxes overlap. + */ +function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { + return !( + ( + bbox1[2] < bbox2[0] || // left + bbox1[0] > bbox2[2] || // right + bbox1[3] < bbox2[1] || // bottom + bbox1[1] > bbox2[3] + ) // top + ) +} + +export function compareFields( + localFields: Field[], + rvoFields: RvoField[], +): ReconciliationItem[] { + const results: ReconciliationItem[] = [] + const matchedRvoIds = new Set() + const matchedLocalIds = new Set() + + // Tier 1: ID Match + for (const local of localFields) { + if (local.b_id_source) { + const rvoMatch = rvoFields.find( + (r) => r.properties.CropFieldID === local.b_id_source, + ) + if (rvoMatch) { + matchedLocalIds.add(local.b_id) + matchedRvoIds.add(rvoMatch.properties.CropFieldID) + + const diffs = detectDiffs(local, rvoMatch) + results.push({ + status: + diffs.length > 0 + ? ReconciliationStatus.CONFLICT + : ReconciliationStatus.MATCH, + localField: local, + rvoField: rvoMatch, + diffs, + }) + } + } + } + + // Tier 2: Spatial Match (for remaining fields) + // Pre-calculate bboxes for remaining local fields + const remainingLocals = localFields + .filter((f) => !matchedLocalIds.has(f.b_id)) + .map((f) => ({ + field: f, + bbox: bbox(f.b_geometry as any), + })) + + for (const rvo of rvoFields) { + if (matchedRvoIds.has(rvo.properties.CropFieldID)) continue + + const rvoBbox = bbox(rvo.geometry) + let bestMatch: Field | null = null + let bestIoU = 0 + + // Filter candidates by BBox first + const candidates = remainingLocals.filter((l) => + bboxOverlap(l.bbox, rvoBbox), + ) + + for (const candidate of candidates) { + const iou = calculateIoU(candidate.field.b_geometry, rvo.geometry) + if (iou > bestIoU) { + bestIoU = iou + bestMatch = candidate.field + } + } + + if (bestMatch && bestIoU > IOU_THRESHOLD) { + matchedRvoIds.add(rvo.properties.CropFieldID) + matchedLocalIds.add(bestMatch.b_id) // Important: mark as matched so it doesn't duplicate + + const diffs = detectDiffs(bestMatch, rvo) + results.push({ + status: + diffs.length > 0 + ? ReconciliationStatus.CONFLICT + : ReconciliationStatus.MATCH, + localField: bestMatch, + rvoField: rvo, + diffs, + }) + } else { + // No match found -> New Remote Field + results.push({ + status: ReconciliationStatus.NEW_REMOTE, + rvoField: rvo, + diffs: [], + }) + } + } + + // Remaining Local Fields -> New Local (suggest remove?) + for (const local of localFields) { + if (!matchedLocalIds.has(local.b_id)) { + results.push({ + status: ReconciliationStatus.NEW_LOCAL, + localField: local, + diffs: [], + }) + } + } + + return results +} + +function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { + const diffs: FieldDiff[] = [] + + // 1. Name + if ( + local.b_name !== rvo.properties.CropFieldDesignator && + rvo.properties.CropFieldDesignator + ) { + diffs.push("b_name") + } + + // 2. Geometry (Approximate check using IoU) + const iou = calculateIoU(local.b_geometry, rvo.geometry) + if (iou < 0.99) { + // Strict threshold for "exact" match + diffs.push("b_geometry") + } + + // 3. Dates (Start) + const localStart = + local.b_start instanceof Date + ? local.b_start.toISOString().split("T")[0] + : local.b_start + const rvoStart = rvo.properties.BeginDate + ? new Date(rvo.properties.BeginDate).toISOString().split("T")[0] + : null + + if (localStart !== rvoStart) { + diffs.push("b_start") + } + + // 4. Dates (End) + const localEnd = + local.b_end instanceof Date + ? local.b_end.toISOString().split("T")[0] + : null + const rvoEnd = rvo.properties.EndDate + ? new Date(rvo.properties.EndDate).toISOString().split("T")[0] + : null + + if (localEnd !== rvoEnd) { + diffs.push("b_end") + } + + return diffs +} diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts new file mode 100644 index 000000000..a270bc11c --- /dev/null +++ b/fdm-rvo/src/data.ts @@ -0,0 +1,26 @@ +import { RvoClient } from "@nmi-agro/rvo-connector" +import { RvoFieldSchema, type RvoField } from "./types" +import { z } from "zod" + +export async function fetchRvoFields( + rvoClient: RvoClient, + year: number, + kvkNumber: string, +): Promise { + const fieldsRaw = await rvoClient.opvragenBedrijfspercelen({ + periodBeginDate: `${year}-01-01`, + periodEndDate: `${year}-12-31`, + farmId: kvkNumber, + outputFormat: "geojson", + }) + + // Check if fieldsRaw has 'features' property (i.e. is a FeatureCollection) + const features = (fieldsRaw as any).features // Temporary unsafe cast to access features + + if (Array.isArray(features)) { + const RvoFieldsArraySchema = z.array(RvoFieldSchema) + return RvoFieldsArraySchema.parse(features) + } + + return [] // Return empty if no features or wrong format +} diff --git a/fdm-rvo/src/index.ts b/fdm-rvo/src/index.ts new file mode 100644 index 000000000..965a83463 --- /dev/null +++ b/fdm-rvo/src/index.ts @@ -0,0 +1,5 @@ +export * from "./auth" +export * from "./compare" +export * from "./data" +export * from "./types" +export * from "./auth" diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts new file mode 100644 index 000000000..fc701e6ae --- /dev/null +++ b/fdm-rvo/src/types.ts @@ -0,0 +1,42 @@ +import { z } from "zod"; + +// Define RvoFieldSchema matching the GeoJSON Feature structure from rvo-connector +export const RvoFieldSchema = z.object({ + type: z.literal("Feature"), + geometry: z.any(), // GeoJSON geometry + properties: z.object({ + CropFieldID: z.string(), + ThirdPartyCropFieldID: z.string().optional(), + CropFieldVersion: z.string(), + CropFieldDesignator: z.string(), + BeginDate: z.string(), + EndDate: z.string().optional(), + Country: z.string(), + CropTypeCode: z.union([z.string(), z.number()]), + VarietyCode: z.union([z.string(), z.number()]).optional(), + CropProductionPurposeCode: z.union([z.string(), z.number()]).optional(), + FieldUseCode: z.union([z.string(), z.number()]).optional(), + RegulatorySoiltypeCode: z.union([z.string(), z.number()]).optional(), + UseTitleCode: z.string(), // "01" | "02" etc. + CropFieldCause: z.string().optional(), + // QualityIndicatorType omitted for brevity/relevance for now, can be added if needed + }), +}); + +export type RvoField = z.infer; + +export enum ReconciliationStatus { + MATCH = "MATCH", + NEW_REMOTE = "NEW_REMOTE", + NEW_LOCAL = "NEW_LOCAL", + CONFLICT = "CONFLICT", +} + +export type FieldDiff = "b_name" | "b_geometry" | "b_start" | "b_end" | "b_acquiring_method"; + +export interface ReconciliationItem { + status: ReconciliationStatus; + localField?: TLocal; + rvoField?: RvoField; + diffs: FieldDiff[]; +} diff --git a/fdm-rvo/tsconfig.build.json b/fdm-rvo/tsconfig.build.json new file mode 100644 index 000000000..dcd8e95ba --- /dev/null +++ b/fdm-rvo/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "allowImportingTsExtensions": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "./dist", + "rootDir": "./src", + "tsBuildInfoFile": "./dist/.tsbuildinfo" + }, + "include": ["src/**/*"] +} diff --git a/fdm-rvo/tsconfig.json b/fdm-rvo/tsconfig.json new file mode 100644 index 000000000..6bafbc494 --- /dev/null +++ b/fdm-rvo/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "isolatedModules": true, + "moduleDetection": "force", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*", "vitest.config.ts"] +} diff --git a/package.json b/package.json index b7b107ce0..4956e4306 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "fdm-data", "fdm-docs", "fdm-app", - "fdm-calculator" + "fdm-calculator", + "fdm-rvo" ], "scripts": { "test": "turbo run test-coverage --filter=@svenvw/fdm-core --filter=@svenvw/fdm-calculator", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b19455776..bd6270443 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -148,6 +148,9 @@ importers: '@svenvw/fdm-data': specifier: workspace:* version: link:../fdm-data + '@svenvw/fdm-rvo': + specifier: workspace:^ + version: link:../fdm-rvo '@tailwindcss/vite': specifier: ^4.1.17 version: 4.1.17(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) @@ -574,6 +577,61 @@ importers: specifier: 'catalog:' version: 5.9.3 + fdm-rvo: + dependencies: + '@nmi-agro/rvo-connector': + specifier: 0.1.0 + version: 0.1.0 + '@svenvw/fdm-core': + specifier: workspace:* + version: link:../fdm-core + '@turf/area': + specifier: ^7.0.0 + version: 7.3.1 + '@turf/bbox': + specifier: ^7.0.0 + version: 7.3.1 + '@turf/helpers': + specifier: ^7.0.0 + version: 7.3.1 + '@turf/intersect': + specifier: ^7.0.0 + version: 7.3.1 + '@turf/union': + specifier: ^7.0.0 + version: 7.3.1 + zod: + specifier: ^3.23.8 + version: 3.25.76 + devDependencies: + '@rollup/plugin-commonjs': + specifier: 'catalog:' + version: 29.0.0(rollup@4.53.3) + '@rollup/plugin-node-resolve': + specifier: 'catalog:' + version: 16.0.3(rollup@4.53.3) + '@types/node': + specifier: ^20.14.9 + version: 20.19.26 + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.0.14(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + rollup: + specifier: 'catalog:' + version: 4.53.3 + rollup-plugin-esbuild: + specifier: 'catalog:' + version: 6.2.1(esbuild@0.25.12)(rollup@4.53.3) + typedoc: + specifier: 'catalog:' + version: 0.28.15(typescript@5.9.3) + typescript: + specifier: 'catalog:' + version: 5.9.3 + vitest: + specifier: 'catalog:' + version: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + packages: '@ai-sdk/gateway@2.0.18': @@ -2425,6 +2483,10 @@ packages: '@napi-rs/wasm-runtime@1.0.7': resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} + '@nmi-agro/rvo-connector@0.1.0': + resolution: {integrity: sha512-7upHmBwsFcsbBv+SrBKDxryabU7qPn7exJUWQGBBtoZa+phP1qZhtlUyxC+avz4rsbdKftxDyGSLXJXVw90GOg==} + engines: {node: '>=18'} + '@noble/ciphers@1.3.0': resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} @@ -4889,6 +4951,9 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@20.19.26': + resolution: {integrity: sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==} + '@types/node@24.10.1': resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} @@ -5419,6 +5484,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -6214,6 +6282,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + eciesjs@0.4.16: resolution: {integrity: sha512-dS5cbA9rA2VR4Ybuvhg6jvdmp46ubLn3E+px8cG/35aEDNclrqoCjg6mt0HYZ/M+OoESS3jSkCrqk1kWAEhWAw==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} @@ -7386,10 +7457,20 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} + jsts@2.7.1: resolution: {integrity: sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==} engines: {node: '>= 12'} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + katex@0.16.25: resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} hasBin: true @@ -7532,9 +7613,30 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -9989,6 +10091,9 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} @@ -10130,6 +10235,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -10436,6 +10545,14 @@ packages: xml-utils@1.10.2: resolution: {integrity: sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==} + xml2js@0.6.2: + resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} + engines: {node: '>=4.0.0'} + + xmlbuilder@11.0.1: + resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} + engines: {node: '>=4.0'} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -11394,7 +11511,7 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@4.1.13))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0)': + '@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@3.25.76))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0)': dependencies: '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.18 @@ -11405,9 +11522,9 @@ snapshots: nanostores: 1.1.0 zod: 4.1.13 - '@better-auth/telemetry@1.4.5(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@4.1.13))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0))': + '@better-auth/telemetry@1.4.5(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@3.25.76))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0))': dependencies: - '@better-auth/core': 1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@4.1.13))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0) + '@better-auth/core': 1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@3.25.76))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0) '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.18 @@ -13177,6 +13294,14 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@nmi-agro/rvo-connector@0.1.0': + dependencies: + jsonwebtoken: 9.0.3 + proj4: 2.20.2 + qs: 6.14.0 + uuid: 13.0.0 + xml2js: 0.6.2 + '@noble/ciphers@1.3.0': {} '@noble/ciphers@2.0.1': {} @@ -16475,6 +16600,10 @@ snapshots: '@types/node@17.0.45': {} + '@types/node@20.19.26': + dependencies: + undici-types: 6.21.0 + '@types/node@24.10.1': dependencies: undici-types: 7.16.0 @@ -16606,6 +16735,23 @@ snapshots: optionalDependencies: maplibre-gl: 5.14.0 + '@vitest/coverage-v8@4.0.14(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.14 + ast-v8-to-istanbul: 0.3.8 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magicast: 0.5.1 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-v8@4.0.14(vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -16632,6 +16778,14 @@ snapshots: chai: 6.2.1 tinyrainbow: 3.0.3 + '@vitest/mocker@4.0.15(vite@7.2.6(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 4.0.15 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.6(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + '@vitest/mocker@4.0.15(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.15 @@ -17000,8 +17154,8 @@ snapshots: better-auth@1.4.5(react-dom@19.2.1(react@19.2.1))(react@19.2.1): dependencies: - '@better-auth/core': 1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@4.1.13))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0) - '@better-auth/telemetry': 1.4.5(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@4.1.13))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0)) + '@better-auth/core': 1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@3.25.76))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0) + '@better-auth/telemetry': 1.4.5(@better-auth/core@1.4.5(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.4(zod@3.25.76))(jose@6.1.3)(kysely@0.28.8)(nanostores@1.1.0)) '@better-auth/utils': 0.3.0 '@better-fetch/fetch': 1.1.18 '@noble/ciphers': 2.0.1 @@ -17103,6 +17257,8 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.2(browserslist@4.28.1) + buffer-equal-constant-time@1.0.1: {} + buffer-from@1.1.2: {} bundle-name@4.1.0: @@ -17805,6 +17961,10 @@ snapshots: eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + eciesjs@0.4.16: dependencies: '@ecies/ciphers': 0.2.5(@noble/ciphers@1.3.0) @@ -19169,8 +19329,32 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonwebtoken@9.0.3: + dependencies: + jws: 4.0.1 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.3 + jsts@2.7.1: {} + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + katex@0.16.25: dependencies: commander: 8.3.0 @@ -19281,8 +19465,22 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + lodash.memoize@4.1.2: {} + lodash.once@4.1.1: {} + lodash.startcase@4.4.0: {} lodash.throttle@4.1.1: {} @@ -22252,6 +22450,8 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + undici-types@6.21.0: {} + undici-types@7.16.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -22400,6 +22600,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@13.0.0: {} + uuid@8.3.2: {} valibot@1.2.0(typescript@5.9.3): @@ -22496,6 +22698,22 @@ snapshots: - supports-color - typescript + vite@7.2.6(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.26 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + terser: 5.44.1 + yaml: 2.8.2 + vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): dependencies: esbuild: 0.25.12 @@ -22512,6 +22730,44 @@ snapshots: terser: 5.44.1 yaml: 2.8.2 + vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@7.2.6(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.6(@types/node@20.19.26)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 20.19.26 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vitest@4.0.15(@opentelemetry/api@1.9.0)(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.15 @@ -22804,6 +23060,13 @@ snapshots: xml-utils@1.10.2: {} + xml2js@0.6.2: + dependencies: + sax: 1.4.3 + xmlbuilder: 11.0.1 + + xmlbuilder@11.0.1: {} + xtend@4.0.2: {} xxhash-wasm@1.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2bde9f744..37f728f78 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,6 +4,7 @@ packages: - fdm-calculator - fdm-docs - fdm-app + - fdm-rvo catalog: '@dotenvx/dotenvx': ^1.51.1 From c2bdafe0940d5680559e297b79c4891f53a8d26f Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:37:59 +0100 Subject: [PATCH 002/108] docs: add typedocs and readme for fdm-rvo --- fdm-rvo/README.md | 123 +++++++++++++++++++++++++++++++++++++ fdm-rvo/package.json | 2 +- fdm-rvo/src/auth.ts | 32 ++++++++++ fdm-rvo/src/compare.ts | 85 +++++++++++++++++++++----- fdm-rvo/src/data.ts | 25 +++++++- fdm-rvo/src/index.ts | 29 ++++++++- fdm-rvo/src/types.ts | 134 +++++++++++++++++++++++++++++++---------- fdm-rvo/typedoc.json | 10 +++ 8 files changed, 388 insertions(+), 52 deletions(-) create mode 100644 fdm-rvo/README.md create mode 100644 fdm-rvo/typedoc.json diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md new file mode 100644 index 000000000..0c41206af --- /dev/null +++ b/fdm-rvo/README.md @@ -0,0 +1,123 @@ +# @svenvw/fdm-rvo + +## RVO Synchronization Logic for FDM + +This package provides the core logic for synchronizing agricultural field data with the RVO (Rijksdienst voor Ondernemend Nederland) webservices. It wraps the `@nmi-agro/rvo-connector` to handle authentication and data fetching, and implements a robust field comparison mechanism to detect new, missing, and conflicting field data between local and RVO records. + +### Features + +- **RVO Authentication Flow**: Helpers for generating authorization URLs and exchanging authorization codes for access tokens using the `RvoClient`. +- **Field Data Fetching**: Retrieves agricultural field data from RVO, with GeoJSON parsing and validation. +- **Field Reconciliation Engine**: + - Compares local FDM fields (`@svenvw/fdm-core`'s `Field` type) against RVO fields. + - Utilizes a two-tier matching strategy: ID-based matching followed by spatial (IoU) matching. + - Detects and categorizes fields as `MATCH`, `NEW_REMOTE` (in RVO but not local), `NEW_LOCAL` (in local but not RVO), or `CONFLICT` (different properties in both). + - Identifies specific differing properties (`b_name`, `b_geometry`, `b_start`, `b_end`) for conflicts. +- **Type Safety**: Fully typed for a seamless development experience. + +### Installation + +```bash +pnpm add @svenvw/fdm-rvo +# Or if in a monorepo, ensure it's linked as a workspace dependency +``` + +### Usage + +#### 1. Configuration + +Ensure your `fdm-app` or consuming application has the following environment variables configured and exposed via its `serverConfig`: + +```env +RVO_CLIENT_ID= +RVO_CLIENT_SECRET= +RVO_REDIRECT_URI= +RVO_PKIO_PRIVATE_KEY= +RVO_ENVIRONMENT=production # or acceptance +``` + +#### 2. Authentication Flow + +```typescript +// In your React Router v7 component (client-side) +import { createRvoClient, generateAuthUrl, exchangeToken } from '@svenvw/fdm-rvo'; +// Assuming you have a way to securely access server config on the client, +// or that these are provided as build-time env vars (e.g., VITE_RVO_CLIENT_ID) +import { serverConfig } from '~/lib/config.client'; // Example client-side config + +// To initiate authentication (e.g., from a button click handler) +const handleAuthInitiation = () => { + const { clientId, redirectUri, pkioPrivateKey, environment } = serverConfig.auth.rvo; + const clientName = serverConfig.name; // Use your app's name + + const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); + + // Encode current location to return to it after RVO login + const state = Buffer.from(JSON.stringify({ returnPath: window.location.pathname })).toString('base64'); + const authUrl = generateAuthUrl(rvoClient, state); + + window.location.href = authUrl; // Redirect the user +}; + +// To handle the RVO callback in a component rendered at the redirectUri +const handleRvoCallback = async (code: string, state: string) => { + const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.auth.rvo; + const clientName = serverConfig.name; + + // Validate state to prevent CSRF + const decodedState = JSON.parse(Buffer.from(state, 'base64').toString('utf-8')); + // ... perform validation, e.g., against a session/cookie based state ... + + const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); + const accessToken = await exchangeToken(rvoClient, code); + + // Now you have accessToken, you can fetch fields client-side or send it to a serverless function/backend API + return { accessToken, decodedState }; +}; +``` + +#### 3. Fetching and Reconciling Fields + +```typescript +// In your React Router v7 component (client-side), after getting accessToken +import { fetchRvoFields, compareFields } from '@svenvw/fdm-rvo'; +// Assuming you have an API endpoint to fetch local fields +const fetchLocalFieldsApi = async (farmId: string, principalId: string) => { + const response = await fetch(`/api/farm/${farmId}/fields?principalId=${principalId}`); + return response.json(); +}; + +const processRvoData = async (accessToken: string, farmId: string, principalId: string, year: number, kvkNumber: string) => { + const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.auth.rvo; + const clientName = serverConfig.name; + + const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); + + // Fetch RVO fields client-side + const rvoFields = await fetchRvoFields(rvoClient, year, kvkNumber); + + // Fetch local fields from your backend API + const localFields = await fetchLocalFieldsApi(farmId, principalId); + + const reconciliationResults = compareFields(localFields, rvoFields); + return reconciliationResults; +}; + +// To apply changes, you would send the user's decisions to a backend API endpoint +const applyChangesApi = async (farmId: string, decisions: any) => { + const response = await fetch(`/api/farm/${farmId}/apply-rvo-sync`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(decisions), + }); + return response.json(); +}; +``` + +### TypeDoc Generation + +To generate API documentation using TypeDoc, ensure your `tsconfig.json` and `typedoc.json` (if applicable) are configured correctly. The package introduction will be included from the JSDoc comment in `src/index.ts`. + +### Development + +For development and testing, ensure all required `@turf/*` dependencies are installed. diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json index 2dd59ec94..8f294bca1 100644 --- a/fdm-rvo/package.json +++ b/fdm-rvo/package.json @@ -38,7 +38,7 @@ ], "scripts": { "coverage": "vitest run --coverage", - "document": "npx typedoc src --plugin typedoc-plugin-missing-exports", + "document": "typedoc", "build": "rollup -c && tsc -p tsconfig.build.json", "check-types": "tsc --noEmit", "test": "vitest run", diff --git a/fdm-rvo/src/auth.ts b/fdm-rvo/src/auth.ts index 550a042d9..d438c619c 100644 --- a/fdm-rvo/src/auth.ts +++ b/fdm-rvo/src/auth.ts @@ -1,5 +1,18 @@ import { RvoClient } from "@nmi-agro/rvo-connector" +/** + * Creates and configures an instance of the RVO Client. + * + * This client is the main entry point for interacting with RVO services. + * It handles OAuth authentication and API requests. + * + * @param clientId - The OAuth 2.0 Client ID issued by RVO. + * @param clientName - A human-readable name for your application, used in logs or RVO consent screens. + * @param redirectUri - The callback URL where RVO will redirect the user after login. Must match the registered URI. + * @param pkioPrivateKey - The private key (PKIO) used for signing client assertions in the OAuth flow. + * @param environment - The RVO environment to connect to. Defaults to "production". Use "acceptance" for testing. + * @returns An initialized `RvoClient` instance ready for authentication. + */ export const createRvoClient = ( clientId: string, clientName: string, @@ -19,10 +32,29 @@ export const createRvoClient = ( }) } +/** + * Generates the authorization URL for the RVO OAuth 2.0 flow. + * + * This URL is where you should redirect the user to log in with eHerkenning. + * + * @param rvoClient - The initialized `RvoClient` instance. + * @param state - A unique, random string used to prevent CSRF attacks and maintain state (e.g., farm ID) across the redirect. + * @returns The full URL to redirect the user to. + */ export const generateAuthUrl = (rvoClient: RvoClient, state: string) => { return rvoClient.getAuthorizationUrl({ state }) } +/** + * Exchanges an authorization code for an access token. + * + * This function should be called in the callback route after the user returns from RVO. + * + * @param rvoClient - The initialized `RvoClient` instance. + * @param code - The authorization code received in the query parameters of the callback URL. + * @returns A promise that resolves to the `accessToken` string. + * @throws Will throw an error if the token exchange fails (e.g., invalid code, network error). + */ export const exchangeToken = async (rvoClient: RvoClient, code: string) => { const tokenResponse = await rvoClient.exchangeAuthCode(code) return tokenResponse.accessToken diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 7fee0165a..cbb65e23a 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -11,11 +11,19 @@ import { type FieldDiff, } from "./types" -// Threshold for IoU (Intersection over Union) to consider fields "the same" spatially -const IOU_THRESHOLD = 0.9 +// Threshold for IoU (Intersection over Union) to consider fields "the same" spatially. +// A value of 0.99 means the intersection area must be at least 99% of the union area. +const IOU_THRESHOLD = 0.99 /** * Calculates Intersection over Union (IoU) for two geometries. + * + * IoU is a standard metric for measuring the overlap between two shapes. + * Formula: Area(Intersection) / Area(Union) + * + * @param geom1 - The first geometry (GeoJSON). + * @param geom2 - The second geometry (GeoJSON). + * @returns A number between 0 (no overlap) and 1 (perfect match). Returns 0 on error. */ function calculateIoU(geom1: any, geom2: any): number { try { @@ -23,11 +31,11 @@ function calculateIoU(geom1: any, geom2: any): number { const f1 = feature(geom1) const f2 = feature(geom2) - // Turf v7 intersect takes a FeatureCollection of polygons + // Turf v7 intersect takes a FeatureCollection of polygons to intersect const intResult = intersect(featureCollection([f1, f2])) if (!intResult) return 0 - // Union also takes a FeatureCollection in some versions or strict types + // Union also takes a FeatureCollection const unionResult = union(featureCollection([f1, f2])) if (!unionResult) return 0 @@ -44,6 +52,12 @@ function calculateIoU(geom1: any, geom2: any): number { /** * Checks if two bounding boxes overlap. + * + * Used as a fast pre-filter before calculating expensive IoU operations. + * + * @param bbox1 - [minX, minY, maxX, maxY] + * @param bbox2 - [minX, minY, maxX, maxY] + * @returns True if boxes overlap, false otherwise. */ function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { return !( @@ -56,6 +70,19 @@ function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { ) } +/** + * Compares a list of local fields against a list of RVO fields to determine their reconciliation status. + * + * The matching strategy operates in two tiers: + * 1. **Tier 1: ID Match**: Checks if `localField.b_id_source` matches `rvoField.CropFieldID`. + * This is the most reliable method for fields that have been synced before. + * 2. **Tier 2: Spatial Match**: For fields unmatched by ID, it calculates the spatial overlap (IoU). + * If the overlap exceeds `IOU_THRESHOLD` (0.9), they are considered the same field. + * + * @param localFields - Array of fields currently in the local database. + * @param rvoFields - Array of fields retrieved from the RVO webservice. + * @returns An array of `ReconciliationItem` objects, each representing a field and its status (MATCH, CONFLICT, NEW_REMOTE, NEW_LOCAL). + */ export function compareFields( localFields: Field[], rvoFields: RvoField[], @@ -64,16 +91,20 @@ export function compareFields( const matchedRvoIds = new Set() const matchedLocalIds = new Set() - // Tier 1: ID Match + // --------------------------------------------------------- + // Tier 1: Match by Source ID (CropFieldID) + // --------------------------------------------------------- for (const local of localFields) { if (local.b_id_source) { const rvoMatch = rvoFields.find( (r) => r.properties.CropFieldID === local.b_id_source, ) if (rvoMatch) { + // Mark as matched to prevent re-matching in Tier 2 matchedLocalIds.add(local.b_id) matchedRvoIds.add(rvoMatch.properties.CropFieldID) + // Detect any property differences (geometry, name, dates) const diffs = detectDiffs(local, rvoMatch) results.push({ status: @@ -88,27 +119,33 @@ export function compareFields( } } - // Tier 2: Spatial Match (for remaining fields) - // Pre-calculate bboxes for remaining local fields + // --------------------------------------------------------- + // Tier 2: Spatial Match (IoU) for remaining fields + // --------------------------------------------------------- + + // Prepare candidates: Only local fields that haven't been matched yet const remainingLocals = localFields .filter((f) => !matchedLocalIds.has(f.b_id)) .map((f) => ({ field: f, + // Pre-calculate BBox for performance (avoid recalc inside loop) bbox: bbox(f.b_geometry as any), })) for (const rvo of rvoFields) { + // Skip if this RVO field was already matched in Tier 1 if (matchedRvoIds.has(rvo.properties.CropFieldID)) continue const rvoBbox = bbox(rvo.geometry) let bestMatch: Field | null = null let bestIoU = 0 - // Filter candidates by BBox first + // Optimization: Fast BBox overlap check before accurate IoU const candidates = remainingLocals.filter((l) => bboxOverlap(l.bbox, rvoBbox), ) + // Find the best spatial match among candidates for (const candidate of candidates) { const iou = calculateIoU(candidate.field.b_geometry, rvo.geometry) if (iou > bestIoU) { @@ -117,9 +154,10 @@ export function compareFields( } } + // If the best match exceeds our threshold, link them if (bestMatch && bestIoU > IOU_THRESHOLD) { matchedRvoIds.add(rvo.properties.CropFieldID) - matchedLocalIds.add(bestMatch.b_id) // Important: mark as matched so it doesn't duplicate + matchedLocalIds.add(bestMatch.b_id) const diffs = detectDiffs(bestMatch, rvo) results.push({ @@ -132,7 +170,7 @@ export function compareFields( diffs, }) } else { - // No match found -> New Remote Field + // No match found -> This is a NEW field from RVO results.push({ status: ReconciliationStatus.NEW_REMOTE, rvoField: rvo, @@ -141,7 +179,9 @@ export function compareFields( } } - // Remaining Local Fields -> New Local (suggest remove?) + // --------------------------------------------------------- + // Identify orphaned local fields + // --------------------------------------------------------- for (const local of localFields) { if (!matchedLocalIds.has(local.b_id)) { results.push({ @@ -155,10 +195,24 @@ export function compareFields( return results } +/** + * Detects specific property differences between a matched pair of Local and RVO fields. + * + * Compares: + * - Name (`b_name` vs `CropFieldDesignator`) + * - Geometry (via IoU < 0.99) + * - Start Date (`b_start` vs `BeginDate`) + * - End Date (`b_end` vs `EndDate`) + * + * @param local - The local field object. + * @param rvo - The RVO field object. + * @returns An array of property names (`FieldDiff`) that differ. + */ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { const diffs: FieldDiff[] = [] // 1. Name + // We check if RVO has a name (designator) and if it differs from local if ( local.b_name !== rvo.properties.CropFieldDesignator && rvo.properties.CropFieldDesignator @@ -166,10 +220,10 @@ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { diffs.push("b_name") } - // 2. Geometry (Approximate check using IoU) + // 2. Geometry + // We use a very strict IoU (0.99) to detect if the shape has been modified, even slightly. const iou = calculateIoU(local.b_geometry, rvo.geometry) - if (iou < 0.99) { - // Strict threshold for "exact" match + if (iou < IOU_THRESHOLD) { diffs.push("b_geometry") } @@ -195,7 +249,8 @@ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { ? new Date(rvo.properties.EndDate).toISOString().split("T")[0] : null - if (localEnd !== rvoEnd) { + // Treat null/undefined as equal if both are missing + if (localEnd !== rvoEnd && (localEnd !== null || rvoEnd !== null)) { diffs.push("b_end") } diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts index a270bc11c..2b426b231 100644 --- a/fdm-rvo/src/data.ts +++ b/fdm-rvo/src/data.ts @@ -2,11 +2,25 @@ import { RvoClient } from "@nmi-agro/rvo-connector" import { RvoFieldSchema, type RvoField } from "./types" import { z } from "zod" +/** + * Fetches agricultural fields (bedrijfspercelen) from the RVO webservice for a specific year and farm. + * + * This function retrieves the fields in GeoJSON format and validates them against the `RvoFieldSchema`. + * + * @param rvoClient - An authenticated instance of `RvoClient` (must have a valid access token). + * @param year - The calendar year for which to retrieve the fields (e.g., 2024). + * @param kvkNumber - The Chamber of Commerce (KvK) number of the farm/organization. This acts as the identifier for the data request. + * @returns A promise that resolves to an array of validated `RvoField` objects. + * @throws Will throw a ZodError if the response from RVO does not match the expected schema. + * @throws Will throw an error if the API request fails. + */ export async function fetchRvoFields( rvoClient: RvoClient, year: number, kvkNumber: string, ): Promise { + // Request fields from RVO API + // We request the full calendar year period const fieldsRaw = await rvoClient.opvragenBedrijfspercelen({ periodBeginDate: `${year}-01-01`, periodEndDate: `${year}-12-31`, @@ -14,13 +28,18 @@ export async function fetchRvoFields( outputFormat: "geojson", }) - // Check if fieldsRaw has 'features' property (i.e. is a FeatureCollection) - const features = (fieldsRaw as any).features // Temporary unsafe cast to access features + // The raw response is expected to be a GeoJSON FeatureCollection. + // We access the 'features' array to iterate over individual fields. + // Safe casting is handled by the subsequent Zod validation. + const features = (fieldsRaw as any).features if (Array.isArray(features)) { + // Define a schema for an array of fields and parse the data. + // This ensures runtime type safety and filters out malformed records if configured (though Zod default is strict). const RvoFieldsArraySchema = z.array(RvoFieldSchema) return RvoFieldsArraySchema.parse(features) } - return [] // Return empty if no features or wrong format + // Return empty array if the response format is unexpected or contains no features. + return [] } diff --git a/fdm-rvo/src/index.ts b/fdm-rvo/src/index.ts index 965a83463..214023f18 100644 --- a/fdm-rvo/src/index.ts +++ b/fdm-rvo/src/index.ts @@ -1,5 +1,32 @@ +/** + * # @svenvw/fdm-rvo: RVO Field Synchronization Logic + * + * This package provides the core logic for synchronizing agricultural field data with the + * RVO (Rijksdienst voor Ondernemend Nederland) webservices. It wraps the + * `@nmi-agro/rvo-connector` to handle authentication and data fetching, and implements a + * robust field comparison mechanism to detect new, missing, and conflicting field data + * between local and RVO records. + * + * ## Features + * + * - **RVO Authentication Flow**: Helpers for generating authorization URLs and exchanging + * authorization codes for access tokens using the `RvoClient`. + * - **Field Data Fetching**: Retrieves agricultural field data from RVO, with GeoJSON + * parsing and validation against `RvoFieldSchema`. + * - **Field Reconciliation Engine**: + * - Compares local FDM fields (`@svenvw/fdm-core`'s `Field` type) against RVO fields. + * - Utilizes a two-tier matching strategy: ID-based matching followed by spatial + * (IoU) matching. + * - Detects and categorizes fields as `MATCH`, `NEW_REMOTE` (in RVO but not local), + * `NEW_LOCAL` (in local but not RVO), or `CONFLICT` (different properties in both). + * - Identifies specific differing properties (`b_name`, `b_geometry`, `b_start`, `b_end`) + * for conflicts, allowing granular resolution. + * - **Type Safety**: Fully typed for a seamless development experience. + * + * @packageDocumentation + */ + export * from "./auth" export * from "./compare" export * from "./data" export * from "./types" -export * from "./auth" diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index fc701e6ae..b1277f0b8 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -1,42 +1,112 @@ -import { z } from "zod"; +import { z } from "zod" -// Define RvoFieldSchema matching the GeoJSON Feature structure from rvo-connector +/** + * Zod schema for validating RVO Field data. + * + * This schema matches the GeoJSON Feature structure returned by the RVO connector. + * It validates the essential properties required for field synchronization. + * + * @remarks + * The geometry is typed as `z.any()` here to allow flexibility with GeoJSON types, + * but in practice, it will be a Polygon or MultiPolygon. + */ export const RvoFieldSchema = z.object({ - type: z.literal("Feature"), - geometry: z.any(), // GeoJSON geometry - properties: z.object({ - CropFieldID: z.string(), - ThirdPartyCropFieldID: z.string().optional(), - CropFieldVersion: z.string(), - CropFieldDesignator: z.string(), - BeginDate: z.string(), - EndDate: z.string().optional(), - Country: z.string(), - CropTypeCode: z.union([z.string(), z.number()]), - VarietyCode: z.union([z.string(), z.number()]).optional(), - CropProductionPurposeCode: z.union([z.string(), z.number()]).optional(), - FieldUseCode: z.union([z.string(), z.number()]).optional(), - RegulatorySoiltypeCode: z.union([z.string(), z.number()]).optional(), - UseTitleCode: z.string(), // "01" | "02" etc. - CropFieldCause: z.string().optional(), - // QualityIndicatorType omitted for brevity/relevance for now, can be added if needed - }), -}); + /** Fixed type for GeoJSON Feature */ + type: z.literal("Feature"), + /** GeoJSON geometry of the field (Polygon or MultiPolygon) */ + geometry: z.any(), + /** Properties specific to the RVO crop field */ + properties: z.object({ + /** Unique identifier for the crop field (Gewasperceel ID) */ + CropFieldID: z.string(), + /** Optional third-party identifier */ + ThirdPartyCropFieldID: z.string().optional(), + /** Version identifier of the crop field data */ + CropFieldVersion: z.string(), + /** Name or designator of the field (e.g., "Perceel 1") */ + CropFieldDesignator: z.string(), + /** Start date of the crop field registration (ISO 8601 string) */ + BeginDate: z.string(), + /** End date of the crop field registration (ISO 8601 string), optional */ + EndDate: z.string().optional(), + /** Country code (e.g., "NL") */ + Country: z.string(), + /** Code representing the type of crop grown */ + CropTypeCode: z.union([z.string(), z.number()]), + /** Optional code for the specific variety of the crop */ + VarietyCode: z.union([z.string(), z.number()]).optional(), + /** Optional code for the production purpose */ + CropProductionPurposeCode: z.union([z.string(), z.number()]).optional(), + /** Optional code for field usage */ + FieldUseCode: z.union([z.string(), z.number()]).optional(), + /** Optional code for regulatory soil type */ + RegulatorySoiltypeCode: z.union([z.string(), z.number()]).optional(), + /** Code indicating the title/right of use (e.g., "01" for ownership, "02" for lease) */ + UseTitleCode: z.string(), + /** Optional cause for the field record */ + CropFieldCause: z.string().optional(), + }), +}) -export type RvoField = z.infer; +/** + * TypeScript type inferred from `RvoFieldSchema`. + * Represents a single agricultural field as retrieved from the RVO webservice. + */ +export type RvoField = z.infer +/** + * Status of a field during the reconciliation process between local data and RVO data. + */ export enum ReconciliationStatus { - MATCH = "MATCH", - NEW_REMOTE = "NEW_REMOTE", - NEW_LOCAL = "NEW_LOCAL", - CONFLICT = "CONFLICT", + /** The field exists in both systems and is identical (no conflicts). */ + MATCH = "MATCH", + /** The field exists in RVO but is missing locally. Suggests adding it to the local system. */ + NEW_REMOTE = "NEW_REMOTE", + /** The field exists locally but is missing in RVO. Suggests removing it or keeping it as local-only. */ + NEW_LOCAL = "NEW_LOCAL", + /** The field exists in both systems but has differing properties (e.g., geometry, name). */ + CONFLICT = "CONFLICT", } -export type FieldDiff = "b_name" | "b_geometry" | "b_start" | "b_end" | "b_acquiring_method"; +/** + * Identifiers for specific properties that differ between a local field and an RVO field. + * Used to highlight changes in the UI. + */ +export type FieldDiff = + | "b_name" // Name difference + | "b_geometry" // Spatial/Shape difference + | "b_start" // Start date difference + | "b_end" // End date difference + | "b_acquiring_method" // Method of acquisition difference (implied) +/** + * Represents the explicit decision made by the user for a reconciliation item. + */ +export enum UserReconciliationDecision { + /** Use the RVO field's data (for CONFLICT, NEW_REMOTE) */ + USE_RVO = "USE_RVO", + /** Keep the local field's data (for CONFLICT, NEW_LOCAL) */ + KEEP_LOCAL = "KEEP_LOCAL", + /** Explicitly add a new RVO field */ + ADD = "ADD", + /** Explicitly remove a local field */ + REMOVE = "REMOVE", + /** Ignore this reconciliation item (take no action) */ + IGNORE = "IGNORE", +} + +/** + * Represents the result of comparing a single field between the local database and RVO. + * + * @template TLocal - The type of the local field object (defaults to `any` if not specified, typically `Field` from fdm-core). + */ export interface ReconciliationItem { - status: ReconciliationStatus; - localField?: TLocal; - rvoField?: RvoField; - diffs: FieldDiff[]; + /** The reconciliation status (Match, New, Conflict, etc.) */ + status: ReconciliationStatus + /** The local field object, if it exists (undefined for NEW_REMOTE) */ + localField?: TLocal + /** The RVO field object, if it exists (undefined for NEW_LOCAL) */ + rvoField?: RvoField + /** List of specific properties that differ (empty for MATCH, NEW_REMOTE, NEW_LOCAL) */ + diffs: FieldDiff[] } diff --git a/fdm-rvo/typedoc.json b/fdm-rvo/typedoc.json new file mode 100644 index 000000000..96bc340e4 --- /dev/null +++ b/fdm-rvo/typedoc.json @@ -0,0 +1,10 @@ +{ + "entryPoints": ["src/index.ts"], + "out": "docs", + "readme": "README.md", + "name": "@svenvw/fdm-rvo", + "excludePrivate": true, + "excludeProtected": true, + "hideGenerator": true, + "plugin": ["typedoc-plugin-missing-exports"] +} From 81dd1b684fb10f188ebfb7aa769c4027c69f991f Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:42:45 +0100 Subject: [PATCH 003/108] docs: add the reference for fdm-rvo --- .changeset/public-llamas-clap.md | 5 +++++ fdm-docs/blog/tags.yml | 5 +++++ fdm-docs/typedoc.json | 3 ++- tsconfig.typedoc.json | 3 ++- 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/public-llamas-clap.md diff --git a/.changeset/public-llamas-clap.md b/.changeset/public-llamas-clap.md new file mode 100644 index 000000000..a92baf843 --- /dev/null +++ b/.changeset/public-llamas-clap.md @@ -0,0 +1,5 @@ +--- +"@svenvw/fdm-docs": minor +--- + +Add the reference for fdm-rvo diff --git a/fdm-docs/blog/tags.yml b/fdm-docs/blog/tags.yml index b9f7b30e4..c8380ddf0 100644 --- a/fdm-docs/blog/tags.yml +++ b/fdm-docs/blog/tags.yml @@ -28,6 +28,11 @@ fdm-app: description: Posts related to the React web application to interact with FDM (@svenvw/fdm-app). permalink: /fdm-app +fdm-rvo: + label: fdm-rvo + description: Posts related to the RVO Synchronization Logic for FDM (@svenvw/fdm-rvo). + permalink: /fdm-rvo + update: label: Update description: General updates and improvements to the FDM ecosystem. diff --git a/fdm-docs/typedoc.json b/fdm-docs/typedoc.json index 504e21a12..4b87a0cd0 100644 --- a/fdm-docs/typedoc.json +++ b/fdm-docs/typedoc.json @@ -3,7 +3,8 @@ "entryPoints": [ "../fdm-core/src/index.ts", "../fdm-data/src/index.ts", - "../fdm-calculator/src/index.ts" + "../fdm-calculator/src/index.ts", + "../fdm-rvo/src/index.ts" ], "entryPointStrategy": "resolve", "plugin": ["typedoc-plugin-markdown"], diff --git a/tsconfig.typedoc.json b/tsconfig.typedoc.json index c0b19fdeb..06fd43c43 100644 --- a/tsconfig.typedoc.json +++ b/tsconfig.typedoc.json @@ -12,7 +12,8 @@ "include": [ "fdm-core/src/**/*", "fdm-data/src/**/*", - "fdm-calculator/src/**/*" + "fdm-calculator/src/**/*", + "fdm-rvo/src/**/*" ], "exclude": ["node_modules", "**/*.test.ts", "**/dist", "**/build"] } From 8fa4cb0f35557b72b2f49a43f89f141303eeb6bb Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:42:05 +0100 Subject: [PATCH 004/108] refactor: rename to RVO Import Review --- fdm-rvo/README.md | 6 +++--- fdm-rvo/src/compare.ts | 24 ++++++++++++------------ fdm-rvo/src/index.ts | 2 +- fdm-rvo/src/types.ts | 16 ++++++++-------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index 0c41206af..0bad5f07a 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -8,7 +8,7 @@ This package provides the core logic for synchronizing agricultural field data w - **RVO Authentication Flow**: Helpers for generating authorization URLs and exchanging authorization codes for access tokens using the `RvoClient`. - **Field Data Fetching**: Retrieves agricultural field data from RVO, with GeoJSON parsing and validation. -- **Field Reconciliation Engine**: +- **RVO Import Review Engine**: - Compares local FDM fields (`@svenvw/fdm-core`'s `Field` type) against RVO fields. - Utilizes a two-tier matching strategy: ID-based matching followed by spatial (IoU) matching. - Detects and categorizes fields as `MATCH`, `NEW_REMOTE` (in RVO but not local), `NEW_LOCAL` (in local but not RVO), or `CONFLICT` (different properties in both). @@ -99,8 +99,8 @@ const processRvoData = async (accessToken: string, farmId: string, principalId: // Fetch local fields from your backend API const localFields = await fetchLocalFieldsApi(farmId, principalId); - const reconciliationResults = compareFields(localFields, rvoFields); - return reconciliationResults; + const rvoImportReviewResults = compareFields(localFields, rvoFields); + return rvoImportReviewResults; }; // To apply changes, you would send the user's decisions to a backend API endpoint diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index cbb65e23a..f7d6445f3 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -6,8 +6,8 @@ import { feature, featureCollection } from "@turf/helpers" import type { Field } from "@svenvw/fdm-core" import { type RvoField, - ReconciliationStatus, - type ReconciliationItem, + RvoImportReviewStatus, + type RvoImportReviewItem, type FieldDiff, } from "./types" @@ -71,7 +71,7 @@ function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { } /** - * Compares a list of local fields against a list of RVO fields to determine their reconciliation status. + * Compares a list of local fields against a list of RVO fields to determine their import status. * * The matching strategy operates in two tiers: * 1. **Tier 1: ID Match**: Checks if `localField.b_id_source` matches `rvoField.CropFieldID`. @@ -81,13 +81,13 @@ function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { * * @param localFields - Array of fields currently in the local database. * @param rvoFields - Array of fields retrieved from the RVO webservice. - * @returns An array of `ReconciliationItem` objects, each representing a field and its status (MATCH, CONFLICT, NEW_REMOTE, NEW_LOCAL). + * @returns An array of `RvoImportReviewItem` objects, each representing a field and its status (MATCH, CONFLICT, NEW_REMOTE, NEW_LOCAL). */ export function compareFields( localFields: Field[], rvoFields: RvoField[], -): ReconciliationItem[] { - const results: ReconciliationItem[] = [] +): RvoImportReviewItem[] { + const results: RvoImportReviewItem[] = [] const matchedRvoIds = new Set() const matchedLocalIds = new Set() @@ -109,8 +109,8 @@ export function compareFields( results.push({ status: diffs.length > 0 - ? ReconciliationStatus.CONFLICT - : ReconciliationStatus.MATCH, + ? RvoImportReviewStatus.CONFLICT + : RvoImportReviewStatus.MATCH, localField: local, rvoField: rvoMatch, diffs, @@ -163,8 +163,8 @@ export function compareFields( results.push({ status: diffs.length > 0 - ? ReconciliationStatus.CONFLICT - : ReconciliationStatus.MATCH, + ? RvoImportReviewStatus.CONFLICT + : RvoImportReviewStatus.MATCH, localField: bestMatch, rvoField: rvo, diffs, @@ -172,7 +172,7 @@ export function compareFields( } else { // No match found -> This is a NEW field from RVO results.push({ - status: ReconciliationStatus.NEW_REMOTE, + status: RvoImportReviewStatus.NEW_REMOTE, rvoField: rvo, diffs: [], }) @@ -185,7 +185,7 @@ export function compareFields( for (const local of localFields) { if (!matchedLocalIds.has(local.b_id)) { results.push({ - status: ReconciliationStatus.NEW_LOCAL, + status: RvoImportReviewStatus.NEW_LOCAL, localField: local, diffs: [], }) diff --git a/fdm-rvo/src/index.ts b/fdm-rvo/src/index.ts index 214023f18..8df12d1eb 100644 --- a/fdm-rvo/src/index.ts +++ b/fdm-rvo/src/index.ts @@ -13,7 +13,7 @@ * authorization codes for access tokens using the `RvoClient`. * - **Field Data Fetching**: Retrieves agricultural field data from RVO, with GeoJSON * parsing and validation against `RvoFieldSchema`. - * - **Field Reconciliation Engine**: + * - **Field RVO Import Review Engine**: * - Compares local FDM fields (`@svenvw/fdm-core`'s `Field` type) against RVO fields. * - Utilizes a two-tier matching strategy: ID-based matching followed by spatial * (IoU) matching. diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index b1277f0b8..af63f38bf 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -55,9 +55,9 @@ export const RvoFieldSchema = z.object({ export type RvoField = z.infer /** - * Status of a field during the reconciliation process between local data and RVO data. + * Status of a field during the RVO Import Review process between local data and RVO data. */ -export enum ReconciliationStatus { +export enum RvoImportReviewStatus { /** The field exists in both systems and is identical (no conflicts). */ MATCH = "MATCH", /** The field exists in RVO but is missing locally. Suggests adding it to the local system. */ @@ -80,9 +80,9 @@ export type FieldDiff = | "b_acquiring_method" // Method of acquisition difference (implied) /** - * Represents the explicit decision made by the user for a reconciliation item. + * Represents the explicit decision made by the user for a RVO Import Review item. */ -export enum UserReconciliationDecision { +export enum UserRvoImportReviewDecision { /** Use the RVO field's data (for CONFLICT, NEW_REMOTE) */ USE_RVO = "USE_RVO", /** Keep the local field's data (for CONFLICT, NEW_LOCAL) */ @@ -91,7 +91,7 @@ export enum UserReconciliationDecision { ADD = "ADD", /** Explicitly remove a local field */ REMOVE = "REMOVE", - /** Ignore this reconciliation item (take no action) */ + /** Ignore this RVO Import Review item (take no action) */ IGNORE = "IGNORE", } @@ -100,9 +100,9 @@ export enum UserReconciliationDecision { * * @template TLocal - The type of the local field object (defaults to `any` if not specified, typically `Field` from fdm-core). */ -export interface ReconciliationItem { - /** The reconciliation status (Match, New, Conflict, etc.) */ - status: ReconciliationStatus +export interface RvoImportReviewItem { + /** The RVO Import Review status (Match, New, Conflict, etc.) */ + status: RvoImportReviewStatus /** The local field object, if it exists (undefined for NEW_REMOTE) */ localField?: TLocal /** The RVO field object, if it exists (undefined for NEW_LOCAL) */ From fd0a675dc39300c23c3eabbae86d79ecbbb7b47c Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Wed, 10 Dec 2025 15:19:21 +0100 Subject: [PATCH 005/108] feat: Add the option to import fields from RVO in farm create wizard and also at the fields page of the farm --- .changeset/chatty-bananas-sink.md | 5 + fdm-app/.env.example | 8 + .../blocks/rvo/import-review-table.tsx | 312 +++++++++ fdm-app/app/lib/config.server.ts | 6 + fdm-app/app/lib/rvo.server.ts | 1 + ...farm.$b_id_farm.$calendar.field._index.tsx | 22 +- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 602 ++++++++++++++++++ ...arm.create.$b_id_farm.$calendar._index.tsx | 49 +- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 537 ++++++++++++++++ fdm-app/app/routes/farm.create._index.tsx | 46 +- fdm-app/app/types/config.d.ts | 6 + fdm-app/package.json | 2 + 12 files changed, 1591 insertions(+), 5 deletions(-) create mode 100644 .changeset/chatty-bananas-sink.md create mode 100644 fdm-app/app/components/blocks/rvo/import-review-table.tsx create mode 100644 fdm-app/app/lib/rvo.server.ts create mode 100644 fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx create mode 100644 fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx diff --git a/.changeset/chatty-bananas-sink.md b/.changeset/chatty-bananas-sink.md new file mode 100644 index 000000000..1c89e5836 --- /dev/null +++ b/.changeset/chatty-bananas-sink.md @@ -0,0 +1,5 @@ +--- +"@svenvw/fdm-app": minor +--- + +Add the option to import fields from RVO in farm create wizard and also at the fields page of the farm diff --git a/fdm-app/.env.example b/fdm-app/.env.example index f682f37b6..4d4e49a35 100644 --- a/fdm-app/.env.example +++ b/fdm-app/.env.example @@ -68,6 +68,14 @@ GOOGLE_CLIENT_SECRET= MS_CLIENT_ID= MS_CLIENT_SECRET= +# RVO OAuth Credentials (Optional: Leave empty to disable RVO Integration) +# Required: No +RVO_CLIENT_ID= +RVO_CLIENT_NAME= +RVO_CLIENT_SECRET= +RVO_REDIRECT_URI= + + # ------------------------------------- # Map & Data Configuration # ------------------------------------- diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx new file mode 100644 index 000000000..134ac8f33 --- /dev/null +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -0,0 +1,312 @@ +import { + type ColumnDef, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table" +import type { RvoImportReviewItem } from "@svenvw/fdm-rvo/types" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "~/components/ui/table" +import { Badge } from "~/components/ui/badge" +import { Check, Plus, Trash2, ArrowLeftRight, X } from "lucide-react" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "~/components/ui/select" + +export type ImportReviewAction = + | "ADD_REMOTE" + | "UPDATE_FROM_REMOTE" + | "KEEP_LOCAL" + | "REMOVE_LOCAL" + | "IGNORE" + | "NO_ACTION" + +export type UserChoiceMap = Record + +interface RvoImportReviewTableProps { + data: RvoImportReviewItem[] + userChoices: UserChoiceMap + onChoiceChange: (id: string, action: ImportReviewAction) => void +} + +// Helper to derive a stable ID for the row +export function getItemId(item: RvoImportReviewItem): string { + return ( + item.localField?.b_id || + item.rvoField?.properties.CropFieldID || + "unknown" + ) +} + +export const columns: ColumnDef>[] = [ + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => { + const status = row.getValue("status") as string + switch (status) { + case "MATCH": + return ( + + Gelijk + + ) + case "NEW_REMOTE": + return ( + + Nieuw (RVO) + + ) + case "NEW_LOCAL": + return ( + + Nieuw (Lokaal) + + ) + case "CONFLICT": + return ( + + Conflict + + ) + default: + return {status} + } + }, + }, + { + header: "Lokaal Perceel", + cell: ({ row }) => { + const local = row.original.localField + if (!local) + return ( + + Geen + + ) + return ( +
+ {local.b_name} + + {local.b_id_source || "Geen Bron ID"} + +
+ ) + }, + }, + { + header: "RVO Perceel", + cell: ({ row }) => { + const remote = row.original.rvoField + if (!remote) + return ( + + Geen + + ) + return ( +
+ + {remote.properties.CropFieldDesignator || "Naamloos"} + + + {remote.properties.CropFieldID} + +
+ ) + }, + }, + { + header: "Verschillen", + cell: ({ row }) => { + const diffs = row.original.diffs + if (!diffs || diffs.length === 0) + return - + return ( +
+ {diffs.map((diff) => ( + + {diff} + + ))} +
+ ) + }, + }, + { + id: "actions", + header: "Actie", + cell: ({ row, table }) => { + const item = row.original + const id = getItemId(item) + // @ts-ignore - meta is not strictly typed in React Table basic usage but useful here + const { userChoices, onChoiceChange } = table.options.meta as any + const currentChoice = userChoices[id] as ImportReviewAction + + if (item.status === "MATCH") { + return ( +
+ + Geimporteerd +
+ ) + } + + return ( + + ) + }, + }, +] + +export function RvoImportReviewTable({ + data, + userChoices, + onChoiceChange, +}: RvoImportReviewTableProps) { + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + meta: { + userChoices, + onChoiceChange, + }, + }) + + return ( +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef + .header, + header.getContext(), + )} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + Geen resultaten. + + + )} + +
+
+ ) +} diff --git a/fdm-app/app/lib/config.server.ts b/fdm-app/app/lib/config.server.ts index d0b455b08..8a32ac021 100644 --- a/fdm-app/app/lib/config.server.ts +++ b/fdm-app/app/lib/config.server.ts @@ -19,6 +19,12 @@ export const serverConfig: ServerConfig = { clientId: String(process.env.MS_CLIENT_ID), clientSecret: String(process.env.MS_CLIENT_SECRET), }, + rvo: { + clientId: String(process.env.RVO_CLIENT_ID), + clientSecret: String(process.env.RVO_CLIENT_SECRET), + redirectUri: String(process.env.RVO_REDIRECT_URI), + clientName: String(process.env.RVO_CLIENT_NAME), + }, }, // Database diff --git a/fdm-app/app/lib/rvo.server.ts b/fdm-app/app/lib/rvo.server.ts new file mode 100644 index 000000000..afeab46d4 --- /dev/null +++ b/fdm-app/app/lib/rvo.server.ts @@ -0,0 +1 @@ +export * from "@svenvw/fdm-rvo" diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx index 3bf3a2bbd..d8bd4bf9f 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx @@ -25,7 +25,7 @@ import { BreadcrumbItem, BreadcrumbSeparator } from "~/components/ui/breadcrumb" import { Button } from "~/components/ui/button" import { SidebarInset } from "~/components/ui/sidebar" import { getSession } from "~/lib/auth.server" -import { getTimeframe } from "~/lib/calendar" +import { getCalendar, getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" @@ -77,6 +77,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { // Get timeframe from calendar store const timeframe = getTimeframe(params) + const calendar = getCalendar(params) // Get a list of possible farms of the user const farms = await getFarms(fdm, session.principal_id) @@ -193,6 +194,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { // Return user information from loader return { b_id_farm: b_id_farm, + calendar: calendar, farmOptions: farmOptions, fieldOptions: fieldOptions, fieldsExtended: fieldsExtended, @@ -277,6 +279,19 @@ export default function FarmFieldIndex() { Maak een perceel + + + Percelen ophalen bij RVO + + Importeer percelen direct vanuit RVO. + + + + + {/*

*/} @@ -289,6 +304,11 @@ export default function FarmFieldIndex() { title={`Percelen van ${currentFarmName}`} description="Selecteer een perceel voor details of voeg een nieuw perceel toe." /> + + +
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx new file mode 100644 index 000000000..842330f27 --- /dev/null +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -0,0 +1,602 @@ +import { + type ActionFunctionArgs, + Form, + type LoaderFunctionArgs, + type MetaFunction, + redirect, + useActionData, + useLoaderData, + useNavigation, + useParams, + Link, +} from "react-router" +import { getSession } from "~/lib/auth.server" +import { fdm } from "~/lib/fdm.server" +import { + generateAuthUrl, + fetchRvoFields, + compareFields, + createRvoClient, +} from "~/lib/rvo.server" +import { + type RvoImportReviewItem, + RvoImportReviewStatus, +} from "@svenvw/fdm-rvo/types" +import { serverConfig } from "~/lib/config.server" +import { + ImportReviewAction, + RvoImportReviewTable, + type UserChoiceMap, + getItemId, +} from "~/components/blocks/rvo/import-review-table" +import { + getFields, + addField, + updateField, + removeField, + getFarm, + getFarms, +} from "@svenvw/fdm-core" +import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" +import { Button } from "~/components/ui/button" +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "~/components/ui/card" +import { + Loader2, + ExternalLink, + CheckCircle2, + AlertTriangle, + FlaskConical, +} from "lucide-react" +import { useEffect, useState } from "react" +import { Header } from "~/components/blocks/header/base" +import { HeaderFarm } from "~/components/blocks/header/farm" +import { SidebarInset } from "~/components/ui/sidebar" +import { + BreadcrumbItem, + BreadcrumbSeparator, + BreadcrumbLink, +} from "~/components/ui/breadcrumb" + +export const meta: MetaFunction = ({ params }) => { + return [{ title: `RVO Koppeling - Bedrijf ${params.b_id_farm}` }] +} + +export async function loader({ request, params }: LoaderFunctionArgs) { + const { b_id_farm } = params + if (!b_id_farm) { + throw new Response("Farm ID is required", { status: 400 }) + } + + const session = await getSession(request) + const url = new URL(request.url) + const code = url.searchParams.get("code") + const state = url.searchParams.get("state") + + let rvoImportReviewData: RvoImportReviewItem[] = [] + let error: string | null = null + let b_businessid_farm: string | null = null + let b_name_farm: string | null = null + + // Check if RVO is configured + const rvoConfigured = + serverConfig.auth.rvo.clientId !== "undefined" && + serverConfig.auth.rvo.clientId !== "" && + serverConfig.auth.rvo.clientSecret !== "undefined" && + serverConfig.auth.rvo.clientSecret !== "" + + const farm = await getFarm(fdm, session.principal_id, b_id_farm) + if (farm) { + b_businessid_farm = farm.b_businessid_farm + b_name_farm = farm.b_name_farm + } + + const farms = await getFarms(fdm, session.principal_id) + + if (code && state) { + try { + if (!rvoConfigured) { + throw new Response("RVO client is not configured.", { + status: 500, + }) + } + const decodedState = JSON.parse( + Buffer.from(state, "base64").toString("utf-8"), + ) + if (decodedState.farmId !== b_id_farm) { + throw new Response("Invalid state parameter", { status: 403 }) + } + + if (!farm || !farm.b_businessid_farm) { + throw new Response("b_businessid_farm is not available", { + status: 400, + }) + } + + const { clientId, clientSecret, redirectUri, clientName } = + serverConfig.auth.rvo + const rvoClient = createRvoClient( + clientId, + clientName, + redirectUri, + clientSecret, + process.env.NODE_ENV === "production" + ? "production" + : "acceptance", + ) + + const rvoFields = await fetchRvoFields( + rvoClient, + new Date().getFullYear(), + farm.b_businessid_farm, + ) + + const localFields = await getFields( + fdm, + session.principal_id, + b_id_farm, + ) + rvoImportReviewData = compareFields(localFields, rvoFields) + } catch (e: any) { + console.error("Error with importing from RVO:", e) + error = e.message + } + } else if (!url.searchParams.has("start_import")) { + return { + b_id_farm, + rvoImportReviewData: [], + error: null, + showimportButton: true, + b_businessid_farm, + rvoConfigured, + farms, + b_name_farm, + } + } + + return { + b_id_farm, + rvoImportReviewData, + error, + showimportButton: false, + b_businessid_farm, + rvoConfigured, + farms, + b_name_farm, + } +} + +export default function RvoImportReviewPage() { + const { b_id_farm } = useParams() + const { + rvoImportReviewData, + error, + b_businessid_farm, + rvoConfigured, + farms, + } = useLoaderData() + const actionData = useActionData() + const navigation = useNavigation() + + const isImporting = + navigation.state === "submitting" && + navigation.formData?.get("intent") === "start_import" + const isApplying = + navigation.state === "submitting" && + navigation.formData?.get("intent") === "apply_changes" + + const [userChoices, setUserChoices] = useState({}) + + useEffect(() => { + const initialChoices: UserChoiceMap = {} + rvoImportReviewData.forEach((item) => { + const id = getItemId(item) + let defaultAction: ImportReviewAction = "NO_ACTION" + + switch (item.status) { + case RvoImportReviewStatus.NEW_REMOTE: + defaultAction = "ADD_REMOTE" + break + case RvoImportReviewStatus.NEW_LOCAL: + defaultAction = "KEEP_LOCAL" + break + case RvoImportReviewStatus.CONFLICT: + defaultAction = "UPDATE_FROM_REMOTE" + break + case RvoImportReviewStatus.MATCH: + defaultAction = "NO_ACTION" + break + } + initialChoices[id] = defaultAction + }) + setUserChoices(initialChoices) + }, [rvoImportReviewData]) + + const handleChoiceChange = (id: string, action: ImportReviewAction) => { + setUserChoices((prev) => ({ ...prev, [id]: action })) + } + + return ( + +
+ + + + Percelen ophalen bij RVO + +
+
+
+ {" "} + {/* Centered layout */} +
+ {" "} + {/* Max width constraint */} + {error && ( + + + Er is iets misgegaan bij het ophalen van + percelen bij RVO + + {error} + + )} + {actionData?.message && ( + + + {actionData.success ? "Succes" : "Fout"} + + + {actionData.message} + + + )} + {/* Config Warning */} + {!rvoConfigured && ( + + + + RVO import is niet beschikbaar + + + De RVO koppeling is nog niet ingesteld op + deze server. Neem contact op met de + beheerder om de RVO credentials toe te + voegen. + + + )} + {/* Intro / Connection Card */} + {rvoImportReviewData.length === 0 && ( + + {" "} + {/* Removed border-t-4 border-t-blue-600 shadow-sm */} + + + Percelen ophalen bij RVO + + + + + Experimentele functie + + + Deze functie is nog in ontwikkeling. + Laat ons het weten als je feedback + hebt! + + + + Lees hieronder wat u nodig heeft om te + verbinden. + + + +
+

+ Voorwaarden voor gebruik: +

+
    +
  • + U heeft een geldig KvK-nummer + gekoppeld aan uw account. +
  • +
  • + U heeft een eHerkenning account + met machtiging voor dit + KvK-nummer. +
  • +
  • + U geeft ons toestemming om + perceelsgegevens op te halen. +
  • +
+
+ +
+
+

+ KvK Nummer +

+ {b_businessid_farm ? ( +
+ + + {b_businessid_farm} + +
+ ) : ( +
+ Geen KvK-nummer gevonden. + Voeg deze toe in de + bedrijfsinstellingen. +
+ )} +
+
+

+ Wat gebeurt er? +

+

+ Na het klikken op "Verbinden met + RVO" wordt u doorgestuurd naar + de inlogpagina van RVO. Na + authenticatie met eHerkenning + keert u terug naar deze pagina + om de verschillen te beoordelen. +

+
+
+
+ + {!b_businessid_farm ? ( + + ) : ( +
+ + +
+ )} +
+
+ )} + {/* RvoImportReview UI */} + {rvoImportReviewData.length > 0 && ( + + {" "} + {/* Wider card for table */} + + + Verwerken van opgehaalde percelen + + + Beoordeel de verschillen tussen uw + lokale gegevens en de RVO registratie. + + + + + + +
+ Controleer alle acties zorgvuldig + voordat u toepast. +
+
+ + + + +
+
+
+ )} +
+
+
+
+ ) +} + +export async function action({ request, params }: ActionFunctionArgs) { + const { b_id_farm } = params + if (!b_id_farm) { + throw new Response("Farm ID is required", { status: 400 }) + } + + const session = await getSession(request) + const formData = await request.formData() + const intent = formData.get("intent") + + if (intent === "start_import") { + const rvoConfigured = + serverConfig.auth.rvo.clientId !== "undefined" && + serverConfig.auth.rvo.clientId !== "" && + serverConfig.auth.rvo.clientSecret !== "undefined" && + serverConfig.auth.rvo.clientSecret !== "" + + if (!rvoConfigured) { + throw new Response("RVO client is not configured.", { status: 500 }) + } + + const { clientId, clientSecret, redirectUri } = serverConfig.auth.rvo + const rvoClient = createRvoClient( + clientId, + redirectUri, + clientSecret, + process.env.NODE_ENV === "production" ? "production" : "acceptance", + ) + const state = Buffer.from( + JSON.stringify({ farmId: b_id_farm, returnUrl: request.url }), + ).toString("base64") + const authUrl = generateAuthUrl(rvoClient, state) + return redirect(authUrl) + } + + if (intent === "apply_changes") { + const rvoImportReviewDataJson = formData.get("rvoImportReviewDataJson") + const userChoicesJson = formData.get("userChoices") + + if (!rvoImportReviewDataJson || !userChoicesJson) { + return { + success: false, + message: + "Geen data gevonden om te verwerken. Start de RVO importhronisatie opnieuw.", + } + } + + try { + const rvoImportReviewData: RvoImportReviewItem[] = JSON.parse( + String(rvoImportReviewDataJson), + ) + const userChoices: UserChoiceMap = JSON.parse( + String(userChoicesJson), + ) + + for (const item of rvoImportReviewData) { + const id = getItemId(item) + const action = userChoices[id] + + if (!action || action === "IGNORE" || action === "NO_ACTION") { + continue + } + + switch (action) { + case "ADD_REMOTE": + if (item.rvoField) { + await addField( + fdm, + session.principal_id, + b_id_farm, + item.rvoField.properties.CropFieldDesignator || + `RVO Perceel ${item.rvoField.properties.CropFieldID}`, + item.rvoField.properties.CropFieldID, + item.rvoField.geometry, + new Date(item.rvoField.properties.BeginDate), + "rvo_import", + item.rvoField.properties.EndDate + ? new Date(item.rvoField.properties.EndDate) + : undefined, + ) + } + break + case "UPDATE_FROM_REMOTE": + if (item.localField && item.rvoField) { + await updateField( + fdm, + session.principal_id, + item.localField.b_id, + item.rvoField.properties.CropFieldDesignator || + item.localField.b_name, + item.rvoField.properties.CropFieldID, + item.rvoField.geometry, + new Date(item.rvoField.properties.BeginDate), + "rvo_import", + item.rvoField.properties.EndDate + ? new Date(item.rvoField.properties.EndDate) + : undefined, + ) + } + break + case "KEEP_LOCAL": // Keep Local for Conflict + case "REMOVE_LOCAL": + if (item.localField) { + await removeField( + fdm, + session.principal_id, + item.localField.b_id, + ) + } + break + } + } + return redirect(`/farm/${b_id_farm}/overview`) + } catch (e: any) { + console.error("Error with processing RVO import: ", e) + return { + success: false, + message: `Error with processing RVO import: ${e.message}`, + } + } + } + + return {} +} diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx index 39560d6cd..1d36ce12c 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx @@ -72,7 +72,54 @@ export default function ChooseFieldImportMethod() {

Hoe wil je de percelen van je bedrijf importeren?

-
+
+ + + + Importeren vanuit RVO + + Importeer je percelen door via EHerkenning toestemming te geven. + + + + + + + Wat heb ik nodig om percelen te + importeren vanuit RVO? + + +
    +
  1. + U heeft een geldig + KvK-nummer gekoppeld aan uw + account. +
  2. +
  3. + U heeft een eHerkenning + account met machtiging voor + dit KvK-nummer. +
  4. +
  5. + U geeft ons toestemming om + perceelsgegevens op te + halen. +
  6. +
+
+
+
+ + + +
+
diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx new file mode 100644 index 000000000..bf3241427 --- /dev/null +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -0,0 +1,537 @@ +import { + type ActionFunctionArgs, + Form, + type LoaderFunctionArgs, + type MetaFunction, + NavLink, + redirect, + useActionData, + useLoaderData, + useNavigation, + useParams, +} from "react-router" +import { getSession } from "~/lib/auth.server" +import { fdm } from "~/lib/fdm.server" +import { + generateAuthUrl, + fetchRvoFields, + compareFields, + createRvoClient, + type RvoImportReviewItem, +} from "~/lib/rvo.server" +import { serverConfig } from "~/lib/config.server" +import { + type ImportReviewAction, + RvoImportReviewTable, + type UserChoiceMap, + getItemId, +} from "~/components/blocks/rvo/import-review-table" +import { addField, getFarm } from "@svenvw/fdm-core" +import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" +import { Button } from "~/components/ui/button" +import { Loader2, AlertTriangle, FlaskConical } from "lucide-react" +import { useEffect, useState } from "react" +import { Header } from "~/components/blocks/header/base" +import { HeaderFarmCreate } from "~/components/blocks/header/create-farm" +import { SidebarInset } from "~/components/ui/sidebar" +import { + BreadcrumbItem, + BreadcrumbSeparator, + BreadcrumbLink, +} from "~/components/ui/breadcrumb" +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "~/components/ui/card" +import { CheckCircle2 } from "lucide-react" + +export const meta: MetaFunction = ({ params }) => { + const b_id_farm = params.b_id_farm + return [{ title: `RVO Import - Nieuw Bedrijf ${b_id_farm}` }] +} + +export async function loader({ request, params }: LoaderFunctionArgs) { + const { b_id_farm, calendar } = params + if (!b_id_farm || !calendar) { + throw new Response("Farm ID en Kalender zijn verplicht", { + status: 400, + }) + } + + const session = await getSession(request) + const url = new URL(request.url) + const code = url.searchParams.get("code") + const state = url.searchParams.get("state") + + let RvoImportReviewData: RvoImportReviewItem[] = [] + let error: string | null = null + let b_businessid_farm: string | null = null + let b_name_farm: string | null | undefined = null + + // Check if RVO is configured + const rvoConfigured = + serverConfig.auth.rvo.clientId !== "undefined" && + serverConfig.auth.rvo.clientId !== "" && + serverConfig.auth.rvo.clientSecret !== "undefined" && + serverConfig.auth.rvo.clientSecret !== "" + + const farm = await getFarm(fdm, session.principal_id, b_id_farm) + if (farm) { + b_businessid_farm = farm.b_businessid_farm + b_name_farm = farm.b_name_farm + } + + if (code && state) { + try { + if (!rvoConfigured) { + throw new Response("RVO is niet geconfigureerd.", { + status: 500, + }) + } + + const decodedState = JSON.parse( + Buffer.from(state, "base64").toString("utf-8"), + ) + if (decodedState.farmId !== b_id_farm) { + throw new Response("Ongeldige state parameter", { status: 403 }) + } + + if (!farm || !farm.b_businessid_farm) { + throw new Response( + "Geen KvK-nummer gevonden voor dit bedrijf.", + { status: 400 }, + ) + } + + const { clientId, clientSecret, redirectUri, clientName } = + serverConfig.auth.rvo + const rvoClient = createRvoClient( + clientId, + clientName, + redirectUri, + clientSecret, + process.env.NODE_ENV === "production" + ? "production" + : "acceptance", + ) // Instantiate RvoClient + + const rvoFields = await fetchRvoFields( + rvoClient, + new Date().getFullYear(), + farm.b_businessid_farm, + ) + + const localFields: any[] = [] // No existing fields to compare against yet + RvoImportReviewData = compareFields(localFields, rvoFields) + } catch (e: any) { + console.error("RVO Import Fout:", e) + error = e.message + } + } else if (!url.searchParams.has("start_import")) { + return { + b_id_farm, + b_businessid_farm, + calendar, + RvoImportReviewData: [], + error: null, + showImportButton: true, + rvoConfigured, + b_name_farm, + } + } + + return { + b_id_farm, + calendar, + RvoImportReviewData, + error, + showImportButton: false, + rvoConfigured, + b_name_farm, + } +} + +export default function RvoImportCreatePage() { + const { b_id_farm, b_businessid_farm } = useParams() + const { + RvoImportReviewData, + error, + showImportButton, + rvoConfigured, + b_name_farm, + } = useLoaderData() + const actionData = useActionData() + const navigation = useNavigation() + const isImporting = + navigation.state === "submitting" && + navigation.formData?.get("intent") === "start_import" + const isSaving = + navigation.state === "submitting" && + navigation.formData?.get("intent") === "save_fields" + + const [userChoices, setUserChoices] = useState({}) + + useEffect(() => { + // Initialize user choices with defaults + const initialChoices: UserChoiceMap = {} + RvoImportReviewData.forEach((item) => { + const id = getItemId(item) + let defaultAction: ImportReviewAction = "NO_ACTION" + + switch (item.status) { + case "NEW_REMOTE": + defaultAction = "ADD_REMOTE" + break + // In creation wizard, other statuses are unlikely but good to handle defaults + default: + defaultAction = "NO_ACTION" + break + } + initialChoices[id] = defaultAction + }) + setUserChoices(initialChoices) + }, [RvoImportReviewData]) + + const handleChoiceChange = (id: string, action: ImportReviewAction) => { + setUserChoices((prev) => ({ ...prev, [id]: action })) + } + + return ( + +
+ + + + Percelen ophalen bij RVO + +
+
+
+ {" "} + {/* Centered layout like upload page */} +
+ {" "} + {/* Max width constraint */} + {error && ( + + + Er is iets misgegaan bij het importeren + + {error} + + )} + {actionData?.message && ( + + + {actionData.success ? "Succes" : "Fout"} + + + {actionData.message} + + + )} + {/* Config Warning */} + {!rvoConfigured && ( + + + + Importen vanuit RVO is niet beschikbaar + + + De RVO koppeling is nog niet ingesteld op + deze server. Neem contact op met de + beheerder om de RVO toeggangegevens toe te + voegen. + + + )} + {showImportButton && ( + + + + Percelen ophalen bij RVO + + + + + Experimentele functie + + + Deze functie is nog in ontwikkeling. + Laat ons het weten als je feedback + hebt! + + + + Lees hieronder wat u nodig heeft om te + verbinden. + + + +
+

+ Voorwaarden voor gebruik: +

+
    +
  • + U heeft een geldig KvK-nummer + gekoppeld aan uw account. +
  • +
  • + U heeft een eHerkenning account + met machtiging voor dit + KvK-nummer. +
  • +
  • + U geeft ons toestemming om + perceelsgegevens op te halen. +
  • +
+
+ +
+
+

+ KvK Nummer +

+ {b_businessid_farm ? ( +
+ + + {b_businessid_farm} + +
+ ) : ( +
+ Geen KvK-nummer gevonden. + Voeg deze toe in de + bedrijfsgegevens. +
+ )} +
+
+

+ Wat gebeurt er? +

+

+ Na het klikken op "Importeer van + RVO" wordt u doorgestuurd naar + de inlogpagina van RVO. Na + authenticatie met eHerkenning + keert u terug naar deze pagina + om de verschillen te beoordelen. +

+
+
+
+ + {!b_businessid_farm ? ( + + ) : ( +
+ + +
+ )} +
+
+ )} + {RvoImportReviewData.length > 0 && ( + + {" "} + {/* Wider card for table */} + + + Verwerken van geïmporteerde percelen + + + Controleer de percelen opgehaald vanuit + RVO. Deze worden toegevoegd aan uw + nieuwe bedrijf. + + + + + + +
+ + + + +
+
+
+ )} +
+
+
+
+ ) +} + +export async function action({ request, params }: ActionFunctionArgs) { + const { b_id_farm, calendar } = params + if (!b_id_farm || !calendar) { + throw new Response("b_id_farm and calendar are required", { + status: 400, + }) + } + + const session = await getSession(request) + const formData = await request.formData() + const intent = formData.get("intent") + + if (intent === "start_import") { + const rvoConfigured = + serverConfig.auth.rvo.clientId !== "undefined" && + serverConfig.auth.rvo.clientId !== "" && + serverConfig.auth.rvo.clientSecret !== "undefined" && + serverConfig.auth.rvo.clientSecret !== "" + + if (!rvoConfigured) { + throw new Response("RVO client is not available", { status: 500 }) + } + + const { clientId, clientSecret, redirectUri, clientName } = + serverConfig.auth.rvo + const rvoClient = createRvoClient( + clientId, + clientName, + redirectUri, + clientSecret, + process.env.NODE_ENV === "production" ? "production" : "acceptance", + ) // Instantiate RvoClient + const state = Buffer.from( + JSON.stringify({ farmId: b_id_farm, returnUrl: request.url }), + ).toString("base64") + const authUrl = generateAuthUrl(rvoClient, state) // Pass rvoClient instance + return redirect(authUrl) + } + + if (intent === "save_fields") { + const RvoImportReviewDataJson = formData.get("RvoImportReviewDataJson") + const userChoicesJson = formData.get("userChoices") + + if (!RvoImportReviewDataJson || !userChoicesJson) { + return { + success: false, + message: + "Geen data gevonden om te verwerken. Start de RVO import opnieuw.", + } + } + + try { + const RvoImportReviewData: RvoImportReviewItem[] = JSON.parse( + String(formData.get("RvoImportReviewDataJson")), + ) + const userChoices: UserChoiceMap = JSON.parse( + String(userChoicesJson), + ) + + for (const item of RvoImportReviewData) { + const id = getItemId(item) + const action = userChoices[id] + + if (action === "ADD_REMOTE" && item.rvoField) { + await addField( + fdm, + session.principal_id, + b_id_farm, + item.rvoField.properties.CropFieldDesignator || + `RVO Perceel ${item.rvoField.properties.CropFieldID}`, + item.rvoField.properties.CropFieldID, + item.rvoField.geometry, + new Date(item.rvoField.properties.BeginDate), + "rvo_import", + item.rvoField.properties.EndDate + ? new Date(item.rvoField.properties.EndDate) + : undefined, + ) + } + } + return redirect(`/farm/create/${b_id_farm}/${calendar}/fields`) + } catch (e: any) { + console.error("Error at saving RVO fields: ", e) + return { + success: false, + message: `Error at saving RVO fields: ${e.message}`, + } + } + } + + return {} +} diff --git a/fdm-app/app/routes/farm.create._index.tsx b/fdm-app/app/routes/farm.create._index.tsx index ececfc69f..8b616d476 100644 --- a/fdm-app/app/routes/farm.create._index.tsx +++ b/fdm-app/app/routes/farm.create._index.tsx @@ -86,6 +86,18 @@ const FormSchema = z.object({ message: "Startjaar mag maximaal 2025 zijn", }) .optional(), + b_businessid_farm: z + .string() + .optional() + .refine( + (val) => { + if (val === undefined || val === "") return true // Optional, so allow empty + return /^[0-9]{8}$/.test(val) + }, + { + message: "KvK nummer must be exactly 8 digits", + }, + ), }) // Loader @@ -163,6 +175,29 @@ export default function AddFarmPage() { )} /> + ( + + + Kvk nummer + + + + + + Optioneel, + vereist voor RVO + synchronisatie. + + + + )} + />
Date: Wed, 10 Dec 2025 15:51:08 +0100 Subject: [PATCH 006/108] refactor: move processing the rvo import review results into fdm-rvo --- .../blocks/rvo/import-review-table.tsx | 26 ++---- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 84 +++---------------- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 37 +++----- fdm-rvo/src/compare.ts | 2 + fdm-rvo/src/index.ts | 2 + fdm-rvo/src/process.ts | 83 ++++++++++++++++++ fdm-rvo/src/types.ts | 18 ++++ fdm-rvo/src/utils.ts | 19 +++++ 8 files changed, 155 insertions(+), 116 deletions(-) create mode 100644 fdm-rvo/src/process.ts create mode 100644 fdm-rvo/src/utils.ts diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index 134ac8f33..c1cc2aa10 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -4,7 +4,12 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table" -import type { RvoImportReviewItem } from "@svenvw/fdm-rvo/types" +import { + type RvoImportReviewItem, + type ImportReviewAction, + type UserChoiceMap, + getItemId, +} from "@svenvw/fdm-rvo" import { Table, TableBody, @@ -23,31 +28,12 @@ import { SelectValue, } from "~/components/ui/select" -export type ImportReviewAction = - | "ADD_REMOTE" - | "UPDATE_FROM_REMOTE" - | "KEEP_LOCAL" - | "REMOVE_LOCAL" - | "IGNORE" - | "NO_ACTION" - -export type UserChoiceMap = Record - interface RvoImportReviewTableProps { data: RvoImportReviewItem[] userChoices: UserChoiceMap onChoiceChange: (id: string, action: ImportReviewAction) => void } -// Helper to derive a stable ID for the row -export function getItemId(item: RvoImportReviewItem): string { - return ( - item.localField?.b_id || - item.rvoField?.properties.CropFieldID || - "unknown" - ) -} - export const columns: ColumnDef>[] = [ { accessorKey: "status", diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 842330f27..e198f7a02 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -21,22 +21,14 @@ import { import { type RvoImportReviewItem, RvoImportReviewStatus, -} from "@svenvw/fdm-rvo/types" -import { serverConfig } from "~/lib/config.server" -import { - ImportReviewAction, - RvoImportReviewTable, type UserChoiceMap, + type ImportReviewAction, getItemId, -} from "~/components/blocks/rvo/import-review-table" -import { - getFields, - addField, - updateField, - removeField, - getFarm, - getFarms, -} from "@svenvw/fdm-core" + processRvoImport, +} from "@svenvw/fdm-rvo" +import { serverConfig } from "~/lib/config.server" +import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" +import { getFields, getFarm, getFarms } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" import { @@ -531,63 +523,13 @@ export async function action({ request, params }: ActionFunctionArgs) { String(userChoicesJson), ) - for (const item of rvoImportReviewData) { - const id = getItemId(item) - const action = userChoices[id] - - if (!action || action === "IGNORE" || action === "NO_ACTION") { - continue - } - - switch (action) { - case "ADD_REMOTE": - if (item.rvoField) { - await addField( - fdm, - session.principal_id, - b_id_farm, - item.rvoField.properties.CropFieldDesignator || - `RVO Perceel ${item.rvoField.properties.CropFieldID}`, - item.rvoField.properties.CropFieldID, - item.rvoField.geometry, - new Date(item.rvoField.properties.BeginDate), - "rvo_import", - item.rvoField.properties.EndDate - ? new Date(item.rvoField.properties.EndDate) - : undefined, - ) - } - break - case "UPDATE_FROM_REMOTE": - if (item.localField && item.rvoField) { - await updateField( - fdm, - session.principal_id, - item.localField.b_id, - item.rvoField.properties.CropFieldDesignator || - item.localField.b_name, - item.rvoField.properties.CropFieldID, - item.rvoField.geometry, - new Date(item.rvoField.properties.BeginDate), - "rvo_import", - item.rvoField.properties.EndDate - ? new Date(item.rvoField.properties.EndDate) - : undefined, - ) - } - break - case "KEEP_LOCAL": // Keep Local for Conflict - case "REMOVE_LOCAL": - if (item.localField) { - await removeField( - fdm, - session.principal_id, - item.localField.b_id, - ) - } - break - } - } + await processRvoImport( + fdm, + session.principal_id, + b_id_farm, + rvoImportReviewData, + userChoices, + ) return redirect(`/farm/${b_id_farm}/overview`) } catch (e: any) { console.error("Error with processing RVO import: ", e) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index bf3241427..174f6e3e9 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -17,16 +17,17 @@ import { fetchRvoFields, compareFields, createRvoClient, - type RvoImportReviewItem, } from "~/lib/rvo.server" import { serverConfig } from "~/lib/config.server" import { + type RvoImportReviewItem, type ImportReviewAction, - RvoImportReviewTable, type UserChoiceMap, + processRvoImport, getItemId, -} from "~/components/blocks/rvo/import-review-table" -import { addField, getFarm } from "@svenvw/fdm-core" +} from "@svenvw/fdm-rvo" +import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" +import { getFarm } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" import { Loader2, AlertTriangle, FlaskConical } from "lucide-react" @@ -502,27 +503,13 @@ export async function action({ request, params }: ActionFunctionArgs) { String(userChoicesJson), ) - for (const item of RvoImportReviewData) { - const id = getItemId(item) - const action = userChoices[id] - - if (action === "ADD_REMOTE" && item.rvoField) { - await addField( - fdm, - session.principal_id, - b_id_farm, - item.rvoField.properties.CropFieldDesignator || - `RVO Perceel ${item.rvoField.properties.CropFieldID}`, - item.rvoField.properties.CropFieldID, - item.rvoField.geometry, - new Date(item.rvoField.properties.BeginDate), - "rvo_import", - item.rvoField.properties.EndDate - ? new Date(item.rvoField.properties.EndDate) - : undefined, - ) - } - } + await processRvoImport( + fdm, + session.principal_id, + b_id_farm, + RvoImportReviewData, + userChoices, + ) return redirect(`/farm/create/${b_id_farm}/${calendar}/fields`) } catch (e: any) { console.error("Error at saving RVO fields: ", e) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index f7d6445f3..615742934 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -222,6 +222,8 @@ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { // 2. Geometry // We use a very strict IoU (0.99) to detect if the shape has been modified, even slightly. + // If IoU is less than this threshold, we flag it as a geometry difference. + // We don't require 1.0 because of potential minor floating point differences in coordinates. const iou = calculateIoU(local.b_geometry, rvo.geometry) if (iou < IOU_THRESHOLD) { diffs.push("b_geometry") diff --git a/fdm-rvo/src/index.ts b/fdm-rvo/src/index.ts index 8df12d1eb..8cd69cb05 100644 --- a/fdm-rvo/src/index.ts +++ b/fdm-rvo/src/index.ts @@ -30,3 +30,5 @@ export * from "./auth" export * from "./compare" export * from "./data" export * from "./types" +export * from "./process" +export * from "./utils" diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts new file mode 100644 index 000000000..676bba76a --- /dev/null +++ b/fdm-rvo/src/process.ts @@ -0,0 +1,83 @@ +import { + addField, + updateField, + removeField, + type FdmType, +} from "@svenvw/fdm-core" +import type { RvoImportReviewItem, UserChoiceMap } from "./types" +import { getItemId } from "./utils" + +/** + * Processes the RVO import review results by applying user-selected actions. + * + * Iterates through the provided review items and executes the corresponding action + * (add, update, remove) based on the `userChoices` map. + * + * @param fdm - The FDM client instance for database operations. + * @param principal_id - The ID of the principal (user) performing the import. + * @param b_id_farm - The ID of the farm the fields belong to. + * @param rvoImportReviewData - The list of review items resulting from the comparison. + * @param userChoices - A map where keys are item IDs and values are the chosen `ImportReviewAction`. + * @returns A promise that resolves when all actions have been processed. + */ +export async function processRvoImport( + fdm: FdmType, + principal_id: string, + b_id_farm: string, + rvoImportReviewData: RvoImportReviewItem[], + userChoices: UserChoiceMap, +) { + for (const item of rvoImportReviewData) { + const id = getItemId(item) + const action = userChoices[id] + + if (!action || action === "IGNORE" || action === "NO_ACTION") { + continue + } + + switch (action) { + case "ADD_REMOTE": + if (item.rvoField) { + await addField( + fdm, + principal_id, + b_id_farm, + item.rvoField.properties.CropFieldDesignator || + `RVO Perceel ${item.rvoField.properties.CropFieldID}`, + item.rvoField.properties.CropFieldID, + item.rvoField.geometry, + new Date(item.rvoField.properties.BeginDate), + "rvo_import", + item.rvoField.properties.EndDate + ? new Date(item.rvoField.properties.EndDate) + : undefined, + ) + } + break + case "UPDATE_FROM_REMOTE": + if (item.localField && item.rvoField) { + await updateField( + fdm, + principal_id, + item.localField.b_id, + item.rvoField.properties.CropFieldDesignator || + item.localField.b_name, + item.rvoField.properties.CropFieldID, + item.rvoField.geometry, + new Date(item.rvoField.properties.BeginDate), + "rvo_import", + item.rvoField.properties.EndDate + ? new Date(item.rvoField.properties.EndDate) + : undefined, + ) + } + break + case "KEEP_LOCAL": // Keep Local for Conflict + case "REMOVE_LOCAL": + if (item.localField) { + await removeField(fdm, principal_id, item.localField.b_id) + } + break + } + } +} diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index af63f38bf..0e360177d 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -110,3 +110,21 @@ export interface RvoImportReviewItem { /** List of specific properties that differ (empty for MATCH, NEW_REMOTE, NEW_LOCAL) */ diffs: FieldDiff[] } + +/** + * Action to be taken for a specific review item during the import process. + */ +export type ImportReviewAction = + | "ADD_REMOTE" // Add the remote field to the local database + | "UPDATE_FROM_REMOTE" // Update the local field with remote data + | "KEEP_LOCAL" // Keep the local version, ignoring the remote conflict + | "REMOVE_LOCAL" // Remove the local field + | "IGNORE" // Do nothing for this item + | "NO_ACTION" // No specific action selected + +/** + * A map of user choices, keyed by the item ID (see `getItemId`). + * + * Each entry represents the action selected by the user for a specific review item. + */ +export type UserChoiceMap = Record diff --git a/fdm-rvo/src/utils.ts b/fdm-rvo/src/utils.ts new file mode 100644 index 000000000..483c4937a --- /dev/null +++ b/fdm-rvo/src/utils.ts @@ -0,0 +1,19 @@ +import type { RvoImportReviewItem } from "./types" + +/** + * Generates a stable unique identifier for a review item. + * + * This ID is used to key items in the UI (e.g., for React lists) and to map user choices. + * It prioritizes the local field ID (`b_id`), falling back to the RVO field ID (`CropFieldID`) + * if the item represents a new remote field. + * + * @param item - The review item to generate an ID for. + * @returns A unique string identifier for the item. + */ +export function getItemId(item: RvoImportReviewItem): string { + return ( + item.localField?.b_id || + item.rvoField?.properties.CropFieldID || + "unknown" + ) +} From 6a78f3be9f4db12ae9b11b3f03dc69eb129ed946 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:15:35 +0100 Subject: [PATCH 007/108] refactor: lot of improvements to get rvo import working in fdm-app --- .../components/blocks/rvo/connect-card.tsx | 137 +++++ .../blocks/rvo/import-review-table.tsx | 426 ++++++++++++--- .../components/blocks/rvo/rvo-error-alert.tsx | 106 ++++ fdm-app/app/integrations/calculator.ts | 2 +- .../integrations/{nmi.ts => nmi.server.ts} | 0 fdm-app/app/integrations/rvo.ts | 27 + fdm-app/app/lib/cache.server.ts | 2 +- fdm-app/app/lib/config.server.ts | 12 +- fdm-app/app/lib/constants.ts | 21 + ..._farm.$calendar.atlas.fields.$centroid.tsx | 2 +- ...calendar.field.$b_id.fertilizer._index.tsx | 2 +- ...r.field.$b_id.soil.analysis.new.upload.tsx | 2 +- ...farm.$b_id_farm.$calendar.field._index.tsx | 18 - .../farm.$b_id_farm.$calendar.field.new.tsx | 2 +- ...d_farm.$calendar.nutrient_advice.$b_id.tsx | 2 +- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 499 ++++++++---------- fdm-app/app/routes/farm.$b_id_farm._index.tsx | 11 + ...arm.create.$b_id_farm.$calendar._index.tsx | 34 +- ...farm.create.$b_id_farm.$calendar.atlas.tsx | 2 +- ...ndar.fields.$b_id.soil.analysis.upload.tsx | 2 +- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 469 +++++++--------- ...arm.create.$b_id_farm.$calendar.upload.tsx | 2 +- fdm-app/app/routes/farm.create._index.tsx | 11 +- fdm-app/app/types/config.d.ts | 12 +- fdm-core/src/index.ts | 5 + fdm-rvo/README.md | 6 +- fdm-rvo/package.json | 6 + fdm-rvo/src/process.ts | 11 +- 28 files changed, 1160 insertions(+), 671 deletions(-) create mode 100644 fdm-app/app/components/blocks/rvo/connect-card.tsx create mode 100644 fdm-app/app/components/blocks/rvo/rvo-error-alert.tsx rename fdm-app/app/integrations/{nmi.ts => nmi.server.ts} (100%) create mode 100644 fdm-app/app/integrations/rvo.ts create mode 100644 fdm-app/app/lib/constants.ts diff --git a/fdm-app/app/components/blocks/rvo/connect-card.tsx b/fdm-app/app/components/blocks/rvo/connect-card.tsx new file mode 100644 index 000000000..20987d55e --- /dev/null +++ b/fdm-app/app/components/blocks/rvo/connect-card.tsx @@ -0,0 +1,137 @@ +import { Link, Form } from "react-router" +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "~/components/ui/card" +import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" +import { Button } from "~/components/ui/button" +import { + Loader2, + FlaskConical, + CheckCircle2, + ExternalLink, +} from "lucide-react" + +interface RvoConnectCardProps { + b_businessid_farm: string | null + b_id_farm: string + isImporting: boolean + isRvoConfigured: boolean +} + +export function RvoConnectCard({ + b_businessid_farm, + b_id_farm, + isImporting, + isRvoConfigured, +}: RvoConnectCardProps) { + return ( + + + Percelen ophalen bij RVO + + + Experimentele functie + + Deze functie is nog in ontwikkeling. Laat ons het weten + als je feedback hebt! + + + + Lees hieronder wat u nodig heeft om te verbinden. + + + +
+

+ Voorwaarden voor gebruik: +

+
    +
  • + U heeft een geldig KvK-nummer gekoppeld aan uw + account. +
  • +
  • + U heeft een eHerkenning account met machtiging voor + dit KvK-nummer. +
  • +
  • + U geeft ons toestemming om perceelsgegevens op te + halen. +
  • +
+
+ +
+
+

+ KvK Nummer +

+ {b_businessid_farm ? ( +
+ + + {b_businessid_farm} + +
+ ) : ( +
+ Geen KvK-nummer gevonden. Voeg deze toe in de + bedrijfsgegevens. +
+ )} +
+
+

+ Wat gebeurt er? +

+

+ Na het klikken op "Verbinden met RVO" wordt u + doorgestuurd naar de inlogpagina van RVO. Na + authenticatie met eHerkenning keert u terug naar + deze pagina om de verschillen te beoordelen. +

+
+
+
+ + {!b_businessid_farm ? ( + + ) : ( +
+ + +
+ )} +
+
+ ) +} diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index c1cc2aa10..c9aa10572 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -8,8 +8,8 @@ import { type RvoImportReviewItem, type ImportReviewAction, type UserChoiceMap, - getItemId, -} from "@svenvw/fdm-rvo" +} from "@svenvw/fdm-rvo/types" +import { getItemId } from "@svenvw/fdm-rvo/utils" import { Table, TableBody, @@ -27,6 +27,16 @@ import { SelectTrigger, SelectValue, } from "~/components/ui/select" +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "~/components/ui/tooltip" +import { area } from "@turf/turf" +import { format } from "date-fns" +import { cn } from "~/lib/utils" +import { acquiringMethodOptions } from "~/lib/constants" interface RvoImportReviewTableProps { data: RvoImportReviewItem[] @@ -34,10 +44,142 @@ interface RvoImportReviewTableProps { onChoiceChange: (id: string, action: ImportReviewAction) => void } +const USE_TITLE_MAP: Record = { + "01": "Eigendom", + "02": "Pacht", + "03": "Erfpacht", + "04": "Bruikleen", +} + +function formatDate(dateString?: string | Date) { + if (!dateString) return "-" + try { + return format(new Date(dateString), "dd-MM-yyyy") + } catch { + return dateString.toString() + } +} + +function formatArea(geometry: any) { + if (!geometry) return "-" + const a = area(geometry) + return (a / 10000).toFixed(2) + " ha" +} + +// Helper to render diff cells +const DiffCell = ({ + local, + remote, + status, + action, + formatter = (v: any) => v, +}: { + local?: any + remote?: any + status: string + action: ImportReviewAction + formatter?: (v: any) => React.ReactNode +}) => { + // If MATCH, just show one value + if (status === "MATCH") { + return {formatter(local)} + } + + // NEW REMOTE -> Show remote without badge + if (status === "NEW_REMOTE") { + return ( + + {formatter(remote)} + + ) + } + + // NEW LOCAL -> Show local without badge + if (status === "NEW_LOCAL") { + return ( + + {formatter(local)} + + ) + } + + // CONFLICT + if (status === "CONFLICT") { + // If values are effectively equal (loose check), show one + if (local == remote) { + return {formatter(local)} + } + + const useRemote = + action === "UPDATE_FROM_REMOTE" || action === "ADD_REMOTE" + const useLocal = action === "KEEP_LOCAL" + + return ( +
+ {local !== undefined && ( +
+ + Lokaal + + + {formatter(local)} + +
+ )} + {remote !== undefined && ( +
+ + RVO + + + {formatter(remote)} + +
+ )} +
+ ) + } + + return null +} + export const columns: ColumnDef>[] = [ { accessorKey: "status", - header: "Status", + header: () => ( + + Status + + Geeft de vergelijkingsstatus weer tussen lokaal en RVO. + + + ), cell: ({ row }) => { const status = row.getValue("status") as string switch (status) { @@ -47,7 +189,12 @@ export const columns: ColumnDef>[] = [ variant="outline" className="bg-green-50 text-green-700 border-green-200" > - Gelijk + + Gelijk + + Lokaal en RVO perceel komen volledig overeen. + + ) case "NEW_REMOTE": @@ -56,7 +203,12 @@ export const columns: ColumnDef>[] = [ variant="outline" className="bg-blue-50 text-blue-700 border-blue-200" > - Nieuw (RVO) + + Nieuw (RVO) + + Perceel bestaat in RVO, maar niet lokaal. + + ) case "NEW_LOCAL": @@ -65,7 +217,12 @@ export const columns: ColumnDef>[] = [ variant="outline" className="bg-yellow-50 text-yellow-700 border-yellow-200" > - Nieuw (Lokaal) + + Nieuw (Lokaal) + + Perceel bestaat lokaal, maar niet in RVO. + + ) case "CONFLICT": @@ -74,7 +231,13 @@ export const columns: ColumnDef>[] = [ variant="destructive" className="bg-red-50 text-red-700 border-red-200" > - Conflict + + Conflict + + Perceel bestaat in beide, maar met + verschillende gegevens. + + ) default: @@ -83,71 +246,183 @@ export const columns: ColumnDef>[] = [ }, }, { - header: "Lokaal Perceel", - cell: ({ row }) => { - const local = row.original.localField - if (!local) - return ( - - Geen - - ) + id: "perceel", + header: () => ( + + Perceel + + De naam en het bron-ID van het perceel. + + + ), + cell: ({ row, table }) => { + const item = row.original + const id = getItemId(item) + // @ts-ignore + const { userChoices } = table.options.meta as any + const action = userChoices[id] as ImportReviewAction + return ( -
- {local.b_name} - - {local.b_id_source || "Geen Bron ID"} - -
+ ( + {val || "Naamloos"} + )} + /> ) }, }, { - header: "RVO Perceel", - cell: ({ row }) => { - const remote = row.original.rvoField - if (!remote) - return ( - - Geen - - ) + id: "oppervlakte", + header: () => ( + + Oppervlakte + + De oppervlakte van het perceel in hectaren. + + + ), + cell: ({ row, table }) => { + const item = row.original + const id = getItemId(item) + // @ts-ignore + const { userChoices } = table.options.meta as any + const action = userChoices[id] as ImportReviewAction + + const localArea = item.localField + ? `${item.localField.b_area.toFixed(2)} ha` + : undefined + const remoteArea = item.rvoField + ? formatArea(item.rvoField.geometry) + : undefined + return ( -
- - {remote.properties.CropFieldDesignator || "Naamloos"} - - - {remote.properties.CropFieldID} - -
+ ) }, }, { - header: "Verschillen", - cell: ({ row }) => { - const diffs = row.original.diffs - if (!diffs || diffs.length === 0) - return - + id: "ingangsdatum", + header: () => ( + + Ingangsdatum + + De datum vanaf wanneer het perceel actief is. + + + ), + cell: ({ row, table }) => { + const item = row.original + const id = getItemId(item) + // @ts-ignore + const { userChoices } = table.options.meta as any + const action = userChoices[id] as ImportReviewAction + return ( -
- {diffs.map((diff) => ( - - {diff} - - ))} -
+ + ) + }, + }, + { + id: "einddatum", + header: () => ( + + Einddatum + + De datum waarop het perceel niet meer actief is. + + + ), + cell: ({ row, table }) => { + const item = row.original + const id = getItemId(item) + // @ts-ignore + const { userChoices } = table.options.meta as any + const action = userChoices[id] as ImportReviewAction + + return ( + val || "-"} + /> + ) + }, + }, + { + id: "recht", + header: () => ( + + Recht + + De vorm van gebruiksrecht (bv. eigendom, pacht). + + + ), + cell: ({ row, table }) => { + const item = row.original + const id = getItemId(item) + // @ts-ignore + const { userChoices } = table.options.meta as any + const action = userChoices[id] as ImportReviewAction + + // Map RVO code to label + const rvoCode = item.rvoField?.properties.UseTitleCode + const rvoLabel = rvoCode + ? USE_TITLE_MAP[rvoCode] || rvoCode + : undefined + + // Map local acquiring method to label (simplified) + const localMethod = item.localField?.b_acquiring_method + // Assuming localMethod is english enum like 'purchase', 'lease'. Map to NL for consistency + const localLabel = acquiringMethodOptions.find( + (opt) => opt.value === localMethod, + )?.label || localMethod + + return ( + val || "-"} + /> ) }, }, { id: "actions", - header: "Actie", + header: () => ( + + Actie + + Kies welke actie moet worden uitgevoerd voor dit perceel. + + + ), cell: ({ row, table }) => { const item = row.original const id = getItemId(item) @@ -171,7 +446,7 @@ export const columns: ColumnDef>[] = [ onChoiceChange(id, val as ImportReviewAction) } > - + @@ -191,17 +466,17 @@ export const columns: ColumnDef>[] = [ )} {item.status === "NEW_LOCAL" && ( <> - -
- Behouden -
-
{" "} Verwijderen
+ +
+ Behouden +
+
)} {item.status === "CONFLICT" && ( @@ -227,13 +502,30 @@ export const columns: ColumnDef>[] = [ }, ] +import { useMemo } from "react" + export function RvoImportReviewTable({ data, userChoices, onChoiceChange, }: RvoImportReviewTableProps) { + const sortedData = useMemo(() => { + return [...data].sort((a, b) => { + const getArea = (item: typeof a) => { + if (item.rvoField?.geometry) { + return area(item.rvoField.geometry) / 10000 // Convert m2 to ha + } + if (item.localField) { + return item.localField.b_area + } + return 0 + } + return getArea(b) - getArea(a) + }) + }, [data]) + const table = useReactTable({ - data, + data: sortedData, columns, getCoreRowModel: getCoreRowModel(), meta: { @@ -243,9 +535,10 @@ export function RvoImportReviewTable({ }) return ( -
- - + +
+
+ {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { @@ -294,5 +587,6 @@ export function RvoImportReviewTable({
+ ) } diff --git a/fdm-app/app/components/blocks/rvo/rvo-error-alert.tsx b/fdm-app/app/components/blocks/rvo/rvo-error-alert.tsx new file mode 100644 index 000000000..20a382167 --- /dev/null +++ b/fdm-app/app/components/blocks/rvo/rvo-error-alert.tsx @@ -0,0 +1,106 @@ +import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" +import { Button } from "~/components/ui/button" +import { AlertTriangle, RefreshCw } from "lucide-react" +import { Link } from "react-router" + +interface RvoErrorAlertProps { + error: string | Error + onRetry?: () => void + retryPath?: string +} + +export function RvoErrorAlert({ + error, + onRetry, + retryPath, +}: RvoErrorAlertProps) { + const rawMessage = typeof error === "string" ? error : error.message + let friendlyTitle = "Er is iets misgegaan" + let friendlyMessage = + "Er is een onverwachte fout opgetreden. Probeer het later opnieuw." + + // Map technical errors to user-friendly messages + if ( + rawMessage.includes("TVS Authorize Endpoint") || + rawMessage.includes("TVS Token Endpoint") || + rawMessage.includes("Client Name is required") || + rawMessage.includes("PKIO Private Key") || + rawMessage.includes("configuration is missing") + ) { + friendlyTitle = "Configuratiefout" + friendlyMessage = + "De RVO koppeling is niet correct geconfigureerd op deze server. Neem contact op met de beheerder." + } else if ( + rawMessage.includes("Failed to obtain access token") || + rawMessage.includes("Access token is missing") + ) { + friendlyTitle = "Authenticatie mislukt" + friendlyMessage = + "Het is niet gelukt om in te loggen bij RVO of uw sessie is verlopen. Probeer opnieuw verbinding te maken." + } else if (rawMessage.includes("Request failed: 500")) { + friendlyTitle = "RVO Storing" + friendlyMessage = + "De RVO webservice geeft een interne serverfout (500). Dit ligt meestal aan RVO. Probeer het later opnieuw." + } else if ( + rawMessage.includes("Request failed: 401") || + rawMessage.includes("Request failed: 403") + ) { + friendlyTitle = "Geen toegang" + friendlyMessage = + "U heeft geen toegang tot de gegevens van dit bedrijf bij RVO. Controleer of u de juiste eHerkenning machtigingen heeft voor dit KvK-nummer." + } else if (rawMessage.includes("Request failed")) { + friendlyTitle = "Communicatiefout" + friendlyMessage = `Er is een fout opgetreden bij het ophalen van gegevens bij RVO. (${rawMessage})` + } else if (rawMessage.includes("Zod") || rawMessage.includes("parse")) { + friendlyTitle = "Gegevensfout" + friendlyMessage = + "De gegevens ontvangen van RVO kwamen niet overeen met het verwachte formaat." + } else if (rawMessage.includes("b_businessid_farm is not available")) { + friendlyTitle = "Geen KvK nummer" + friendlyMessage = + "Dit bedrijf heeft geen KvK nummer geconfigureerd. Voeg dit toe in de bedrijfsinstellingen." + } + + return ( + + + + {friendlyTitle} + + +
+

{friendlyMessage}

+ {retryPath ? ( + + ) : onRetry ? ( + + ) : null} +
+ {process.env.NODE_ENV === "development" && ( +
+ DEBUG: {rawMessage} +
+ )} +
+
+ ) +} diff --git a/fdm-app/app/integrations/calculator.ts b/fdm-app/app/integrations/calculator.ts index e865aa8ee..331564b74 100644 --- a/fdm-app/app/integrations/calculator.ts +++ b/fdm-app/app/integrations/calculator.ts @@ -16,7 +16,7 @@ import { type PrincipalId, type Timeframe, } from "@svenvw/fdm-core" -import { getNmiApiKey } from "./nmi" +import { getNmiApiKey } from "./nmi.server" // Get nitrogen balance for the field export async function getNitrogenBalanceforField({ diff --git a/fdm-app/app/integrations/nmi.ts b/fdm-app/app/integrations/nmi.server.ts similarity index 100% rename from fdm-app/app/integrations/nmi.ts rename to fdm-app/app/integrations/nmi.server.ts diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts new file mode 100644 index 000000000..24fb03888 --- /dev/null +++ b/fdm-app/app/integrations/rvo.ts @@ -0,0 +1,27 @@ +import { serverConfig } from "../lib/config.server" + +export function getRvoCredentials(): RvoCredentials | undefined { + // Check if RVO is configured + const rvoConfigured = + serverConfig.integrations.rvo.clientId !== "undefined" && + serverConfig.integrations.rvo.clientId !== "" && + serverConfig.integrations.rvo.clientSecret !== "undefined" && + serverConfig.integrations.rvo.clientSecret !== "" + if (!rvoConfigured) { + return undefined + } + + return { + clientId: serverConfig.integrations.rvo.clientId, + clientSecret: serverConfig.integrations.rvo.clientSecret, + redirectUri: serverConfig.integrations.rvo.redirectUri, + clientName: serverConfig.integrations.rvo.clientName, + } +} + +type RvoCredentials = { + clientId: string + clientSecret: string + redirectUri: string + clientName: string +} diff --git a/fdm-app/app/lib/cache.server.ts b/fdm-app/app/lib/cache.server.ts index 04811cd39..2712f9778 100644 --- a/fdm-app/app/lib/cache.server.ts +++ b/fdm-app/app/lib/cache.server.ts @@ -143,7 +143,7 @@ export function addSecurityHeaders(headers: Headers): Headers { media-src 'self' https://*.posthog.com; object-src 'none'; base-uri 'self'; - form-action 'self'; + form-action 'self' https:; frame-ancestors 'none';` // Add report-uri only if it exists diff --git a/fdm-app/app/lib/config.server.ts b/fdm-app/app/lib/config.server.ts index 8a32ac021..dd5e2b62b 100644 --- a/fdm-app/app/lib/config.server.ts +++ b/fdm-app/app/lib/config.server.ts @@ -19,12 +19,6 @@ export const serverConfig: ServerConfig = { clientId: String(process.env.MS_CLIENT_ID), clientSecret: String(process.env.MS_CLIENT_SECRET), }, - rvo: { - clientId: String(process.env.RVO_CLIENT_ID), - clientSecret: String(process.env.RVO_CLIENT_SECRET), - redirectUri: String(process.env.RVO_REDIRECT_URI), - clientName: String(process.env.RVO_CLIENT_NAME), - }, }, // Database @@ -45,6 +39,12 @@ export const serverConfig: ServerConfig = { nmi: { api_key: String(process.env.NMI_API_KEY), }, + rvo: { + clientId: String(process.env.RVO_CLIENT_ID), + clientSecret: String(process.env.RVO_CLIENT_SECRET), + redirectUri: String(process.env.RVO_REDIRECT_URI), + clientName: String(process.env.RVO_CLIENT_NAME), + }, }, // Analytics diff --git a/fdm-app/app/lib/constants.ts b/fdm-app/app/lib/constants.ts new file mode 100644 index 000000000..de234f0e7 --- /dev/null +++ b/fdm-app/app/lib/constants.ts @@ -0,0 +1,21 @@ +export const acquiringMethodOptions = [ + { value: "nl_01", label: "Eigendom" }, + { value: "nl_02", label: "Reguliere pacht" }, + { + value: "nl_03", + label: "In gebruik van een terreinbeherende organisatie", + }, + { + value: "nl_04", + label: "Tijdelijk gebruik in het kader van landinrichting", + }, + { value: "nl_07", label: "Overige exploitatievormen" }, + { value: "nl_09", label: "Erfpacht" }, + { value: "nl_10", label: "Pacht van geringe oppervlakten" }, + { value: "nl_11", label: "Natuurpacht" }, + { value: "nl_12", label: "Geliberaliseerde pacht, langer dan 6 jaar" }, + { value: "nl_13", label: "Geliberaliseerde pacht, 6 jaar of korter" }, + { value: "nl_61", label: "Reguliere pacht kortlopend" }, + { value: "nl_63", label: "Teeltpacht" }, + { value: "unknown", label: "Onbekend" }, +] diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx index 3cfc473c1..3499dcaff 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx @@ -23,7 +23,7 @@ import { FieldDetailsAtlasSkeleton } from "~/components/blocks/atlas-fields/skel import { SoilTextureCard } from "~/components/blocks/atlas-fields/soil-texture" import { FarmTitle } from "~/components/blocks/farm/farm-title" import { ErrorBlock } from "~/components/custom/error" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi" +import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" import { getCalendar } from "~/lib/calendar" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx index 393352567..e03ae0156 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx @@ -32,7 +32,7 @@ import { clientConfig } from "~/lib/config" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { extractFormValuesFromRequest } from "~/lib/form" -import { getNmiApiKey } from "~/integrations/nmi" +import { getNmiApiKey } from "~/integrations/nmi.server" import { getDefaultCultivation } from "~/lib/cultivation-helpers" import { getNitrogenBalanceforField, diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx index 77a831068..cf9bd3294 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.soil.analysis.new.upload.tsx @@ -16,7 +16,7 @@ import { FormSchema, SoilAnalysisUploadForm, } from "~/components/blocks/soil/form-upload" -import { extractSoilAnalysis } from "~/integrations/nmi" +import { extractSoilAnalysis } from "~/integrations/nmi.server" import { getSession } from "~/lib/auth.server" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx index d8bd4bf9f..5ea5624f7 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field._index.tsx @@ -279,19 +279,6 @@ export default function FarmFieldIndex() { Maak een perceel - - - Percelen ophalen bij RVO - - Importeer percelen direct vanuit RVO. - - - - -
{/*

*/} @@ -304,11 +291,6 @@ export default function FarmFieldIndex() { title={`Percelen van ${currentFarmName}`} description="Selecteer een perceel voor details of voeg een nieuw perceel toe." /> - - -
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx index 0ace7c8b1..c54b28bea 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.new.tsx @@ -47,7 +47,7 @@ import { Separator } from "~/components/ui/separator" import { SidebarInset } from "~/components/ui/sidebar" import { Skeleton } from "~/components/ui/skeleton" import { getMapStyle } from "~/integrations/map" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi" +import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" import { getSession } from "~/lib/auth.server" import { getCalendar, getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx index df41b23e2..32bb7e47b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.nutrient_advice.$b_id.tsx @@ -30,7 +30,7 @@ import { clientConfig } from "~/lib/config" import { getDefaultCultivation } from "~/lib/cultivation-helpers" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" -import { getNmiApiKey } from "../integrations/nmi" +import { getNmiApiKey } from "../integrations/nmi.server" // Meta export const meta: MetaFunction = () => { diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index e198f7a02..4e2ea9ac7 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -9,6 +9,8 @@ import { useNavigation, useParams, Link, + useNavigate, + useLocation, } from "react-router" import { getSession } from "~/lib/auth.server" import { fdm } from "~/lib/fdm.server" @@ -17,36 +19,25 @@ import { fetchRvoFields, compareFields, createRvoClient, + exchangeToken, } from "~/lib/rvo.server" import { type RvoImportReviewItem, RvoImportReviewStatus, type UserChoiceMap, type ImportReviewAction, - getItemId, - processRvoImport, -} from "@svenvw/fdm-rvo" +} from "@svenvw/fdm-rvo/types" +import { getItemId } from "@svenvw/fdm-rvo/utils" +import { processRvoImport } from "@svenvw/fdm-rvo" import { serverConfig } from "~/lib/config.server" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" import { getFields, getFarm, getFarms } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "~/components/ui/card" -import { - Loader2, - ExternalLink, - CheckCircle2, - AlertTriangle, - FlaskConical, -} from "lucide-react" +import { AlertTriangle, Loader2 } from "lucide-react" import { useEffect, useState } from "react" +import { FarmContent } from "~/components/blocks/farm/farm-content" +import { FarmTitle } from "~/components/blocks/farm/farm-title" import { Header } from "~/components/blocks/header/base" import { HeaderFarm } from "~/components/blocks/header/farm" import { SidebarInset } from "~/components/ui/sidebar" @@ -55,6 +46,12 @@ import { BreadcrumbSeparator, BreadcrumbLink, } from "~/components/ui/breadcrumb" +import { getRvoCredentials } from "../integrations/rvo" +import { get } from "proj4/dist/lib/projections" +import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" +import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" +import { addSoilAnalysis } from "@svenvw/fdm-core" +import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" export const meta: MetaFunction = ({ params }) => { return [{ title: `RVO Koppeling - Bedrijf ${params.b_id_farm}` }] @@ -70,6 +67,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { const url = new URL(request.url) const code = url.searchParams.get("code") const state = url.searchParams.get("state") + const calendar = params.calendar let rvoImportReviewData: RvoImportReviewItem[] = [] let error: string | null = null @@ -77,11 +75,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) { let b_name_farm: string | null = null // Check if RVO is configured - const rvoConfigured = - serverConfig.auth.rvo.clientId !== "undefined" && - serverConfig.auth.rvo.clientId !== "" && - serverConfig.auth.rvo.clientSecret !== "undefined" && - serverConfig.auth.rvo.clientSecret !== "" + const rvoCredentials = getRvoCredentials() + const isRvoConfigured = rvoCredentials !== undefined const farm = await getFarm(fdm, session.principal_id, b_id_farm) if (farm) { @@ -93,7 +88,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { if (code && state) { try { - if (!rvoConfigured) { + if (!isRvoConfigured) { throw new Response("RVO client is not configured.", { status: 500, }) @@ -111,17 +106,16 @@ export async function loader({ request, params }: LoaderFunctionArgs) { }) } - const { clientId, clientSecret, redirectUri, clientName } = - serverConfig.auth.rvo const rvoClient = createRvoClient( - clientId, - clientName, - redirectUri, - clientSecret, + rvoCredentials.clientId, + rvoCredentials.clientName, + rvoCredentials.redirectUri, + rvoCredentials.clientSecret, process.env.NODE_ENV === "production" ? "production" : "acceptance", ) + await exchangeToken(rvoClient, code) const rvoFields = await fetchRvoFields( rvoClient, @@ -146,7 +140,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { error: null, showimportButton: true, b_businessid_farm, - rvoConfigured, + isRvoConfigured, farms, b_name_farm, } @@ -158,9 +152,10 @@ export async function loader({ request, params }: LoaderFunctionArgs) { error, showimportButton: false, b_businessid_farm, - rvoConfigured, + isRvoConfigured, farms, b_name_farm, + calendar, } } @@ -170,11 +165,14 @@ export default function RvoImportReviewPage() { rvoImportReviewData, error, b_businessid_farm, - rvoConfigured, + isRvoConfigured, farms, + calendar, + showimportButton, } = useLoaderData() const actionData = useActionData() const navigation = useNavigation() + const location = useLocation() const isImporting = navigation.state === "submitting" && @@ -196,7 +194,7 @@ export default function RvoImportReviewPage() { defaultAction = "ADD_REMOTE" break case RvoImportReviewStatus.NEW_LOCAL: - defaultAction = "KEEP_LOCAL" + defaultAction = "REMOVE_LOCAL" break case RvoImportReviewStatus.CONFLICT: defaultAction = "UPDATE_FROM_REMOTE" @@ -214,255 +212,166 @@ export default function RvoImportReviewPage() { setUserChoices((prev) => ({ ...prev, [id]: action })) } + const currentFarmName = + farms.find((farm) => farm.b_id_farm === b_id_farm)?.b_name_farm ?? "" + + if (error) { + return ( + +
+ + + + Percelen ophalen bij RVO + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ ) + } + return ( -
+
- - Percelen ophalen bij RVO + + Percelen ophalen bij RVO
-
-
- {" "} - {/* Centered layout */} -
- {" "} - {/* Max width constraint */} - {error && ( - - - Er is iets misgegaan bij het ophalen van - percelen bij RVO - - {error} - - )} - {actionData?.message && ( - - - {actionData.success ? "Succes" : "Fout"} - - - {actionData.message} - - - )} - {/* Config Warning */} - {!rvoConfigured && ( - - - - RVO import is niet beschikbaar - - - De RVO koppeling is nog niet ingesteld op - deze server. Neem contact op met de - beheerder om de RVO credentials toe te - voegen. - - - )} - {/* Intro / Connection Card */} - {rvoImportReviewData.length === 0 && ( - - {" "} - {/* Removed border-t-4 border-t-blue-600 shadow-sm */} - - - Percelen ophalen bij RVO - - - - - Experimentele functie - - - Deze functie is nog in ontwikkeling. - Laat ons het weten als je feedback - hebt! - - - - Lees hieronder wat u nodig heeft om te - verbinden. - - - -
-

- Voorwaarden voor gebruik: -

-
    -
  • - U heeft een geldig KvK-nummer - gekoppeld aan uw account. -
  • -
  • - U heeft een eHerkenning account - met machtiging voor dit - KvK-nummer. -
  • -
  • - U geeft ons toestemming om - perceelsgegevens op te halen. -
  • -
-
- -
-
-

- KvK Nummer -

- {b_businessid_farm ? ( -
- - - {b_businessid_farm} - -
- ) : ( -
- Geen KvK-nummer gevonden. - Voeg deze toe in de - bedrijfsinstellingen. -
- )} -
-
-

- Wat gebeurt er? -

-

- Na het klikken op "Verbinden met - RVO" wordt u doorgestuurd naar - de inlogpagina van RVO. Na - authenticatie met eHerkenning - keert u terug naar deze pagina - om de verschillen te beoordelen. -

-
-
-
- - {!b_businessid_farm ? ( - - ) : ( -
- - -
- )} -
-
+
+ {actionData?.message && ( +
+ + + {actionData.success ? "Succes" : "Fout"} + + + {actionData.message} + + +
+ )} + + {/* Config Warning */} + {!isRvoConfigured && ( +
+ + + + RVO import is niet beschikbaar + + + De RVO koppeling is nog niet ingesteld op deze + server. Neem contact op met de beheerder om de + RVO credentials toe te voegen. + + +
+ )} + + {rvoImportReviewData.length === 0 ? ( +
+ {showimportButton && ( + )} - {/* RvoImportReview UI */} - {rvoImportReviewData.length > 0 && ( - - {" "} - {/* Wider card for table */} - - - Verwerken van opgehaalde percelen - - - Beoordeel de verschillen tussen uw - lokale gegevens en de RVO registratie. - - - +
+ ) : ( + <> +
+ +
+
+ + + + +
+
+
+ +
+
- - -
- Controleer alle acties zorgvuldig - voordat u toepast. -
-
- - - - -
-
- - )} -
-
+
+
+ + + )}
) @@ -479,21 +388,18 @@ export async function action({ request, params }: ActionFunctionArgs) { const intent = formData.get("intent") if (intent === "start_import") { - const rvoConfigured = - serverConfig.auth.rvo.clientId !== "undefined" && - serverConfig.auth.rvo.clientId !== "" && - serverConfig.auth.rvo.clientSecret !== "undefined" && - serverConfig.auth.rvo.clientSecret !== "" + const rvoCredentials = getRvoCredentials() + const isRvoConfigured = rvoCredentials !== undefined - if (!rvoConfigured) { + if (!isRvoConfigured) { throw new Response("RVO client is not configured.", { status: 500 }) } - const { clientId, clientSecret, redirectUri } = serverConfig.auth.rvo const rvoClient = createRvoClient( - clientId, - redirectUri, - clientSecret, + rvoCredentials.clientId, + rvoCredentials.clientName, + rvoCredentials.redirectUri, + rvoCredentials.clientSecret, process.env.NODE_ENV === "production" ? "production" : "acceptance", ) const state = Buffer.from( @@ -523,14 +429,43 @@ export async function action({ request, params }: ActionFunctionArgs) { String(userChoicesJson), ) + const onFieldAdded = async (b_id: string, geometry: any) => { + const nmiApiKey = getNmiApiKey() + if (nmiApiKey) { + try { + const soilEstimates = await getSoilParameterEstimates( + geometry, + nmiApiKey, + ) + await addSoilAnalysis( + fdm, + session.principal_id, + new Date(), + "nl-other-nmi", + b_id, + soilEstimates.a_depth_lower ?? 30, + new Date(), + soilEstimates, + soilEstimates.a_depth_upper, + ) + } catch (e) { + console.warn( + `Failed to fetch soil estimates for field ${b_id}:`, + e, + ) + } + } + } + await processRvoImport( fdm, session.principal_id, b_id_farm, rvoImportReviewData, userChoices, + onFieldAdded, ) - return redirect(`/farm/${b_id_farm}/overview`) + return redirect(`/farm/${b_id_farm}`) } catch (e: any) { console.error("Error with processing RVO import: ", e) return { diff --git a/fdm-app/app/routes/farm.$b_id_farm._index.tsx b/fdm-app/app/routes/farm.$b_id_farm._index.tsx index ac3eeca2e..f1d0c8517 100644 --- a/fdm-app/app/routes/farm.$b_id_farm._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm._index.tsx @@ -2,6 +2,7 @@ import { cowHead } from "@lucide/lab" import { getFarm, getFarms, getFields } from "@svenvw/fdm-core" import { ArrowRightLeft, + ArrowDownToLine, BookOpenText, ChevronUp, Home, @@ -396,6 +397,16 @@ export default function FarmDashboardIndex() {
+
diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx index 1d36ce12c..b2c5b07d7 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx @@ -1,5 +1,5 @@ import { getFarm } from "@svenvw/fdm-core" -import { Map as MapIcon, UploadCloud } from "lucide-react" +import { DownloadCloud, Map as MapIcon, UploadCloud } from "lucide-react" import type { LoaderFunctionArgs, MetaFunction } from "react-router" import { data, NavLink, useLoaderData } from "react-router" import { Header } from "~/components/blocks/header/base" @@ -21,7 +21,9 @@ import { import { SidebarInset } from "~/components/ui/sidebar" import { clientConfig } from "~/lib/config" import { getSession } from "../lib/auth.server" -import { fdm } from "../lib/fdm.server" +import { fdm } from "~/lib/fdm.server" +import { getRvoCredentials } from "~/integrations/rvo" +import { cn } from "~/lib/utils" // Meta export const meta: MetaFunction = () => { @@ -53,11 +55,14 @@ export async function loader({ request, params }: LoaderFunctionArgs) { }) } - return { farm } + // Check if RVO import is available + const isRvoConfigured = getRvoCredentials() !== undefined + + return { farm, isRvoConfigured } } export default function ChooseFieldImportMethod() { - const { farm } = useLoaderData() + const { farm, isRvoConfigured } = useLoaderData() return ( @@ -72,13 +77,26 @@ export default function ChooseFieldImportMethod() {

Hoe wil je de percelen van je bedrijf importeren?

-
- +
+ - + Importeren vanuit RVO - Importeer je percelen door via EHerkenning toestemming te geven. + Importeer je percelen door via eHerkenning + toestemming te geven. diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.atlas.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.atlas.tsx index a6bff7304..6cbda9b02 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.atlas.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.atlas.tsx @@ -51,7 +51,7 @@ import { Separator } from "~/components/ui/separator" import { SidebarInset } from "~/components/ui/sidebar" import { Skeleton } from "~/components/ui/skeleton" import { getMapStyle } from "~/integrations/map" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi" +import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" import { getSession } from "~/lib/auth.server" import { getCalendar, getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx index f0e3ecd50..d2884ff0c 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.fields.$b_id.soil.analysis.upload.tsx @@ -16,7 +16,7 @@ import { FormSchema, SoilAnalysisUploadForm, } from "~/components/blocks/soil/form-upload" -import { extractSoilAnalysis } from "~/integrations/nmi" +import { extractSoilAnalysis } from "~/integrations/nmi.server" import { getSession } from "~/lib/auth.server" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 174f6e3e9..e5120e52d 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -9,6 +9,8 @@ import { useLoaderData, useNavigation, useParams, + useNavigate, + useLocation, } from "react-router" import { getSession } from "~/lib/auth.server" import { fdm } from "~/lib/fdm.server" @@ -17,21 +19,23 @@ import { fetchRvoFields, compareFields, createRvoClient, + exchangeToken, } from "~/lib/rvo.server" -import { serverConfig } from "~/lib/config.server" import { type RvoImportReviewItem, type ImportReviewAction, type UserChoiceMap, - processRvoImport, - getItemId, -} from "@svenvw/fdm-rvo" +} from "@svenvw/fdm-rvo/types" +import { getItemId } from "@svenvw/fdm-rvo/utils" +import { processRvoImport } from "@svenvw/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" import { getFarm } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" -import { Loader2, AlertTriangle, FlaskConical } from "lucide-react" +import { AlertTriangle, Loader2 } from "lucide-react" import { useEffect, useState } from "react" +import { FarmContent } from "~/components/blocks/farm/farm-content" +import { FarmTitle } from "~/components/blocks/farm/farm-title" import { Header } from "~/components/blocks/header/base" import { HeaderFarmCreate } from "~/components/blocks/header/create-farm" import { SidebarInset } from "~/components/ui/sidebar" @@ -41,14 +45,15 @@ import { BreadcrumbLink, } from "~/components/ui/breadcrumb" import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "~/components/ui/card" -import { CheckCircle2 } from "lucide-react" + BreadcrumbItem, + BreadcrumbSeparator, + BreadcrumbLink, +} from "~/components/ui/breadcrumb" +import { getRvoCredentials } from "../integrations/rvo" +import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" +import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" +import { addSoilAnalysis } from "@svenvw/fdm-core" +import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" export const meta: MetaFunction = ({ params }) => { const b_id_farm = params.b_id_farm @@ -74,11 +79,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) { let b_name_farm: string | null | undefined = null // Check if RVO is configured - const rvoConfigured = - serverConfig.auth.rvo.clientId !== "undefined" && - serverConfig.auth.rvo.clientId !== "" && - serverConfig.auth.rvo.clientSecret !== "undefined" && - serverConfig.auth.rvo.clientSecret !== "" + const rvoCredentials = getRvoCredentials() + const isRvoConfigured = rvoCredentials !== undefined const farm = await getFarm(fdm, session.principal_id, b_id_farm) if (farm) { @@ -88,7 +90,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { if (code && state) { try { - if (!rvoConfigured) { + if (!isRvoConfigured) { throw new Response("RVO is niet geconfigureerd.", { status: 500, }) @@ -108,17 +110,16 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) } - const { clientId, clientSecret, redirectUri, clientName } = - serverConfig.auth.rvo const rvoClient = createRvoClient( - clientId, - clientName, - redirectUri, - clientSecret, + rvoCredentials.clientId, + rvoCredentials.clientName, + rvoCredentials.redirectUri, + rvoCredentials.clientSecret, process.env.NODE_ENV === "production" ? "production" : "acceptance", ) // Instantiate RvoClient + await exchangeToken(rvoClient, code) const rvoFields = await fetchRvoFields( rvoClient, @@ -140,33 +141,38 @@ export async function loader({ request, params }: LoaderFunctionArgs) { RvoImportReviewData: [], error: null, showImportButton: true, - rvoConfigured, + isRvoConfigured, b_name_farm, } } return { b_id_farm, + b_businessid_farm, calendar, RvoImportReviewData, error, showImportButton: false, - rvoConfigured, + isRvoConfigured, b_name_farm, } } export default function RvoImportCreatePage() { - const { b_id_farm, b_businessid_farm } = useParams() const { + b_id_farm, + b_businessid_farm, + calendar, RvoImportReviewData, error, showImportButton, - rvoConfigured, + isRvoConfigured, b_name_farm, } = useLoaderData() const actionData = useActionData() const navigation = useNavigation() + const location = useLocation() + const isImporting = navigation.state === "submitting" && navigation.formData?.get("intent") === "start_import" @@ -201,6 +207,40 @@ export default function RvoImportCreatePage() { setUserChoices((prev) => ({ ...prev, [id]: action })) } + if (error) { + return ( + +
+ + + + + Percelen ophalen bij RVO + + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ ) + } + return (
@@ -211,234 +251,111 @@ export default function RvoImportCreatePage() {
-
- {" "} - {/* Centered layout like upload page */} -
- {" "} - {/* Max width constraint */} - {error && ( - - - Er is iets misgegaan bij het importeren - - {error} - - )} - {actionData?.message && ( - - - {actionData.success ? "Succes" : "Fout"} - - - {actionData.message} - - - )} - {/* Config Warning */} - {!rvoConfigured && ( - - - - Importen vanuit RVO is niet beschikbaar - - - De RVO koppeling is nog niet ingesteld op - deze server. Neem contact op met de - beheerder om de RVO toeggangegevens toe te - voegen. - - - )} - {showImportButton && ( - - - - Percelen ophalen bij RVO - - - - - Experimentele functie - - - Deze functie is nog in ontwikkeling. - Laat ons het weten als je feedback - hebt! - - - - Lees hieronder wat u nodig heeft om te - verbinden. - - - -
-

- Voorwaarden voor gebruik: -

-
    -
  • - U heeft een geldig KvK-nummer - gekoppeld aan uw account. -
  • -
  • - U heeft een eHerkenning account - met machtiging voor dit - KvK-nummer. -
  • -
  • - U geeft ons toestemming om - perceelsgegevens op te halen. -
  • -
-
- -
-
-

- KvK Nummer -

- {b_businessid_farm ? ( -
- - - {b_businessid_farm} - -
- ) : ( -
- Geen KvK-nummer gevonden. - Voeg deze toe in de - bedrijfsgegevens. -
- )} -
-
-

- Wat gebeurt er? -

-

- Na het klikken op "Importeer van - RVO" wordt u doorgestuurd naar - de inlogpagina van RVO. Na - authenticatie met eHerkenning - keert u terug naar deze pagina - om de verschillen te beoordelen. -

-
-
-
- - {!b_businessid_farm ? ( - - ) : ( -
- - -
- )} -
-
- )} - {RvoImportReviewData.length > 0 && ( - - {" "} - {/* Wider card for table */} - - - Verwerken van geïmporteerde percelen - - - Controleer de percelen opgehaald vanuit - RVO. Deze worden toegevoegd aan uw - nieuwe bedrijf. - - - + {actionData?.message && ( +
+ + + {actionData.success ? "Succes" : "Fout"} + + + {actionData.message} + + +
+ )} + {/* Config Warning */} + {!isRvoConfigured && ( +
+ + + + Importen vanuit RVO is niet beschikbaar + + + De RVO koppeling is nog niet ingesteld op deze + server. Neem contact op met de beheerder om de + RVO toeggangegevens toe te voegen. + + +
+ )} + + {RvoImportReviewData.length === 0 ? ( +
+
+ {showImportButton && ( + + )} +
+
+ ) : ( + <> +
+ +
+
+ + + + +
+
+
+ + +
+
- - -
- - - - -
-
- - )} -
-
+
+
+ + + )}
) @@ -457,23 +374,18 @@ export async function action({ request, params }: ActionFunctionArgs) { const intent = formData.get("intent") if (intent === "start_import") { - const rvoConfigured = - serverConfig.auth.rvo.clientId !== "undefined" && - serverConfig.auth.rvo.clientId !== "" && - serverConfig.auth.rvo.clientSecret !== "undefined" && - serverConfig.auth.rvo.clientSecret !== "" + const rvoCredentials = getRvoCredentials() + const isRvoConfigured = rvoCredentials !== undefined - if (!rvoConfigured) { + if (!isRvoConfigured) { throw new Response("RVO client is not available", { status: 500 }) } - const { clientId, clientSecret, redirectUri, clientName } = - serverConfig.auth.rvo const rvoClient = createRvoClient( - clientId, - clientName, - redirectUri, - clientSecret, + rvoCredentials.clientId, + rvoCredentials.clientName, + rvoCredentials.redirectUri, + rvoCredentials.clientSecret, process.env.NODE_ENV === "production" ? "production" : "acceptance", ) // Instantiate RvoClient const state = Buffer.from( @@ -503,12 +415,41 @@ export async function action({ request, params }: ActionFunctionArgs) { String(userChoicesJson), ) + const onFieldAdded = async (b_id: string, geometry: any) => { + const nmiApiKey = getNmiApiKey() + if (nmiApiKey) { + try { + const soilEstimates = await getSoilParameterEstimates( + geometry, + nmiApiKey, + ) + await addSoilAnalysis( + fdm, + session.principal_id, + new Date(), + "nl-other-nmi", + b_id, + soilEstimates.a_depth_lower ?? 30, + new Date(), + soilEstimates, + soilEstimates.a_depth_upper, + ) + } catch (e) { + console.warn( + `Failed to fetch soil estimates for field ${b_id}:`, + e, + ) + } + } + } + await processRvoImport( fdm, session.principal_id, b_id_farm, RvoImportReviewData, userChoices, + onFieldAdded, ) return redirect(`/farm/create/${b_id_farm}/${calendar}/fields`) } catch (e: any) { diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.upload.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.upload.tsx index ea050fef6..6651b9efb 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.upload.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.upload.tsx @@ -22,7 +22,7 @@ import { MijnPercelenUploadForm } from "@/app/components/blocks/mijnpercelen/for import { Header } from "~/components/blocks/header/base" import { HeaderFarmCreate } from "~/components/blocks/header/create-farm" import { SidebarInset } from "~/components/ui/sidebar" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi" +import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" import { getSession } from "~/lib/auth.server" import { getCalendar } from "~/lib/calendar" import { clientConfig } from "~/lib/config" diff --git a/fdm-app/app/routes/farm.create._index.tsx b/fdm-app/app/routes/farm.create._index.tsx index 8b616d476..df588b78e 100644 --- a/fdm-app/app/routes/farm.create._index.tsx +++ b/fdm-app/app/routes/farm.create._index.tsx @@ -149,12 +149,12 @@ export default function AddFarmPage() { Bedrijf - Wat voor soort bedrijf heb je? + Vul de gegevens over het bedrijf in om te beginnen?
-
+
( - Kvk nummer + KvK nummer Optioneel, - vereist voor RVO - synchronisatie. + vereist voor + percelen ophalen + uit RVO. diff --git a/fdm-app/app/types/config.d.ts b/fdm-app/app/types/config.d.ts index a4687803b..315f1f2d1 100644 --- a/fdm-app/app/types/config.d.ts +++ b/fdm-app/app/types/config.d.ts @@ -18,12 +18,6 @@ export interface ServerConfig { clientSecret: string } | undefined - rvo: { - clientId: string - clientSecret: string - redirectUri: string - clientName: string - } } database: { password: string @@ -40,6 +34,12 @@ export interface ServerConfig { nmi?: { api_key: string } + rvo: { + clientId: string + clientSecret: string + redirectUri: string + clientName: string + } } analytics: { sentry?: { diff --git a/fdm-core/src/index.ts b/fdm-core/src/index.ts index b18c3cda3..280490bc6 100644 --- a/fdm-core/src/index.ts +++ b/fdm-core/src/index.ts @@ -110,6 +110,11 @@ export { updateField, } from "./field" export type { Field } from "./field.d" +export { + acquiringMethodOptions, + soilTypesOptions, + gwlClassesOptions, +} from "./db/schema" export { getGrazingIntention, getGrazingIntentions, diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index 0bad5f07a..237e5d30f 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -47,7 +47,7 @@ import { serverConfig } from '~/lib/config.client'; // Example client-side confi // To initiate authentication (e.g., from a button click handler) const handleAuthInitiation = () => { - const { clientId, redirectUri, pkioPrivateKey, environment } = serverConfig.auth.rvo; + const { clientId, redirectUri, pkioPrivateKey, environment } = serverConfig.integrations.rvo; const clientName = serverConfig.name; // Use your app's name const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); @@ -61,7 +61,7 @@ const handleAuthInitiation = () => { // To handle the RVO callback in a component rendered at the redirectUri const handleRvoCallback = async (code: string, state: string) => { - const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.auth.rvo; + const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.integrations.rvo; const clientName = serverConfig.name; // Validate state to prevent CSRF @@ -88,7 +88,7 @@ const fetchLocalFieldsApi = async (farmId: string, principalId: string) => { }; const processRvoData = async (accessToken: string, farmId: string, principalId: string, year: number, kvkNumber: string) => { - const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.auth.rvo; + const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.integrations.rvo; const clientName = serverConfig.name; const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json index 8f294bca1..a06925f66 100644 --- a/fdm-rvo/package.json +++ b/fdm-rvo/package.json @@ -31,6 +31,12 @@ "types": "./dist/types.d.ts", "default": "./dist/types.js" } + }, + "./utils": { + "import": { + "types": "./dist/utils.d.ts", + "default": "./dist/utils.js" + } } }, "files": [ diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index 676bba76a..fdd944797 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -26,6 +26,7 @@ export async function processRvoImport( b_id_farm: string, rvoImportReviewData: RvoImportReviewItem[], userChoices: UserChoiceMap, + onFieldAdded?: (b_id: string, geometry: any) => Promise, ) { for (const item of rvoImportReviewData) { const id = getItemId(item) @@ -38,7 +39,7 @@ export async function processRvoImport( switch (action) { case "ADD_REMOTE": if (item.rvoField) { - await addField( + const b_id = await addField( fdm, principal_id, b_id_farm, @@ -47,11 +48,14 @@ export async function processRvoImport( item.rvoField.properties.CropFieldID, item.rvoField.geometry, new Date(item.rvoField.properties.BeginDate), - "rvo_import", + `nl_${item.rvoField.properties.UseTitleCode}` as any, item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, ) + if (onFieldAdded) { + await onFieldAdded(b_id, item.rvoField.geometry) + } } break case "UPDATE_FROM_REMOTE": @@ -65,7 +69,7 @@ export async function processRvoImport( item.rvoField.properties.CropFieldID, item.rvoField.geometry, new Date(item.rvoField.properties.BeginDate), - "rvo_import", + `nl_${item.rvoField.properties.UseTitleCode}` as any, item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, @@ -73,6 +77,7 @@ export async function processRvoImport( } break case "KEEP_LOCAL": // Keep Local for Conflict + break case "REMOVE_LOCAL": if (item.localField) { await removeField(fdm, principal_id, item.localField.b_id) From 7106e77028313e843b35ba861e5b63b2358192ec Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:22:25 +0100 Subject: [PATCH 008/108] feat: add Acties to farm overview page --- fdm-app/app/routes/farm.$b_id_farm._index.tsx | 96 ++++++++++++++++--- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm._index.tsx b/fdm-app/app/routes/farm.$b_id_farm._index.tsx index f1d0c8517..e6550bfc2 100644 --- a/fdm-app/app/routes/farm.$b_id_farm._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm._index.tsx @@ -1,5 +1,5 @@ import { cowHead } from "@lucide/lab" -import { getFarm, getFarms, getFields } from "@svenvw/fdm-core" +import { checkPermission, getFarm, getFarms, getFields } from "@svenvw/fdm-core" import { ArrowRightLeft, ArrowDownToLine, @@ -9,12 +9,14 @@ import { Icon, Landmark, MapIcon, + MapPin, ScrollText, Shapes, Sprout, Square, Trash2, UserRoundCheck, + CloudDownload, } from "lucide-react" import { data, @@ -49,6 +51,8 @@ import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { useCalendarStore } from "~/store/calendar" +import { getRvoCredentials } from "../integrations/rvo" +import { cn } from "~/lib/utils" // Meta export const meta: MetaFunction = () => { @@ -104,6 +108,19 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } }) + const farmWritePermission = await checkPermission( + fdm, + "farm", + "write", + b_id_farm, + session.principal_id, + new URL(request.url).pathname, + false, + ) + + const rvoCredentials = getRvoCredentials() + const isRvoConfigured = rvoCredentials !== undefined + // Return the farm ID and session info return { b_id_farm: b_id_farm, @@ -112,6 +129,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) { farmArea: Math.round(farmArea), farmOptions: farmOptions, roles: farm.roles, + farmWritePermission, + isRvoConfigured, } } catch (error) { throw handleLoaderError(error) @@ -322,6 +341,71 @@ export default function FarmDashboardIndex() {
+ + {/* Acties */} +
+

+ Acties +

+
+ + + +
+
+ +
+
+ + Nieuw perceel + + + Teken of selecteer + een perceel op de + kaart. + +
+
+
+
+
+ {loaderData.isRvoConfigured && ( + + + +
+
+ +
+
+ + Ophalen bij RVO + + + Importeer + percelen vanuit + RVO. + +
+
+
+
+
+ )} +
+
{/* Right Column */} @@ -397,16 +481,6 @@ export default function FarmDashboardIndex() {
-
From 62dff30cb2edede631f7225c6b08fc08f9493d36 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:37:21 +0100 Subject: [PATCH 009/108] fix: use selected calendar instead of current year for requesting fields --- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 10 ++++++++-- .../routes/farm.create.$b_id_farm.$calendar.rvo.tsx | 12 ++++++++++-- fdm-rvo/README.md | 2 +- fdm-rvo/src/data.ts | 8 ++++---- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 4e2ea9ac7..a32c11390 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -52,6 +52,7 @@ import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" import { addSoilAnalysis } from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" +import { getCalendar } from "../lib/calendar" export const meta: MetaFunction = ({ params }) => { return [{ title: `RVO Koppeling - Bedrijf ${params.b_id_farm}` }] @@ -117,9 +118,14 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) await exchangeToken(rvoClient, code) + const calendar = getCalendar(params) + if (!calendar) { + throw new Response("Calendar not found", { status: 404 }) + } + const rvoFields = await fetchRvoFields( rvoClient, - new Date().getFullYear(), + calendar, farm.b_businessid_farm, ) @@ -168,7 +174,7 @@ export default function RvoImportReviewPage() { isRvoConfigured, farms, calendar, - showimportButton, + showimportButton = false, } = useLoaderData() const actionData = useActionData() const navigation = useNavigation() diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index e5120e52d..c1a235e2f 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -51,9 +51,13 @@ import { } from "~/components/ui/breadcrumb" import { getRvoCredentials } from "../integrations/rvo" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" +import { + getNmiApiKey, + getSoilParameterEstimates, +} from "~/integrations/nmi.server" import { addSoilAnalysis } from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" +import { getCalendar } from "../lib/calendar" export const meta: MetaFunction = ({ params }) => { const b_id_farm = params.b_id_farm @@ -121,9 +125,13 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) // Instantiate RvoClient await exchangeToken(rvoClient, code) + const calendar = getCalendar(params) + if (!calendar) { + throw new Response("Calendar not found", { status: 404 }) + } const rvoFields = await fetchRvoFields( rvoClient, - new Date().getFullYear(), + calendar, farm.b_businessid_farm, ) diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index 237e5d30f..4d1a779ac 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -87,7 +87,7 @@ const fetchLocalFieldsApi = async (farmId: string, principalId: string) => { return response.json(); }; -const processRvoData = async (accessToken: string, farmId: string, principalId: string, year: number, kvkNumber: string) => { +const processRvoData = async (accessToken: string, farmId: string, principalId: string, year: string, kvkNumber: string) => { const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.integrations.rvo; const clientName = serverConfig.name; diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts index 2b426b231..fb52c4c3e 100644 --- a/fdm-rvo/src/data.ts +++ b/fdm-rvo/src/data.ts @@ -8,7 +8,7 @@ import { z } from "zod" * This function retrieves the fields in GeoJSON format and validates them against the `RvoFieldSchema`. * * @param rvoClient - An authenticated instance of `RvoClient` (must have a valid access token). - * @param year - The calendar year for which to retrieve the fields (e.g., 2024). + * @param calendar - The calendar year for which to retrieve the fields (e.g., 2024). * @param kvkNumber - The Chamber of Commerce (KvK) number of the farm/organization. This acts as the identifier for the data request. * @returns A promise that resolves to an array of validated `RvoField` objects. * @throws Will throw a ZodError if the response from RVO does not match the expected schema. @@ -16,14 +16,14 @@ import { z } from "zod" */ export async function fetchRvoFields( rvoClient: RvoClient, - year: number, + calendar: string, kvkNumber: string, ): Promise { // Request fields from RVO API // We request the full calendar year period const fieldsRaw = await rvoClient.opvragenBedrijfspercelen({ - periodBeginDate: `${year}-01-01`, - periodEndDate: `${year}-12-31`, + periodBeginDate: `${calendar}-01-01`, + periodEndDate: `${calendar}-12-31`, farmId: kvkNumber, outputFormat: "geojson", }) From d93d24a18fec6a728664f6040b34b2e60f85e6c3 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:09:34 +0100 Subject: [PATCH 010/108] feat: add importing of cultivation as well --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 20 +++- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 8 +- fdm-rvo/src/compare.ts | 101 +++++++++++++----- fdm-rvo/src/process.ts | 58 ++++++++++ fdm-rvo/src/types.ts | 5 + 5 files changed, 162 insertions(+), 30 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index a32c11390..976bbbdd4 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -134,7 +134,25 @@ export async function loader({ request, params }: LoaderFunctionArgs) { session.principal_id, b_id_farm, ) - rvoImportReviewData = compareFields(localFields, rvoFields) + const localFieldsExtended = await Promise.all( + localFields.map(async (field) => { + const cultivations = await getCultivations( + fdm, + session.principal_id, + field.b_id, + { + start: new Date(`${yearString}-01-01`), + end: new Date(`${yearString}-12-31`), + }, + ) + return { ...field, cultivations } + }), + ) + rvoImportReviewData = compareFields( + localFieldsExtended, + rvoFields, + year, + ) } catch (e: any) { console.error("Error with importing from RVO:", e) error = e.message diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index c1a235e2f..dc708a58a 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -135,8 +135,12 @@ export async function loader({ request, params }: LoaderFunctionArgs) { farm.b_businessid_farm, ) - const localFields: any[] = [] // No existing fields to compare against yet - RvoImportReviewData = compareFields(localFields, rvoFields) + const localFieldsExtended: (Field & { cultivations: Cultivation[] })[] = [] // No existing fields to compare against yet in create wizard, so localFields is empty + RvoImportReviewData = compareFields( + localFieldsExtended, + rvoFields, + year, + ) // Pass year here } catch (e: any) { console.error("RVO Import Fout:", e) error = e.message diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 615742934..d7aa9f3b9 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -3,7 +3,7 @@ import intersect from "@turf/intersect" import union from "@turf/union" import area from "@turf/area" import { feature, featureCollection } from "@turf/helpers" -import type { Field } from "@svenvw/fdm-core" +import type { Field, Cultivation } from "@svenvw/fdm-core" import { type RvoField, RvoImportReviewStatus, @@ -70,6 +70,18 @@ function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { ) } +function findActiveCultivation( + cultivations: Cultivation[], + calendar: number, +): Cultivation | undefined { + const referenceDate = new Date(`${calendar}-05-15`).getTime() + return cultivations.find((c) => { + const start = c.b_lu_start.getTime() + const end = c.b_lu_end ? c.b_lu_end.getTime() : Number.POSITIVE_INFINITY + return start <= referenceDate && end >= referenceDate + }) +} + /** * Compares a list of local fields against a list of RVO fields to determine their import status. * @@ -84,13 +96,58 @@ function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { * @returns An array of `RvoImportReviewItem` objects, each representing a field and its status (MATCH, CONFLICT, NEW_REMOTE, NEW_LOCAL). */ export function compareFields( - localFields: Field[], + localFields: (Field & { cultivations?: Cultivation[] })[], rvoFields: RvoField[], + calendar = new Date().getFullYear(), ): RvoImportReviewItem[] { const results: RvoImportReviewItem[] = [] const matchedRvoIds = new Set() const matchedLocalIds = new Set() + const processMatch = (local: Field & { cultivations?: Cultivation[] }, rvo: RvoField) => { + // Detect property differences + const diffs = detectDiffs(local, rvo) + + // Check for cultivation differences + const localCultivation = local.cultivations + ? findActiveCultivation(local.cultivations, calendar) + : undefined + const rvoCode = `nl_${rvo.properties.CropTypeCode}` + + let rvoCultivationInfo + let localCultivationInfo + + if (localCultivation) { + localCultivationInfo = { + b_lu_catalogue: localCultivation.b_lu_catalogue, + b_lu: localCultivation.b_lu, + } + } + + if (rvoCode) { + rvoCultivationInfo = { + b_lu_catalogue: rvoCode, + } + } + + // If local has active cultivation and it differs from RVO, flag it + if (localCultivation && localCultivation.b_lu_catalogue !== rvoCode) { + diffs.push("b_lu_catalogue") + } + + return { + status: + diffs.length > 0 + ? RvoImportReviewStatus.CONFLICT + : RvoImportReviewStatus.MATCH, + localField: local, + rvoField: rvo, + localCultivation: localCultivationInfo, + rvoCultivation: rvoCultivationInfo, + diffs, + } + } + // --------------------------------------------------------- // Tier 1: Match by Source ID (CropFieldID) // --------------------------------------------------------- @@ -100,21 +157,9 @@ export function compareFields( (r) => r.properties.CropFieldID === local.b_id_source, ) if (rvoMatch) { - // Mark as matched to prevent re-matching in Tier 2 matchedLocalIds.add(local.b_id) matchedRvoIds.add(rvoMatch.properties.CropFieldID) - - // Detect any property differences (geometry, name, dates) - const diffs = detectDiffs(local, rvoMatch) - results.push({ - status: - diffs.length > 0 - ? RvoImportReviewStatus.CONFLICT - : RvoImportReviewStatus.MATCH, - localField: local, - rvoField: rvoMatch, - diffs, - }) + results.push(processMatch(local, rvoMatch)) } } } @@ -137,7 +182,7 @@ export function compareFields( if (matchedRvoIds.has(rvo.properties.CropFieldID)) continue const rvoBbox = bbox(rvo.geometry) - let bestMatch: Field | null = null + let bestMatch: (Field & { cultivations?: Cultivation[] }) | null = null let bestIoU = 0 // Optimization: Fast BBox overlap check before accurate IoU @@ -158,22 +203,15 @@ export function compareFields( if (bestMatch && bestIoU > IOU_THRESHOLD) { matchedRvoIds.add(rvo.properties.CropFieldID) matchedLocalIds.add(bestMatch.b_id) - - const diffs = detectDiffs(bestMatch, rvo) - results.push({ - status: - diffs.length > 0 - ? RvoImportReviewStatus.CONFLICT - : RvoImportReviewStatus.MATCH, - localField: bestMatch, - rvoField: rvo, - diffs, - }) + results.push(processMatch(bestMatch, rvo)) } else { // No match found -> This is a NEW field from RVO results.push({ status: RvoImportReviewStatus.NEW_REMOTE, rvoField: rvo, + rvoCultivation: { + b_lu_catalogue: `nl_${rvo.properties.CropTypeCode}`, + }, diffs: [], }) } @@ -184,9 +222,18 @@ export function compareFields( // --------------------------------------------------------- for (const local of localFields) { if (!matchedLocalIds.has(local.b_id)) { + const localCultivation = local.cultivations + ? findActiveCultivation(local.cultivations, calendar) + : undefined results.push({ status: RvoImportReviewStatus.NEW_LOCAL, localField: local, + localCultivation: localCultivation + ? { + b_lu_catalogue: localCultivation.b_lu_catalogue, + b_lu: localCultivation.b_lu, + } + : undefined, diffs: [], }) } diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index fdd944797..ba128cfa3 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -2,6 +2,9 @@ import { addField, updateField, removeField, + addCultivation, + removeCultivation, + getDefaultDatesOfCultivation, type FdmType, } from "@svenvw/fdm-core" import type { RvoImportReviewItem, UserChoiceMap } from "./types" @@ -18,6 +21,7 @@ import { getItemId } from "./utils" * @param b_id_farm - The ID of the farm the fields belong to. * @param rvoImportReviewData - The list of review items resulting from the comparison. * @param userChoices - A map where keys are item IDs and values are the chosen `ImportReviewAction`. + * @param year - The calendar year for the import context. * @returns A promise that resolves when all actions have been processed. */ export async function processRvoImport( @@ -26,6 +30,7 @@ export async function processRvoImport( b_id_farm: string, rvoImportReviewData: RvoImportReviewItem[], userChoices: UserChoiceMap, + year: number, onFieldAdded?: (b_id: string, geometry: any) => Promise, ) { for (const item of rvoImportReviewData) { @@ -53,6 +58,26 @@ export async function processRvoImport( ? new Date(item.rvoField.properties.EndDate) : undefined, ) + + // Add cultivation from RVO + const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` + const defaultDates = await getDefaultDatesOfCultivation( + fdm, + principal_id, + b_id_farm, + b_lu_catalogue, + year, + ) + + await addCultivation( + fdm, + principal_id, + b_lu_catalogue, + b_id, + defaultDates.b_lu_start, + defaultDates.b_lu_end, + ) + if (onFieldAdded) { await onFieldAdded(b_id, item.rvoField.geometry) } @@ -74,6 +99,39 @@ export async function processRvoImport( ? new Date(item.rvoField.properties.EndDate) : undefined, ) + + // Update cultivation if different + if ( + item.localCultivation && + item.localCultivation.b_lu_catalogue !== + `nl_${item.rvoField.properties.CropTypeCode}` + ) { + // Remove old cultivation + await removeCultivation( + fdm, + principal_id, + item.localCultivation.b_lu, + ) + + // Add new RVO cultivation + const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` + const defaultDates = await getDefaultDatesOfCultivation( + fdm, + principal_id, + b_id_farm, + b_lu_catalogue, + year, + ) + + await addCultivation( + fdm, + principal_id, + b_lu_catalogue, + item.localField.b_id, + defaultDates.b_lu_start, + defaultDates.b_lu_end, + ) + } } break case "KEEP_LOCAL": // Keep Local for Conflict diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index 0e360177d..15a3c945b 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -78,6 +78,7 @@ export type FieldDiff = | "b_start" // Start date difference | "b_end" // End date difference | "b_acquiring_method" // Method of acquisition difference (implied) + | "b_lu_catalogue" // Cultivation difference /** * Represents the explicit decision made by the user for a RVO Import Review item. @@ -107,6 +108,10 @@ export interface RvoImportReviewItem { localField?: TLocal /** The RVO field object, if it exists (undefined for NEW_LOCAL) */ rvoField?: RvoField + /** The local cultivation on May 15th */ + localCultivation?: { b_lu_catalogue: string; b_lu: string } + /** The RVO cultivation based on CropTypeCode */ + rvoCultivation?: { b_lu_catalogue: string } /** List of specific properties that differ (empty for MATCH, NEW_REMOTE, NEW_LOCAL) */ diffs: FieldDiff[] } From 0cb023c6178ee39594fec2febda7afc8baa7b3de Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:09:59 +0100 Subject: [PATCH 011/108] refactor: be consistent with year --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 21 ++++++++----------- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 21 +++++++++---------- fdm-rvo/src/data.ts | 8 +++---- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 976bbbdd4..de3f50381 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -50,7 +50,7 @@ import { getRvoCredentials } from "../integrations/rvo" import { get } from "proj4/dist/lib/projections" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" -import { addSoilAnalysis } from "@svenvw/fdm-core" +import { addSoilAnalysis, getCultivations, type Cultivation } from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" import { getCalendar } from "../lib/calendar" @@ -59,16 +59,16 @@ export const meta: MetaFunction = ({ params }) => { } export async function loader({ request, params }: LoaderFunctionArgs) { - const { b_id_farm } = params + const { b_id_farm, calendar: yearString } = params if (!b_id_farm) { throw new Response("Farm ID is required", { status: 400 }) } + const year = Number(yearString) const session = await getSession(request) const url = new URL(request.url) const code = url.searchParams.get("code") const state = url.searchParams.get("state") - const calendar = params.calendar let rvoImportReviewData: RvoImportReviewItem[] = [] let error: string | null = null @@ -118,14 +118,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) await exchangeToken(rvoClient, code) - const calendar = getCalendar(params) - if (!calendar) { - throw new Response("Calendar not found", { status: 404 }) - } - const rvoFields = await fetchRvoFields( rvoClient, - calendar, + yearString, farm.b_businessid_farm, ) @@ -179,7 +174,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { isRvoConfigured, farms, b_name_farm, - calendar, + calendar: yearString, } } @@ -402,10 +397,11 @@ export default function RvoImportReviewPage() { } export async function action({ request, params }: ActionFunctionArgs) { - const { b_id_farm } = params - if (!b_id_farm) { + const { b_id_farm, calendar: yearString } = params + if (!b_id_farm || !yearString) { throw new Response("Farm ID is required", { status: 400 }) } + const year = Number(yearString) const session = await getSession(request) const formData = await request.formData() @@ -487,6 +483,7 @@ export async function action({ request, params }: ActionFunctionArgs) { b_id_farm, rvoImportReviewData, userChoices, + year, onFieldAdded, ) return redirect(`/farm/${b_id_farm}`) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index dc708a58a..99772a768 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -65,12 +65,13 @@ export const meta: MetaFunction = ({ params }) => { } export async function loader({ request, params }: LoaderFunctionArgs) { - const { b_id_farm, calendar } = params - if (!b_id_farm || !calendar) { + const { b_id_farm, calendar: yearString } = params + if (!b_id_farm || !yearString) { throw new Response("Farm ID en Kalender zijn verplicht", { status: 400, }) } + const year = Number(yearString) const session = await getSession(request) const url = new URL(request.url) @@ -125,13 +126,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) // Instantiate RvoClient await exchangeToken(rvoClient, code) - const calendar = getCalendar(params) - if (!calendar) { - throw new Response("Calendar not found", { status: 404 }) - } const rvoFields = await fetchRvoFields( rvoClient, - calendar, + yearString, // Pass yearString here farm.b_businessid_farm, ) @@ -149,7 +146,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { return { b_id_farm, b_businessid_farm, - calendar, + calendar: yearString, RvoImportReviewData: [], error: null, showImportButton: true, @@ -161,7 +158,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { return { b_id_farm, b_businessid_farm, - calendar, + calendar: yearString, RvoImportReviewData, error, showImportButton: false, @@ -374,12 +371,13 @@ export default function RvoImportCreatePage() { } export async function action({ request, params }: ActionFunctionArgs) { - const { b_id_farm, calendar } = params - if (!b_id_farm || !calendar) { + const { b_id_farm, calendar: yearString } = params + if (!b_id_farm || !yearString) { throw new Response("b_id_farm and calendar are required", { status: 400, }) } + const year = Number(yearString) const session = await getSession(request) const formData = await request.formData() @@ -461,6 +459,7 @@ export async function action({ request, params }: ActionFunctionArgs) { b_id_farm, RvoImportReviewData, userChoices, + Number(calendar), onFieldAdded, ) return redirect(`/farm/create/${b_id_farm}/${calendar}/fields`) diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts index fb52c4c3e..2a53ea035 100644 --- a/fdm-rvo/src/data.ts +++ b/fdm-rvo/src/data.ts @@ -8,7 +8,7 @@ import { z } from "zod" * This function retrieves the fields in GeoJSON format and validates them against the `RvoFieldSchema`. * * @param rvoClient - An authenticated instance of `RvoClient` (must have a valid access token). - * @param calendar - The calendar year for which to retrieve the fields (e.g., 2024). + * @param year - The calendar year for which to retrieve the fields (e.g., 2024). * @param kvkNumber - The Chamber of Commerce (KvK) number of the farm/organization. This acts as the identifier for the data request. * @returns A promise that resolves to an array of validated `RvoField` objects. * @throws Will throw a ZodError if the response from RVO does not match the expected schema. @@ -16,14 +16,14 @@ import { z } from "zod" */ export async function fetchRvoFields( rvoClient: RvoClient, - calendar: string, + year: string, kvkNumber: string, ): Promise { // Request fields from RVO API // We request the full calendar year period const fieldsRaw = await rvoClient.opvragenBedrijfspercelen({ - periodBeginDate: `${calendar}-01-01`, - periodEndDate: `${calendar}-12-31`, + periodBeginDate: `${year}-01-01`, + periodEndDate: `${year}-12-31`, farmId: kvkNumber, outputFormat: "geojson", }) From c851ab14d3ce91c5f4c466be1c54ff5019ba0a0a Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:08:31 +0100 Subject: [PATCH 012/108] refactor: more improvements --- .../blocks/rvo/import-review-table.tsx | 181 +++++++++++------- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 21 +- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 34 ++-- fdm-rvo/src/compare.ts | 41 +++- fdm-rvo/src/types.ts | 8 +- 5 files changed, 187 insertions(+), 98 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index c9aa10572..74e65dda6 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -44,13 +44,6 @@ interface RvoImportReviewTableProps { onChoiceChange: (id: string, action: ImportReviewAction) => void } -const USE_TITLE_MAP: Record = { - "01": "Eigendom", - "02": "Pacht", - "03": "Erfpacht", - "04": "Bruikleen", -} - function formatDate(dateString?: string | Date) { if (!dateString) return "-" try { @@ -82,13 +75,15 @@ const DiffCell = ({ }) => { // If MATCH, just show one value if (status === "MATCH") { - return {formatter(local)} + return ( + {formatter(local)} + ) } // NEW REMOTE -> Show remote without badge if (status === "NEW_REMOTE") { return ( - + {formatter(remote)} ) @@ -97,7 +92,7 @@ const DiffCell = ({ // NEW LOCAL -> Show local without badge if (status === "NEW_LOCAL") { return ( - + {formatter(local)} ) @@ -106,8 +101,12 @@ const DiffCell = ({ // CONFLICT if (status === "CONFLICT") { // If values are effectively equal (loose check), show one - if (local == remote) { - return {formatter(local)} + if (local === remote) { + return ( + + {formatter(local)} + + ) } const useRemote = @@ -120,6 +119,7 @@ const DiffCell = ({
@@ -129,20 +129,27 @@ const DiffCell = ({ > Lokaal + {formatter(local)}
)} + {remote !== undefined && (
@@ -152,10 +159,15 @@ const DiffCell = ({ > RVO + {formatter(remote)} @@ -192,7 +204,8 @@ export const columns: ColumnDef>[] = [ Gelijk - Lokaal en RVO perceel komen volledig overeen. + Lokaal en RVO perceel komen volledig + overeen. @@ -251,7 +264,7 @@ export const columns: ColumnDef>[] = [ Perceel - De naam en het bron-ID van het perceel. + De naam en het RVO ID van het perceel. ), @@ -309,6 +322,34 @@ export const columns: ColumnDef>[] = [ ) }, }, + { + id: "gewas", + header: () => ( + + Gewas + + Het gewas dat op 15 mei wordt geteeld. + + + ), + cell: ({ row, table }) => { + const item = row.original + const id = getItemId(item) + // @ts-ignore + const { userChoices } = table.options.meta as any + const action = userChoices[id] as ImportReviewAction + + return ( + val || "-"} + /> + ) + }, + }, { id: "ingangsdatum", header: () => ( @@ -389,18 +430,20 @@ export const columns: ColumnDef>[] = [ const { userChoices } = table.options.meta as any const action = userChoices[id] as ImportReviewAction - // Map RVO code to label + // Map RVO code to label using FDM dictionary const rvoCode = item.rvoField?.properties.UseTitleCode const rvoLabel = rvoCode - ? USE_TITLE_MAP[rvoCode] || rvoCode + ? acquiringMethodOptions.find( + (opt) => opt.value === `nl_${rvoCode}`, + )?.label || rvoCode : undefined // Map local acquiring method to label (simplified) const localMethod = item.localField?.b_acquiring_method // Assuming localMethod is english enum like 'purchase', 'lease'. Map to NL for consistency - const localLabel = acquiringMethodOptions.find( - (opt) => opt.value === localMethod, - )?.label || localMethod + const localLabel = + acquiringMethodOptions.find((opt) => opt.value === localMethod) + ?.label || localMethod return ( - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef - .header, - header.getContext(), - )} - - ) - })} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef + .header, + header.getContext(), + )} + + ) + })} - )) - ) : ( - - - Geen resultaten. - - - )} - -
-
+ ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + + )) + ) : ( + + + Geen resultaten. + + + )} + + +
) } diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index de3f50381..7da73db0f 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -50,7 +50,7 @@ import { getRvoCredentials } from "../integrations/rvo" import { get } from "proj4/dist/lib/projections" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" -import { addSoilAnalysis, getCultivations, type Cultivation } from "@svenvw/fdm-core" +import { addSoilAnalysis, getCultivations, type Cultivation, getCultivationsFromCatalogue } from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" import { getCalendar } from "../lib/calendar" @@ -143,10 +143,18 @@ export async function loader({ request, params }: LoaderFunctionArgs) { return { ...field, cultivations } }), ) + + const cultivationsCatalogue = await getCultivationsFromCatalogue( + fdm, + session.principal_id, + b_id_farm, + ) + rvoImportReviewData = compareFields( localFieldsExtended, rvoFields, year, + cultivationsCatalogue, ) } catch (e: any) { console.error("Error with importing from RVO:", e) @@ -433,6 +441,9 @@ export async function action({ request, params }: ActionFunctionArgs) { const rvoImportReviewDataJson = formData.get("rvoImportReviewDataJson") const userChoicesJson = formData.get("userChoices") + let rvoImportReviewData: RvoImportReviewItem[] = [] + let userChoices: UserChoiceMap = {} + if (!rvoImportReviewDataJson || !userChoicesJson) { return { success: false, @@ -442,12 +453,8 @@ export async function action({ request, params }: ActionFunctionArgs) { } try { - const rvoImportReviewData: RvoImportReviewItem[] = JSON.parse( - String(rvoImportReviewDataJson), - ) - const userChoices: UserChoiceMap = JSON.parse( - String(userChoicesJson), - ) + rvoImportReviewData = JSON.parse(String(rvoImportReviewDataJson)) + userChoices = JSON.parse(String(userChoicesJson)) const onFieldAdded = async (b_id: string, geometry: any) => { const nmiApiKey = getNmiApiKey() diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 99772a768..171e5af82 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -51,11 +51,8 @@ import { } from "~/components/ui/breadcrumb" import { getRvoCredentials } from "../integrations/rvo" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" -import { - getNmiApiKey, - getSoilParameterEstimates, -} from "~/integrations/nmi.server" -import { addSoilAnalysis } from "@svenvw/fdm-core" +import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" +import { addSoilAnalysis, getCultivationsFromCatalogue } from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" import { getCalendar } from "../lib/calendar" @@ -128,16 +125,23 @@ export async function loader({ request, params }: LoaderFunctionArgs) { const rvoFields = await fetchRvoFields( rvoClient, - yearString, // Pass yearString here + yearString, farm.b_businessid_farm, ) + const cultivationsCatalogue = await getCultivationsFromCatalogue( + fdm, + session.principal_id, + b_id_farm, + ) + const localFieldsExtended: (Field & { cultivations: Cultivation[] })[] = [] // No existing fields to compare against yet in create wizard, so localFields is empty RvoImportReviewData = compareFields( localFieldsExtended, rvoFields, year, - ) // Pass year here + cultivationsCatalogue, + ) } catch (e: any) { console.error("RVO Import Fout:", e) error = e.message @@ -409,6 +413,9 @@ export async function action({ request, params }: ActionFunctionArgs) { const RvoImportReviewDataJson = formData.get("RvoImportReviewDataJson") const userChoicesJson = formData.get("userChoices") + let RvoImportReviewData: RvoImportReviewItem[] = [] + let userChoices: UserChoiceMap = {} + if (!RvoImportReviewDataJson || !userChoicesJson) { return { success: false, @@ -418,12 +425,9 @@ export async function action({ request, params }: ActionFunctionArgs) { } try { - const RvoImportReviewData: RvoImportReviewItem[] = JSON.parse( - String(formData.get("RvoImportReviewDataJson")), - ) - const userChoices: UserChoiceMap = JSON.parse( - String(userChoicesJson), - ) + RvoImportReviewData = JSON.parse(String(RvoImportReviewDataJson)) + userChoices = JSON.parse(String(userChoicesJson)) + const onFieldAdded = async (b_id: string, geometry: any) => { const nmiApiKey = getNmiApiKey() @@ -459,10 +463,10 @@ export async function action({ request, params }: ActionFunctionArgs) { b_id_farm, RvoImportReviewData, userChoices, - Number(calendar), + year, onFieldAdded, ) - return redirect(`/farm/create/${b_id_farm}/${calendar}/fields`) + return redirect(`/farm/create/${b_id_farm}/${yearString}/fields`) } catch (e: any) { console.error("Error at saving RVO fields: ", e) return { diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index d7aa9f3b9..27c02d093 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -3,7 +3,7 @@ import intersect from "@turf/intersect" import union from "@turf/union" import area from "@turf/area" import { feature, featureCollection } from "@turf/helpers" -import type { Field, Cultivation } from "@svenvw/fdm-core" +import type { Field, Cultivation, CultivationCatalogue } from "@svenvw/fdm-core" import { type RvoField, RvoImportReviewStatus, @@ -99,12 +99,16 @@ export function compareFields( localFields: (Field & { cultivations?: Cultivation[] })[], rvoFields: RvoField[], calendar = new Date().getFullYear(), + cultivationsCatalogue: CultivationCatalogue[] = [], ): RvoImportReviewItem[] { const results: RvoImportReviewItem[] = [] const matchedRvoIds = new Set() const matchedLocalIds = new Set() - const processMatch = (local: Field & { cultivations?: Cultivation[] }, rvo: RvoField) => { + const processMatch = ( + local: Field & { cultivations?: Cultivation[] }, + rvo: RvoField, + ) => { // Detect property differences const diffs = detectDiffs(local, rvo) @@ -112,21 +116,38 @@ export function compareFields( const localCultivation = local.cultivations ? findActiveCultivation(local.cultivations, calendar) : undefined - const rvoCode = `nl_${rvo.properties.CropTypeCode}` + const rvoCode = rvo.properties.CropTypeCode + ? `nl_${rvo.properties.CropTypeCode}` + : undefined - let rvoCultivationInfo - let localCultivationInfo + let rvoCultivationInfo: + | { b_lu_catalogue: string; b_lu_name: string } + | undefined + let localCultivationInfo: + | { + b_lu_catalogue: string + b_lu: string + b_lu_name: string + } + | undefined if (localCultivation) { localCultivationInfo = { b_lu_catalogue: localCultivation.b_lu_catalogue, b_lu: localCultivation.b_lu, + b_lu_name: localCultivation.b_lu_name, } } if (rvoCode) { + const rvoCatalogueEntry = cultivationsCatalogue.find( + (c) => c.b_lu_catalogue === rvoCode, + ) rvoCultivationInfo = { b_lu_catalogue: rvoCode, + b_lu_name: rvoCatalogueEntry + ? rvoCatalogueEntry.b_lu_name + : rvoCode, } } @@ -206,11 +227,18 @@ export function compareFields( results.push(processMatch(bestMatch, rvo)) } else { // No match found -> This is a NEW field from RVO + const rvoCode = `nl_${rvo.properties.CropTypeCode}` + const rvoCatalogueEntry = cultivationsCatalogue.find( + (c) => c.b_lu_catalogue === rvoCode, + ) results.push({ status: RvoImportReviewStatus.NEW_REMOTE, rvoField: rvo, rvoCultivation: { - b_lu_catalogue: `nl_${rvo.properties.CropTypeCode}`, + b_lu_catalogue: rvoCode, + b_lu_name: rvoCatalogueEntry + ? rvoCatalogueEntry.b_lu_name + : rvoCode, }, diffs: [], }) @@ -232,6 +260,7 @@ export function compareFields( ? { b_lu_catalogue: localCultivation.b_lu_catalogue, b_lu: localCultivation.b_lu, + b_lu_name: localCultivation.b_lu_name, } : undefined, diffs: [], diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index 15a3c945b..13ef4a445 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -109,9 +109,13 @@ export interface RvoImportReviewItem { /** The RVO field object, if it exists (undefined for NEW_LOCAL) */ rvoField?: RvoField /** The local cultivation on May 15th */ - localCultivation?: { b_lu_catalogue: string; b_lu: string } + localCultivation?: { + b_lu_catalogue: string + b_lu: string + b_lu_name?: string + } /** The RVO cultivation based on CropTypeCode */ - rvoCultivation?: { b_lu_catalogue: string } + rvoCultivation?: { b_lu_catalogue: string; b_lu_name?: string } /** List of specific properties that differ (empty for MATCH, NEW_REMOTE, NEW_LOCAL) */ diffs: FieldDiff[] } From 49d701ee4250b33c34baf6c561f8e5131d9af454 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:41:27 +0100 Subject: [PATCH 013/108] feat: add action to close field if local field is not in rvo list --- .../blocks/rvo/import-review-table.tsx | 34 +++++++++++++++++-- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 3 ++ fdm-rvo/src/compare.ts | 29 +++++++++++++++- fdm-rvo/src/process.ts | 19 +++++++++++ fdm-rvo/src/types.ts | 5 +++ 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index 74e65dda6..f083278e4 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -19,7 +19,7 @@ import { TableRow, } from "~/components/ui/table" import { Badge } from "~/components/ui/badge" -import { Check, Plus, Trash2, ArrowLeftRight, X } from "lucide-react" +import { Check, Plus, Trash2, ArrowLeftRight, X, Archive } from "lucide-react" import { Select, SelectContent, @@ -90,7 +90,7 @@ const DiffCell = ({ } // NEW LOCAL -> Show local without badge - if (status === "NEW_LOCAL") { + if (status === "NEW_LOCAL" || status === "EXPIRED_LOCAL") { return ( {formatter(local)} @@ -238,6 +238,21 @@ export const columns: ColumnDef>[] = [ ) + case "EXPIRED_LOCAL": + return ( + + + Verlopen + + Perceel is lokaal actief, maar niet meer in + RVO. + + + + ) case "CONFLICT": return ( >[] = [ )} + {item.status === "EXPIRED_LOCAL" && ( + <> + +
+ {" "} + Afsluiten +
+
+ +
+ Behouden +
+
+ + )} {item.status === "CONFLICT" && ( <> diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 7da73db0f..57c949500 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -223,6 +223,9 @@ export default function RvoImportReviewPage() { case RvoImportReviewStatus.NEW_LOCAL: defaultAction = "REMOVE_LOCAL" break + case RvoImportReviewStatus.EXPIRED_LOCAL: + defaultAction = "CLOSE_LOCAL" + break case RvoImportReviewStatus.CONFLICT: defaultAction = "UPDATE_FROM_REMOTE" break diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 27c02d093..ef05ff196 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -253,8 +253,35 @@ export function compareFields( const localCultivation = local.cultivations ? findActiveCultivation(local.cultivations, calendar) : undefined + + // Check if this field should be considered "expired" (closed) instead of just new local + // Conditions: + // 1. Started before the current import year + // 2. Currently open (no end date) or ends in/after this year (though if it ends in this year, it might be a match? No, if it was unmatched, it means RVO doesn't have it) + // Actually, if it ends *after* the start of this year, it's considered "active" in this year. + // If RVO doesn't have it, we should close it effectively ending it before this year starts. + const localStart = + local.b_start instanceof Date + ? local.b_start + : new Date(local.b_start) + const importYearStart = new Date(calendar, 0, 1) // Jan 1st of import year + const isStartedBeforeYear = localStart < importYearStart + + const localEnd = local.b_end + ? local.b_end instanceof Date + ? local.b_end + : new Date(local.b_end) + : null + // If it has no end date, OR the end date is after the start of the import year + const isOpenOrEndsInYear = !localEnd || localEnd >= importYearStart + + const status = + isStartedBeforeYear && isOpenOrEndsInYear + ? RvoImportReviewStatus.EXPIRED_LOCAL + : RvoImportReviewStatus.NEW_LOCAL + results.push({ - status: RvoImportReviewStatus.NEW_LOCAL, + status, localField: local, localCultivation: localCultivation ? { diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index ba128cfa3..c0c565cea 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -141,6 +141,25 @@ export async function processRvoImport( await removeField(fdm, principal_id, item.localField.b_id) } break + case "CLOSE_LOCAL": + if (item.localField) { + // Close the field on Dec 31st of the previous year + const closeDate = new Date(year - 1, 11, 31) + await updateField( + fdm, + principal_id, + item.localField.b_id, + item.localField.b_name, + item.localField.b_id_source, + item.localField.b_geometry, + item.localField.b_start instanceof Date + ? item.localField.b_start + : new Date(item.localField.b_start), + item.localField.b_acquiring_method, + closeDate, + ) + } + break } } } diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index 13ef4a445..75c82a9da 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -66,6 +66,8 @@ export enum RvoImportReviewStatus { NEW_LOCAL = "NEW_LOCAL", /** The field exists in both systems but has differing properties (e.g., geometry, name). */ CONFLICT = "CONFLICT", + /** The field exists locally, started before the import year, and is missing in RVO for the import year. Suggests closing it. */ + EXPIRED_LOCAL = "EXPIRED_LOCAL", } /** @@ -92,6 +94,8 @@ export enum UserRvoImportReviewDecision { ADD = "ADD", /** Explicitly remove a local field */ REMOVE = "REMOVE", + /** Explicitly close a local field (set end date) */ + CLOSE = "CLOSE", /** Ignore this RVO Import Review item (take no action) */ IGNORE = "IGNORE", } @@ -128,6 +132,7 @@ export type ImportReviewAction = | "UPDATE_FROM_REMOTE" // Update the local field with remote data | "KEEP_LOCAL" // Keep the local version, ignoring the remote conflict | "REMOVE_LOCAL" // Remove the local field + | "CLOSE_LOCAL" // Close the local field (set end date) | "IGNORE" // Do nothing for this item | "NO_ACTION" // No specific action selected From ae7bf72a8641d96263f43b9fbd5d149f24983380 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:57:59 +0100 Subject: [PATCH 014/108] test: add unit tests for fdm-rvo and include it in the gh actions --- .github/workflows/tests.yml | 93 ++++++++ fdm-rvo/src/auth.test.ts | 39 ++++ fdm-rvo/src/compare.test.ts | 421 ++++++++++++++++++++++++++++++++++++ fdm-rvo/src/data.test.ts | 84 +++++++ fdm-rvo/src/process.test.ts | 383 ++++++++++++++++++++++++++++++++ fdm-rvo/src/utils.test.ts | 31 +++ 6 files changed, 1051 insertions(+) create mode 100644 fdm-rvo/src/auth.test.ts create mode 100644 fdm-rvo/src/compare.test.ts create mode 100644 fdm-rvo/src/data.test.ts create mode 100644 fdm-rvo/src/process.test.ts create mode 100644 fdm-rvo/src/utils.test.ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b18e967b0..22437f2cb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -252,3 +252,96 @@ jobs: files: ./fdm-data/coverage/coverage-final.json flags: fdm-data token: ${{ secrets.CODECOV_TOKEN }} + + # Label of the container job for fdm-rvo + test-rvo: + name: rvo + # Containers must run in Linux based operating systems + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [24] + permissions: + contents: read + packages: write + # Docker Hub image that `container-job` executes in + container: node:${{ matrix.node-version }}-bookworm-slim + + # Service containers to run with `container-job` + services: + # Label used to access the service container + postgres: + # Docker Hub image with postgis extension + image: postgis/postgis:17-3.5 + # Provide the password for postgres + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + # Include dependencies for codecov + - name: Install system dependencies + run: apt-get update && apt-get install -y git curl gpg + + # Downloads a copy of the code in your repository before running CI tests + - name: Check out repository code + uses: actions/checkout@v4 + + - name: Cache turbo build setup + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-turbo- + + - name: Setup pnpm 10 + uses: pnpm/action-setup@v4 + with: + version: 10.23.0 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + registry-url: 'https://npm.pkg.github.com' + cache: 'pnpm' + + - name: Install Dependencies + run: pnpm i + + - name: Build fdm-data + run: pnpm build + working-directory: ./fdm-data + + - name: Build fdm-core + run: pnpm build + working-directory: ./fdm-core + + - name: Run tests with coverage + run: pnpm test-coverage + working-directory: ./fdm-rvo + env: + # The hostname used to communicate with the PostgreSQL service container + POSTGRES_HOST: postgres + # The default PostgreSQL port + POSTGRES_PORT: 5432 + # the default usernam + POSTGRES_USER: postgres + # the default password + POSTGRES_PASSWORD: postgres + # the default database + POSTGRES_DB: postgres + + - name: fdm-rvo - Upload results to Codecov + uses: codecov/codecov-action@v5 + with: + files: ./fdm-rvo/coverage/coverage-final.json + flags: fdm-rvo + token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/fdm-rvo/src/auth.test.ts b/fdm-rvo/src/auth.test.ts new file mode 100644 index 000000000..4ae7f9608 --- /dev/null +++ b/fdm-rvo/src/auth.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect, vi } from "vitest" +import { createRvoClient, generateAuthUrl, exchangeToken } from "./auth" +import { RvoClient } from "@nmi-agro/rvo-connector" + +// Mock the external library +vi.mock("@nmi-agro/rvo-connector", () => { + const RvoClient = vi.fn() + RvoClient.prototype.getAuthorizationUrl = vi + .fn() + .mockReturnValue("https://example.com/auth") + RvoClient.prototype.exchangeAuthCode = vi + .fn() + .mockResolvedValue({ accessToken: "fake-token" }) + return { RvoClient } +}) + +describe("auth", () => { + it("createRvoClient should instantiate RvoClient", () => { + const client = createRvoClient("id", "name", "uri", "key") + expect(RvoClient).toHaveBeenCalled() + expect(client).toBeDefined() + }) + + it("generateAuthUrl should call getAuthorizationUrl", () => { + const mockClient = new RvoClient({} as any) + const url = generateAuthUrl(mockClient, "state123") + expect(mockClient.getAuthorizationUrl).toHaveBeenCalledWith({ + state: "state123", + }) + expect(url).toBe("https://example.com/auth") + }) + + it("exchangeToken should return access token", async () => { + const mockClient = new RvoClient({} as any) + const token = await exchangeToken(mockClient, "code123") + expect(mockClient.exchangeAuthCode).toHaveBeenCalledWith("code123") + expect(token).toBe("fake-token") + }) +}) diff --git a/fdm-rvo/src/compare.test.ts b/fdm-rvo/src/compare.test.ts new file mode 100644 index 000000000..c24951449 --- /dev/null +++ b/fdm-rvo/src/compare.test.ts @@ -0,0 +1,421 @@ +import { describe, it, expect } from "vitest" +import { compareFields } from "./compare" +import { RvoImportReviewStatus, type RvoField } from "./types" + +describe("compareFields", () => { + const calendar = 2025 + + // Helper to create a basic local field + const createLocalField = (overrides: Partial = {}): any => ({ + b_id: "local-1", + b_id_source: "rvo-1", + b_name: "Field 1", + b_geometry: { + type: "Polygon", + coordinates: [ + [ + [0, 0], + [0, 10], + [10, 10], + [10, 0], + [0, 0], + ], + ], + }, + b_start: new Date("2024-01-01"), + b_end: undefined, + cultivations: [], + ...overrides, + }) + + // Helper to create a basic RVO field + const createRvoField = (overrides: any = {}): RvoField => { + const { geometry, ...props } = overrides + return { + type: "Feature", + geometry: geometry || { + type: "Polygon", + coordinates: [ + [ + [0, 0], + [0, 10], + [10, 10], + [10, 0], + [0, 0], + ], + ], + }, + properties: { + CropFieldID: "rvo-1", + CropFieldVersion: "1", + CropFieldDesignator: "Field 1", + BeginDate: "2024-01-01", + EndDate: undefined, + Country: "NL", + CropTypeCode: "101", + UseTitleCode: "01", + ...props, + }, + } + } + + describe("Tier 1: ID Match", () => { + it("should MATCH fields with same ID and identical properties", () => { + const local = createLocalField() + const rvo = createRvoField() + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.MATCH) + expect(result[0].diffs).toHaveLength(0) + expect(result[0].localField).toBe(local) + expect(result[0].rvoField).toBe(rvo) + }) + + it("should detect CONFLICT when name differs", () => { + const local = createLocalField({ b_name: "Old Name" }) + const rvo = createRvoField({ CropFieldDesignator: "New Name" }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) + expect(result[0].diffs).toContain("b_name") + }) + + it("should detect CONFLICT when start date differs", () => { + const local = createLocalField({ b_start: new Date("2023-01-01") }) + const rvo = createRvoField({ BeginDate: "2024-01-01" }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) + expect(result[0].diffs).toContain("b_start") + }) + + it("should detect CONFLICT when end date differs", () => { + const local = createLocalField({ b_end: undefined }) + const rvo = createRvoField({ EndDate: "2025-12-31" }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) + expect(result[0].diffs).toContain("b_end") + }) + + it("should detect CONFLICT when geometry differs significantly", () => { + const local = createLocalField() + const rvo = createRvoField({ + geometry: { + type: "Polygon", + coordinates: [ + [ + [0, 0], + [0, 5], + [5, 5], + [5, 0], + [0, 0], + ], + ], // Quarter the size + }, + }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) + expect(result[0].diffs).toContain("b_geometry") + }) + + it("should detect CONFLICT when cultivation differs", () => { + const local = createLocalField({ + cultivations: [ + { + b_lu_catalogue: "nl_101", + b_lu: "cult-1", + b_lu_name: "Grass", + b_lu_start: new Date(`${calendar}-01-01`), + b_lu_end: new Date(`${calendar}-12-31`), + }, + ], + }) + const rvo = createRvoField({ + CropTypeCode: "202", // Different crop code + }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) + expect(result[0].diffs).toContain("b_lu_catalogue") + }) + }) + + describe("Tier 2: Spatial Match", () => { + it("should MATCH fields with different IDs but high spatial overlap (IoU > 0.99)", () => { + const local = createLocalField({ b_id_source: "old-id" }) + const rvo = createRvoField({ CropFieldID: "new-id" }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.MATCH) + expect(result[0].localField).toBe(local) + expect(result[0].rvoField).toBe(rvo) + }) + + it("should NOT match fields with low spatial overlap", () => { + const local = createLocalField({ b_id_source: "local-only" }) + // Shifted geometry, no overlap + const rvo = createRvoField({ + CropFieldID: "remote-only", + geometry: { + type: "Polygon", + coordinates: [ + [ + [100, 100], + [100, 110], + [110, 110], + [110, 100], + [100, 100], + ], + ], + }, + }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(2) + const expired = result.find( + (r) => r.status === RvoImportReviewStatus.EXPIRED_LOCAL, + ) + const newRemote = result.find( + (r) => r.status === RvoImportReviewStatus.NEW_REMOTE, + ) + + expect(expired).toBeDefined() + expect(newRemote).toBeDefined() + }) + }) + + describe("Orphaned Fields (Status determination)", () => { + it("should identify a field as NEW_LOCAL if it started in the import year", () => { + const local = createLocalField({ + b_start: new Date("2025-01-01"), + b_id_source: "local-only", + }) + const result = compareFields([local], [], calendar) + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.NEW_LOCAL) + }) + + it("should identify a field as EXPIRED_LOCAL if it started before import year and has no end date", () => { + const local = createLocalField({ + b_start: new Date("2024-01-01"), + b_id_source: "local-only", + }) + const result = compareFields([local], [], calendar) + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.EXPIRED_LOCAL) + }) + + it("should identify a field as EXPIRED_LOCAL if it started before import year and ends IN the import year", () => { + const local = createLocalField({ + b_start: new Date("2024-01-01"), + b_end: new Date("2025-06-01"), + b_id_source: "local-only", + }) + const result = compareFields([local], [], calendar) + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.EXPIRED_LOCAL) + }) + + it("should identify a field as NEW_LOCAL if it ends BEFORE the import year", () => { + const local = createLocalField({ + b_start: new Date("2024-01-01"), + b_end: new Date("2024-12-31"), + b_id_source: "local-only", + }) + const result = compareFields([local], [], calendar) + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.NEW_LOCAL) + }) + }) + + describe("New Remote Fields", () => { + it("should identify NEW_REMOTE fields", () => { + const rvo = createRvoField({ CropFieldID: "new-remote" }) + const result = compareFields([], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.NEW_REMOTE) + expect(result[0].rvoField).toBe(rvo) + }) + }) +}) + +describe("compareFields Edge Cases", () => { + const calendar = 2025 + + const createLocalField = (overrides: Partial = {}): any => ({ + b_id: "local-1", + b_id_source: "rvo-1", + b_name: "Field 1", + b_geometry: { + type: "Polygon", + coordinates: [ + [ + [0, 0], + [0, 10], + [10, 10], + [10, 0], + [0, 0], + ], + ], + }, + b_start: new Date("2024-01-01"), + b_end: undefined, + cultivations: [], + ...overrides, + }) + + const createRvoField = (overrides: any = {}): RvoField => { + const { geometry, ...props } = overrides + return { + type: "Feature", + geometry: geometry || { + type: "Polygon", + coordinates: [ + [ + [0, 0], + [0, 10], + [10, 10], + [10, 0], + [0, 0], + ], + ], + }, + properties: { + CropFieldID: "rvo-1", + CropFieldVersion: "1", + CropFieldDesignator: "Field 1", + BeginDate: "2024-01-01", + Country: "NL", + CropTypeCode: "101", + UseTitleCode: "01", + ...props, + }, + } + } + + it("should handle IoU calculation when polygons touch (area 0)", () => { + // Two squares touching at x=10. + // S1: 0,0 to 10,10 + // S2: 10,0 to 20,10 + // BBoxes: 0,0,10,10 and 10,0,20,10. Overlap at x=10 line. + + const local = createLocalField({ + b_geometry: { + type: "Polygon", + coordinates: [ + [ + [0, 0], + [0, 10], + [10, 10], + [10, 0], + [0, 0], + ], + ], + }, + }) + const rvo = createRvoField({ + geometry: { + type: "Polygon", + coordinates: [ + [ + [10, 0], + [10, 10], + [20, 10], + [20, 0], + [10, 0], + ], + ], + }, + }) + local.b_id_source = "id1" + rvo.properties.CropFieldID = "id2" + + const result = compareFields([local], [rvo], calendar) + // IoU should be 0 (intersection area is 0) + expect(result).toHaveLength(2) + }) + + it("should use cultivation catalogue name when code exists", () => { + const local = createLocalField() + const rvo = createRvoField({ CropTypeCode: "101" }) + + const catalogue = [ + { + b_lu_catalogue: "nl_101", + b_lu_name: "Official Grass Name", + }, + ] as any + + const result = compareFields([local], [rvo], calendar, catalogue) + + expect(result).toHaveLength(1) + // Should match (if everything else matches) + // Check rvoCultivationInfo name + expect(result[0].rvoCultivation?.b_lu_name).toBe("Official Grass Name") + }) + + it("should use cultivation catalogue name for NEW_REMOTE when code exists", () => { + const rvo = createRvoField({ CropFieldID: "new", CropTypeCode: "101" }) + const catalogue = [ + { + b_lu_catalogue: "nl_101", + b_lu_name: "Official Grass Name", + }, + ] as any + + const result = compareFields([], [rvo], calendar, catalogue) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.NEW_REMOTE) + expect(result[0].rvoCultivation?.b_lu_name).toBe("Official Grass Name") + }) + + it("should handle missing RVO CropTypeCode", () => { + const local = createLocalField() + const rvo = createRvoField({ CropTypeCode: "" }) // Empty string -> falsy + + const result = compareFields([local], [rvo], calendar) + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.MATCH) + // Check if logic handles undefined rvoCultivationInfo + expect(result[0].rvoCultivation).toBeUndefined() + }) + + it("should handle RVO cultivation lookup failure (unknown code)", () => { + const local = createLocalField() + const rvo = createRvoField({ CropTypeCode: "999" }) // Unknown code + + const result = compareFields([local], [rvo], calendar, []) // Empty catalogue + + expect(result).toHaveLength(1) + // rvoCultivationInfo should use code as name + expect(result[0].rvoCultivation?.b_lu_name).toBe("nl_999") + }) + + it("should handle NEW_REMOTE with unknown crop code", () => { + const rvo = createRvoField({ CropFieldID: "new", CropTypeCode: "999" }) + const result = compareFields([], [rvo], calendar, []) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.NEW_REMOTE) + expect(result[0].rvoCultivation?.b_lu_name).toBe("nl_999") + }) +}) diff --git a/fdm-rvo/src/data.test.ts b/fdm-rvo/src/data.test.ts new file mode 100644 index 000000000..b21392d77 --- /dev/null +++ b/fdm-rvo/src/data.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect, vi } from "vitest" +import { fetchRvoFields } from "./data" +import type { RvoClient } from "@nmi-agro/rvo-connector" + +describe("fetchRvoFields", () => { + it("should fetch and validate fields successfully", async () => { + const mockFeatures = [ + { + type: "Feature", + geometry: { type: "Polygon", coordinates: [] }, + properties: { + CropFieldID: "123", + CropFieldVersion: "1", + CropFieldDesignator: "Field A", + BeginDate: "2024-01-01", + Country: "NL", + CropTypeCode: "101", + UseTitleCode: "01", + }, + }, + ] + + const mockClient = { + opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ + features: mockFeatures, + }), + } as unknown as RvoClient + + const result = await fetchRvoFields(mockClient, "2024", "12345678") + + expect(mockClient.opvragenBedrijfspercelen).toHaveBeenCalledWith({ + periodBeginDate: "2024-01-01", + periodEndDate: "2024-12-31", + farmId: "12345678", + outputFormat: "geojson", + }) + expect(result).toHaveLength(1) + expect(result[0].properties.CropFieldID).toBe("123") + }) + + it("should return empty array if no features found", async () => { + const mockClient = { + opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ + features: [], + }), + } as unknown as RvoClient + + const result = await fetchRvoFields(mockClient, "2024", "12345678") + expect(result).toEqual([]) + }) + + it("should return empty array if response is malformed (no features array)", async () => { + const mockClient = { + opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ + somethingElse: [], + }), + } as unknown as RvoClient + + const result = await fetchRvoFields(mockClient, "2024", "12345678") + expect(result).toEqual([]) + }) + + it("should throw error if validation fails", async () => { + const mockFeatures = [ + { + type: "Feature", + // Missing properties + properties: { + CropFieldID: "123", + }, + }, + ] + + const mockClient = { + opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ + features: mockFeatures, + }), + } as unknown as RvoClient + + await expect( + fetchRvoFields(mockClient, "2024", "12345678"), + ).rejects.toThrow() + }) +}) diff --git a/fdm-rvo/src/process.test.ts b/fdm-rvo/src/process.test.ts new file mode 100644 index 000000000..533e67d7e --- /dev/null +++ b/fdm-rvo/src/process.test.ts @@ -0,0 +1,383 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" +import { processRvoImport } from "./process" +import { + addField, + updateField, + removeField, + addCultivation, + removeCultivation, + getDefaultDatesOfCultivation, +} from "@svenvw/fdm-core" +import { RvoImportReviewStatus, type RvoImportReviewItem } from "./types" + +// Mock fdm-core +vi.mock("@svenvw/fdm-core", () => ({ + addField: vi.fn(), + updateField: vi.fn(), + removeField: vi.fn(), + addCultivation: vi.fn(), + removeCultivation: vi.fn(), + getDefaultDatesOfCultivation: vi.fn(), +})) + +describe("processRvoImport", () => { + const mockFdm = {} as any + const principalId = "user-1" + const farmId = "farm-1" + const year = 2025 + + beforeEach(() => { + vi.clearAllMocks() + // Default mocks + ;(getDefaultDatesOfCultivation as any).mockResolvedValue({ + b_lu_start: new Date(`${year}-01-01`), + b_lu_end: new Date(`${year}-12-31`), + }) + ;(addField as any).mockResolvedValue("new-field-id") + }) + + it("should process ADD_REMOTE action", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.NEW_REMOTE, + rvoField: { + type: "Feature", + geometry: { type: "Polygon", coordinates: [] }, + properties: { + CropFieldID: "rvo-1", + CropFieldDesignator: "New Field", + BeginDate: "2025-01-01", + UseTitleCode: "01", + CropTypeCode: "101", + CropFieldVersion: "1", + Country: "NL", + }, + }, + diffs: [], + } + const choices = { "rvo-1": "ADD_REMOTE" as const } + const onFieldAdded = vi.fn() + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + onFieldAdded, + ) + + expect(addField).toHaveBeenCalledWith( + mockFdm, + principalId, + farmId, + "New Field", + "rvo-1", + expect.anything(), // geometry + expect.any(Date), // start + "nl_01", + undefined, // end + ) + expect(addCultivation).toHaveBeenCalled() + expect(onFieldAdded).toHaveBeenCalledWith( + "new-field-id", + expect.anything(), + ) + }) + + it("should process UPDATE_FROM_REMOTE action", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.CONFLICT, + localField: { + b_id: "local-1", + b_name: "Old Name", + }, + rvoField: { + type: "Feature", + geometry: { type: "Polygon", coordinates: [] }, + properties: { + CropFieldID: "rvo-1", + CropFieldDesignator: "New Name", + BeginDate: "2025-01-01", + UseTitleCode: "01", + CropTypeCode: "101", + CropFieldVersion: "1", + Country: "NL", + }, + }, + diffs: ["b_name"], + } + const choices = { "local-1": "UPDATE_FROM_REMOTE" as const } + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + ) + + expect(updateField).toHaveBeenCalledWith( + mockFdm, + principalId, + "local-1", + "New Name", + "rvo-1", + expect.anything(), + expect.any(Date), + "nl_01", + undefined, + ) + // No cultivation change implies no cultivation update call unless localCultivation differs + expect(removeCultivation).not.toHaveBeenCalled() + expect(addCultivation).not.toHaveBeenCalled() + }) + + it("should process UPDATE_FROM_REMOTE action with cultivation change", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.CONFLICT, + localField: { b_id: "local-1" }, + localCultivation: { + b_lu_catalogue: "nl_202", // Different from rvo 101 + b_lu: "cult-1", + }, + rvoField: { + type: "Feature", + geometry: { type: "Polygon", coordinates: [] }, + properties: { + CropFieldID: "rvo-1", + CropFieldDesignator: "Name", + BeginDate: "2025-01-01", + UseTitleCode: "01", + CropTypeCode: "101", + CropFieldVersion: "1", + Country: "NL", + }, + }, + diffs: ["b_lu_catalogue"], + } + const choices = { "local-1": "UPDATE_FROM_REMOTE" as const } + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + ) + + expect(updateField).toHaveBeenCalled() + expect(removeCultivation).toHaveBeenCalledWith( + mockFdm, + principalId, + "cult-1", + ) + expect(addCultivation).toHaveBeenCalledWith( + mockFdm, + principalId, + "nl_101", + "local-1", + expect.any(Date), + expect.any(Date), + ) + }) + + it("should process REMOVE_LOCAL action", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.NEW_LOCAL, + localField: { b_id: "local-1" }, + diffs: [], + } + const choices = { "local-1": "REMOVE_LOCAL" as const } + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + ) + + expect(removeField).toHaveBeenCalledWith( + mockFdm, + principalId, + "local-1", + ) + }) + + it("should process CLOSE_LOCAL action", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.EXPIRED_LOCAL, + localField: { + b_id: "local-1", + b_name: "Field 1", + b_id_source: "rvo-1", + b_geometry: {}, + b_start: new Date("2024-01-01"), + b_acquiring_method: "purchase", + }, + diffs: [], + } + const choices = { "local-1": "CLOSE_LOCAL" as const } + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + ) + + // Should update field with end date = Dec 31st of previous year (2024) + const expectedCloseDate = new Date(year - 1, 11, 31) + expect(updateField).toHaveBeenCalledWith( + mockFdm, + principalId, + "local-1", + "Field 1", + "rvo-1", + {}, + item.localField?.b_start, + "purchase", + expectedCloseDate, + ) + }) + + it("should process CLOSE_LOCAL action even if b_start is a string", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.EXPIRED_LOCAL, + localField: { + b_id: "local-1", + b_name: "Field 1", + b_id_source: "rvo-1", + b_geometry: {}, + b_start: "2024-01-01", // String date + b_acquiring_method: "purchase", + }, + diffs: [], + } + const choices = { "local-1": "CLOSE_LOCAL" as const } + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + ) + + expect(updateField).toHaveBeenCalledWith( + mockFdm, + principalId, + "local-1", + "Field 1", + "rvo-1", + {}, + expect.any(Date), // Should convert to Date + "purchase", + expect.any(Date), + ) + }) + + it("should process KEEP_LOCAL action (do nothing)", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.CONFLICT, + localField: { b_id: "local-1" }, + diffs: ["b_name"], + } + const choices = { "local-1": "KEEP_LOCAL" as const } + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + ) + + expect(addField).not.toHaveBeenCalled() + expect(updateField).not.toHaveBeenCalled() + expect(removeField).not.toHaveBeenCalled() + }) + + it("should skip items with no action selected", async () => { + const item: RvoImportReviewItem = { + status: RvoImportReviewStatus.NEW_REMOTE, + rvoField: { properties: { CropFieldID: "rvo-1" } } as any, + diffs: [], + } + // No choice provided for rvo-1 + const choices = {} + + await processRvoImport( + mockFdm, + principalId, + farmId, + [item], + choices, + year, + ) + + expect(addField).not.toHaveBeenCalled() + expect(updateField).not.toHaveBeenCalled() + expect(removeField).not.toHaveBeenCalled() + }) + + it("should ignore actions when required fields are missing", async () => { + // ADD_REMOTE without rvoField + await processRvoImport( + mockFdm, + principalId, + farmId, + [{ status: RvoImportReviewStatus.NEW_REMOTE, diffs: [] }], + { unknown: "ADD_REMOTE" }, + year, + ) + expect(addField).not.toHaveBeenCalled() + + // UPDATE_FROM_REMOTE without rvoField + await processRvoImport( + mockFdm, + principalId, + farmId, + [ + { + status: RvoImportReviewStatus.CONFLICT, + localField: { b_id: "l1" }, + diffs: [], + }, + ], + { l1: "UPDATE_FROM_REMOTE" }, + year, + ) + expect(updateField).not.toHaveBeenCalled() + + // REMOVE_LOCAL without localField + await processRvoImport( + mockFdm, + principalId, + farmId, + [{ status: RvoImportReviewStatus.NEW_LOCAL, diffs: [] }], + { unknown: "REMOVE_LOCAL" }, + year, + ) + expect(removeField).not.toHaveBeenCalled() + + // CLOSE_LOCAL without localField + await processRvoImport( + mockFdm, + principalId, + farmId, + [{ status: RvoImportReviewStatus.EXPIRED_LOCAL, diffs: [] }], + { unknown: "CLOSE_LOCAL" }, + year, + ) + expect(updateField).not.toHaveBeenCalled() + }) +}) diff --git a/fdm-rvo/src/utils.test.ts b/fdm-rvo/src/utils.test.ts new file mode 100644 index 000000000..a262646ee --- /dev/null +++ b/fdm-rvo/src/utils.test.ts @@ -0,0 +1,31 @@ +import { describe, it, expect } from "vitest" +import { getItemId } from "./utils" +import { RvoImportReviewStatus } from "./types" + +describe("getItemId", () => { + it("should return local ID if present", () => { + const item = { + status: RvoImportReviewStatus.MATCH, + localField: { b_id: "local-1" }, + diffs: [], + } as any + expect(getItemId(item)).toBe("local-1") + }) + + it("should return RVO ID if local not present", () => { + const item = { + status: RvoImportReviewStatus.NEW_REMOTE, + rvoField: { properties: { CropFieldID: "rvo-1" } }, + diffs: [], + } as any + expect(getItemId(item)).toBe("rvo-1") + }) + + it("should return 'unknown' if neither present", () => { + const item = { + status: RvoImportReviewStatus.NEW_REMOTE, + diffs: [], + } as any + expect(getItemId(item)).toBe("unknown") + }) +}) From 85b4b9a455dcaff7da52ed150d7e900543b97502 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:58:55 +0100 Subject: [PATCH 015/108] fix: improve texts at rvo import --- .../blocks/rvo/import-review-table.tsx | 38 +++++++++++-------- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 17 ++++----- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 35 ++++++++--------- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index f083278e4..a622c376c 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -4,10 +4,10 @@ import { getCoreRowModel, useReactTable, } from "@tanstack/react-table" -import { - type RvoImportReviewItem, - type ImportReviewAction, - type UserChoiceMap, +import type { + RvoImportReviewItem, + ImportReviewAction, + UserChoiceMap, } from "@svenvw/fdm-rvo/types" import { getItemId } from "@svenvw/fdm-rvo/utils" import { @@ -127,7 +127,7 @@ const DiffCell = ({ variant="outline" className="h-5 px-1 text-[10px] text-muted-foreground border-border min-w-[45px] justify-center" > - Lokaal + {clientConfig.name}
>[] = [ Status - Geeft de vergelijkingsstatus weer tussen lokaal en RVO. + Geeft de vergelijkingsstatus weer tussen {clientConfig.name}{" "} + en RVO. ), @@ -204,8 +205,8 @@ export const columns: ColumnDef>[] = [ Gelijk - Lokaal en RVO perceel komen volledig - overeen. + {clientConfig.name} en RVO perceel komen + volledig overeen. @@ -219,7 +220,8 @@ export const columns: ColumnDef>[] = [ Nieuw (RVO) - Perceel bestaat in RVO, maar niet lokaal. + Perceel bestaat bij RVO, maar niet in{" "} + {clientConfig.name}. @@ -231,9 +233,12 @@ export const columns: ColumnDef>[] = [ className="bg-yellow-50 text-yellow-700 border-yellow-200" > - Nieuw (Lokaal) + + Nieuw ( {clientConfig.name}) + - Perceel bestaat lokaal, maar niet in RVO. + Perceel bestaat in {clientConfig.name}, maar + niet bij RVO. @@ -247,8 +252,8 @@ export const columns: ColumnDef>[] = [ Verlopen - Perceel is lokaal actief, maar niet meer in - RVO. + Perceel is in {clientConfig.name} actief, + maar niet meer bij RVO. @@ -260,7 +265,7 @@ export const columns: ColumnDef>[] = [ className="bg-red-50 text-red-700 border-red-200" > - Conflict + Verschil Perceel bestaat in beide, maar met verschillende gegevens. @@ -492,7 +497,7 @@ export const columns: ColumnDef>[] = [ return (
- Geimporteerd + Aanwezig
) } @@ -563,7 +568,7 @@ export const columns: ColumnDef>[] = [
Gebruik - Lokaal + {clientConfig.name}
@@ -576,6 +581,7 @@ export const columns: ColumnDef>[] = [ ] import { useMemo } from "react" +import { clientConfig } from "@/app/lib/config" export function RvoImportReviewTable({ data, diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 57c949500..c4267dad9 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -29,7 +29,6 @@ import { } from "@svenvw/fdm-rvo/types" import { getItemId } from "@svenvw/fdm-rvo/utils" import { processRvoImport } from "@svenvw/fdm-rvo" -import { serverConfig } from "~/lib/config.server" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" import { getFields, getFarm, getFarms } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" @@ -44,15 +43,13 @@ import { SidebarInset } from "~/components/ui/sidebar" import { BreadcrumbItem, BreadcrumbSeparator, - BreadcrumbLink, } from "~/components/ui/breadcrumb" import { getRvoCredentials } from "../integrations/rvo" -import { get } from "proj4/dist/lib/projections" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" -import { addSoilAnalysis, getCultivations, type Cultivation, getCultivationsFromCatalogue } from "@svenvw/fdm-core" +import { addSoilAnalysis, getCultivations, getCultivationsFromCatalogue } from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" -import { getCalendar } from "../lib/calendar" +import { clientConfig } from "../lib/config" export const meta: MetaFunction = ({ params }) => { return [{ title: `RVO Koppeling - Bedrijf ${params.b_id_farm}` }] @@ -264,7 +261,7 @@ export default function RvoImportReviewPage() {
@@ -325,7 +322,7 @@ export default function RvoImportReviewPage() { > - RVO import is niet beschikbaar + Perceleh ophalen bij RVO is niet beschikbaar De RVO koppeling is nog niet ingesteld op deze @@ -352,7 +349,7 @@ export default function RvoImportReviewPage() {
) : ( - "Wijzigingen Toepassen" + "Wijzigingen toepassen" )}
@@ -451,7 +448,7 @@ export async function action({ request, params }: ActionFunctionArgs) { return { success: false, message: - "Geen data gevonden om te verwerken. Start de RVO importhronisatie opnieuw.", + "Geen data gevonden om te verwerken. Start 'percelehn ophalen bij RVO' opnieuw.", } } diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 171e5af82..8a042cbe5 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -3,13 +3,10 @@ import { Form, type LoaderFunctionArgs, type MetaFunction, - NavLink, redirect, useActionData, useLoaderData, useNavigation, - useParams, - useNavigate, useLocation, } from "react-router" import { getSession } from "~/lib/auth.server" @@ -21,15 +18,15 @@ import { createRvoClient, exchangeToken, } from "~/lib/rvo.server" -import { - type RvoImportReviewItem, - type ImportReviewAction, - type UserChoiceMap, +import type { + RvoImportReviewItem, + ImportReviewAction, + UserChoiceMap, } from "@svenvw/fdm-rvo/types" import { getItemId } from "@svenvw/fdm-rvo/utils" import { processRvoImport } from "@svenvw/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" -import { getFarm } from "@svenvw/fdm-core" +import { Cultivation, Field, getFarm } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" import { AlertTriangle, Loader2 } from "lucide-react" @@ -44,17 +41,14 @@ import { BreadcrumbSeparator, BreadcrumbLink, } from "~/components/ui/breadcrumb" -import { - BreadcrumbItem, - BreadcrumbSeparator, - BreadcrumbLink, -} from "~/components/ui/breadcrumb" import { getRvoCredentials } from "../integrations/rvo" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" +import { + getNmiApiKey, + getSoilParameterEstimates, +} from "~/integrations/nmi.server" import { addSoilAnalysis, getCultivationsFromCatalogue } from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" -import { getCalendar } from "../lib/calendar" export const meta: MetaFunction = ({ params }) => { const b_id_farm = params.b_id_farm @@ -135,7 +129,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) { b_id_farm, ) - const localFieldsExtended: (Field & { cultivations: Cultivation[] })[] = [] // No existing fields to compare against yet in create wizard, so localFields is empty + const localFieldsExtended: (Field & { + cultivations: Cultivation[] + })[] = [] // No existing fields to compare against yet in create wizard, so localFields is empty RvoImportReviewData = compareFields( localFieldsExtended, rvoFields, @@ -235,8 +231,8 @@ export default function RvoImportCreatePage() {
@@ -289,7 +285,7 @@ export default function RvoImportCreatePage() { > - Importen vanuit RVO is niet beschikbaar + Percelen ophalen bij RVO is niet beschikbaar De RVO koppeling is nog niet ingesteld op deze @@ -428,7 +424,6 @@ export async function action({ request, params }: ActionFunctionArgs) { RvoImportReviewData = JSON.parse(String(RvoImportReviewDataJson)) userChoices = JSON.parse(String(userChoicesJson)) - const onFieldAdded = async (b_id: string, geometry: any) => { const nmiApiKey = getNmiApiKey() if (nmiApiKey) { From faab0b8057f8d072c7af36b779b1dc19461bc69d Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Mon, 15 Dec 2025 16:55:16 +0100 Subject: [PATCH 016/108] feat: add dialog to provide summary and confirm changes --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 143 ++++++++++++++---- 1 file changed, 111 insertions(+), 32 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index c4267dad9..254c6909e 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -33,6 +33,16 @@ import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-tabl import { getFields, getFarm, getFarms } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogClose, +} from "~/components/ui/dialog" import { AlertTriangle, Loader2 } from "lucide-react" import { useEffect, useState } from "react" import { FarmContent } from "~/components/blocks/farm/farm-content" @@ -242,6 +252,18 @@ export default function RvoImportReviewPage() { const currentFarmName = farms.find((farm) => farm.b_id_farm === b_id_farm)?.b_name_farm ?? "" + const changes = Object.values(userChoices).reduce( + (acc, action) => { + if (action === "ADD_REMOTE") acc.add++ + if (action === "REMOVE_LOCAL") acc.remove++ + if (action === "UPDATE_FROM_REMOTE") acc.update++ + if (action === "CLOSE_LOCAL") acc.close++ + return acc + }, + { add: 0, remove: 0, update: 0, close: 0 }, + ) + const hasChanges = Object.values(changes).some((count) => count > 0) + if (error) { return ( @@ -352,38 +374,95 @@ export default function RvoImportReviewPage() { description={`Beoordeel de verschillen tussen de percelen in ${clientConfig.name} en bij RVO.`} />
-
- - - - -
+ + + + + + + Wijzigingen toepassen + + U staat op het punt de volgende wijzigingen door te voeren: + + +
+
    + {changes.add > 0 && ( +
  • + {changes.add}{" "} + perceel + {changes.add !== 1 && "en"}{" "} + toevoegen +
  • + )} + {changes.remove > 0 && ( +
  • + {changes.remove}{" "} + perceel + {changes.remove !== 1 && "en"}{" "} + verwijderen +
  • + )} + {changes.update > 0 && ( +
  • + {changes.update}{" "} + perceel + {changes.update !== 1 && "en"}{" "} + bijwerken +
  • + )} + {changes.close > 0 && ( +
  • + {changes.close}{" "} + perceel + {changes.close !== 1 && "en"}{" "} + afsluiten +
  • + )} +
+ {!hasChanges &&

Geen wijzigingen geselecteerd.

} +
+ + + + +
+ + + + +
+
+
+
From 7e436408b9e103b46133179ee4f120c43daff8f0 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Mon, 15 Dec 2025 17:03:22 +0100 Subject: [PATCH 017/108] fix: ignore fields that have ended before requested --- fdm-rvo/src/compare.test.ts | 5 ++--- fdm-rvo/src/compare.ts | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/fdm-rvo/src/compare.test.ts b/fdm-rvo/src/compare.test.ts index c24951449..4cd57254c 100644 --- a/fdm-rvo/src/compare.test.ts +++ b/fdm-rvo/src/compare.test.ts @@ -233,15 +233,14 @@ describe("compareFields", () => { expect(result[0].status).toBe(RvoImportReviewStatus.EXPIRED_LOCAL) }) - it("should identify a field as NEW_LOCAL if it ends BEFORE the import year", () => { + it("should IGNORE a field if it ends BEFORE the import year", () => { const local = createLocalField({ b_start: new Date("2024-01-01"), b_end: new Date("2024-12-31"), b_id_source: "local-only", }) const result = compareFields([local], [], calendar) - expect(result).toHaveLength(1) - expect(result[0].status).toBe(RvoImportReviewStatus.NEW_LOCAL) + expect(result).toHaveLength(0) }) }) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index ef05ff196..e0206bf61 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -275,6 +275,12 @@ export function compareFields( // If it has no end date, OR the end date is after the start of the import year const isOpenOrEndsInYear = !localEnd || localEnd >= importYearStart + // If the field ended before the import year, it's a historical field that is already closed. + // We should ignore it. + if (!isOpenOrEndsInYear) { + continue + } + const status = isStartedBeforeYear && isOpenOrEndsInYear ? RvoImportReviewStatus.EXPIRED_LOCAL From c9027a072a279ab7ecc01c5be2a6897dc9bf5274 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Mon, 15 Dec 2025 17:03:37 +0100 Subject: [PATCH 018/108] fix: improve text for gebruikstitel --- fdm-app/app/components/blocks/rvo/import-review-table.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index a622c376c..cc9102b2a 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -434,12 +434,12 @@ export const columns: ColumnDef>[] = [ }, }, { - id: "recht", + id: "gebruikstitel", header: () => ( - Recht + Gebruikstitel - De vorm van gebruiksrecht (bv. eigendom, pacht). + De vorm van gebruikstitel (bv. eigendom, pacht). ), From d4b221b94e1a6f822eaec4a96c9735599c80b0bb Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Mon, 15 Dec 2025 17:05:55 +0100 Subject: [PATCH 019/108] fix: show continue button in case of no changes --- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 254c6909e..ef70bcaca 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -370,20 +370,22 @@ export default function RvoImportReviewPage() { <>
- From e7cd91bdf33598658a672a22688e9eb20c87eb38 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Mon, 15 Dec 2025 17:09:37 +0100 Subject: [PATCH 020/108] fix --- fdm-app/app/components/blocks/fields/columns.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fdm-app/app/components/blocks/fields/columns.tsx b/fdm-app/app/components/blocks/fields/columns.tsx index 114cf067c..fb79a2a7d 100644 --- a/fdm-app/app/components/blocks/fields/columns.tsx +++ b/fdm-app/app/components/blocks/fields/columns.tsx @@ -179,7 +179,9 @@ export const columns: ColumnDef[] = [ const field = row.original return (

- {`${field.a_som_loi.toFixed(2)} %`} + {field.a_som_loi !== null && field.a_som_loi !== undefined + ? `${field.a_som_loi.toFixed(2)} %` + : "-"}

) }, From dcee237f15c21653951aa3d329b2701216ccc058 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:20:08 +0100 Subject: [PATCH 021/108] fix: improve text for not active fields --- .../app/components/blocks/rvo/import-review-table.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index cc9102b2a..e93e6028a 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -205,8 +205,7 @@ export const columns: ColumnDef>[] = [ Gelijk - {clientConfig.name} en RVO perceel komen - volledig overeen. + Perceel komt in {clientConfig.name} en bij RVO volledig overeen. @@ -250,10 +249,10 @@ export const columns: ColumnDef>[] = [ className="bg-orange-50 text-orange-700 border-orange-200" > - Verlopen + Niet meer actief - Perceel is in {clientConfig.name} actief, - maar niet meer bij RVO. + Perceel is in {clientConfig.name} nog + actief, maar komt niet meer voor bij RVO. From 3e9b91567346182c16f1e031d828417958977d98 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Wed, 17 Dec 2025 15:29:44 +0100 Subject: [PATCH 022/108] refactor: improve displaying kvk number and show also when not known --- fdm-app/app/routes/farm._index.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fdm-app/app/routes/farm._index.tsx b/fdm-app/app/routes/farm._index.tsx index e8eee9ce2..b15069891 100644 --- a/fdm-app/app/routes/farm._index.tsx +++ b/fdm-app/app/routes/farm._index.tsx @@ -30,7 +30,7 @@ import { getTimeBasedGreeting } from "~/lib/greetings" // Meta export const meta: MetaFunction = () => { return [ - { title: `Bedrijf | ${clientConfig.name}` }, + { title: `Bedrijven | ${clientConfig.name}` }, { name: "description", content: "Selecteer een bedrijf.", @@ -310,18 +310,22 @@ export default function AppIndex() {

)}
- {farm.b_businessid_farm && ( -
-

- KvK-nummer: -

-

+

+

+ KvK-nummer: +

+ {farm.b_businessid_farm ? ( +

{ farm.b_businessid_farm }

-
- )} + ) : ( +

+ {"Onbekend"} +

+ )} +
From 79bca9bd385a015abf2d64f9e8137f70a1a528f0 Mon Sep 17 00:00:00 2001 From: Sven Verweij <37927107+SvenVw@users.noreply.github.com> Date: Mon, 22 Dec 2025 10:03:30 +0100 Subject: [PATCH 023/108] fix: do not set sampling date when using estimates --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 71 ++++++++++++------- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 6 +- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index ef70bcaca..f96df48e5 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -50,14 +50,18 @@ import { FarmTitle } from "~/components/blocks/farm/farm-title" import { Header } from "~/components/blocks/header/base" import { HeaderFarm } from "~/components/blocks/header/farm" import { SidebarInset } from "~/components/ui/sidebar" -import { - BreadcrumbItem, - BreadcrumbSeparator, -} from "~/components/ui/breadcrumb" +import { BreadcrumbItem, BreadcrumbSeparator } from "~/components/ui/breadcrumb" import { getRvoCredentials } from "../integrations/rvo" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi.server" -import { addSoilAnalysis, getCultivations, getCultivationsFromCatalogue } from "@svenvw/fdm-core" +import { + getNmiApiKey, + getSoilParameterEstimates, +} from "~/integrations/nmi.server" +import { + addSoilAnalysis, + getCultivations, + getCultivationsFromCatalogue, +} from "@svenvw/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" import { clientConfig } from "../lib/config" @@ -391,51 +395,61 @@ export default function RvoImportReviewPage() { - Wijzigingen toepassen + + Wijzigingen toepassen + - U staat op het punt de volgende wijzigingen door te voeren: + U staat op het punt de volgende + wijzigingen door te voeren:
    {changes.add > 0 && (
  • - {changes.add}{" "} - perceel - {changes.add !== 1 && "en"}{" "} + {changes.add} perceel + {changes.add !== 1 && + "en"}{" "} toevoegen
  • )} {changes.remove > 0 && (
  • - {changes.remove}{" "} - perceel - {changes.remove !== 1 && "en"}{" "} + {changes.remove} perceel + {changes.remove !== 1 && + "en"}{" "} verwijderen
  • )} {changes.update > 0 && (
  • - {changes.update}{" "} - perceel - {changes.update !== 1 && "en"}{" "} + {changes.update} perceel + {changes.update !== 1 && + "en"}{" "} bijwerken
  • )} {changes.close > 0 && (
  • - {changes.close}{" "} - perceel - {changes.close !== 1 && "en"}{" "} + {changes.close} perceel + {changes.close !== 1 && + "en"}{" "} afsluiten
  • )}
- {!hasChanges &&

Geen wijzigingen geselecteerd.

} + {!hasChanges && ( +

+ Geen wijzigingen + geselecteerd. +

+ )}
- +
-
@@ -548,11 +567,11 @@ export async function action({ request, params }: ActionFunctionArgs) { await addSoilAnalysis( fdm, session.principal_id, - new Date(), + undefined, "nl-other-nmi", b_id, soilEstimates.a_depth_lower ?? 30, - new Date(), + undefined, soilEstimates, soilEstimates.a_depth_upper, ) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 8a042cbe5..bb6c9629a 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -26,7 +26,7 @@ import type { import { getItemId } from "@svenvw/fdm-rvo/utils" import { processRvoImport } from "@svenvw/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" -import { Cultivation, Field, getFarm } from "@svenvw/fdm-core" +import { type Cultivation, type Field, getFarm } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" import { AlertTriangle, Loader2 } from "lucide-react" @@ -435,11 +435,11 @@ export async function action({ request, params }: ActionFunctionArgs) { await addSoilAnalysis( fdm, session.principal_id, - new Date(), + undefined, "nl-other-nmi", b_id, soilEstimates.a_depth_lower ?? 30, - new Date(), + undefined, soilEstimates, soilEstimates.a_depth_upper, ) From 4e6850136dbc21d9c3f544525364f3b56ec43e31 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:09:20 +0100 Subject: [PATCH 024/108] chore: rename the organization --- .changeset/tangy-ways-win.md | 2 +- .../blocks/rvo/import-review-table.tsx | 11 +++++++---- fdm-app/app/lib/rvo.server.ts | 2 +- .../app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 6 +++--- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 6 +++--- fdm-app/package.json | 4 ++-- fdm-docs/blog/tags.yml | 2 +- fdm-rvo/README.md | 8 ++++---- fdm-rvo/package.json | 2 +- fdm-rvo/src/index.ts | 2 +- fdm-rvo/typedoc.json | 16 ++++++++-------- 11 files changed, 32 insertions(+), 29 deletions(-) diff --git a/.changeset/tangy-ways-win.md b/.changeset/tangy-ways-win.md index 367741b78..8d3906472 100644 --- a/.changeset/tangy-ways-win.md +++ b/.changeset/tangy-ways-win.md @@ -1,5 +1,5 @@ --- -"@svenvw/fdm-rvo": minor +"@nmi-agro/fdm-rvo": minor --- Initial version of `fdm-rvo` that that provides the logic for fdm-app to connect to RVO and sync fields with fdm-core diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index e93e6028a..a8f491a4d 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -8,8 +8,8 @@ import type { RvoImportReviewItem, ImportReviewAction, UserChoiceMap, -} from "@svenvw/fdm-rvo/types" -import { getItemId } from "@svenvw/fdm-rvo/utils" +} from "@nmi-agro/fdm-rvo/types" +import { getItemId } from "@nmi-agro/fdm-rvo/utils" import { Table, TableBody, @@ -205,7 +205,8 @@ export const columns: ColumnDef>[] = [ Gelijk - Perceel komt in {clientConfig.name} en bij RVO volledig overeen. + Perceel komt in {clientConfig.name} en bij + RVO volledig overeen. @@ -249,7 +250,9 @@ export const columns: ColumnDef>[] = [ className="bg-orange-50 text-orange-700 border-orange-200" > - Niet meer actief + + Niet meer actief + Perceel is in {clientConfig.name} nog actief, maar komt niet meer voor bij RVO. diff --git a/fdm-app/app/lib/rvo.server.ts b/fdm-app/app/lib/rvo.server.ts index afeab46d4..ee848e36c 100644 --- a/fdm-app/app/lib/rvo.server.ts +++ b/fdm-app/app/lib/rvo.server.ts @@ -1 +1 @@ -export * from "@svenvw/fdm-rvo" +export * from "@nmi-agro/fdm-rvo" diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index f96df48e5..813c96db6 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -26,9 +26,9 @@ import { RvoImportReviewStatus, type UserChoiceMap, type ImportReviewAction, -} from "@svenvw/fdm-rvo/types" -import { getItemId } from "@svenvw/fdm-rvo/utils" -import { processRvoImport } from "@svenvw/fdm-rvo" +} from "@nmi-agro/fdm-rvo/types" +import { getItemId } from "@nmi-agro/fdm-rvo/utils" +import { processRvoImport } from "@nmi-agro/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" import { getFields, getFarm, getFarms } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index bb6c9629a..75eadebee 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -22,9 +22,9 @@ import type { RvoImportReviewItem, ImportReviewAction, UserChoiceMap, -} from "@svenvw/fdm-rvo/types" -import { getItemId } from "@svenvw/fdm-rvo/utils" -import { processRvoImport } from "@svenvw/fdm-rvo" +} from "@nmi-agro/fdm-rvo/types" +import { getItemId } from "@nmi-agro/fdm-rvo/utils" +import { processRvoImport } from "@nmi-agro/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" import { type Cultivation, type Field, getFarm } from "@svenvw/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" diff --git a/fdm-app/package.json b/fdm-app/package.json index 87b852935..9f5a63a1a 100644 --- a/fdm-app/package.json +++ b/fdm-app/package.json @@ -33,7 +33,7 @@ "@sentry/profiling-node": "^10.42.0", "@sentry/react-router": "^10.42.0", "@tailwindcss/vite": "^4.2.1", - "@svenvw/fdm-rvo": "workspace:^", + "@nmi-agro/fdm-rvo": "workspace:^", "@tanstack/react-table": "^8.21.3", "@turf/boolean-intersects": "^7.3.4", "@turf/centroid": "^7.3.4", @@ -92,7 +92,7 @@ "@react-router/dev": "^7.13.1", "@react-router/fs-routes": "^7.13.1", "@tailwindcss/postcss": "^4.2.1", - "@svenvw/fdm-rvo": "workspace:*", + "@nmi-agro/fdm-rvo": "workspace:*", "@types/geojson": "^7946.0.16", "@types/lodash.throttle": "^4.1.9", "@types/mapbox__geojson-extent": "^1.0.3", diff --git a/fdm-docs/blog/tags.yml b/fdm-docs/blog/tags.yml index e218323d0..5e2ab6126 100644 --- a/fdm-docs/blog/tags.yml +++ b/fdm-docs/blog/tags.yml @@ -30,7 +30,7 @@ fdm-app: fdm-rvo: label: fdm-rvo - description: Posts related to the RVO Synchronization Logic for FDM (@svenvw/fdm-rvo). + description: Posts related to the RVO Synchronization Logic for FDM (@nmi-agro/fdm-rvo). permalink: /fdm-rvo update: diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index 4d1a779ac..7b2972f41 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -1,4 +1,4 @@ -# @svenvw/fdm-rvo +# @nmi-agro/fdm-rvo ## RVO Synchronization Logic for FDM @@ -18,7 +18,7 @@ This package provides the core logic for synchronizing agricultural field data w ### Installation ```bash -pnpm add @svenvw/fdm-rvo +pnpm add @nmi-agro/fdm-rvo # Or if in a monorepo, ensure it's linked as a workspace dependency ``` @@ -40,7 +40,7 @@ RVO_ENVIRONMENT=production # or acceptance ```typescript // In your React Router v7 component (client-side) -import { createRvoClient, generateAuthUrl, exchangeToken } from '@svenvw/fdm-rvo'; +import { createRvoClient, generateAuthUrl, exchangeToken } from '@nmi-agro/fdm-rvo'; // Assuming you have a way to securely access server config on the client, // or that these are provided as build-time env vars (e.g., VITE_RVO_CLIENT_ID) import { serverConfig } from '~/lib/config.client'; // Example client-side config @@ -80,7 +80,7 @@ const handleRvoCallback = async (code: string, state: string) => { ```typescript // In your React Router v7 component (client-side), after getting accessToken -import { fetchRvoFields, compareFields } from '@svenvw/fdm-rvo'; +import { fetchRvoFields, compareFields } from '@nmi-agro/fdm-rvo'; // Assuming you have an API endpoint to fetch local fields const fetchLocalFieldsApi = async (farmId: string, principalId: string) => { const response = await fetch(`/api/farm/${farmId}/fields?principalId=${principalId}`); diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json index a06925f66..ade8ad029 100644 --- a/fdm-rvo/package.json +++ b/fdm-rvo/package.json @@ -1,5 +1,5 @@ { - "name": "@svenvw/fdm-rvo", + "name": "@nmi-agro/fdm-rvo", "private": false, "version": "0.1.0", "description": "RVO Synchronization Logic for FDM", diff --git a/fdm-rvo/src/index.ts b/fdm-rvo/src/index.ts index 8cd69cb05..19cf3381e 100644 --- a/fdm-rvo/src/index.ts +++ b/fdm-rvo/src/index.ts @@ -1,5 +1,5 @@ /** - * # @svenvw/fdm-rvo: RVO Field Synchronization Logic + * # @nmi-agro/fdm-rvo: RVO Field Synchronization Logic * * This package provides the core logic for synchronizing agricultural field data with the * RVO (Rijksdienst voor Ondernemend Nederland) webservices. It wraps the diff --git a/fdm-rvo/typedoc.json b/fdm-rvo/typedoc.json index 96bc340e4..c4782f959 100644 --- a/fdm-rvo/typedoc.json +++ b/fdm-rvo/typedoc.json @@ -1,10 +1,10 @@ { - "entryPoints": ["src/index.ts"], - "out": "docs", - "readme": "README.md", - "name": "@svenvw/fdm-rvo", - "excludePrivate": true, - "excludeProtected": true, - "hideGenerator": true, - "plugin": ["typedoc-plugin-missing-exports"] + "entryPoints": ["src/index.ts"], + "out": "docs", + "readme": "README.md", + "name": "@nmi-agro/fdm-rvo", + "excludePrivate": true, + "excludeProtected": true, + "hideGenerator": true, + "plugin": ["typedoc-plugin-missing-exports"] } From 5780878dadf7783b0d7f56cb2bd75e833a084be8 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:22:52 +0100 Subject: [PATCH 025/108] chore: even more renaming --- .changeset/chatty-bananas-sink.md | 2 +- .changeset/public-llamas-clap.md | 2 +- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 4 +- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 7 +- fdm-rvo/README.md | 4 +- fdm-rvo/package.json | 24 +- fdm-rvo/src/compare.ts | 6 +- fdm-rvo/src/data.ts | 4 +- fdm-rvo/src/index.ts | 2 +- fdm-rvo/src/process.test.ts | 4 +- fdm-rvo/src/process.ts | 2 +- pnpm-lock.yaml | 275 +++++++++++++----- pnpm-workspace.yaml | 2 +- 13 files changed, 239 insertions(+), 99 deletions(-) diff --git a/.changeset/chatty-bananas-sink.md b/.changeset/chatty-bananas-sink.md index 1c89e5836..661ba5b03 100644 --- a/.changeset/chatty-bananas-sink.md +++ b/.changeset/chatty-bananas-sink.md @@ -1,5 +1,5 @@ --- -"@svenvw/fdm-app": minor +"@nmi-agro/fdm-app": minor --- Add the option to import fields from RVO in farm create wizard and also at the fields page of the farm diff --git a/.changeset/public-llamas-clap.md b/.changeset/public-llamas-clap.md index a92baf843..0b55389fd 100644 --- a/.changeset/public-llamas-clap.md +++ b/.changeset/public-llamas-clap.md @@ -1,5 +1,5 @@ --- -"@svenvw/fdm-docs": minor +"@nmi-agro/fdm-docs": minor --- Add the reference for fdm-rvo diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 813c96db6..d10fe41d1 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -30,7 +30,7 @@ import { import { getItemId } from "@nmi-agro/fdm-rvo/utils" import { processRvoImport } from "@nmi-agro/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" -import { getFields, getFarm, getFarms } from "@svenvw/fdm-core" +import { getFields, getFarm, getFarms } from "@nmi-agro/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" import { @@ -61,7 +61,7 @@ import { addSoilAnalysis, getCultivations, getCultivationsFromCatalogue, -} from "@svenvw/fdm-core" +} from "@nmi-agro/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" import { clientConfig } from "../lib/config" diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 75eadebee..191077f68 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -26,7 +26,7 @@ import type { import { getItemId } from "@nmi-agro/fdm-rvo/utils" import { processRvoImport } from "@nmi-agro/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" -import { type Cultivation, type Field, getFarm } from "@svenvw/fdm-core" +import { type Cultivation, type Field, getFarm } from "@nmi-agro/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" import { Button } from "~/components/ui/button" import { AlertTriangle, Loader2 } from "lucide-react" @@ -47,7 +47,10 @@ import { getNmiApiKey, getSoilParameterEstimates, } from "~/integrations/nmi.server" -import { addSoilAnalysis, getCultivationsFromCatalogue } from "@svenvw/fdm-core" +import { + addSoilAnalysis, + getCultivationsFromCatalogue, +} from "@nmi-agro/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" export const meta: MetaFunction = ({ params }) => { diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index 7b2972f41..d53550d9a 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -2,14 +2,14 @@ ## RVO Synchronization Logic for FDM -This package provides the core logic for synchronizing agricultural field data with the RVO (Rijksdienst voor Ondernemend Nederland) webservices. It wraps the `@nmi-agro/rvo-connector` to handle authentication and data fetching, and implements a robust field comparison mechanism to detect new, missing, and conflicting field data between local and RVO records. +This package provides the core logic for synchronizing agricultural field data with the RVO (Rijksdienst voor Ondernemend Nederland) webservices. It wraps the `@svenvw/rvo-connector` to handle authentication and data fetching, and implements a robust field comparison mechanism to detect new, missing, and conflicting field data between local and RVO records. ### Features - **RVO Authentication Flow**: Helpers for generating authorization URLs and exchanging authorization codes for access tokens using the `RvoClient`. - **Field Data Fetching**: Retrieves agricultural field data from RVO, with GeoJSON parsing and validation. - **RVO Import Review Engine**: - - Compares local FDM fields (`@svenvw/fdm-core`'s `Field` type) against RVO fields. + - Compares local FDM fields (`@nmi-agro/fdm-core`'s `Field` type) against RVO fields. - Utilizes a two-tier matching strategy: ID-based matching followed by spatial (IoU) matching. - Detects and categorizes fields as `MATCH`, `NEW_REMOTE` (in RVO but not local), `NEW_LOCAL` (in local but not RVO), or `CONFLICT` (different properties in both). - Identifies specific differing properties (`b_name`, `b_geometry`, `b_start`, `b_end`) for conflicts. diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json index ade8ad029..81d3db9b4 100644 --- a/fdm-rvo/package.json +++ b/fdm-rvo/package.json @@ -6,9 +6,9 @@ "homepage": "https://svenvw.github.io/fdm/", "repository": { "type": "git", - "url": "git+https://github.com/SvenVw/fdm.git" + "url": "git+https://github.com/nmi-agro/fdm.git" }, - "bugs": "https://github.com/SvenVw/fdm/issues/new", + "bugs": "https://github.com/nmi-agro/fdm/issues/new", "author": { "name": "Sven Verweij", "email": "37927107+SvenVw@users.noreply.github.com", @@ -51,19 +51,19 @@ "test-coverage": "vitest run --coverage" }, "dependencies": { - "@nmi-agro/rvo-connector": "0.1.0", - "@svenvw/fdm-core": "workspace:*", - "@turf/area": "^7.0.0", - "@turf/bbox": "^7.0.0", - "@turf/intersect": "^7.0.0", - "@turf/union": "^7.0.0", - "@turf/helpers": "^7.0.0", - "zod": "^3.23.8" + "@nmi-agro/fdm-core": "workspace:*", + "@nmi-agro/rvo-connector": "^2.1.1", + "@turf/area": "^7.3.4", + "@turf/bbox": "^7.3.4", + "@turf/helpers": "^7.3.4", + "@turf/intersect": "^7.3.4", + "@turf/union": "^7.3.4", + "zod": "^3.25.76" }, "devDependencies": { "@rollup/plugin-commonjs": "catalog:", "@rollup/plugin-node-resolve": "catalog:", - "@types/node": "^20.14.9", + "@types/node": "^20.19.37", "@vitest/coverage-v8": "catalog:", "rollup": "catalog:", "rollup-plugin-esbuild": "catalog:", @@ -71,7 +71,7 @@ "typescript": "catalog:", "vitest": "catalog:" }, - "packageManager": "pnpm@10.23.0", + "packageManager": "pnpm@10.30.3", "publishConfig": { "registry": "https://npm.pkg.github.com" } diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index e0206bf61..35c35f920 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -3,7 +3,11 @@ import intersect from "@turf/intersect" import union from "@turf/union" import area from "@turf/area" import { feature, featureCollection } from "@turf/helpers" -import type { Field, Cultivation, CultivationCatalogue } from "@svenvw/fdm-core" +import type { + Field, + Cultivation, + CultivationCatalogue, +} from "@nmi-agro/fdm-core" import { type RvoField, RvoImportReviewStatus, diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts index 2a53ea035..411372ab2 100644 --- a/fdm-rvo/src/data.ts +++ b/fdm-rvo/src/data.ts @@ -31,7 +31,7 @@ export async function fetchRvoFields( // The raw response is expected to be a GeoJSON FeatureCollection. // We access the 'features' array to iterate over individual fields. // Safe casting is handled by the subsequent Zod validation. - const features = (fieldsRaw as any).features + const features = (fieldsRaw as any).features if (Array.isArray(features)) { // Define a schema for an array of fields and parse the data. @@ -41,5 +41,5 @@ export async function fetchRvoFields( } // Return empty array if the response format is unexpected or contains no features. - return [] + return [] } diff --git a/fdm-rvo/src/index.ts b/fdm-rvo/src/index.ts index 19cf3381e..ad1433521 100644 --- a/fdm-rvo/src/index.ts +++ b/fdm-rvo/src/index.ts @@ -14,7 +14,7 @@ * - **Field Data Fetching**: Retrieves agricultural field data from RVO, with GeoJSON * parsing and validation against `RvoFieldSchema`. * - **Field RVO Import Review Engine**: - * - Compares local FDM fields (`@svenvw/fdm-core`'s `Field` type) against RVO fields. + * - Compares local FDM fields (`@nmi-agro/fdm-core`'s `Field` type) against RVO fields. * - Utilizes a two-tier matching strategy: ID-based matching followed by spatial * (IoU) matching. * - Detects and categorizes fields as `MATCH`, `NEW_REMOTE` (in RVO but not local), diff --git a/fdm-rvo/src/process.test.ts b/fdm-rvo/src/process.test.ts index 533e67d7e..644d3cacf 100644 --- a/fdm-rvo/src/process.test.ts +++ b/fdm-rvo/src/process.test.ts @@ -7,11 +7,11 @@ import { addCultivation, removeCultivation, getDefaultDatesOfCultivation, -} from "@svenvw/fdm-core" +} from "@nmi-agro/fdm-core" import { RvoImportReviewStatus, type RvoImportReviewItem } from "./types" // Mock fdm-core -vi.mock("@svenvw/fdm-core", () => ({ +vi.mock("@nmi-agro/fdm-core", () => ({ addField: vi.fn(), updateField: vi.fn(), removeField: vi.fn(), diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index c0c565cea..3a766e10d 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -6,7 +6,7 @@ import { removeCultivation, getDefaultDatesOfCultivation, type FdmType, -} from "@svenvw/fdm-core" +} from "@nmi-agro/fdm-core" import type { RvoImportReviewItem, UserChoiceMap } from "./types" import { getItemId } from "./utils" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b2f54e19..43656a180 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,8 +10,8 @@ catalogs: specifier: ^1.52.0 version: 1.52.0 '@rollup/plugin-commonjs': - specifier: ^29.0.0 - version: 29.0.0 + specifier: ^29.0.1 + version: 29.0.1 '@rollup/plugin-json': specifier: ^6.1.0 version: 6.1.0 @@ -106,6 +106,9 @@ importers: '@nmi-agro/fdm-data': specifier: workspace:* version: link:../fdm-data + '@nmi-agro/fdm-rvo': + specifier: workspace:^ + version: link:../fdm-rvo '@react-email/components': specifier: ^1.0.8 version: 1.0.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -579,58 +582,58 @@ importers: fdm-rvo: dependencies: - '@nmi-agro/rvo-connector': - specifier: 0.1.0 - version: 0.1.0 - '@svenvw/fdm-core': + '@nmi-agro/fdm-core': specifier: workspace:* version: link:../fdm-core + '@nmi-agro/rvo-connector': + specifier: latest + version: 2.1.1 '@turf/area': - specifier: ^7.0.0 - version: 7.3.1 + specifier: ^7.3.4 + version: 7.3.4 '@turf/bbox': - specifier: ^7.0.0 - version: 7.3.1 + specifier: ^7.3.4 + version: 7.3.4 '@turf/helpers': - specifier: ^7.0.0 - version: 7.3.1 + specifier: ^7.3.4 + version: 7.3.4 '@turf/intersect': - specifier: ^7.0.0 - version: 7.3.1 + specifier: ^7.3.4 + version: 7.3.4 '@turf/union': - specifier: ^7.0.0 - version: 7.3.1 + specifier: ^7.3.4 + version: 7.3.4 zod: - specifier: ^3.23.8 + specifier: ^3.25.76 version: 3.25.76 devDependencies: '@rollup/plugin-commonjs': specifier: 'catalog:' - version: 29.0.0(rollup@4.53.5) + version: 29.0.1(rollup@4.59.0) '@rollup/plugin-node-resolve': specifier: 'catalog:' - version: 16.0.3(rollup@4.53.5) + version: 16.0.3(rollup@4.59.0) '@types/node': - specifier: ^20.14.9 - version: 20.19.27 + specifier: ^20.19.37 + version: 20.19.37 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.16(vitest@4.0.16(@opentelemetry/api@1.9.0)(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2)) rollup: specifier: 'catalog:' - version: 4.53.5 + version: 4.59.0 rollup-plugin-esbuild: specifier: 'catalog:' - version: 6.2.1(esbuild@0.27.2)(rollup@4.53.5) + version: 6.2.1(esbuild@0.27.3)(rollup@4.59.0) typedoc: specifier: 'catalog:' - version: 0.28.15(typescript@5.9.3) + version: 0.28.17(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.16(@opentelemetry/api@1.9.0)(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2) packages: @@ -2734,8 +2737,8 @@ packages: '@napi-rs/wasm-runtime@1.0.7': resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} - '@nmi-agro/rvo-connector@0.1.0': - resolution: {integrity: sha512-7upHmBwsFcsbBv+SrBKDxryabU7qPn7exJUWQGBBtoZa+phP1qZhtlUyxC+avz4rsbdKftxDyGSLXJXVw90GOg==} + '@nmi-agro/rvo-connector@2.1.1': + resolution: {integrity: sha512-vHfiJquhs9bxQOS36aBJVKOSFo3gi/cpQ65tGuea9E2C1Cw1shXxxes4cBCQNpXLW4ua2OAWV2mcfoKFGmcZsA==} engines: {node: '>=18'} '@noble/ciphers@1.3.0': @@ -4137,6 +4140,15 @@ packages: rollup: optional: true + '@rollup/plugin-commonjs@29.0.1': + resolution: {integrity: sha512-VUEHINN2rQEWPfNUR3mzidRObM1XZKXMQsaG6qBlDqd6M1qyw91nDZvcSozgyjt3x/QKrgKBc5MdxfdxAy6tdg==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/plugin-inject@5.0.5': resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} engines: {node: '>=14.0.0'} @@ -5447,6 +5459,9 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@20.19.37': + resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} + '@types/node@25.3.3': resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==} @@ -6019,10 +6034,6 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - brace-expansion@5.0.3: - resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} - engines: {node: 18 || 20 || >=22} - brace-expansion@5.0.4: resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} engines: {node: 18 || 20 || >=22} @@ -6870,6 +6881,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + eciesjs@0.4.17: resolution: {integrity: sha512-TOOURki4G7sD1wDCjj7NfLaXZZ49dFOeEb5y39IXpb8p0hRzVvfvzZHOi5JcT+PpyAbi/Y+lxPb8eTag2WYH8w==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} @@ -8062,6 +8076,12 @@ packages: resolution: {integrity: sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==} engines: {node: '>= 12'} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + katex@0.16.33: resolution: {integrity: sha512-q3N5u+1sY9Bu7T4nlXoiRBXWfwSefNGoKeOwekV+gw0cAXQlz2Ww6BLcmBxVDeXBMUDQv6fK5bcNaJLxob3ZQA==} hasBin: true @@ -8617,10 +8637,6 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.6: - resolution: {integrity: sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} @@ -9547,6 +9563,10 @@ packages: resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} engines: {node: '>=0.6'} + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + engines: {node: '>=0.6'} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -10713,6 +10733,9 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} @@ -11209,6 +11232,9 @@ packages: zarrita@0.6.1: resolution: {integrity: sha512-YOMTW8FT55Rz+vadTIZeOFZ/F2h4svKizyldvPtMYSxPgSNcRkOzkxCsWpIWlWzB1I/LmISmi0bEekOhLlI+Zw==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -13771,7 +13797,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -14099,11 +14125,11 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@nmi-agro/rvo-connector@0.1.0': + '@nmi-agro/rvo-connector@2.1.1': dependencies: jsonwebtoken: 9.0.3 - proj4: 2.20.2 - qs: 6.14.0 + proj4: 2.20.3 + qs: 6.15.0 uuid: 13.0.0 xml2js: 0.6.2 @@ -15717,6 +15743,18 @@ snapshots: optionalDependencies: rollup: 4.59.0 + '@rollup/plugin-commonjs@29.0.1(rollup@4.59.0)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.3) + is-reference: 1.2.1 + magic-string: 0.30.21 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.59.0 + '@rollup/plugin-inject@5.0.5(rollup@4.59.0)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.59.0) @@ -17576,11 +17614,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/bonjour@3.5.13': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/chai@5.2.3': dependencies: @@ -17590,11 +17628,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.8 - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/connect@3.4.38': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/d3-array@3.2.2': {} @@ -17646,7 +17684,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -17680,7 +17718,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/istanbul-lib-coverage@2.0.6': {} @@ -17726,12 +17764,16 @@ snapshots: '@types/mysql@2.15.27': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/node@12.20.55': {} '@types/node@17.0.45': {} + '@types/node@20.19.37': + dependencies: + undici-types: 6.21.0 + '@types/node@25.3.3': dependencies: undici-types: 7.18.2 @@ -17742,7 +17784,7 @@ snapshots: '@types/pg@8.15.6': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 pg-protocol: 1.12.0 pg-types: 2.2.0 @@ -17793,16 +17835,16 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 17.0.45 + '@types/node': 20.19.37 '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/send@1.2.1': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/serve-index@1.9.4': dependencies: @@ -17811,12 +17853,12 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/send': 0.17.6 '@types/sockjs@0.3.36': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/supercluster@7.1.3': dependencies: @@ -17824,7 +17866,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/trusted-types@2.0.7': optional: true @@ -17841,7 +17883,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 '@types/yargs-parser@21.0.3': {} @@ -17864,6 +17906,20 @@ snapshots: optionalDependencies: maplibre-gl: 5.19.0 + '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.18 + ast-v8-to-istanbul: 0.3.12 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2) + '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -17887,6 +17943,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2) + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 @@ -18364,10 +18428,6 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.3: - dependencies: - balanced-match: 4.0.4 - brace-expansion@5.0.4: dependencies: balanced-match: 4.0.4 @@ -19119,6 +19179,10 @@ snapshots: eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + eciesjs@0.4.17: dependencies: '@ecies/ciphers': 0.2.5(@noble/ciphers@1.3.0) @@ -19424,7 +19488,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 require-like: 0.1.2 eventemitter3@4.0.7: {} @@ -20441,7 +20505,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.3.3 + '@types/node': 20.19.37 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -20449,13 +20513,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 25.3.3 + '@types/node': 20.19.37 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -20528,10 +20592,21 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.7.3 + semver: 7.7.4 jsts@2.7.1: {} + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + katex@0.16.33: dependencies: commander: 8.3.0 @@ -21378,10 +21453,6 @@ snapshots: dependencies: brace-expansion: 1.1.12 - minimatch@9.0.6: - dependencies: - brace-expansion: 5.0.3 - minimatch@9.0.9: dependencies: brace-expansion: 2.0.2 @@ -22313,7 +22384,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 25.3.3 + '@types/node': 20.19.37 long: 5.3.2 protocol-buffers-schema@3.6.0: {} @@ -22343,6 +22414,10 @@ snapshots: dependencies: side-channel: 1.1.0 + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + quansync@0.2.11: {} query-selector-shadow-dom@1.0.1: {} @@ -23663,7 +23738,7 @@ snapshots: '@gerrit0/mini-shiki': 3.22.0 lunr: 2.3.9 markdown-it: 14.1.1 - minimatch: 9.0.6 + minimatch: 9.0.9 typescript: 5.9.3 yaml: 2.8.2 @@ -23686,6 +23761,8 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + undici-types@6.21.0: {} + undici-types@7.18.2: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -23948,6 +24025,22 @@ snapshots: - supports-color - typescript + vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.37 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.31.1 + terser: 5.46.0 + yaml: 2.8.2 + vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2): dependencies: esbuild: 0.27.3 @@ -23964,6 +24057,44 @@ snapshots: terser: 5.46.0 yaml: 2.8.2 + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 20.19.37 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 @@ -24259,7 +24390,7 @@ snapshots: xml2js@0.6.2: dependencies: - sax: 1.4.3 + sax: 1.5.0 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} @@ -24284,6 +24415,8 @@ snapshots: numcodecs: 0.3.2 optional: true + zod@3.25.76: {} + zod@4.3.6: {} zstddec@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 226b157ec..72b0d758d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,7 +8,7 @@ packages: catalog: '@dotenvx/dotenvx': ^1.52.0 - '@rollup/plugin-commonjs': ^29.0.0 + '@rollup/plugin-commonjs': ^29.0.1 '@rollup/plugin-json': ^6.1.0 '@rollup/plugin-node-resolve': ^16.0.3 '@rollup/plugin-typescript': ^12.3.0 From 0e99021dd148aa194e2b2948aeb6b2a54e353b76 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 6 Mar 2026 17:11:26 +0100 Subject: [PATCH 026/108] refactor: implement change for breaking change --- fdm-rvo/src/auth.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/fdm-rvo/src/auth.ts b/fdm-rvo/src/auth.ts index d438c619c..95f3783e8 100644 --- a/fdm-rvo/src/auth.ts +++ b/fdm-rvo/src/auth.ts @@ -1,4 +1,5 @@ import { RvoClient } from "@nmi-agro/rvo-connector" +import fs from "node:fs" /** * Creates and configures an instance of the RVO Client. @@ -20,6 +21,17 @@ export const createRvoClient = ( pkioPrivateKey: string, environment: "acceptance" | "production" = "production", ) => { + let privateKey = pkioPrivateKey + if ( + pkioPrivateKey.startsWith("/") || + pkioPrivateKey.startsWith("./") || + pkioPrivateKey.includes(":") + ) { + if (fs.existsSync(pkioPrivateKey)) { + privateKey = fs.readFileSync(pkioPrivateKey, "utf8") + } + } + return new RvoClient({ clientId, clientName, @@ -27,7 +39,7 @@ export const createRvoClient = ( tvs: { clientId, redirectUri, - pkioPrivateKey, + pkioPrivateKey: privateKey, }, }) } From 9409ab645a38886fe61111674088a9256f0f5ab4 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 6 Mar 2026 17:20:03 +0100 Subject: [PATCH 027/108] fix: config issue of fdm-rvo --- fdm-rvo/tsconfig.json | 7 ++++++- pnpm-lock.yaml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/fdm-rvo/tsconfig.json b/fdm-rvo/tsconfig.json index 6bafbc494..f6036312d 100644 --- a/fdm-rvo/tsconfig.json +++ b/fdm-rvo/tsconfig.json @@ -11,7 +11,12 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], + "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] + } }, "include": ["src/**/*", "vitest.config.ts"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43656a180..0e8d998f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -586,7 +586,7 @@ importers: specifier: workspace:* version: link:../fdm-core '@nmi-agro/rvo-connector': - specifier: latest + specifier: ^2.1.1 version: 2.1.1 '@turf/area': specifier: ^7.3.4 From ae2c1871d0194598766b90353b6469d295573c92 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 6 Mar 2026 17:20:29 +0100 Subject: [PATCH 028/108] fix: type issues --- fdm-rvo/src/compare.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 35c35f920..8884b2f66 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -80,6 +80,7 @@ function findActiveCultivation( ): Cultivation | undefined { const referenceDate = new Date(`${calendar}-05-15`).getTime() return cultivations.find((c) => { + if (!c.b_lu_start) return false const start = c.b_lu_start.getTime() const end = c.b_lu_end ? c.b_lu_end.getTime() : Number.POSITIVE_INFINITY return start <= referenceDate && end >= referenceDate @@ -267,7 +268,9 @@ export function compareFields( const localStart = local.b_start instanceof Date ? local.b_start - : new Date(local.b_start) + : local.b_start + ? new Date(local.b_start) + : new Date(0) const importYearStart = new Date(calendar, 0, 1) // Jan 1st of import year const isStartedBeforeYear = localStart < importYearStart From 489f8886e860d8e5191069a3cf788221441a5a85 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:45:15 +0100 Subject: [PATCH 029/108] fix: lockfile --- pnpm-lock.yaml | 175 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 146 insertions(+), 29 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f4071d3d..8dd557c1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -618,7 +618,7 @@ importers: version: 20.19.37 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2)) + version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) rollup: specifier: 'catalog:' version: 4.59.0 @@ -633,7 +633,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.46.0)(yaml@2.8.2) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) packages: @@ -4257,8 +4257,8 @@ packages: '@repeaterjs/repeater@3.0.6': resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} - '@rollup/plugin-commonjs@29.0.2': - resolution: {integrity: sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==} + '@rollup/plugin-commonjs@29.0.1': + resolution: {integrity: sha512-VUEHINN2rQEWPfNUR3mzidRObM1XZKXMQsaG6qBlDqd6M1qyw91nDZvcSozgyjt3x/QKrgKBc5MdxfdxAy6tdg==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -4266,8 +4266,8 @@ packages: rollup: optional: true - '@rollup/plugin-commonjs@29.0.1': - resolution: {integrity: sha512-VUEHINN2rQEWPfNUR3mzidRObM1XZKXMQsaG6qBlDqd6M1qyw91nDZvcSozgyjt3x/QKrgKBc5MdxfdxAy6tdg==} + '@rollup/plugin-commonjs@29.0.2': + resolution: {integrity: sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -5581,6 +5581,9 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@20.19.37': + resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} + '@types/node@25.4.0': resolution: {integrity: sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==} @@ -6186,6 +6189,9 @@ packages: resolution: {integrity: sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==} engines: {node: '>=20.19.0'} + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -7039,6 +7045,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + eciesjs@0.4.18: resolution: {integrity: sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} @@ -8272,6 +8281,12 @@ packages: resolution: {integrity: sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==} engines: {node: '>= 12'} + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + katex@0.16.38: resolution: {integrity: sha512-cjHooZUmIAUmDsHBN+1n8LaZdpmbj03LtYeYPyuYB7OuloiaeaV6N4LcfjcnHVzGWjVQmKrxxTrpDcmSzEZQwQ==} hasBin: true @@ -11631,6 +11646,9 @@ packages: zeptomatch@2.1.0: resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -14256,7 +14274,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -14595,7 +14613,7 @@ snapshots: '@nmi-agro/rvo-connector@2.1.1': dependencies: jsonwebtoken: 9.0.3 - proj4: 2.20.3 + proj4: 2.20.4 qs: 6.15.0 uuid: 13.0.0 xml2js: 0.6.2 @@ -16273,7 +16291,7 @@ snapshots: '@repeaterjs/repeater@3.0.6': {} - '@rollup/plugin-commonjs@29.0.2(rollup@4.59.0)': + '@rollup/plugin-commonjs@29.0.1(rollup@4.59.0)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.59.0) commondir: 1.0.1 @@ -16285,7 +16303,7 @@ snapshots: optionalDependencies: rollup: 4.59.0 - '@rollup/plugin-commonjs@29.0.1(rollup@4.59.0)': + '@rollup/plugin-commonjs@29.0.2(rollup@4.59.0)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.59.0) commondir: 1.0.1 @@ -18154,11 +18172,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/bonjour@3.5.13': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/chai@5.2.3': dependencies: @@ -18168,11 +18186,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.8 - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/connect@3.4.38': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/d3-array@3.2.2': {} @@ -18224,7 +18242,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -18258,7 +18276,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/istanbul-lib-coverage@2.0.6': {} @@ -18304,12 +18322,16 @@ snapshots: '@types/mysql@2.15.27': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/node@12.20.55': {} '@types/node@17.0.45': {} + '@types/node@20.19.37': + dependencies: + undici-types: 6.21.0 + '@types/node@25.4.0': dependencies: undici-types: 7.18.2 @@ -18320,7 +18342,7 @@ snapshots: '@types/pg@8.15.6': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 pg-protocol: 1.13.0 pg-types: 2.2.0 @@ -18376,11 +18398,11 @@ snapshots: '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/send@1.2.1': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/serve-index@1.9.4': dependencies: @@ -18389,12 +18411,12 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/send': 0.17.6 '@types/sockjs@0.3.36': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/supercluster@7.1.3': dependencies: @@ -18402,7 +18424,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/trusted-types@2.0.7': optional: true @@ -18425,7 +18447,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 '@types/yargs-parser@21.0.3': {} @@ -18448,6 +18470,20 @@ snapshots: optionalDependencies: maplibre-gl: 5.20.0 + '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.18 + ast-v8-to-istanbul: 0.3.12 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -18471,6 +18507,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 @@ -19019,6 +19063,8 @@ snapshots: bson@7.2.0: {} + buffer-equal-constant-time@1.0.1: {} + buffer-from@1.1.2: {} bundle-name@4.1.0: @@ -19794,6 +19840,10 @@ snapshots: eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + eciesjs@0.4.18: dependencies: '@ecies/ciphers': 0.2.5(@noble/ciphers@1.3.0) @@ -20106,7 +20156,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 require-like: 0.1.2 eventemitter3@4.0.7: {} @@ -21157,7 +21207,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.4.0 + '@types/node': 20.19.37 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -21165,13 +21215,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 25.4.0 + '@types/node': 20.19.37 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -21248,6 +21298,17 @@ snapshots: jsts@2.7.1: {} + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + katex@0.16.38: dependencies: commander: 8.3.0 @@ -23138,7 +23199,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 25.4.0 + '@types/node': 20.19.37 long: 5.3.2 protocol-buffers-schema@3.6.0: {} @@ -24810,6 +24871,22 @@ snapshots: - supports-color - typescript + vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.37 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.32.0 + terser: 5.46.0 + yaml: 2.8.2 + vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): dependencies: esbuild: 0.27.3 @@ -24826,6 +24903,44 @@ snapshots: terser: 5.46.0 yaml: 2.8.2 + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 20.19.37 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 @@ -25158,6 +25273,8 @@ snapshots: grammex: 3.1.12 graphmatch: 1.1.1 + zod@3.25.76: {} + zod@4.3.6: {} zstddec@0.1.0: {} From 6d222c626a9be66c50768591b64d9a28896aa446 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:47:47 +0100 Subject: [PATCH 030/108] chore: update fdm-rvo packages --- fdm-rvo/package.json | 9 +- pnpm-lock.yaml | 384 ++++++++++++++++++++++++------------------- pnpm-workspace.yaml | 2 +- 3 files changed, 225 insertions(+), 170 deletions(-) diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json index 81d3db9b4..6c2c88c98 100644 --- a/fdm-rvo/package.json +++ b/fdm-rvo/package.json @@ -51,19 +51,20 @@ "test-coverage": "vitest run --coverage" }, "dependencies": { - "@nmi-agro/fdm-core": "workspace:*", - "@nmi-agro/rvo-connector": "^2.1.1", + "@nmi-agro/fdm-core": "workspace:^", + "@nmi-agro/rvo-connector": "^2.2.0", "@turf/area": "^7.3.4", "@turf/bbox": "^7.3.4", "@turf/helpers": "^7.3.4", "@turf/intersect": "^7.3.4", "@turf/union": "^7.3.4", - "zod": "^3.25.76" + "zod": "^4.3.6" }, "devDependencies": { + "@nmi-agro/fdm-core": "workspace:*", "@rollup/plugin-commonjs": "catalog:", "@rollup/plugin-node-resolve": "catalog:", - "@types/node": "^20.19.37", + "@types/node": "catalog:", "@vitest/coverage-v8": "catalog:", "rollup": "catalog:", "rollup-plugin-esbuild": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8dd557c1e..bc527a931 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -55,7 +55,7 @@ catalogs: specifier: ^7.3.1 version: 7.3.1 vitest: - specifier: ^4.0.18 + specifier: ^4.1.0 version: 4.0.18 packageExtensionsChecksum: sha256-VQuFGSJ2NQgmUfUYujaw/wyx7PoF3FcUJ5DmmDpAEDo= @@ -153,7 +153,7 @@ importers: version: 7.3.4 better-auth: specifier: 'catalog:' - version: 1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))) chrono-node: specifier: ^2.9.0 version: 2.9.0 @@ -174,7 +174,7 @@ importers: version: 3.3.3 drizzle-orm: specifier: 'catalog:' - version: 0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) + version: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) file-type: specifier: ^21.3.1 version: 21.3.1 @@ -583,11 +583,11 @@ importers: fdm-rvo: dependencies: '@nmi-agro/fdm-core': - specifier: workspace:* + specifier: workspace:^ version: link:../fdm-core '@nmi-agro/rvo-connector': - specifier: ^2.1.1 - version: 2.1.1 + specifier: ^2.2.0 + version: 2.2.0 '@turf/area': specifier: ^7.3.4 version: 7.3.4 @@ -604,21 +604,21 @@ importers: specifier: ^7.3.4 version: 7.3.4 zod: - specifier: ^3.25.76 - version: 3.25.76 + specifier: ^4.3.6 + version: 4.3.6 devDependencies: '@rollup/plugin-commonjs': specifier: 'catalog:' - version: 29.0.1(rollup@4.59.0) + version: 29.0.2(rollup@4.59.0) '@rollup/plugin-node-resolve': specifier: 'catalog:' version: 16.0.3(rollup@4.59.0) '@types/node': - specifier: ^20.19.37 - version: 20.19.37 + specifier: 'catalog:' + version: 25.4.0 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))) rollup: specifier: 'catalog:' version: 4.59.0 @@ -633,7 +633,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) packages: @@ -822,6 +822,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -1272,6 +1277,10 @@ packages: resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -2811,8 +2820,8 @@ packages: '@napi-rs/wasm-runtime@1.0.7': resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} - '@nmi-agro/rvo-connector@2.1.1': - resolution: {integrity: sha512-vHfiJquhs9bxQOS36aBJVKOSFo3gi/cpQ65tGuea9E2C1Cw1shXxxes4cBCQNpXLW4ua2OAWV2mcfoKFGmcZsA==} + '@nmi-agro/rvo-connector@2.2.0': + resolution: {integrity: sha512-PAbpaB/3YKV8/EqqZn3qdGXojTTxl9BquF63WHxEKeT1Oxj/1iQyBozwRTVeUD2yOb0YfL0os8UA+hH+8HJioA==} engines: {node: '>=18'} '@noble/ciphers@1.3.0': @@ -4257,15 +4266,6 @@ packages: '@repeaterjs/repeater@3.0.6': resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} - '@rollup/plugin-commonjs@29.0.1': - resolution: {integrity: sha512-VUEHINN2rQEWPfNUR3mzidRObM1XZKXMQsaG6qBlDqd6M1qyw91nDZvcSozgyjt3x/QKrgKBc5MdxfdxAy6tdg==} - engines: {node: '>=16.0.0 || 14 >= 14.17'} - peerDependencies: - rollup: ^2.68.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - '@rollup/plugin-commonjs@29.0.2': resolution: {integrity: sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -5581,9 +5581,6 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - '@types/node@20.19.37': - resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} - '@types/node@25.4.0': resolution: {integrity: sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==} @@ -5720,6 +5717,9 @@ packages: '@vitest/expect@4.0.18': resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + '@vitest/expect@4.1.0': + resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + '@vitest/mocker@4.0.18': resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} peerDependencies: @@ -5731,21 +5731,47 @@ packages: vite: optional: true + '@vitest/mocker@4.1.0': + resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@4.0.18': resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} + '@vitest/pretty-format@4.1.0': + resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + '@vitest/runner@4.0.18': resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + '@vitest/runner@4.1.0': + resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + '@vitest/snapshot@4.0.18': resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + '@vitest/snapshot@4.1.0': + resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + '@vitest/spy@4.0.18': resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} + '@vitest/spy@4.1.0': + resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@vitest/utils@4.1.0': + resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -10465,6 +10491,10 @@ packages: resolution: {integrity: sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==} engines: {node: '>=11.0.0'} + sax@1.6.0: + resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + engines: {node: '>=11.0.0'} + scheduler@0.25.0-rc-603e6108-20241029: resolution: {integrity: sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==} @@ -10734,6 +10764,9 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.0.0: + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} @@ -10929,6 +10962,10 @@ packages: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} + tinyexec@1.0.4: + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -10947,6 +10984,10 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -11136,9 +11177,6 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} @@ -11422,6 +11460,41 @@ packages: jsdom: optional: true + vitest@4.1.0: + resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.0 + '@vitest/browser-preview': 4.1.0 + '@vitest/browser-webdriverio': 4.1.0 + '@vitest/ui': 4.1.0 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + watchpack@2.5.1: resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} engines: {node: '>=10.13.0'} @@ -11646,9 +11719,6 @@ packages: zeptomatch@2.1.0: resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -11949,6 +12019,10 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -12521,6 +12595,8 @@ snapshots: '@babel/runtime@7.28.6': {} + '@babel/runtime@7.29.2': {} + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 @@ -12557,12 +12633,6 @@ snapshots: nanostores: 1.1.1 zod: 4.3.6 - '@better-auth/drizzle-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))': - dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) - '@better-auth/utils': 0.3.1 - drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) - '@better-auth/drizzle-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))': dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) @@ -13160,7 +13230,7 @@ snapshots: '@babel/preset-env': 7.29.0(@babel/core@7.29.0) '@babel/preset-react': 7.28.5(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@babel/runtime-corejs3': 7.29.0 '@babel/traverse': 7.29.0 '@docusaurus/logger': 3.9.2 @@ -14274,7 +14344,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -14435,7 +14505,7 @@ snapshots: '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -14610,7 +14680,7 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@nmi-agro/rvo-connector@2.1.1': + '@nmi-agro/rvo-connector@2.2.0': dependencies: jsonwebtoken: 9.0.3 proj4: 2.20.4 @@ -16291,18 +16361,6 @@ snapshots: '@repeaterjs/repeater@3.0.6': {} - '@rollup/plugin-commonjs@29.0.1(rollup@4.59.0)': - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.59.0) - commondir: 1.0.1 - estree-walker: 2.0.2 - fdir: 6.5.0(picomatch@4.0.3) - is-reference: 1.2.1 - magic-string: 0.30.21 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.59.0 - '@rollup/plugin-commonjs@29.0.2(rollup@4.59.0)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.59.0) @@ -16728,7 +16786,7 @@ snapshots: '@slorber/react-helmet-async@1.3.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 invariant: 2.2.4 prop-types: 15.8.1 react: 19.2.4 @@ -18172,11 +18230,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/bonjour@3.5.13': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/chai@5.2.3': dependencies: @@ -18186,11 +18244,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.8 - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/connect@3.4.38': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/d3-array@3.2.2': {} @@ -18242,7 +18300,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -18276,7 +18334,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/istanbul-lib-coverage@2.0.6': {} @@ -18322,16 +18380,12 @@ snapshots: '@types/mysql@2.15.27': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/node@12.20.55': {} '@types/node@17.0.45': {} - '@types/node@20.19.37': - dependencies: - undici-types: 6.21.0 - '@types/node@25.4.0': dependencies: undici-types: 7.18.2 @@ -18342,7 +18396,7 @@ snapshots: '@types/pg@8.15.6': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 pg-protocol: 1.13.0 pg-types: 2.2.0 @@ -18393,16 +18447,16 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/send@1.2.1': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/serve-index@1.9.4': dependencies: @@ -18411,12 +18465,12 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/send': 0.17.6 '@types/sockjs@0.3.36': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/supercluster@7.1.3': dependencies: @@ -18424,7 +18478,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/trusted-types@2.0.7': optional: true @@ -18447,7 +18501,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 '@types/yargs-parser@21.0.3': {} @@ -18470,7 +18524,7 @@ snapshots: optionalDependencies: maplibre-gl: 5.20.0 - '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.18 @@ -18481,10 +18535,10 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 std-env: 3.10.0 - tinyrainbow: 3.0.3 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + tinyrainbow: 3.1.0 + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) - '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/coverage-v8@4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.18 @@ -18495,8 +18549,8 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 std-env: 3.10.0 - tinyrainbow: 3.0.3 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + tinyrainbow: 3.1.0 + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) '@vitest/expect@4.0.18': dependencies: @@ -18507,17 +18561,26 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/expect@4.1.0': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: - '@vitest/spy': 4.0.18 + '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: @@ -18527,23 +18590,47 @@ snapshots: dependencies: tinyrainbow: 3.0.3 + '@vitest/pretty-format@4.1.0': + dependencies: + tinyrainbow: 3.1.0 + '@vitest/runner@4.0.18': dependencies: '@vitest/utils': 4.0.18 pathe: 2.0.3 + '@vitest/runner@4.1.0': + dependencies: + '@vitest/utils': 4.1.0 + pathe: 2.0.3 + '@vitest/snapshot@4.0.18': dependencies: '@vitest/pretty-format': 4.0.18 magic-string: 0.30.21 pathe: 2.0.3 + '@vitest/snapshot@4.1.0': + dependencies: + '@vitest/pretty-format': 4.1.0 + '@vitest/utils': 4.1.0 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@4.0.18': {} + '@vitest/spy@4.1.0': {} + '@vitest/utils@4.0.18': dependencies: '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 + + '@vitest/utils@4.1.0': + dependencies: + '@vitest/pretty-format': 4.1.0 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 '@webassemblyjs/ast@1.14.1': dependencies: @@ -18895,10 +18982,10 @@ snapshots: batch@0.6.1: {} - better-auth@1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)): + better-auth@1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) - '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))) + '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))) '@better-auth/kysely-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11) '@better-auth/memory-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1) '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0) @@ -18917,7 +19004,7 @@ snapshots: optionalDependencies: '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) drizzle-kit: 0.31.9 - drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) + drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) mongodb: 7.1.0 mysql2: 3.15.3 prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) @@ -18927,7 +19014,7 @@ snapshots: transitivePeerDependencies: - '@cloudflare/workers-types' - better-auth@1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)): + better-auth@1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))): dependencies: '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))) @@ -18955,7 +19042,7 @@ snapshots: prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) transitivePeerDependencies: - '@cloudflare/workers-types' @@ -19739,7 +19826,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 csstype: 3.2.3 dom-serializer@1.4.1: @@ -19804,17 +19891,6 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.45.1(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)): - optionalDependencies: - '@electric-sql/pglite': 0.3.15 - '@opentelemetry/api': 1.9.0 - '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) - '@types/pg': 8.15.6 - kysely: 0.28.11 - mysql2: 3.15.3 - postgres: 3.4.8 - prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)): optionalDependencies: '@electric-sql/pglite': 0.3.16 @@ -20156,7 +20232,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 require-like: 0.1.2 eventemitter3@4.0.7: {} @@ -20749,7 +20825,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -21207,7 +21283,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.19.37 + '@types/node': 25.4.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -21215,13 +21291,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 20.19.37 + '@types/node': 25.4.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -21535,7 +21611,7 @@ snapshots: magicast@0.5.2: dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 source-map-js: 1.2.1 @@ -22347,7 +22423,7 @@ snapshots: dependencies: citty: 0.2.1 pathe: 2.0.3 - tinyexec: 1.0.2 + tinyexec: 1.0.4 object-assign@4.1.1: {} @@ -23199,7 +23275,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.19.37 + '@types/node': 25.4.0 long: 5.3.2 protocol-buffers-schema@3.6.0: {} @@ -23389,7 +23465,7 @@ snapshots: react-loadable-ssr-addon-v5-slorber@1.0.1(@docusaurus/react-loadable@6.0.0(react@19.2.4))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 react-loadable: '@docusaurus/react-loadable@6.0.0(react@19.2.4)' webpack: 5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19)) @@ -23443,13 +23519,13 @@ snapshots: react-router-config@5.1.1(react-router@5.3.4(react@19.2.4))(react@19.2.4): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 react: 19.2.4 react-router: 5.3.4(react@19.2.4) react-router-dom@5.3.4(react@19.2.4): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -23466,7 +23542,7 @@ snapshots: react-router@5.3.4(react@19.2.4): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -23503,7 +23579,7 @@ snapshots: react-transition-group@4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -23902,6 +23978,8 @@ snapshots: sax@1.5.0: {} + sax@1.6.0: {} + scheduler@0.25.0-rc-603e6108-20241029: {} scheduler@0.27.0: {} @@ -24218,6 +24296,8 @@ snapshots: std-env@3.10.0: {} + std-env@4.0.0: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -24351,7 +24431,7 @@ snapshots: css-what: 6.2.2 csso: 5.0.5 picocolors: 1.1.1 - sax: 1.5.0 + sax: 1.6.0 swc-loader@0.2.7(@swc/core@1.15.18(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))): dependencies: @@ -24410,6 +24490,8 @@ snapshots: tinyexec@1.0.2: {} + tinyexec@1.0.4: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -24423,6 +24505,8 @@ snapshots: tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -24607,8 +24691,6 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@6.21.0: {} - undici-types@7.18.2: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -24871,22 +24953,6 @@ snapshots: - supports-color - typescript - vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.8 - rollup: 4.59.0 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 20.19.37 - fsevents: 2.3.3 - jiti: 2.6.1 - lightningcss: 1.32.0 - terser: 5.46.0 - yaml: 2.8.2 - vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): dependencies: esbuild: 0.27.3 @@ -24903,10 +24969,10 @@ snapshots: terser: 5.46.0 yaml: 2.8.2 - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -24923,11 +24989,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@20.19.37)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 - '@types/node': 20.19.37 + '@types/node': 25.4.0 transitivePeerDependencies: - jiti - less @@ -24941,43 +25007,33 @@ snapshots: - tsx - yaml - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): + vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)): dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 + '@vitest/expect': 4.1.0 + '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.1.0 + '@vitest/runner': 4.1.0 + '@vitest/snapshot': 4.1.0 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 + es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 25.4.0 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml watchpack@2.5.1: dependencies: @@ -25237,13 +25293,13 @@ snapshots: xml-js@1.6.11: dependencies: - sax: 1.5.0 + sax: 1.6.0 xml-utils@1.10.2: {} xml2js@0.6.2: dependencies: - sax: 1.5.0 + sax: 1.6.0 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} @@ -25273,8 +25329,6 @@ snapshots: grammex: 3.1.12 graphmatch: 1.1.1 - zod@3.25.76: {} - zod@4.3.6: {} zstddec@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2184fc571..3ce7e08b3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -25,4 +25,4 @@ catalog: typescript: ^5.9.3 vite: ^7.3.1 vite-tsconfig-paths: ^5.1.4 - vitest: ^4.0.18 + vitest: ^4.1.0 From 69f765f17f979b30bad79439a21094b5b66e72ae Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:16:49 +0100 Subject: [PATCH 031/108] feat: request regelingsPercelenMest as well --- fdm-rvo/src/data.test.ts | 35 +++++++++++++++++++++++++++ fdm-rvo/src/data.ts | 52 ++++++++++++++++++++++++++++++++-------- fdm-rvo/src/types.ts | 18 ++++++++++++++ 3 files changed, 95 insertions(+), 10 deletions(-) diff --git a/fdm-rvo/src/data.test.ts b/fdm-rvo/src/data.test.ts index b21392d77..b32752d38 100644 --- a/fdm-rvo/src/data.test.ts +++ b/fdm-rvo/src/data.test.ts @@ -20,10 +20,25 @@ describe("fetchRvoFields", () => { }, ] + const mockMestFeatures = [ + { + type: "Feature", + geometry: { type: "Polygon", coordinates: [] }, + properties: { + CropFieldID: "123", + Bufferstrook: true, + Regelingsgebied: "Yes", + }, + }, + ] + const mockClient = { opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ features: mockFeatures, }), + opvragenRegelingspercelenMest: vi.fn().mockResolvedValue({ + features: mockMestFeatures, + }), } as unknown as RvoClient const result = await fetchRvoFields(mockClient, "2024", "12345678") @@ -34,8 +49,19 @@ describe("fetchRvoFields", () => { farmId: "12345678", outputFormat: "geojson", }) + expect(mockClient.opvragenRegelingspercelenMest).toHaveBeenCalledWith({ + periodBeginDate: "2024-01-01", + periodEndDate: "2024-12-31", + farmId: "12345678", + outputFormat: "geojson", + }) expect(result).toHaveLength(1) expect(result[0].properties.CropFieldID).toBe("123") + expect(result[0].properties.mestData).toEqual({ + CropFieldID: "123", + Bufferstrook: true, + Regelingsgebied: "Yes", + }) }) it("should return empty array if no features found", async () => { @@ -43,6 +69,9 @@ describe("fetchRvoFields", () => { opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ features: [], }), + opvragenRegelingspercelenMest: vi.fn().mockResolvedValue({ + features: [], + }), } as unknown as RvoClient const result = await fetchRvoFields(mockClient, "2024", "12345678") @@ -54,6 +83,9 @@ describe("fetchRvoFields", () => { opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ somethingElse: [], }), + opvragenRegelingspercelenMest: vi.fn().mockResolvedValue({ + features: [], + }), } as unknown as RvoClient const result = await fetchRvoFields(mockClient, "2024", "12345678") @@ -75,6 +107,9 @@ describe("fetchRvoFields", () => { opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ features: mockFeatures, }), + opvragenRegelingspercelenMest: vi.fn().mockResolvedValue({ + features: [], + }), } as unknown as RvoClient await expect( diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts index 411372ab2..ffed7670e 100644 --- a/fdm-rvo/src/data.ts +++ b/fdm-rvo/src/data.ts @@ -19,23 +19,55 @@ export async function fetchRvoFields( year: string, kvkNumber: string, ): Promise { - // Request fields from RVO API + // Request fields and mest fields from RVO API concurrently // We request the full calendar year period - const fieldsRaw = await rvoClient.opvragenBedrijfspercelen({ - periodBeginDate: `${year}-01-01`, - periodEndDate: `${year}-12-31`, - farmId: kvkNumber, - outputFormat: "geojson", - }) + const [fieldsRaw, mestFieldsRaw] = await Promise.all([ + rvoClient.opvragenBedrijfspercelen({ + periodBeginDate: `${year}-01-01`, + periodEndDate: `${year}-12-31`, + farmId: kvkNumber, + outputFormat: "geojson", + }), + rvoClient.opvragenRegelingspercelenMest({ + periodBeginDate: `${year}-01-01`, + periodEndDate: `${year}-12-31`, + farmId: kvkNumber, + outputFormat: "geojson", + }).catch(err => { + // Catching in case this endpoint fails independently so we don't break the main flow. + console.warn("Failed to fetch RegelingspercelenMest:", err) + return { features: [] } + }) + ]) // The raw response is expected to be a GeoJSON FeatureCollection. // We access the 'features' array to iterate over individual fields. - // Safe casting is handled by the subsequent Zod validation. - const features = (fieldsRaw as any).features + const features = (fieldsRaw as any).features || [] + const mestFeatures = (mestFieldsRaw as any).features || [] if (Array.isArray(features)) { + // Create a lookup for MEST fields by CropFieldID + const mestLookup = new Map() + if (Array.isArray(mestFeatures)) { + for (const mf of mestFeatures) { + if (mf?.properties?.CropFieldID) { + mestLookup.set(String(mf.properties.CropFieldID), mf.properties) + } + } + } + + // Merge MEST data into Bedrijfspercelen features + for (const feature of features) { + if (feature?.properties?.CropFieldID) { + const cropFieldId = String(feature.properties.CropFieldID) + if (mestLookup.has(cropFieldId)) { + feature.properties.mestData = mestLookup.get(cropFieldId) + } + } + } + // Define a schema for an array of fields and parse the data. - // This ensures runtime type safety and filters out malformed records if configured (though Zod default is strict). + // This ensures runtime type safety and filters out malformed records if configured. const RvoFieldsArraySchema = z.array(RvoFieldSchema) return RvoFieldsArraySchema.parse(features) } diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index 75c82a9da..bc2904a8f 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -1,5 +1,21 @@ import { z } from "zod" +/** + * Zod schema for properties returned by the RegelingspercelenMest endpoint. + */ +export const MestDataSchema = z.object({ + /** Unique identifier for the crop field (linked to Bedrijfspercelen) */ + CropFieldID: z.string().optional(), + /** Indicates if the field is a buffer strip */ + Bufferstrook: z.boolean().optional(), + /** Soil type code */ + Grondsoort: z.union([z.string(), z.number()]).optional(), + /** Indicates if a catch crop (vanggewas/nateelt) is required or present for manure regulations */ + IndNateeltMest: z.boolean().optional(), + /** Human-readable labels and boolean mappings if `enrichResponse` was used */ + descriptiveValues: z.record(z.string(), z.any()).optional(), +}).catchall(z.any()) + /** * Zod schema for validating RVO Field data. * @@ -45,6 +61,8 @@ export const RvoFieldSchema = z.object({ UseTitleCode: z.string(), /** Optional cause for the field record */ CropFieldCause: z.string().optional(), + /** Optional data from the RegelingspercelenMest endpoint */ + mestData: MestDataSchema.optional(), }), }) From c0a67764759bab1523c3ab9f6518d1feb401e705 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:22:41 +0100 Subject: [PATCH 032/108] fix: tsbuild --- fdm-rvo/tsconfig.build.json | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/fdm-rvo/tsconfig.build.json b/fdm-rvo/tsconfig.build.json index dcd8e95ba..2f6af4264 100644 --- a/fdm-rvo/tsconfig.build.json +++ b/fdm-rvo/tsconfig.build.json @@ -1,12 +1,9 @@ { "extends": "./tsconfig.json", - "compilerOptions": { - "allowImportingTsExtensions": true, - "declaration": true, - "emitDeclarationOnly": true, - "outDir": "./dist", - "rootDir": "./src", - "tsBuildInfoFile": "./dist/.tsbuildinfo" - }, - "include": ["src/**/*"] + "exclude": [ + "**/*.test.ts", + "**/*.spec.ts", + "src/setup-test.ts", + "src/test-utils.ts" + ] } From eeec06fb147b0756a1e4781e3b3db70ad7514cb5 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:34:00 +0100 Subject: [PATCH 033/108] feat: add column in rvo fields table if the field is designated as buffer strip --- .../blocks/rvo/import-review-table.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index a8f491a4d..23c9711c4 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -478,6 +478,36 @@ export const columns: ColumnDef>[] = [ ) }, }, + { + id: "bufferstrook", + header: () => ( + + Bufferstrook + + Geeft aan of het perceel bij RVO geregistreerd staat als + bufferstrook. + + + ), + cell: ({ row }) => { + const item = row.original + const isBufferstrip = item.rvoField?.properties.mestData?.Bufferstrook + + if (isBufferstrip === undefined) return - + + return ( + + {isBufferstrip ? "Ja" : "Nee"} + + ) + }, + }, { id: "actions", header: () => ( From 10d49ebacc37e2e569dbec751eb277155367303c Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:13:41 +0100 Subject: [PATCH 034/108] refactor: modernize and consolidate monorepo ts configuration --- fdm-app/tsconfig.json | 18 ++---------------- fdm-calculator/tsconfig.json | 20 ++------------------ fdm-core/tsconfig.json | 20 ++------------------ fdm-data/tsconfig.json | 19 ++----------------- fdm-rvo/tsconfig.json | 16 +++------------- tsconfig.apps.json | 10 ++++++++++ tsconfig.base.json | 17 +++++++++++++++++ tsconfig.packages.json | 8 ++++++++ 8 files changed, 46 insertions(+), 82 deletions(-) create mode 100644 tsconfig.apps.json create mode 100644 tsconfig.base.json create mode 100644 tsconfig.packages.json diff --git a/fdm-app/tsconfig.json b/fdm-app/tsconfig.json index dbe2a2d85..d94b0c50f 100644 --- a/fdm-app/tsconfig.json +++ b/fdm-app/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": "../tsconfig.apps.json", "include": [ "**/*.ts", "**/*.tsx", @@ -13,20 +14,7 @@ "app/types/public-env.d.ts" ], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], "types": ["@react-router/node", "vite/client"], - "isolatedModules": true, - "esModuleInterop": true, - "jsx": "react-jsx", - "module": "ESNext", - "moduleResolution": "Bundler", - "resolveJsonModule": true, - "target": "ES2022", - "strict": true, - "allowJs": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "baseUrl": ".", "paths": { "@/*": ["./*"], "~/*": ["./app/*"], @@ -34,8 +22,6 @@ "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"], "@nmi-agro/fdm-calculator": ["../fdm-calculator/src/index.ts"] }, - "rootDirs": [".", "./.react-router/types"], - - "noEmit": true + "rootDirs": [".", "./.react-router/types"] } } diff --git a/fdm-calculator/tsconfig.json b/fdm-calculator/tsconfig.json index 732170cd1..3865e1a1f 100644 --- a/fdm-calculator/tsconfig.json +++ b/fdm-calculator/tsconfig.json @@ -1,26 +1,10 @@ { + "extends": "../tsconfig.packages.json", "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "declaration": true, "noEmit": false, - "emitDeclarationOnly": true, + "rootDir": "./src", "outDir": "./dist", "types": ["vitest/globals", "node"], - - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", "paths": { "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] diff --git a/fdm-core/tsconfig.json b/fdm-core/tsconfig.json index 9f3de4232..b1e8dfb25 100644 --- a/fdm-core/tsconfig.json +++ b/fdm-core/tsconfig.json @@ -1,24 +1,8 @@ { + "extends": "../tsconfig.packages.json", "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "declaration": true, + "rootDir": "./src", "outDir": "./dist", - "emitDeclarationOnly": true, - - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", "paths": { "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] } diff --git a/fdm-data/tsconfig.json b/fdm-data/tsconfig.json index 9f478da64..79ed647aa 100644 --- a/fdm-data/tsconfig.json +++ b/fdm-data/tsconfig.json @@ -1,23 +1,8 @@ { + "extends": "../tsconfig.packages.json", "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "emitDeclarationOnly": true, - "declaration": true, + "rootDir": "./src", "outDir": "./dist", - - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, "resolveJsonModule": true }, "include": ["./src/**/*"] diff --git a/fdm-rvo/tsconfig.json b/fdm-rvo/tsconfig.json index f6036312d..63b0cd986 100644 --- a/fdm-rvo/tsconfig.json +++ b/fdm-rvo/tsconfig.json @@ -1,18 +1,8 @@ { + "extends": "../tsconfig.packages.json", "compilerOptions": { - "target": "ES2022", - "useDefineForClassFields": true, - "module": "ESNext", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - "moduleResolution": "bundler", - "isolatedModules": true, - "moduleDetection": "force", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "baseUrl": ".", + "rootDir": "./src", + "outDir": "./dist", "paths": { "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] diff --git a/tsconfig.apps.json b/tsconfig.apps.json new file mode 100644 index 000000000..2f5dbbd7c --- /dev/null +++ b/tsconfig.apps.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "allowJs": true, + "jsx": "react-jsx", + "resolveJsonModule": true, + "esModuleInterop": true + } +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 000000000..38504b1ab --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "isolatedModules": true, + "moduleDetection": "force", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "forceConsistentCasingInFileNames": true, + "useDefineForClassFields": true + } +} diff --git a/tsconfig.packages.json b/tsconfig.packages.json new file mode 100644 index 000000000..341712659 --- /dev/null +++ b/tsconfig.packages.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "allowImportingTsExtensions": true + } +} From 70c1c27a45d4081ff27338e0aa052f65a96dc933 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:36:16 +0100 Subject: [PATCH 035/108] fix: fix various issues regarding the merge conflict --- fdm-app/app/integrations/calculator.ts | 2 +- .../app/routes/api.soil-analysis.extract.ts | 2 +- ..._farm.$calendar.atlas.fields.$centroid.tsx | 5 +- ...calendar.field.$b_id.fertilizer._index.tsx | 2 - fdm-app/app/routes/farm.$b_id_farm._index.tsx | 84 ++++++------------- ...arm.create.$b_id_farm.$calendar._index.tsx | 2 +- 6 files changed, 33 insertions(+), 64 deletions(-) diff --git a/fdm-app/app/integrations/calculator.ts b/fdm-app/app/integrations/calculator.ts index 815c46611..7497919be 100644 --- a/fdm-app/app/integrations/calculator.ts +++ b/fdm-app/app/integrations/calculator.ts @@ -29,7 +29,7 @@ import { type Timeframe, } from "@nmi-agro/fdm-core" import { getDefaultCultivation } from "~/lib/cultivation-helpers" -import { getNmiApiKey } from "./nmi" +import { getNmiApiKey } from "./nmi.server" // Get nitrogen balance for a field export async function getNitrogenBalanceForField({ diff --git a/fdm-app/app/routes/api.soil-analysis.extract.ts b/fdm-app/app/routes/api.soil-analysis.extract.ts index d7b774049..bb3ab2b59 100644 --- a/fdm-app/app/routes/api.soil-analysis.extract.ts +++ b/fdm-app/app/routes/api.soil-analysis.extract.ts @@ -1,5 +1,5 @@ import type { ActionFunctionArgs } from "react-router" -import { extractBulkSoilAnalyses } from "~/integrations/nmi" +import { extractBulkSoilAnalyses } from "~/integrations/nmi.server" import { getSession } from "~/lib/auth.server" /** diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx index b7dcc25a2..3209a694b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.atlas.fields.$centroid.tsx @@ -28,7 +28,10 @@ import { FieldDetailsAtlasSkeleton } from "~/components/blocks/atlas-fields/skel import { SoilTextureCard } from "~/components/blocks/atlas-fields/soil-texture" import { ErrorBlock } from "~/components/custom/error" import { Button } from "~/components/ui/button" -import { getNmiApiKey, getSoilParameterEstimates } from "~/integrations/nmi" +import { + getNmiApiKey, + getSoilParameterEstimates, +} from "~/integrations/nmi.server" import { getCalendar } from "~/lib/calendar" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx index df0d5ced3..7d80f5d38 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.$b_id.fertilizer._index.tsx @@ -26,11 +26,9 @@ import { FormSchemaModify, } from "~/components/blocks/fertilizer-applications/formschema" import { FertilizerApplicationMetricsCard } from "~/components/blocks/fertilizer-applications/metrics" -import { getNmiApiKey } from "~/integrations/nmi" import { getSession } from "~/lib/auth.server" import { getCalendar, getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" -import { getDefaultCultivation } from "~/lib/cultivation-helpers" import { handleActionError, handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { extractFormValuesFromRequest } from "~/lib/form" diff --git a/fdm-app/app/routes/farm.$b_id_farm._index.tsx b/fdm-app/app/routes/farm.$b_id_farm._index.tsx index 3b5fe203b..ad7676ced 100644 --- a/fdm-app/app/routes/farm.$b_id_farm._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm._index.tsx @@ -1,8 +1,12 @@ import { cowHead } from "@lucide/lab" -import { getFarm, getFarms, getFields } from "@nmi-agro/fdm-core" +import { + checkPermission, + getFarm, + getFarms, + getFields, +} from "@nmi-agro/fdm-core" import { ArrowRightLeft, - ArrowDownToLine, BookOpenText, ChevronUp, DownloadIcon, @@ -446,62 +450,6 @@ export default function FarmDashboardIndex() { - - - -
-
- -
-
- - Nieuwe percelen - - - Voeg nieuwe percelen - toe aan dit bedrijf. - -
-
-
-
-
-
- - - {/* Acties */} -
-

- Acties -

-
- - - -
-
- -
-
- - Nieuw perceel - - - Teken of selecteer - een perceel op de - kaart. - -
-
-
-
-
{loaderData.isRvoConfigured && ( )} + + + +
+
+ +
+
+ + Nieuwe percelen + + + Voeg nieuwe percelen + toe aan dit bedrijf. + +
+
+
+
+
diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx index fba4d8427..552663699 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx @@ -1,5 +1,5 @@ import { getFarm } from "@nmi-agro/fdm-core" -import { Map as MapIcon, UploadCloud } from "lucide-react" +import { DownloadCloud, Map as MapIcon, UploadCloud } from "lucide-react" import type { LoaderFunctionArgs, MetaFunction } from "react-router" import { data, NavLink, useLoaderData } from "react-router" import { Header } from "~/components/blocks/header/base" From 50dcdf0e926b14aa07e74589e0b28a4f345a4657 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 10:58:09 +0100 Subject: [PATCH 036/108] fix: path for compilier option --- fdm-calculator/tsconfig.build.json | 3 +++ fdm-core/tsconfig.build.json | 3 +++ fdm-data/tsconfig.build.json | 3 +++ fdm-rvo/tsconfig.build.json | 3 +++ 4 files changed, 12 insertions(+) diff --git a/fdm-calculator/tsconfig.build.json b/fdm-calculator/tsconfig.build.json index 2f6af4264..094d1f14d 100644 --- a/fdm-calculator/tsconfig.build.json +++ b/fdm-calculator/tsconfig.build.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", + "compilerOptions": { + "paths": {} + }, "exclude": [ "**/*.test.ts", "**/*.spec.ts", diff --git a/fdm-core/tsconfig.build.json b/fdm-core/tsconfig.build.json index d31c25068..c0c91e037 100644 --- a/fdm-core/tsconfig.build.json +++ b/fdm-core/tsconfig.build.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", + "compilerOptions": { + "paths": {} + }, "exclude": [ "**/*.test.ts", "**/*.spec.ts", diff --git a/fdm-data/tsconfig.build.json b/fdm-data/tsconfig.build.json index 2f6af4264..094d1f14d 100644 --- a/fdm-data/tsconfig.build.json +++ b/fdm-data/tsconfig.build.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", + "compilerOptions": { + "paths": {} + }, "exclude": [ "**/*.test.ts", "**/*.spec.ts", diff --git a/fdm-rvo/tsconfig.build.json b/fdm-rvo/tsconfig.build.json index 2f6af4264..094d1f14d 100644 --- a/fdm-rvo/tsconfig.build.json +++ b/fdm-rvo/tsconfig.build.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", + "compilerOptions": { + "paths": {} + }, "exclude": [ "**/*.test.ts", "**/*.spec.ts", From 80ea3edf4eba755657e74355024490f182153fe4 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:12:57 +0100 Subject: [PATCH 037/108] fix: lockfile --- pnpm-lock.yaml | 2979 +++++++++++++++++------------------------------- 1 file changed, 1076 insertions(+), 1903 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc527a931..326535998 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,7 +8,7 @@ catalogs: default: '@dotenvx/dotenvx': specifier: ^1.54.1 - version: 1.54.1 + version: 1.55.1 '@rollup/plugin-commonjs': specifier: ^29.0.2 version: 29.0.2 @@ -20,16 +20,16 @@ catalogs: version: 16.0.3 '@types/node': specifier: ^25.4.0 - version: 25.4.0 + version: 25.5.0 '@vitest/coverage-v8': specifier: 4.0.18 version: 4.0.18 better-auth: specifier: ^1.5.4 - version: 1.5.4 + version: 1.5.5 drizzle-kit: specifier: ^0.31.9 - version: 0.31.9 + version: 0.31.10 drizzle-orm: specifier: ^0.45.1 version: 0.45.1 @@ -56,7 +56,7 @@ catalogs: version: 7.3.1 vitest: specifier: ^4.1.0 - version: 4.0.18 + version: 4.1.0 packageExtensionsChecksum: sha256-VQuFGSJ2NQgmUfUYujaw/wyx7PoF3FcUJ5DmmDpAEDo= @@ -66,16 +66,16 @@ importers: devDependencies: '@biomejs/biome': specifier: ^2.4.6 - version: 2.4.6 + version: 2.4.8 '@changesets/changelog-github': specifier: ^0.6.0 version: 0.6.0 '@changesets/cli': specifier: ^2.30.0 - version: 2.30.0(@types/node@25.4.0) + version: 2.30.0(@types/node@25.5.0) turbo: specifier: ^2.8.16 - version: 2.8.16 + version: 2.8.19 fdm-app: dependencies: @@ -84,7 +84,7 @@ importers: version: 1.4.1 '@geomatico/maplibre-cog-protocol': specifier: github:SvenVw/maplibre-cog-protocol#add-prepare - version: https://codeload.github.com/SvenVw/maplibre-cog-protocol/tar.gz/fd6765830cd1453c3d20d290d6c40684153c5b7d(maplibre-gl@5.20.0) + version: https://codeload.github.com/SvenVw/maplibre-cog-protocol/tar.gz/fd6765830cd1453c3d20d290d6c40684153c5b7d(maplibre-gl@5.20.2) '@hookform/resolvers': specifier: ^5.2.2 version: 5.2.2(react-hook-form@7.71.2(react@19.2.4)) @@ -96,7 +96,7 @@ importers: version: 1.0.1 '@maplibre/maplibre-gl-geocoder': specifier: ^1.9.4 - version: 1.9.4(maplibre-gl@5.20.0) + version: 1.9.4(maplibre-gl@5.20.2) '@nmi-agro/fdm-calculator': specifier: workspace:^ version: link:../fdm-calculator @@ -111,10 +111,10 @@ importers: version: link:../fdm-rvo '@react-email/components': specifier: ^1.0.8 - version: 1.0.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.0.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@react-email/tailwind': specifier: ^2.0.5 - version: 2.0.5(@react-email/body@0.2.1(react@19.2.4))(@react-email/button@0.2.1(react@19.2.4))(@react-email/code-block@0.2.1(react@19.2.4))(@react-email/code-inline@0.0.6(react@19.2.4))(@react-email/container@0.0.16(react@19.2.4))(@react-email/heading@0.0.16(react@19.2.4))(@react-email/hr@0.0.12(react@19.2.4))(@react-email/img@0.0.12(react@19.2.4))(@react-email/link@0.0.13(react@19.2.4))(@react-email/preview@0.0.14(react@19.2.4))(@react-email/text@0.1.6(react@19.2.4))(react@19.2.4) + version: 2.0.6(@react-email/body@0.3.0(react@19.2.4))(@react-email/button@0.2.1(react@19.2.4))(@react-email/code-block@0.2.1(react@19.2.4))(@react-email/code-inline@0.0.6(react@19.2.4))(@react-email/container@0.0.16(react@19.2.4))(@react-email/heading@0.0.16(react@19.2.4))(@react-email/hr@0.0.12(react@19.2.4))(@react-email/img@0.0.12(react@19.2.4))(@react-email/link@0.0.13(react@19.2.4))(@react-email/preview@0.0.14(react@19.2.4))(@react-email/text@0.1.6(react@19.2.4))(react@19.2.4) '@react-pdf/renderer': specifier: ^4.3.2 version: 4.3.2(react@19.2.4) @@ -132,13 +132,13 @@ importers: version: 0.15.0 '@sentry/profiling-node': specifier: ^10.43.0 - version: 10.43.0 + version: 10.44.0 '@sentry/react-router': specifier: ^10.43.0 - version: 10.43.0(@react-router/node@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(rollup@4.59.0) + version: 10.44.0(@react-router/node@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(rollup@4.59.0) '@tailwindcss/vite': specifier: ^4.2.1 - version: 4.2.1(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 4.2.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -153,7 +153,7 @@ importers: version: 7.3.4 better-auth: specifier: 'catalog:' - version: 1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))) + version: 1.5.5(drizzle-kit@0.31.10)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8))(mongodb@7.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) chrono-node: specifier: ^2.9.0 version: 2.9.0 @@ -174,16 +174,16 @@ importers: version: 3.3.3 drizzle-orm: specifier: 'catalog:' - version: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) + version: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8) file-type: specifier: ^21.3.1 - version: 21.3.1 + version: 21.3.3 flatgeobuf: specifier: ^4.4.0 version: 4.4.0 framer-motion: specifier: ^12.35.2 - version: 12.35.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) fuzzysort: specifier: ^3.1.0 version: 3.1.0 @@ -201,10 +201,10 @@ importers: version: 0.577.0(react@19.2.4) maplibre-gl: specifier: ^5.20.0 - version: 5.20.0 + version: 5.20.2 nanoid: specifier: ^5.1.6 - version: 5.1.6 + version: 5.1.7 next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -213,10 +213,10 @@ importers: version: 3.4.8 posthog-js: specifier: ^1.360.1 - version: 1.360.1 + version: 1.362.0 posthog-node: specifier: ^5.28.1 - version: 5.28.1 + version: 5.28.4 postmark: specifier: ^4.0.7 version: 4.0.7 @@ -240,7 +240,7 @@ importers: version: 7.71.2(react@19.2.4) react-map-gl: specifier: ^8.1.0 - version: 8.1.0(maplibre-gl@5.20.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 8.1.0(maplibre-gl@5.20.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react-markdown: specifier: ^10.1.0 version: 10.1.0(@types/react@19.2.14)(react@19.2.4) @@ -264,7 +264,7 @@ importers: version: 4.0.0(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) remix-utils: specifier: ^9.3.0 - version: 9.3.0(@standard-schema/spec@1.1.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + version: 9.3.1(@standard-schema/spec@1.1.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) shpjs: specifier: ^6.2.0 version: 6.2.0 @@ -276,7 +276,7 @@ importers: version: 3.5.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@4.2.1) + version: 1.0.7(tailwindcss@4.2.2) validator: specifier: ^13.15.26 version: 13.15.26 @@ -285,20 +285,20 @@ importers: version: 4.3.6 zustand: specifier: ^5.0.11 - version: 5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + version: 5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) devDependencies: '@dotenvx/dotenvx': specifier: 'catalog:' - version: 1.54.1 + version: 1.55.1 '@react-router/dev': specifier: ^7.13.1 - version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))(yaml@2.8.2) + version: 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2) '@react-router/fs-routes': specifier: ^7.13.1 - version: 7.13.1(@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))(yaml@2.8.2))(typescript@5.9.3) + version: 7.13.1(@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2))(typescript@5.9.3) '@tailwindcss/postcss': specifier: ^4.2.1 - version: 4.2.1 + version: 4.2.2 '@types/geojson': specifier: ^7946.0.16 version: 7946.0.16 @@ -325,19 +325,19 @@ importers: version: 8.5.8 tailwindcss: specifier: ^4.2.1 - version: 4.2.1 + version: 4.2.2 typescript: specifier: 'catalog:' version: 5.9.3 vite: specifier: 'catalog:' - version: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + version: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) vite-node: specifier: ^5.3.0 - version: 5.3.0(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + version: 5.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) vite-tsconfig-paths: specifier: ^6.1.1 - version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) fdm-calculator: dependencies: @@ -352,11 +352,11 @@ importers: version: 10.6.0 geotiff: specifier: ^3.0.4 - version: 3.0.4 + version: 3.0.5 devDependencies: '@dotenvx/dotenvx': specifier: 'catalog:' - version: 1.54.1 + version: 1.55.1 '@rollup/plugin-commonjs': specifier: 'catalog:' version: 29.0.2(rollup@4.59.0) @@ -365,10 +365,10 @@ importers: version: 16.0.3(rollup@4.59.0) '@types/node': specifier: 'catalog:' - version: 25.4.0 + version: 25.5.0 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) postgres: specifier: ^3.4.8 version: 3.4.8 @@ -377,7 +377,7 @@ importers: version: 4.59.0 rollup-plugin-esbuild: specifier: 'catalog:' - version: 6.2.1(esbuild@0.27.3)(rollup@4.59.0) + version: 6.2.1(esbuild@0.27.4)(rollup@4.59.0) rollup-plugin-polyfill-node: specifier: 'catalog:' version: 0.13.0(rollup@4.59.0) @@ -392,7 +392,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) fdm-core: dependencies: @@ -407,16 +407,16 @@ importers: version: 7946.0.16 better-auth: specifier: 'catalog:' - version: 1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 1.5.5(drizzle-kit@0.31.10)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8))(mongodb@7.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) decimal.js: specifier: ^10.6.0 version: 10.6.0 drizzle-orm: specifier: 'catalog:' - version: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) + version: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8) nanoid: specifier: ^5.1.6 - version: 5.1.6 + version: 5.1.7 postgres: specifier: ^3.4.8 version: 3.4.8 @@ -432,7 +432,7 @@ importers: devDependencies: '@dotenvx/dotenvx': specifier: 'catalog:' - version: 1.54.1 + version: 1.55.1 '@rollup/plugin-commonjs': specifier: 'catalog:' version: 29.0.2(rollup@4.59.0) @@ -441,16 +441,16 @@ importers: version: 16.0.3(rollup@4.59.0) '@types/node': specifier: 'catalog:' - version: 25.4.0 + version: 25.5.0 '@types/validator': specifier: ^13.15.10 version: 13.15.10 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) drizzle-kit: specifier: 'catalog:' - version: 0.31.9 + version: 0.31.10 fs-extra: specifier: ^11.3.4 version: 11.3.4 @@ -462,7 +462,7 @@ importers: version: 4.59.0 rollup-plugin-esbuild: specifier: 'catalog:' - version: 6.2.1(esbuild@0.27.3)(rollup@4.59.0) + version: 6.2.1(esbuild@0.27.4)(rollup@4.59.0) rollup-plugin-polyfill-node: specifier: 'catalog:' version: 0.13.0(rollup@4.59.0) @@ -477,7 +477,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) fdm-data: dependencies: @@ -487,7 +487,7 @@ importers: devDependencies: '@dotenvx/dotenvx': specifier: 'catalog:' - version: 1.54.1 + version: 1.55.1 '@rollup/plugin-commonjs': specifier: 'catalog:' version: 29.0.2(rollup@4.59.0) @@ -499,13 +499,13 @@ importers: version: 16.0.3(rollup@4.59.0) '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) rollup: specifier: 'catalog:' version: 4.59.0 rollup-plugin-esbuild: specifier: 'catalog:' - version: 6.2.1(esbuild@0.27.3)(rollup@4.59.0) + version: 6.2.1(esbuild@0.27.4)(rollup@4.59.0) rollup-plugin-polyfill-node: specifier: 'catalog:' version: 0.13.0(rollup@4.59.0) @@ -520,22 +520,22 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) fdm-docs: dependencies: '@docusaurus/core': specifier: 3.9.2 - version: 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/faster': specifier: 3.9.2 version: 3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19) '@docusaurus/plugin-content-docs': specifier: 3.9.2 - version: 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/preset-classic': specifier: 3.9.2 - version: 3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3) + version: 3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3) '@mdx-js/react': specifier: ^3.1.1 version: 3.1.1(@types/react@19.2.14)(react@19.2.4) @@ -566,7 +566,7 @@ importers: version: 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) docusaurus-plugin-typedoc: specifier: ^1.4.2 - version: 1.4.2(typedoc-plugin-markdown@4.10.0(typedoc@0.28.17(typescript@5.9.3))) + version: 1.4.2(typedoc-plugin-markdown@4.11.0(typedoc@0.28.17(typescript@5.9.3))) markdownlint-cli2: specifier: ^0.21.0 version: 0.21.0 @@ -575,7 +575,7 @@ importers: version: 0.28.17(typescript@5.9.3) typedoc-plugin-markdown: specifier: ^4.10.0 - version: 4.10.0(typedoc@0.28.17(typescript@5.9.3)) + version: 4.11.0(typedoc@0.28.17(typescript@5.9.3)) typescript: specifier: 'catalog:' version: 5.9.3 @@ -615,16 +615,16 @@ importers: version: 16.0.3(rollup@4.59.0) '@types/node': specifier: 'catalog:' - version: 25.4.0 + version: 25.5.0 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))) + version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) rollup: specifier: 'catalog:' version: 4.59.0 rollup-plugin-esbuild: specifier: 'catalog:' - version: 6.2.1(esbuild@0.27.3)(rollup@4.59.0) + version: 6.2.1(esbuild@0.27.4)(rollup@4.59.0) typedoc: specifier: 'catalog:' version: 0.28.17(typescript@5.9.3) @@ -633,7 +633,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) packages: @@ -750,8 +750,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.7': - resolution: {integrity: sha512-6Fqi8MtQ/PweQ9xvux65emkLQ83uB+qAVtfHkC9UodyHMIZdxNI01HjLCLUtybElp2KY2XNE0nOgyP1E1vXw9w==} + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -813,15 +813,10 @@ packages: resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.29.2': resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} @@ -1246,8 +1241,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.29.0': - resolution: {integrity: sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==} + '@babel/preset-env@7.29.2': + resolution: {integrity: sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1269,12 +1264,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime-corejs3@7.29.0': - resolution: {integrity: sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime-corejs3@7.29.2': + resolution: {integrity: sha512-Lc94FOD5+0aXhdb0Tdg3RUtqT6yWbI/BbFWvlaSJ3gAb9Ks+99nHRDKADVqC37er4eCB0fHyWT+y+K3QOvJKbw==} engines: {node: '>=6.9.0'} '@babel/runtime@7.29.2': @@ -1297,8 +1288,8 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@better-auth/core@1.5.4': - resolution: {integrity: sha512-k5AdwPRQETZn0vdB60EB9CDxxfllpJXKqVxTjyXIUSRz7delNGlU0cR/iRP3VfVJwvYR1NbekphBDNo+KGoEzQ==} + '@better-auth/core@1.5.5': + resolution: {integrity: sha512-1oR/2jAp821Dcf67kQYHUoyNcdc1TcShfw4QMK0YTVntuRES5mUOyvEJql5T6eIuLfaqaN4LOF78l0FtF66HXA==} peerDependencies: '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 @@ -1311,45 +1302,53 @@ packages: '@cloudflare/workers-types': optional: true - '@better-auth/drizzle-adapter@1.5.4': - resolution: {integrity: sha512-4M4nMAWrDd3TmpV6dONkJjybBVKRZghe5Oj0NNyDEoXubxastQdO7Sb5B54I1rTx5yoMgsqaB+kbJnu/9UgjQg==} + '@better-auth/drizzle-adapter@1.5.5': + resolution: {integrity: sha512-HAi9xAP40oDt48QZeYBFTcmg3vt1Jik90GwoRIfangd7VGbxesIIDBJSnvwMbZ52GBIc6+V4FRw9lasNiNrPfw==} peerDependencies: - '@better-auth/core': 1.5.4 + '@better-auth/core': 1.5.5 '@better-auth/utils': ^0.3.0 drizzle-orm: '>=0.41.0' + peerDependenciesMeta: + drizzle-orm: + optional: true - '@better-auth/kysely-adapter@1.5.4': - resolution: {integrity: sha512-DPww7rIfz6Ed7dZlJSW9xMQ42VKaJLB5Cs+pPqd+UHKRyighKjf3VgvMIcAdFPc4olQ0qRHo3+ZJhFlBCxRhxA==} + '@better-auth/kysely-adapter@1.5.5': + resolution: {integrity: sha512-LmHffIVnqbfsxcxckMOoE8MwibWrbVFch+kwPKJ5OFDFv6lin75ufN7ZZ7twH0IMPLT/FcgzaRjP8jRrXRef9g==} peerDependencies: - '@better-auth/core': 1.5.4 + '@better-auth/core': 1.5.5 '@better-auth/utils': ^0.3.0 kysely: ^0.27.0 || ^0.28.0 - '@better-auth/memory-adapter@1.5.4': - resolution: {integrity: sha512-iiWYut9rbQqiAsgRBtj6+nxanwjapxRgpIJbiS2o81h7b9iclE0AiDA0Foes590gdFQvskNauZcCpuF8ytxthg==} + '@better-auth/memory-adapter@1.5.5': + resolution: {integrity: sha512-4X0j1/2L+nsgmObjmy9xEGUFWUv38Qjthp558fwS3DAp6ueWWyCaxaD6VJZ7m5qPNMrsBStO5WGP8CmJTEWm7g==} peerDependencies: - '@better-auth/core': 1.5.4 + '@better-auth/core': 1.5.5 '@better-auth/utils': ^0.3.0 - '@better-auth/mongo-adapter@1.5.4': - resolution: {integrity: sha512-ArzJN5Obk6i6+vLK1HpPzLIcsjxZYXPPUvxVU8eyU5HyoUT2MlswWfPQ8UJAKPn0iq/T4PVp/wZcQMhWk1tuNA==} + '@better-auth/mongo-adapter@1.5.5': + resolution: {integrity: sha512-P1J9ljL5X5k740I8Rx1esPWNgWYPdJR5hf2CY7BwDSrQFPUHuzeCg0YhtEEP55niNateTXhBqGAcy0fVOeamZg==} peerDependencies: - '@better-auth/core': 1.5.4 + '@better-auth/core': 1.5.5 '@better-auth/utils': ^0.3.0 mongodb: ^6.0.0 || ^7.0.0 - '@better-auth/prisma-adapter@1.5.4': - resolution: {integrity: sha512-ZQTbcBopw/ezjjbNFsfR3CRp0QciC4tJCarAnB5G9fZtUYbDjfY0vZOxIRmU4kI3x755CXQpGqTrkwmXaMRa3w==} + '@better-auth/prisma-adapter@1.5.5': + resolution: {integrity: sha512-CliDd78CXHzzwQIXhCdwGr5Ml53i6JdCHWV7PYwTIJz9EAm6qb2RVBdpP3nqEfNjINGM22A6gfleCgCdZkTIZg==} peerDependencies: - '@better-auth/core': 1.5.4 + '@better-auth/core': 1.5.5 '@better-auth/utils': ^0.3.0 '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + '@prisma/client': + optional: true + prisma: + optional: true - '@better-auth/telemetry@1.5.4': - resolution: {integrity: sha512-mGXTY7Ecxo7uvlMr6TFCBUvlH0NUMOeE9LKgPhG4HyhBN6VfCEg/DD9PG0Z2IatmMWQbckkt7ox5A0eBpG9m5w==} + '@better-auth/telemetry@1.5.5': + resolution: {integrity: sha512-1+lklxArn4IMHuU503RcPdXrSG2tlXt4jnGG3omolmspQ7tktg/Y9XO/yAkYDurtvMn1xJ8X1Ov01Ji/r5s9BQ==} peerDependencies: - '@better-auth/core': 1.5.4 + '@better-auth/core': 1.5.5 '@better-auth/utils@0.3.1': resolution: {integrity: sha512-+CGp4UmZSUrHHnpHhLPYu6cV+wSUSvVbZbNykxhUDocpVNTo9uFFxw/NqJlh1iC4wQ9HKKWGCKuZ5wUgS0v6Kg==} @@ -1357,65 +1356,65 @@ packages: '@better-fetch/fetch@1.1.21': resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==} - '@biomejs/biome@2.4.6': - resolution: {integrity: sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ==} + '@biomejs/biome@2.4.8': + resolution: {integrity: sha512-ponn0oKOky1oRXBV+rlSaUlixUxf1aZvWC19Z41zBfUOUesthrQqL3OtiAlSB1EjFjyWpn98Q64DHelhA6jNlA==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.4.6': - resolution: {integrity: sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ==} + '@biomejs/cli-darwin-arm64@2.4.8': + resolution: {integrity: sha512-ARx0tECE8I7S2C2yjnWYLNbBdDoPdq3oyNLhMglmuctThwUsuzFWRKrHmIGwIRWKz0Mat9DuzLEDp52hGnrxGQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.4.6': - resolution: {integrity: sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw==} + '@biomejs/cli-darwin-x64@2.4.8': + resolution: {integrity: sha512-Jg9/PsB9vDCJlANE8uhG7qDhb5w0Ix69D7XIIc8IfZPUoiPrbLm33k2Ig3NOJ/7nb3UbesFz3D1aDKm9DvzjhQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.4.6': - resolution: {integrity: sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A==} + '@biomejs/cli-linux-arm64-musl@2.4.8': + resolution: {integrity: sha512-Zo9OhBQDJ3IBGPlqHiTISloo5H0+FBIpemqIJdW/0edJ+gEcLR+MZeZozcUyz3o1nXkVA7++DdRKQT0599j9jA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [musl] - '@biomejs/cli-linux-arm64@2.4.6': - resolution: {integrity: sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew==} + '@biomejs/cli-linux-arm64@2.4.8': + resolution: {integrity: sha512-5CdrsJct76XG2hpKFwXnEtlT1p+4g4yV+XvvwBpzKsTNLO9c6iLlAxwcae2BJ7ekPGWjNGw9j09T5KGPKKxQig==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.4.6': - resolution: {integrity: sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg==} + '@biomejs/cli-linux-x64-musl@2.4.8': + resolution: {integrity: sha512-Gi8quv8MEuDdKaPFtS2XjEnMqODPsRg6POT6KhoP+VrkNb+T2ywunVB+TvOU0LX1jAZzfBr+3V1mIbBhzAMKvw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [musl] - '@biomejs/cli-linux-x64@2.4.6': - resolution: {integrity: sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw==} + '@biomejs/cli-linux-x64@2.4.8': + resolution: {integrity: sha512-PdKXspVEaMCQLjtZCn6vfSck/li4KX9KGwSDbZdgIqlrizJ2MnMcE3TvHa2tVfXNmbjMikzcfJpuPWH695yJrw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [glibc] - '@biomejs/cli-win32-arm64@2.4.6': - resolution: {integrity: sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg==} + '@biomejs/cli-win32-arm64@2.4.8': + resolution: {integrity: sha512-LoFatS0tnHv6KkCVpIy3qZCih+MxUMvdYiPWLHRri7mhi2vyOOs8OrbZBcLTUEWCS+ktO72nZMy4F96oMhkOHQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.4.6': - resolution: {integrity: sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg==} + '@biomejs/cli-win32-x64@2.4.8': + resolution: {integrity: sha512-vAn7iXDoUbqFXqVocuq1sMYAd33p8+mmurqJkWl6CtIhobd/O6moe4rY5AJvzbunn/qZCdiDVcveqtkFh1e7Hg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] - '@borewit/text-codec@0.2.1': - resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} '@changesets/apply-release-plan@7.1.0': resolution: {integrity: sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ==} @@ -1478,18 +1477,6 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@chevrotain/cst-dts-gen@10.5.0': - resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} - - '@chevrotain/gast@10.5.0': - resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==} - - '@chevrotain/types@10.5.0': - resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==} - - '@chevrotain/utils@10.5.0': - resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} - '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -2011,8 +1998,8 @@ packages: resolution: {integrity: sha512-lBSBiRruFurFKXr5Hbsl2thmGweAPmddhF3jb99U4EMDA5L+e5Y1rAkOS07Nvrup7HUMBDrCV45meaxZnt28nQ==} engines: {node: '>=20.0'} - '@dotenvx/dotenvx@1.54.1': - resolution: {integrity: sha512-41gU3q7v05GM92QPuPUf4CmUw+mmF8p4wLUh6MCRlxpCkJ9ByLcY9jUf6MwrMNmiKyG/rIckNxj9SCfmNCmCqw==} + '@dotenvx/dotenvx@1.55.1': + resolution: {integrity: sha512-WEuKyoe9CA7dfcFBnNbL0ndbCNcptaEYBygfFo9X1qEG+HD7xku4CYIplw6sbAHJavesZWbVBHeRSpvri0eKqw==} hasBin: true '@drizzle-team/brocli@0.10.2': @@ -2024,31 +2011,17 @@ packages: peerDependencies: '@noble/ciphers': ^1.0.0 - '@electric-sql/pglite-socket@0.0.20': - resolution: {integrity: sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==} - hasBin: true - peerDependencies: - '@electric-sql/pglite': 0.3.15 - - '@electric-sql/pglite-tools@0.2.20': - resolution: {integrity: sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==} - peerDependencies: - '@electric-sql/pglite': 0.3.15 - - '@electric-sql/pglite@0.3.15': - resolution: {integrity: sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==} - '@electric-sql/pglite@0.3.16': resolution: {integrity: sha512-mZkZfOd9OqTMHsK+1cje8OSzfAQcpD7JmILXTl5ahdempjUDdmg4euf1biDex5/LfQIDJ3gvCu6qDgdnDxfJmA==} - '@emnapi/core@1.8.1': - resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + '@emnapi/core@1.9.0': + resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==} - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/runtime@1.9.0': + resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} - '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@emnapi/wasi-threads@1.2.0': + resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} @@ -2064,8 +2037,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -2082,8 +2055,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -2100,8 +2073,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -2118,8 +2091,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -2136,8 +2109,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -2154,8 +2127,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -2172,8 +2145,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2190,8 +2163,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2208,8 +2181,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2226,8 +2199,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2244,8 +2217,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2262,8 +2235,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2280,8 +2253,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2298,8 +2271,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2316,8 +2289,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2334,8 +2307,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2352,8 +2325,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -2364,8 +2337,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2382,8 +2355,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -2394,8 +2367,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2412,8 +2385,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -2424,8 +2397,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -2442,8 +2415,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2460,8 +2433,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2478,8 +2451,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2496,14 +2469,14 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@fastify/otel@0.16.0': - resolution: {integrity: sha512-2304BdM5Q/kUvQC9qJO1KZq3Zn1WWsw+WWkVmFEaj1UE2hEIiuFqrPeglQOwEtw/ftngisqfQ3v70TWMmwhhHA==} + '@fastify/otel@0.17.1': + resolution: {integrity: sha512-K4wyxfUZx2ux5o+b6BtTqouYFVILohLZmSbA2tKUueJstNcBnoGPVhllCaOvbQ3ZrXdUxUC/fyrSWSCqHhdOPg==} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -2528,8 +2501,8 @@ packages: peerDependencies: maplibre-gl: ^4.5.0 || ^5.0.0 - '@gerrit0/mini-shiki@3.22.0': - resolution: {integrity: sha512-jMpciqEVUBKE1QwU64S4saNMzpsSza6diNCk4MWAeCxO2+LFi2FIFmL2S0VDLzEJCxuvCbU783xi8Hp/gkM5CQ==} + '@gerrit0/mini-shiki@3.23.0': + resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==} '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -2537,12 +2510,6 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} - '@hono/node-server@1.19.9': - resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - '@hookform/resolvers@5.2.2': resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} peerDependencies: @@ -2757,8 +2724,8 @@ packages: '@maplibre/geojson-vt@5.0.4': resolution: {integrity: sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ==} - '@maplibre/geojson-vt@6.0.1': - resolution: {integrity: sha512-VXoddR6nvJUXGXfN0FZBA2o3Z2FnTdylnsDB2mMXFJavwfWsGDfrNpC2utWFmY1CSiOs1TKSDH2/Aa1yvg0JUA==} + '@maplibre/geojson-vt@6.0.4': + resolution: {integrity: sha512-HYv3POhMRCdhP3UPPATM/hfcy6/WuVIf5FKboH8u/ZuFMTnAIcSVlq5nfOqroLokd925w2QtE7YwquFOIacwVQ==} '@maplibre/maplibre-gl-geocoder@1.9.4': resolution: {integrity: sha512-ss0NMpjUgK1/8YrrikrAtdda41jERiGg+XqwPkj52AhwvQTLZEnZSU7IhqdyuE1FZ/QhlzAauMbyzJUTTxDscw==} @@ -2774,8 +2741,8 @@ packages: resolution: {integrity: sha512-Ed7rcKYU5iELfablg9Mj+TVCsXsPBgdMyXPRAxb2v7oWg9YJnpQdZ5msDs1LESu/mtXy3Z48Vdppv2t/x5kAhw==} hasBin: true - '@maplibre/mlt@1.1.7': - resolution: {integrity: sha512-HZSsXrgn2V6T3o0qklMwKERfKaAxjO8shmiFnVygCtXTg4SPKWVX+U99RkvxUfCsjYBEcT4ltor8lSlBSCca7Q==} + '@maplibre/mlt@1.1.8': + resolution: {integrity: sha512-8vtfYGidr1rNkv5IwIoU2lfe3Oy+Wa8HluzQYcQi9cveU9K3pweAal/poQj4GJ0K/EW4bTQp2wVAs09g2yDRZg==} '@maplibre/vt-pbf@4.3.0': resolution: {integrity: sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==} @@ -2813,10 +2780,6 @@ packages: '@mongodb-js/saslprep@1.4.6': resolution: {integrity: sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==} - '@mrleebo/prisma-ast@0.13.1': - resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==} - engines: {node: '>=16'} - '@napi-rs/wasm-runtime@1.0.7': resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} @@ -2868,8 +2831,12 @@ packages: resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.211.0': - resolution: {integrity: sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==} + '@opentelemetry/api-logs@0.212.0': + resolution: {integrity: sha512-TEEVrLbNROUkYY51sBJGk7lO/OLjuepch8+hmpM6ffMJQ2z/KVCjdHuCFX6fJj8OkJP2zckPjrJzQtXU3IAsFg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api-logs@0.213.0': + resolution: {integrity: sha512-zRM5/Qj6G84Ej3F1yt33xBVY/3tnMxtL1fiDIxYbDWYaZ/eudVw3/PBiZ8G7JwUxXxjW8gU4g6LnOyfGKYHYgw==} engines: {node: '>=8.0.0'} '@opentelemetry/api@1.9.0': @@ -2888,12 +2855,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.5.0': - resolution: {integrity: sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.6.0': resolution: {integrity: sha512-HLM1v2cbZ4TgYN6KEOj+Bbj8rAKriOdkF9Ed3tG25FoprSiQl7kYc+RRT6fUZGOvx0oMi5U67GoFdT+XUn8zEg==} engines: {node: ^18.19.0 || >=20.6.0} @@ -2906,134 +2867,134 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-amqplib@0.58.0': - resolution: {integrity: sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==} + '@opentelemetry/instrumentation-amqplib@0.60.0': + resolution: {integrity: sha512-q/B2IvoVXRm1M00MvhnzpMN6rKYOszPXVsALi6u0ss4AYHe+TidZEtLW9N1ZhrobI1dSriHnBqqtAOZVAv07sg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-connect@0.54.0': - resolution: {integrity: sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==} + '@opentelemetry/instrumentation-connect@0.56.0': + resolution: {integrity: sha512-PKp+sSZ7AfzMvGgO3VCyo1inwNu+q7A1k9X88WK4PQ+S6Hp7eFk8pie+sWHDTaARovmqq5V2osav3lQej2B0nw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-dataloader@0.28.0': - resolution: {integrity: sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==} + '@opentelemetry/instrumentation-dataloader@0.30.0': + resolution: {integrity: sha512-MXHP2Q38cd2OhzEBKAIXUi9uBlPEYzF6BNJbyjUXBQ6kLaf93kRC41vNMIz0Nl5mnuwK7fDvKT+/lpx7BXRwdg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-express@0.59.0': - resolution: {integrity: sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==} + '@opentelemetry/instrumentation-express@0.61.0': + resolution: {integrity: sha512-Xdmqo9RZuZlL29Flg8QdwrrX7eW1CZ7wFQPKHyXljNymgKhN1MCsYuqQ/7uxavhSKwAl7WxkTzKhnqpUApLMvQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-fs@0.30.0': - resolution: {integrity: sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==} + '@opentelemetry/instrumentation-fs@0.32.0': + resolution: {integrity: sha512-koR6apx0g0wX6RRiPpjA4AFQUQUbXrK16kq4/SZjVp7u5cffJhNkY4TnITxcGA4acGSPYAfx3NHRIv4Khn1axQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-generic-pool@0.54.0': - resolution: {integrity: sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==} + '@opentelemetry/instrumentation-generic-pool@0.56.0': + resolution: {integrity: sha512-fg+Jffs6fqrf0uQS0hom7qBFKsbtpBiBl8+Vkc63Gx8xh6pVh+FhagmiO6oM0m3vyb683t1lP7yGYq22SiDnqg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-graphql@0.58.0': - resolution: {integrity: sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==} + '@opentelemetry/instrumentation-graphql@0.61.0': + resolution: {integrity: sha512-pUiVASv6nh2XrerTvlbVHh7vKFzscpgwiQ/xvnZuAIzQ5lRjWVdRPUuXbvZJ/Yq79QsE81TZdJ7z9YsXiss1ew==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-hapi@0.57.0': - resolution: {integrity: sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==} + '@opentelemetry/instrumentation-hapi@0.59.0': + resolution: {integrity: sha512-33wa4mEr+9+ztwdgLor1SeBu4Opz4IsmpcLETXAd3VmBrOjez8uQtrsOhPCa5Vhbm5gzDlMYTgFRLQzf8/YHFA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-http@0.211.0': - resolution: {integrity: sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==} + '@opentelemetry/instrumentation-http@0.213.0': + resolution: {integrity: sha512-B978Xsm5XEPGhm1P07grDoaOFLHapJPkOG9h016cJsyWWxmiLnPu2M/4Nrm7UCkHSiLnkXgC+zVGUAIahy8EEA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-ioredis@0.59.0': - resolution: {integrity: sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==} + '@opentelemetry/instrumentation-ioredis@0.61.0': + resolution: {integrity: sha512-hsHDadUtAFbws1YSDc1XW0svGFKiUbqv2td1Cby+UAiwvojm1NyBo/taifH0t8CuFZ0x/2SDm0iuTwrM5pnVOg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-kafkajs@0.20.0': - resolution: {integrity: sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==} + '@opentelemetry/instrumentation-kafkajs@0.22.0': + resolution: {integrity: sha512-wJU4IBQMUikdJAcTChLFqK5lo+flo7pahqd8DSLv7uMxsdOdAHj6RzKYAm8pPfUS6ItKYutYyuicwKaFwQKsoA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-knex@0.55.0': - resolution: {integrity: sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==} + '@opentelemetry/instrumentation-knex@0.57.0': + resolution: {integrity: sha512-vMCSh8kolEm5rRsc+FZeTZymWmIJwc40hjIKnXH4O0Dv/gAkJJIRXCsPX5cPbe0c0j/34+PsENd0HqKruwhVYw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-koa@0.59.0': - resolution: {integrity: sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==} + '@opentelemetry/instrumentation-koa@0.61.0': + resolution: {integrity: sha512-lvrfWe9ShK/D2X4brmx8ZqqeWPfRl8xekU0FCn7C1dHm5k6+rTOOi36+4fnaHAP8lig9Ux6XQ1D4RNIpPCt1WQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/instrumentation-lru-memoizer@0.55.0': - resolution: {integrity: sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==} + '@opentelemetry/instrumentation-lru-memoizer@0.57.0': + resolution: {integrity: sha512-cEqpUocSKJfwDtLYTTJehRLWzkZ2eoePCxfVIgGkGkb83fMB71O+y4MvRHJPbeV2bdoWdOVrl8uO0+EynWhTEA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongodb@0.64.0': - resolution: {integrity: sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==} + '@opentelemetry/instrumentation-mongodb@0.66.0': + resolution: {integrity: sha512-d7m9QnAY+4TCWI4q1QRkfrc6fo/92VwssaB1DzQfXNRvu51b78P+HJlWP7Qg6N6nkwdb9faMZNBCZJfftmszkw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongoose@0.57.0': - resolution: {integrity: sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==} + '@opentelemetry/instrumentation-mongoose@0.59.0': + resolution: {integrity: sha512-6/jWU+c1NgznkVLDU/2y0bXV2nJo3o9FWZ9mZ9nN6T/JBNRoMnVXZl2FdBmgH+a5MwaWLs5kmRJTP5oUVGIkPw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql2@0.57.0': - resolution: {integrity: sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==} + '@opentelemetry/instrumentation-mysql2@0.59.0': + resolution: {integrity: sha512-n9/xrVCRBfG9egVbffnlU1uhr+HX0vF4GgtAB/Bvm48wpFgRidqD8msBMiym1kRYzmpWvJqTxNT47u1MkgBEdw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql@0.57.0': - resolution: {integrity: sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==} + '@opentelemetry/instrumentation-mysql@0.59.0': + resolution: {integrity: sha512-r+V/Fh0sm7Ga8/zk/TI5H5FQRAjwr0RrpfPf8kNIehlsKf12XnvIaZi8ViZkpX0gyPEpLXqzqWD6QHlgObgzZw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-pg@0.63.0': - resolution: {integrity: sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==} + '@opentelemetry/instrumentation-pg@0.65.0': + resolution: {integrity: sha512-W0zpHEIEuyZ8zvb3njaX9AAbHgPYOsSWVOoWmv1sjVRSF6ZpBqtlxBWbU+6hhq1TFWBeWJOXZ8nZS/PUFpLJYQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-redis@0.59.0': - resolution: {integrity: sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==} + '@opentelemetry/instrumentation-redis@0.61.0': + resolution: {integrity: sha512-JnPexA034/0UJRsvH96B0erQoNOqKJZjE2ZRSw9hiTSC23LzE0nJE/u6D+xqOhgUhRnhhcPHq4MdYtmUdYTF+Q==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-tedious@0.30.0': - resolution: {integrity: sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==} + '@opentelemetry/instrumentation-tedious@0.32.0': + resolution: {integrity: sha512-BQS6gG8RJ1foEqfEZ+wxoqlwfCAzb1ZVG0ad8Gfe4x8T658HJCLGLd4E4NaoQd8EvPfLqOXgzGaE/2U4ytDSWA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-undici@0.21.0': - resolution: {integrity: sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==} + '@opentelemetry/instrumentation-undici@0.23.0': + resolution: {integrity: sha512-LL0VySzKVR2cJSFVZaTYpZl1XTpBGnfzoQPe2W7McS2267ldsaEIqtQY6VXs2KCXN0poFjze5110PIpxHDaDGg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.7.0 @@ -3044,14 +3005,14 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.208.0': - resolution: {integrity: sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==} + '@opentelemetry/instrumentation@0.212.0': + resolution: {integrity: sha512-IyXmpNnifNouMOe0I/gX7ENfv2ZCNdYTF0FpCsoBcpbIHzk81Ww9rQTYTnvghszCg7qGrIhNvWC8dhEifgX9Jg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.211.0': - resolution: {integrity: sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==} + '@opentelemetry/instrumentation@0.213.0': + resolution: {integrity: sha512-3i9NdkET/KvQomeh7UaR/F4r9P25Rx6ooALlWXPIjypcEOUxksCmVu0zA70NBJWlrMW1rPr/LRidFAflLI+s/w==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -3170,69 +3131,17 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@posthog/core@1.23.3': - resolution: {integrity: sha512-nehG2nig9qiU4lEUIyfXQLaBnylm5wdDiIBsp2tBFJX5BcUHNAXSwpkHjKLQ9TDfik0HW1HwZ2mY/3hJgJNToQ==} - - '@posthog/types@1.360.1': - resolution: {integrity: sha512-zzvgckmzmjYB7YGnA2cIMtF9thj8O1TMp2YCXcBb+qnGcI5cKTo8j+lO7Kq1Lbwgp7ZPxBWUXfKHg03K5vVdjg==} - - '@prisma/client-runtime-utils@7.4.2': - resolution: {integrity: sha512-cID+rzOEb38VyMsx5LwJMEY4NGIrWCNpKu/0ImbeooQ2Px7TI+kOt7cm0NelxUzF2V41UVVXAmYjANZQtCu1/Q==} - - '@prisma/client@7.4.2': - resolution: {integrity: sha512-ts2mu+cQHriAhSxngO3StcYubBGTWDtu/4juZhXCUKOwgh26l+s4KD3vT2kMUzFyrYnll9u/3qWrtzRv9CGWzA==} - engines: {node: ^20.19 || ^22.12 || >=24.0} - peerDependencies: - prisma: '*' - typescript: '>=5.4.0' - peerDependenciesMeta: - prisma: - optional: true - typescript: - optional: true - - '@prisma/config@7.4.2': - resolution: {integrity: sha512-CftBjWxav99lzY1Z4oDgomdb1gh9BJFAOmWF6P2v1xRfXqQb56DfBub+QKcERRdNoAzCb3HXy3Zii8Vb4AsXhg==} - - '@prisma/debug@7.2.0': - resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} - - '@prisma/debug@7.4.2': - resolution: {integrity: sha512-aP7qzu+g/JnbF6U69LMwHoUkELiserKmWsE2shYuEpNUJ4GrtxBCvZwCyCBHFSH2kLTF2l1goBlBh4wuvRq62w==} - - '@prisma/dev@0.20.0': - resolution: {integrity: sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==} + '@posthog/core@1.24.0': + resolution: {integrity: sha512-Wkp9mgNfgdf6+G4C1VMKakm2RXKQFf4bb5/CPQRAjpqv9l6BY36zZrD1+X5Y2XIAzZqbMKRxsDu3V1r6uKu7/A==} - '@prisma/engines-version@7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919': - resolution: {integrity: sha512-5FIKY3KoYQlBuZC2yc16EXfVRQ8HY+fLqgxkYfWCtKhRb3ajCRzP/rPeoSx11+NueJDANdh4hjY36mdmrTcGSg==} + '@posthog/types@1.362.0': + resolution: {integrity: sha512-15wOI5uulkfzpkSQKVN4atZecAla2Hxr8IBIB8islqDvqY+42vbR+tMeDKMman9+FUoAqMzE0OnB8VIbM1QY0w==} - '@prisma/engines@7.4.2': - resolution: {integrity: sha512-B+ZZhI4rXlzjVqRw/93AothEKOU5/x4oVyJFGo9RpHPnBwaPwk4Pi0Q4iGXipKxeXPs/dqljgNBjK0m8nocOJA==} - - '@prisma/fetch-engine@7.4.2': - resolution: {integrity: sha512-f/c/MwYpdJO7taLETU8rahEstLeXfYgQGlz5fycG7Fbmva3iPdzGmjiSWHeSWIgNnlXnelUdCJqyZnFocurZuA==} - - '@prisma/get-platform@7.2.0': - resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} - - '@prisma/get-platform@7.4.2': - resolution: {integrity: sha512-UTnChXRwiauzl/8wT4hhe7Xmixja9WE28oCnGpBtRejaHhvekx5kudr3R4Y9mLSA0kqGnAMeyTiKwDVMjaEVsw==} - - '@prisma/instrumentation@7.2.0': - resolution: {integrity: sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==} + '@prisma/instrumentation@7.4.2': + resolution: {integrity: sha512-r9JfchJF1Ae6yAxcaLu/V1TGqBhAuSDe3mRNOssBfx1rMzfZ4fdNvrgUBwyb/TNTGXFxlH9AZix5P257x07nrg==} peerDependencies: '@opentelemetry/api': ^1.8 - '@prisma/query-plan-executor@7.2.0': - resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} - - '@prisma/studio-core@0.13.1': - resolution: {integrity: sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg==} - peerDependencies: - '@types/react': ^18.0.0 || ^19.0.0 - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -3975,8 +3884,8 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@react-email/body@0.2.1': - resolution: {integrity: sha512-ljDiQiJDu/Fq//vSIIP0z5Nuvt4+DX1RqGasstChDGJB/14ogd4VdNS9aacoede/ZjGy3o3Qb+cxyS+XgM6SwQ==} + '@react-email/body@0.3.0': + resolution: {integrity: sha512-uGo0BOOzjbMUo3lu+BIDWayvn5o6Xyfmnlla5VGf05n8gHMvO1ll7U4FtzWe3hxMLwt53pmc4iE0M+B5slG+Ug==} engines: {node: '>=20.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -4005,8 +3914,8 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/components@1.0.8': - resolution: {integrity: sha512-zY81ED6o5MWMzBkr9uZFuT24lWarT+xIbOZxI6C9dsFmCWBczM8IE1BgOI8rhpUK4JcYVDy1uKxYAFqsx2Bc4w==} + '@react-email/components@1.0.10': + resolution: {integrity: sha512-r/BnqfAjr3apcvn/NDx2DqNRD5BP5wZLRdjn2IVHXjt4KmQ5RHWSCAvFiXAzRHys1BWQ2zgIc7cpWePUcAl+nw==} engines: {node: '>=20.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -4090,21 +3999,21 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/tailwind@2.0.5': - resolution: {integrity: sha512-7Ey+kiWliJdxPMCLYsdDts8ffp4idlP//w4Ui3q/A5kokVaLSNKG8DOg/8qAuzWmRiGwNQVOKBk7PXNlK5W+sg==} + '@react-email/tailwind@2.0.6': + resolution: {integrity: sha512-3PgL/GYWmgS+puLPQ2aLlsplHSOFztRl70fowBkbLIb8ZUIgvx5YId6zYCCHeM2+DQ/EG3iXXqLNTahVztuMqQ==} engines: {node: '>=20.0.0'} peerDependencies: - '@react-email/body': 0.2.1 - '@react-email/button': 0.2.1 - '@react-email/code-block': 0.2.1 - '@react-email/code-inline': 0.0.6 - '@react-email/container': 0.0.16 - '@react-email/heading': 0.0.16 - '@react-email/hr': 0.0.12 - '@react-email/img': 0.0.12 - '@react-email/link': 0.0.13 - '@react-email/preview': 0.0.14 - '@react-email/text': 0.1.6 + '@react-email/body': '>=0' + '@react-email/button': '>=0' + '@react-email/code-block': '>=0' + '@react-email/code-inline': '>=0' + '@react-email/container': '>=0' + '@react-email/heading': '>=0' + '@react-email/hr': '>=0' + '@react-email/img': '>=0' + '@react-email/link': '>=0' + '@react-email/preview': '>=0' + '@react-email/text': '>=0' react: ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: '@react-email/body': @@ -4449,64 +4358,64 @@ packages: cpu: [x64] os: [win32] - '@rspack/binding-darwin-arm64@1.7.8': - resolution: {integrity: sha512-KS6SRc+4VYRdX1cKr1j1HEuMNyEzt7onBS0rkenaiCRRYF0z4WNZNyZqRiuxgM3qZ3TISF7gdmgJQyd4ZB43ig==} + '@rspack/binding-darwin-arm64@1.7.9': + resolution: {integrity: sha512-64dgstte0If5czi9bA/cpOe0ryY6wC9AIQRtyJ3DlOF6Tt+y9cKkmUoGu3V+WYaYIZRT7HNk8V7kL8amVjFTYw==} cpu: [arm64] os: [darwin] - '@rspack/binding-darwin-x64@1.7.8': - resolution: {integrity: sha512-uyXSDKLg2CtqIJrsJDlCqQH80YIPsCUiTToJ59cXAG3v4eke0Qbiv6d/+pV0h/mc0u4inAaSkr5dD18zkMIghw==} + '@rspack/binding-darwin-x64@1.7.9': + resolution: {integrity: sha512-2QSLs3w4rLy4UUGVnIlkt6IlIKOzR1e0RPsq2FYQW6s3p9JrwRCtOeHohyh7EJSqF54dtfhe9UZSAwba3LqH1Q==} cpu: [x64] os: [darwin] - '@rspack/binding-linux-arm64-gnu@1.7.8': - resolution: {integrity: sha512-dD6gSHA18Uj0eqc1FCwwQ5IO5mIckrpYN4H4kPk9Pjau+1mxWvC4y5Lryz1Z8P/Rh1lnQ/wwGE0XL9nd80+LqQ==} + '@rspack/binding-linux-arm64-gnu@1.7.9': + resolution: {integrity: sha512-qhUGI/uVfvLmKWts4QkVHGL8yfUyJkblZs+OFD5Upa2y676EOsbQgWsCwX4xGB6Tv+TOzFP0SLh/UfO8ZfdE+w==} cpu: [arm64] os: [linux] libc: [glibc] - '@rspack/binding-linux-arm64-musl@1.7.8': - resolution: {integrity: sha512-m+uBi9mEVGkZ02PPOAYN2BSmmvc00XGa6v9CjV8qLpolpUXQIMzDNG+i1fD5SHp8LO+XWsZJOHypMsT0MzGTGw==} + '@rspack/binding-linux-arm64-musl@1.7.9': + resolution: {integrity: sha512-VjfmR1hgO9n3L6MaE5KG+DXSrrLVqHHOkVcOtS2LMq3bjMTwbBywY7ycymcLnX5KJsol8d3ZGYep6IfSOt3lFA==} cpu: [arm64] os: [linux] libc: [musl] - '@rspack/binding-linux-x64-gnu@1.7.8': - resolution: {integrity: sha512-IAPp2L3yS33MAEkcGn/I1gO+a+WExJHXz2ZlRlL2oFCUGpYi2ZQHyAcJ3o2tJqkXmdqsTiN+OjEVMd/RcLa24g==} + '@rspack/binding-linux-x64-gnu@1.7.9': + resolution: {integrity: sha512-0kldV+3WTs/VYDWzxJ7K40hCW26IHtnk8xPK3whKoo1649rgeXXa0EdsU5P7hG8Ef5SWQjHHHZ/fuHYSO3Y6HA==} cpu: [x64] os: [linux] libc: [glibc] - '@rspack/binding-linux-x64-musl@1.7.8': - resolution: {integrity: sha512-do/QNzb4GWdXCsipblDcroqRDR3BFcbyzpZpAw/3j9ajvEqsOKpdHZpILT2NZX/VahhjqfqB3k0kJVt3uK7UYQ==} + '@rspack/binding-linux-x64-musl@1.7.9': + resolution: {integrity: sha512-Gi4872cFtc2d83FKATR6Qcf2VBa/tFCqffI/IwRRl6Hx5FulEBqx+tH7gAuRVF693vrbXNxK+FQ+k4iEsEJxrw==} cpu: [x64] os: [linux] libc: [musl] - '@rspack/binding-wasm32-wasi@1.7.8': - resolution: {integrity: sha512-mHtgYTpdhx01i0XNKFYBZyCjtv9YUe/sDfpD1QK4FytPFB+1VpYnmZiaJIMM77VpNsjxGAqWhmUYxi2P6jWifw==} + '@rspack/binding-wasm32-wasi@1.7.9': + resolution: {integrity: sha512-5QEzqo6EaolpuZmK6w/mgSueorgGnnzp7dJaAvBj6ECFIg/aLXhXXmWCWbxt7Ws2gKvG5/PgaxDqbUxYL51juA==} cpu: [wasm32] - '@rspack/binding-win32-arm64-msvc@1.7.8': - resolution: {integrity: sha512-Mkxg86F7kIT4pM9XvE/1LAGjK5NOQi/GJxKyyiKbUAeKM8XBUizVeNuvKR0avf2V5IDAIRXiH1SX8SpujMJteA==} + '@rspack/binding-win32-arm64-msvc@1.7.9': + resolution: {integrity: sha512-MMqvcrIc8aOqTuHjWkjdzilvoZ3Hv07Od0Foogiyq3JMudsS3Wcmh7T1dFerGg19MOJcRUeEkrg2NQOMOQ6xDA==} cpu: [arm64] os: [win32] - '@rspack/binding-win32-ia32-msvc@1.7.8': - resolution: {integrity: sha512-VmTOZ/X7M85lKFNwb2qJpCRzr4SgO42vucq/X7Uz1oSoTPAf8UUMNdi7BPnu+D4lgy6l8PwV804ZyHO3gGsvPA==} + '@rspack/binding-win32-ia32-msvc@1.7.9': + resolution: {integrity: sha512-4kYYS+NZ2CuNbKjq40yB/UEyB51o1PHj5wpr+Y943oOJXpEKWU2Q4vkF8VEohPEcnA9cKVotYCnqStme+02suA==} cpu: [ia32] os: [win32] - '@rspack/binding-win32-x64-msvc@1.7.8': - resolution: {integrity: sha512-BK0I4HAwp/yQLnmdJpUtGHcht3x11e9fZwyaiMzznznFc+Oypbf+FS5h+aBgpb53QnNkPpdG7MfAPoKItOcU8A==} + '@rspack/binding-win32-x64-msvc@1.7.9': + resolution: {integrity: sha512-1g+QyXXvs+838Un/4GaUvJfARDGHMCs15eXDYWBl5m/Skubyng8djWAgr6ag1+cVoJZXCPOvybTItcblWF3gbQ==} cpu: [x64] os: [win32] - '@rspack/binding@1.7.8': - resolution: {integrity: sha512-P4fbrQx5hRhAiC8TBTEMCTnNawrIzJLjWwAgrTwRxjgenpjNvimEkQBtSGrXOY+c+MV5Q74P+9wPvVWLKzRkQQ==} + '@rspack/binding@1.7.9': + resolution: {integrity: sha512-A56e0NdfNwbOSJoilMkxzaPuVYaKCNn1shuiwWnCIBmhV9ix1n9S1XvquDjkGyv+gCdR1+zfJBOa5DMB7htLHw==} - '@rspack/core@1.7.8': - resolution: {integrity: sha512-kT6yYo8xjKoDfM7iB8N9AmN9DJIlrs7UmQDbpTu1N4zaZocN1/t2fIAWOKjr5+3eJlZQR2twKZhDVHNLbLPjOw==} + '@rspack/core@1.7.9': + resolution: {integrity: sha512-VHuSKvRkuv42Ya+TxEGO0LE0r9+8P4tKGokmomj4R1f/Nu2vtS3yoaIMfC4fR6VuHGd3MZ+KTI0cNNwHfFcskw==} engines: {node: '>=18.12.0'} peerDependencies: '@swc/helpers': '>=0.5.1' @@ -4520,32 +4429,32 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - '@sentry-internal/browser-utils@10.43.0': - resolution: {integrity: sha512-8zYTnzhAPvNkVH1Irs62wl0J/c+0QcJ62TonKnzpSFUUD3V5qz8YDZbjIDGfxy+1EB9fO0sxtddKCzwTHF/MbQ==} + '@sentry-internal/browser-utils@10.44.0': + resolution: {integrity: sha512-z9xz3T/v+MnfHY6kdUCmOZI8CiAl3LlKYtGH2p3rAsrxhwX+BTnUp01VhMVnEZIDgUXNt3AhJac+4kcDIPu1Hg==} engines: {node: '>=18'} - '@sentry-internal/feedback@10.43.0': - resolution: {integrity: sha512-YoXuwluP6eOcQxTeTtaWb090++MrLyWOVsUTejzUQQ6LFL13Jwt+bDPF1kvBugMq4a7OHw/UNKQfd6//rZMn2g==} + '@sentry-internal/feedback@10.44.0': + resolution: {integrity: sha512-yNS2EGK1bNm8YUI+Orzpa7yr05Da+b1VEe/9x7dl7gTjw/+tfutoXlG6Y+iFZBB3gQ9QU+nxZAhU+KcxiPEURw==} engines: {node: '>=18'} '@sentry-internal/node-cpu-profiler@2.2.0': resolution: {integrity: sha512-oLHVYurqZfADPh5hvmQYS5qx8t0UZzT2u6+/68VXsFruQEOnYJTODKgU3BVLmemRs3WE6kCJjPeFdHVYOQGSzQ==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@10.43.0': - resolution: {integrity: sha512-ZIw1UNKOFXo1LbPCJPMAx9xv7D8TMZQusLDUgb6BsPQJj0igAuwd7KRGTkjjgnrwBp2O/sxcQFRhQhknWk7QPg==} + '@sentry-internal/replay-canvas@10.44.0': + resolution: {integrity: sha512-RA7XgYZWHY7M+vaHvuMxDFT51wCs4puS2smElM5oh+j3YqbFXY7P16fOCwIAGoyI4gVsj8aTeBgVqUmrmzhAXQ==} engines: {node: '>=18'} - '@sentry-internal/replay@10.43.0': - resolution: {integrity: sha512-khCXlGrlH1IU7P5zCEAJFestMeH97zDVCekj8OsNNDtN/1BmCJ46k6Xi0EqAUzdJgrOLJeLdoYdgtiIjovZ8Sg==} + '@sentry-internal/replay@10.44.0': + resolution: {integrity: sha512-KDmoqBsRmkaoc+eKLR2CbScd2eBmLcw+1+D441lLttAO3WWhvYyCaYdu/HIGGUoybuSgt+IcpCJdi7hFuCvYqw==} engines: {node: '>=18'} '@sentry/babel-plugin-component-annotate@5.1.1': resolution: {integrity: sha512-x2wEpBHwsTyTF2rWsLKJlzrRF1TTIGOfX+ngdE+Yd5DBkoS58HwQv824QOviPGQRla4/ypISqAXzjdDPL/zalg==} engines: {node: '>= 18'} - '@sentry/browser@10.43.0': - resolution: {integrity: sha512-2V3I3sXi3SMeiZpKixd9ztokSgK27cmvsD9J5oyOyjhGLTW/6QKCwHbKnluMgQMXq20nixQk5zN4wRjRUma3sg==} + '@sentry/browser@10.44.0': + resolution: {integrity: sha512-UpMx5forbVKieNULma3gT2SsLYqsYT4nLXa6s1io/Y8BFej9sH2dD5ExA8TrkQThQwAWFI3qKsQzYnF+EX/Bfg==} engines: {node: '>=18'} '@sentry/bundler-plugin-core@5.1.1': @@ -4604,12 +4513,12 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/core@10.43.0': - resolution: {integrity: sha512-l0SszQAPiQGWl/ferw8GP3ALyHXiGiRKJaOvNmhGO+PrTQyZTZ6OYyPnGijAFRg58dE1V3RCH/zw5d2xSUIiNg==} + '@sentry/core@10.44.0': + resolution: {integrity: sha512-aa7CiDaNFZvHpqd97LJhuskolfJ/4IH5xyuVVLnv7l6B0v9KTwskPUxb0tH1ej3FxuzfH+i8iTiTFuqpfHS3QA==} engines: {node: '>=18'} - '@sentry/node-core@10.43.0': - resolution: {integrity: sha512-w2H3NSkNMoYOS7o7mR55BM7+xL++dPxMSv1/XDfsra9FYHGppO+Mxk667Ee5k+uDi+wNIioICIh+5XOvZh4+HQ==} + '@sentry/node-core@10.44.0': + resolution: {integrity: sha512-jUGsadMrvZ08UMbqJBfjFFMk1k3VbyxfUypf0iDGGgyLmuHotYQPo/5aND+o2KxMDXR60LwcQrMoZFpanK6jXQ==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -4635,12 +4544,12 @@ packages: '@opentelemetry/semantic-conventions': optional: true - '@sentry/node@10.43.0': - resolution: {integrity: sha512-oNwXcuZUc4uTTr0WbHZBBIKsKwAKvNMTgbXwxfB37CfzV18wbTirbQABZ/Ir3WNxSgi6ZcnC6UE013jF5XWPqw==} + '@sentry/node@10.44.0': + resolution: {integrity: sha512-q+/WR9ZeF9Af8uyehOj2tQQOa7LH07mJfOuDus5X6G6cLuugdRUGUBB5Qhw+J/ULSxbzGADBZv6AYOyoGaNx7w==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.43.0': - resolution: {integrity: sha512-+fIcnnLdvBHdq4nKq23t9v/B9D4L97fPWEDksXbpGs11o6BsqY4Tlzmce6cP95iiQhPckCEag3FthSND+BYtYQ==} + '@sentry/opentelemetry@10.44.0': + resolution: {integrity: sha512-zP4vP8tBxjlmxQ4VcWOwZ0b3lPUxlYPg9FqJwANm9SRJN+7V5psm8TIaAtu9uqtIcJMRHdXkOM4cAggNiLk0KA==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -4649,21 +4558,21 @@ packages: '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 '@opentelemetry/semantic-conventions': ^1.39.0 - '@sentry/profiling-node@10.43.0': - resolution: {integrity: sha512-mzd+1svmgWjqe4ROlLOjWtLg8DFt7p4tkox5UoLuSGEqVuBsDhNgZAotahhMZrKqRImP+JAmaHO0SWCDQEMPGw==} + '@sentry/profiling-node@10.44.0': + resolution: {integrity: sha512-tMYapC3SN0OvwD9/xLszoACfx1UOjdf0zGEFjIkks3vJPt0jThzR9A7ptExy1476FMkIDnc7JkWU4NiUJXs+nQ==} engines: {node: '>=18'} hasBin: true - '@sentry/react-router@10.43.0': - resolution: {integrity: sha512-JR9SF2XTWkeeXOttNQVdR7p1jicI57OMpvgraZiaxauo1WQfGG+suDmmPNE7GCvV5Vj01tv9D2aBs6AqC60VVg==} + '@sentry/react-router@10.44.0': + resolution: {integrity: sha512-7k9OOCmAr4Pgoy7gVhNjknpgRqMxIahvqaPtbRw3ADghsJJeQpOEO78dYrH9gyT7qmNpXaB6UhKAS31uvGE+6w==} engines: {node: '>=20'} peerDependencies: '@react-router/node': 7.x react: '>=18' react-router: 7.x - '@sentry/react@10.43.0': - resolution: {integrity: sha512-shvErEpJ41i0Q3lIZl0CDWYQ7m8yHLi7ECG0gFvN8zf8pEdl5grQIOoe3t/GIUzcpCcor16F148ATmKJJypc/Q==} + '@sentry/react@10.44.0': + resolution: {integrity: sha512-blaYoLk/UgFZXj9ieKZeY1JIiqzeL2VegQt22S9IQk8gHpunDZux5XC4CdcPdavcVusddaB/SmHAmhy2RCBdPQ==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x @@ -4678,17 +4587,17 @@ packages: resolution: {integrity: sha512-i6NWUDi2SDikfSUeMJvJTRdwEKYSfTd+mvBO2Ja51S1YK+hnickBuDfD+RvPerIXLuyRu3GamgNPbNqgCGUg/Q==} engines: {node: '>= 18'} - '@shikijs/engine-oniguruma@3.22.0': - resolution: {integrity: sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA==} + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} - '@shikijs/langs@3.22.0': - resolution: {integrity: sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA==} + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} - '@shikijs/themes@3.22.0': - resolution: {integrity: sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g==} + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} - '@shikijs/types@3.22.0': - resolution: {integrity: sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==} + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -4968,69 +4877,69 @@ packages: resolution: {integrity: sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ==} engines: {node: '>=16.0.0'} - '@tailwindcss/node@4.2.1': - resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} + '@tailwindcss/node@4.2.2': + resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} - '@tailwindcss/oxide-android-arm64@4.2.1': - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + '@tailwindcss/oxide-android-arm64@4.2.2': + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.2.1': - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + '@tailwindcss/oxide-darwin-arm64@4.2.2': + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.1': - resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + '@tailwindcss/oxide-darwin-x64@4.2.2': + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.1': - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + '@tailwindcss/oxide-freebsd-x64@4.2.2': + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': - resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': - resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.2.1': - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.2.1': - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -5041,29 +4950,29 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': - resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.2.1': - resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + '@tailwindcss/oxide@4.2.2': + resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} engines: {node: '>= 20'} - '@tailwindcss/postcss@4.2.1': - resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==} + '@tailwindcss/postcss@4.2.2': + resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} - '@tailwindcss/vite@4.2.1': - resolution: {integrity: sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==} + '@tailwindcss/vite@4.2.2': + resolution: {integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==} peerDependencies: - vite: ^5.2.0 || ^6 || ^7 + vite: ^5.2.0 || ^6 || ^7 || ^8 '@tanstack/react-table@8.21.3': resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} @@ -5083,6 +4992,36 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@turbo/darwin-64@2.8.19': + resolution: {integrity: sha512-WNzpqJV7rO/oKmsxjtSFXuV87fZb/qxzeGWXJng4oHFz7iPOFSWrPb2gTazJgoTvXUfe1kOH2o0F+zFrerwzRw==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.8.19': + resolution: {integrity: sha512-KTq8/PH0/ml9bNFomZ2VjmOqW6RYPRqBqV5CfNeD5ekJMuNwJUdeQz0qXguxCzW4OkHCnTdPeKIehR0cdQObPg==} + cpu: [arm64] + os: [darwin] + + '@turbo/linux-64@2.8.19': + resolution: {integrity: sha512-d0xcXiGUt1Q/HzMfgkqai5KyXrassLRuUZhxmUs4ZX99tQFbqRL2/CkSKCnMqyDv7c0K5eugLerVUY+DYr1DQg==} + cpu: [x64] + os: [linux] + + '@turbo/linux-arm64@2.8.19': + resolution: {integrity: sha512-UX3r1iwqsOK8dMgKFtZpDqZk7feqHXZJ/EQAknh0SVQrnc4vkm6mZqjX4j2j3DVoSo4Ho8xGITYlor9Xxsyomw==} + cpu: [arm64] + os: [linux] + + '@turbo/windows-64@2.8.19': + resolution: {integrity: sha512-3MrvD/gMYWfQKZVZOeilKm1629zPG0+KlErZbI0n9Nr3L32+5qrZOF/cgWErukLnOSR38whgf8t2WaPJQALqxg==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.8.19': + resolution: {integrity: sha512-j+8mi7kyEgT7cUTPOtPJS8KbTWxu4+OQBiDIo+zklz/RKV4hoN+k4+J8iCM9Nj+o+SVnzc01xSI+s3eBG0MDSA==} + cpu: [arm64] + os: [win32] + '@turf/along@7.3.4': resolution: {integrity: sha512-PvIoXin0I1t3nRwJz7uqR6fsxDMqdGwJq90qGOeqkNwlZqlF+5o2wKHPwYwi0RXZhLvxRP5qlbNIvV8ADdbWxw==} @@ -5476,8 +5415,8 @@ packages: '@types/d3-voronoi@1.1.12': resolution: {integrity: sha512-DauBl25PKZZ0WVJr42a6CNvI6efsdzofl9sajqZr2Gf5Gu733WkDdUGiPkUHXiUvYGzNNlFQde2wdZdfQPG+yw==} - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -5581,8 +5520,8 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - '@types/node@25.4.0': - resolution: {integrity: sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==} + '@types/node@25.5.0': + resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} '@types/pg-pool@2.0.7': resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} @@ -5714,23 +5653,9 @@ packages: '@vitest/browser': optional: true - '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} - '@vitest/expect@4.1.0': resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} - '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - '@vitest/mocker@4.1.0': resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} peerDependencies: @@ -5748,21 +5673,12 @@ packages: '@vitest/pretty-format@4.1.0': resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} - '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - '@vitest/runner@4.1.0': resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} - '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - '@vitest/snapshot@4.1.0': resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} - '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - '@vitest/spy@4.1.0': resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} @@ -6012,10 +5928,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-ssl-profiles@1.1.2: - resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} - engines: {node: '>= 6.0.0'} - axios@1.13.6: resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} @@ -6032,8 +5944,8 @@ packages: babel-plugin-dynamic-import-node@2.3.3: resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} - babel-plugin-polyfill-corejs2@0.4.16: - resolution: {integrity: sha512-xaVwwSfebXf0ooE11BJovZYKhFjIvQo7TsyVpETuIeH2JHv0k/T6Y5j22pPTvqYqmpkxdlPAJlyJ0tfOJAoMxw==} + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -6042,13 +5954,13 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-corejs3@0.14.1: - resolution: {integrity: sha512-ENp89vM9Pw4kv/koBb5N2f9bDZsR0hpf3BdPMOg/pkS3pwO4dzNnQZVXtBbeyAadgm865DmQG2jMMLqmZXvuCw==} + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.7: - resolution: {integrity: sha512-OTYbUlSwXhNgr4g6efMZgsO8//jA61P7ZbRX3iTT53VON8l+WQS8IAUEVo4a4cWknrg2W8Cj4gQhRYNCJ8GkAA==} + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -6069,8 +5981,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + baseline-browser-mapping@2.10.8: + resolution: {integrity: sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -6081,8 +5993,8 @@ packages: batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - better-auth@1.5.4: - resolution: {integrity: sha512-ReykcEKx6Kp9560jG1wtlDBnftA7L7xb3ZZdDWm5yGXKKe2pUf+oBjH0fqekrkRII0m4XBVQbQ0mOrFv+3FdYg==} + better-auth@1.5.5: + resolution: {integrity: sha512-GpVPaV1eqr3mOovKfghJXXk6QvlcVeFbS3z+n+FPDid5rK/2PchnDtiaVCzWyXA9jH2KkirOfl+JhAUvnja0Eg==} peerDependencies: '@lynx-js/react': '*' '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -6246,14 +6158,6 @@ packages: bytewise@1.1.0: resolution: {integrity: sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==} - c12@3.1.0: - resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} - peerDependencies: - magicast: ^0.3.5 - peerDependenciesMeta: - magicast: - optional: true - cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -6296,11 +6200,8 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001774: - resolution: {integrity: sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==} - - caniuse-lite@1.0.30001777: - resolution: {integrity: sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==} + caniuse-lite@1.0.30001780: + resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -6343,9 +6244,6 @@ packages: resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} engines: {node: '>= 6'} - chevrotain@10.5.0: - resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==} - chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -6366,12 +6264,6 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - citty@0.1.6: - resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - - citty@0.2.1: - resolution: {integrity: sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==} - cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} @@ -6535,14 +6427,14 @@ packages: peerDependencies: webpack: ^5.1.0 - core-js-compat@3.48.0: - resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} + core-js-compat@3.49.0: + resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} - core-js-pure@3.48.0: - resolution: {integrity: sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw==} + core-js-pure@3.49.0: + resolution: {integrity: sha512-XM4RFka59xATyJv/cS3O3Kml72hQXUeGRuuTmMYFxwzc9/7C8OYTaIR/Ji+Yt8DXzsFLNhat15cE/JP15HrCgw==} - core-js@3.48.0: - resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} + core-js@3.49.0: + resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -6804,10 +6696,6 @@ packages: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - deepmerge-ts@7.1.5: - resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} - engines: {node: '>=16.0.0'} - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -6847,10 +6735,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - denque@2.1.0: - resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} - engines: {node: '>=0.10'} - depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -6863,9 +6747,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -6959,8 +6840,8 @@ packages: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} - drizzle-kit@0.31.9: - resolution: {integrity: sha512-GViD3IgsXn7trFyBUUHyTFBpH/FsHTxYJ66qdbVggxef4UBPHRYxQaRzYLTuekYnk9i5FIEL9pbBIwMqX/Uwrg==} + drizzle-kit@0.31.10: + resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} hasBin: true drizzle-orm@0.45.1: @@ -7081,11 +6962,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - effect@3.18.4: - resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==} - - electron-to-chromium@1.5.302: - resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + electron-to-chromium@1.5.321: + resolution: {integrity: sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ==} emoji-regex-xs@1.0.0: resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} @@ -7106,16 +6984,12 @@ packages: emoticon@4.1.0: resolution: {integrity: sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==} - empathic@2.0.0: - resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} - engines: {node: '>=14'} - encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - enhanced-resolve@5.20.0: - resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -7172,11 +7046,6 @@ packages: esast-util-from-js@2.0.1: resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} - esbuild-register@3.6.0: - resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} - peerDependencies: - esbuild: '>=0.12 <1' - esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} @@ -7187,8 +7056,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} engines: {node: '>=18'} hasBin: true @@ -7319,10 +7188,6 @@ packages: extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - fast-check@3.23.2: - resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} - engines: {node: '>=8.0.0'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -7379,8 +7244,8 @@ packages: peerDependencies: webpack: ^4.0.0 || ^5.0.0 - file-type@21.3.1: - resolution: {integrity: sha512-SrzXX46I/zsRDjTb82eucsGg0ODq2NpGDp4HcsFKApPy8P8vACjpJRDoGGMfEzhFC0ry61ajd7f72J3603anBA==} + file-type@21.3.3: + resolution: {integrity: sha512-pNwbwz8c3aZ+GvbJnIsCnDjKvgCZLHxkFWLEFxU3RMa+Ey++ZSEfisvsWQMcdys6PpxQjWUOIDi1fifXsW3YRg==} engines: {node: '>=20'} fill-range@7.1.1: @@ -7433,10 +7298,6 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - form-data-encoder@2.1.4: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} @@ -7459,8 +7320,8 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - framer-motion@12.35.2: - resolution: {integrity: sha512-dhfuEMaNo0hc+AEqyHiIfiJRNb9U9UQutE9FoKm5pjf7CMitp9xPEF1iWZihR1q86LBmo6EJ7S8cN8QXEy49AA==} + framer-motion@12.38.0: + resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -7511,9 +7372,6 @@ packages: fuzzysort@3.1.0: resolution: {integrity: sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==} - generate-function@2.3.1: - resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} - generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} @@ -7538,8 +7396,8 @@ packages: resolution: {integrity: sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==} engines: {node: '>=10.19'} - geotiff@3.0.4: - resolution: {integrity: sha512-lzcQkSZ5XYAYgDVVCKrPPn6OyzFFEmewYc4PzQyzrKdf7KxCG5WPCnwvpkKNrz1nHQD1HkyYXo7ZRFAmlMrTOw==} + geotiff@3.0.5: + resolution: {integrity: sha512-OWcL9S9+yDZ6iAlXMt32T1iwUApJM8UiD47xbm6ZP1h33d10fqkPs14EG/ttT5EnefpZSx3G15iDFC5FxUNUwA==} engines: {node: '>=10.19'} get-east-asian-width@1.5.0: @@ -7557,9 +7415,6 @@ packages: get-own-enumerable-property-symbols@3.0.2: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} - get-port-please@3.2.0: - resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} - get-port@5.1.1: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} @@ -7583,10 +7438,6 @@ packages: resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} - giget@2.0.0: - resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} - hasBin: true - github-slugger@1.5.0: resolution: {integrity: sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==} @@ -7655,12 +7506,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - grammex@3.1.12: - resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} - - graphmatch@1.1.1: - resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} - gray-matter@4.0.3: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} @@ -7737,10 +7582,6 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - hono@4.11.4: - resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==} - engines: {node: '>=16.9.0'} - hpack.js@2.1.6: resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} @@ -7825,9 +7666,6 @@ packages: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} - http-status-codes@2.3.0: - resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} - http2-wrapper@2.2.1: resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} engines: {node: '>=10.19.0'} @@ -7888,6 +7726,10 @@ packages: import-in-the-middle@2.0.6: resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==} + import-in-the-middle@3.0.0: + resolution: {integrity: sha512-OnGy+eYT7wVejH2XWgLRgbmzujhhVIATQH0ztIeRilwHBjTeG3pD+XnH3PKX0r9gJ0BuJmJ68q/oh9qgXnNDQg==} + engines: {node: '>=18'} + import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} @@ -8102,9 +7944,6 @@ packages: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} engines: {node: '>=0.10.0'} - is-property@1.0.2: - resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} - is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -8240,8 +8079,8 @@ packages: joi@17.13.3: resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -8331,8 +8170,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - kysely@0.28.11: - resolution: {integrity: sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg==} + kysely@0.28.13: + resolution: {integrity: sha512-jCkYDvlfzOyHaVsrvR4vnNZxG30oNv2jbbFBjTQAUG8n0h07HW0sZJHk4KAQIRyu9ay+Rg+L8qGa3lwt8Gve9w==} engines: {node: '>=20.0.0'} latest-version@7.0.0: @@ -8352,73 +8191,36 @@ packages: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [glibc] - lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -8426,13 +8228,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [musl] - lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -8440,13 +8235,6 @@ packages: os: [linux] libc: [musl] - lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [glibc] - lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -8454,13 +8242,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [musl] - lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -8468,42 +8249,22 @@ packages: os: [linux] libc: [musl] - lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} - engines: {node: '>= 12.0.0'} - lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} - lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -8573,9 +8334,6 @@ packages: lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} @@ -8596,17 +8354,13 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} + lru-cache@11.2.7: + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru.min@1.1.4: - resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} - engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} - lucide-react@0.577.0: resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} peerDependencies: @@ -8625,8 +8379,8 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - maplibre-gl@5.20.0: - resolution: {integrity: sha512-hUQ/4KkxVKLbAD4coW+9/tJ9/jOKKcN7q4F92EQ5mjbUJ2m1sz6uoiB3VqW/VaogUxmWd896l1cc9TtV4+uvJA==} + maplibre-gl@5.20.2: + resolution: {integrity: sha512-0UzMWOe+GZmIUmOA99yTI1vRh15YcGnHxADVB2s+JF3etpjj2/MBCqbPEuu4BP9mLsJWJcpHH0Nzr9uuimmbuQ==} engines: {node: '>=16.14.0', npm: '>=8.1.0'} markdown-extensions@2.0.0: @@ -9000,11 +8754,11 @@ packages: resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==} engines: {node: '>= 0.8.0'} - motion-dom@12.35.2: - resolution: {integrity: sha512-pWXFMTwvGDbx1Fe9YL5HZebv2NhvGBzRtiNUv58aoK7+XrsuaydQ0JGRKK2r+bTKlwgSWwWxHbP5249Qr/BNpg==} + motion-dom@12.38.0: + resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} - motion-utils@12.29.2: - resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==} + motion-utils@12.36.0: + resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} @@ -9027,26 +8781,18 @@ packages: murmurhash-js@1.0.0: resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==} - mysql2@3.15.3: - resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} - engines: {node: '>= 8.0'} - - named-placeholders@1.1.6: - resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} - engines: {node: '>=8.0.0'} - nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@5.1.6: - resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + nanoid@5.1.7: + resolution: {integrity: sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==} engines: {node: ^18 || >=20} hasBin: true - nanostores@1.1.1: - resolution: {integrity: sha512-EYJqS25r2iBeTtGQCHidXl1VfZ1jXM7Q04zXJOrMlxVVmD0ptxJaNux92n1mJ7c5lN3zTq12MhH/8x59nP+qmg==} + nanostores@1.2.0: + resolution: {integrity: sha512-F0wCzbsH80G7XXo0Jd9/AVQC7ouWY6idUCTnMwW5t/Rv9W8qmO6endavDwg7TNp5GbugwSukFMVZqzPSrSMndg==} engines: {node: ^20.0.0 || >=22.0.0} negotiator@0.6.3: @@ -9069,17 +8815,14 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-abi@3.88.0: - resolution: {integrity: sha512-At6b4UqIEVudaqPsXjmUO1r/N5BUr4yhDGs5PkBE8/oG5+TfLPhFechiskFsnT6Ql0VfUXbalUUCbfXxtj7K+w==} + node-abi@3.89.0: + resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} engines: {node: '>=10'} node-emoji@2.2.0: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} - node-fetch-native@1.6.7: - resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -9089,8 +8832,8 @@ packages: encoding: optional: true - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-releases@2.0.36: + resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -9122,11 +8865,6 @@ packages: numcodecs@0.3.2: resolution: {integrity: sha512-6YSPnmZgg0P87jnNhi3s+FVLOcIn3y+1CTIgUulA3IdASzK9fJM87sUFkpyA+be9GibGRaST2wCgkD+6U+fWKw==} - nypm@0.6.5: - resolution: {integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==} - engines: {node: '>=18'} - hasBin: true - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -9153,9 +8891,6 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - ohash@2.0.11: - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - ol@10.8.0: resolution: {integrity: sha512-kLk7jIlJvKyhVMAjORTXKjzlM6YIByZ1H/d0DBx3oq8nSPCG6/gbLr5RxukzPgwbhnAqh+xHNCmrvmFKhVMvoQ==} @@ -9364,9 +9099,6 @@ packages: peberminta@0.9.0: resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} @@ -9400,8 +9132,8 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - pkijs@3.3.3: - resolution: {integrity: sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==} + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} engines: {node: '>=16.0.0'} point-in-polygon-hao@1.2.4: @@ -9824,19 +9556,15 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - postgres@3.4.7: - resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} - engines: {node: '>=12'} - postgres@3.4.8: resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} engines: {node: '>=12'} - posthog-js@1.360.1: - resolution: {integrity: sha512-wtKZm0b0SLYLtk0T3SiyXn9cBhwE421fPcJUJ7cRpJmsysHKJcOJ9O/Q7nx7aDj6LvTFILuzjzYO3YTCjbWgqQ==} + posthog-js@1.362.0: + resolution: {integrity: sha512-qPHkAk9G19xVDAQLoQ1FOLNE9BBq+FDePhkOevbdUQcFJVNHVc+j7E/ndQ+olGnDuiSMdgAb5c6yGk7PD9Z0ug==} - posthog-node@5.28.1: - resolution: {integrity: sha512-dfUaeNwKc/YZI/vbP5IJSMuMprPLbtzWM/ZQFkuyWj0fhU3PW0VmxNO1gkqy48SsUauamczEPBKTQRYZVLcacg==} + posthog-node@5.28.4: + resolution: {integrity: sha512-j8JBDNuSwUWR0TBZNuCwNRHQ+OReVz1UwDYMDol+iqFTbYrIKZnyC05oyGtSBRGntrYBiaIWFbH9N3kZuxfVdg==} engines: {node: ^20.20.0 || >=22.22.0} peerDependencies: rxjs: ^7.0.0 @@ -9875,19 +9603,6 @@ packages: peerDependencies: react: '>=16.0.0' - prisma@7.4.2: - resolution: {integrity: sha512-2bP8Ruww3Q95Z2eH4Yqh4KAENRsj/SxbdknIVBfd6DmjPwmpsC4OVFMLOeHt6tM3Amh8ebjvstrUz3V/hOe1dA==} - engines: {node: ^20.19 || ^22.12 || >=24.0} - hasBin: true - peerDependencies: - better-sqlite3: '>=9.0.0' - typescript: '>=5.4.0' - peerDependenciesMeta: - better-sqlite3: - optional: true - typescript: - optional: true - prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -9909,9 +9624,6 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - proper-lockfile@4.1.2: - resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -9944,9 +9656,6 @@ packages: resolution: {integrity: sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==} engines: {node: '>=12.20'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - pvtsutils@1.3.6: resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} @@ -10032,9 +9741,6 @@ packages: rbush@4.0.1: resolution: {integrity: sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==} - rc9@2.1.2: - resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} - rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -10240,9 +9946,6 @@ packages: regenerate@1.4.2: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} - regexp-to-ast@0.5.0: - resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} - regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} @@ -10301,9 +10004,6 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - remeda@2.33.4: - resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==} - remix-hook-form@7.1.1: resolution: {integrity: sha512-czEFVv8OrzV+BCjwwwJW4x9/OMxR7dnO/PFtHZJ/wLITur+OQ2jtqaOMh6VD+VHJ2Wu8IWf2P0mE4cgiw2cYXw==} peerDependencies: @@ -10317,8 +10017,8 @@ packages: peerDependencies: react-router: '>=7.9.0' - remix-utils@9.3.0: - resolution: {integrity: sha512-xrLRW6Xbm3/QT97Pt8W7Au2605iFkiq9X0THpuGnY93uc7BJefNLpSok8ORFgRzbHIbpp3rCJr19r05k861YZQ==} + remix-utils@9.3.1: + resolution: {integrity: sha512-Z/CVE9mPKYOKxhSV5q2IeXd5W4zl/B4q9YLQfuR3EYNSOop8riQso6QXGJXd+71S96Ox8/haMl34GyV/WBnoVg==} engines: {node: '>=20.0.0'} peerDependencies: '@edgefirst-dev/batcher': ^1.0.0 @@ -10406,10 +10106,6 @@ packages: restructure@3.0.2: resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -10487,10 +10183,6 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sax@1.5.0: - resolution: {integrity: sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==} - engines: {node: '>=11.0.0'} - sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} @@ -10546,9 +10238,6 @@ packages: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} - seq-queue@0.0.5: - resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} - serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -10742,10 +10431,6 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - sqlstring@2.3.3: - resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} - engines: {node: '>= 0.6'} - srcset@4.0.0: resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} engines: {node: '>=12'} @@ -10905,8 +10590,11 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@4.2.1: - resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} + + tailwindcss@4.2.2: + resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -10932,8 +10620,8 @@ packages: uglify-js: optional: true - terser@5.46.0: - resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} + terser@5.46.1: + resolution: {integrity: sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==} engines: {node: '>=10'} hasBin: true @@ -10958,10 +10646,6 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} - tinyexec@1.0.4: resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} engines: {node: '>=18'} @@ -10980,10 +10664,6 @@ packages: tinyqueue@3.0.0: resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} - engines: {node: '>=14.0.0'} - tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} @@ -11051,42 +10731,17 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + tsyringe@4.10.0: resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} engines: {node: '>= 6.0.0'} - turbo-darwin-64@2.8.16: - resolution: {integrity: sha512-KWa4hUMWrpADC6Q/wIHRkBLw6X6MV9nx6X7hSXbTrrMz0KdaKhmfudUZ3sS76bJFmgArBU25cSc0AUyyrswYxg==} - cpu: [x64] - os: [darwin] - - turbo-darwin-arm64@2.8.16: - resolution: {integrity: sha512-NBgaqBDLQSZlJR4D5XCkQq6noaO0RvIgwm5eYFJYL3bH5dNu8o0UBpq7C5DYnQI8+ybyoHFjT5/icN4LeUYLow==} - cpu: [arm64] - os: [darwin] - - turbo-linux-64@2.8.16: - resolution: {integrity: sha512-VYPdcCRevI9kR/hr1H1xwXy7QQt/jNKiim1e1mjANBXD2E9VZWMkIL74J1Huad5MbU3/jw7voHOqDPLJPC2p6w==} - cpu: [x64] - os: [linux] - - turbo-linux-arm64@2.8.16: - resolution: {integrity: sha512-beq8tgUVI3uwkQkXJMiOr/hfxQRw54M3elpBwqgYFfemiK5LhCjjcwO0DkE8GZZfElBIlk+saMAQOZy3885wNQ==} - cpu: [arm64] - os: [linux] - - turbo-windows-64@2.8.16: - resolution: {integrity: sha512-Ig7b46iUgiOIkea/D3Z7H+zNzvzSnIJcLYFpZLA0RxbUTrbLhv9qIPwv3pT9p/abmu0LXVKHxaOo+p26SuDhzw==} - cpu: [x64] - os: [win32] - - turbo-windows-arm64@2.8.16: - resolution: {integrity: sha512-fOWjbEA2PiE2HEnFQrwNZKYEdjewyPc2no9GmrXklZnTCuMsxeCN39aVlKpKpim03Zq/ykIuvApGwq8ZbfS2Yw==} - cpu: [arm64] - os: [win32] - - turbo@2.8.16: - resolution: {integrity: sha512-u6e9e3cTTpE2adQ1DYm3A3r8y3LAONEx1jYvJx6eIgSY4bMLxIxs0riWzI0Z/IK903ikiUzRPZ2c1Ph5lVLkhA==} + turbo@2.8.19: + resolution: {integrity: sha512-9NTKvQZ/02XnJCSU4mF5gvAPK+nuLbvCtft/NE8dJeOwaup0bJ14rYx7TxmSXp8JJuAN8nAjqfXWFsWwoQSFHg==} hasBin: true type-fest@0.21.3: @@ -11101,8 +10756,8 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - type-fest@5.4.4: - resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} + type-fest@5.5.0: + resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} engines: {node: '>=20'} type-is@1.6.18: @@ -11137,8 +10792,8 @@ packages: peerDependencies: typedoc-plugin-markdown: '>=4.8.0' - typedoc-plugin-markdown@4.10.0: - resolution: {integrity: sha512-psrg8Rtnv4HPWCsoxId+MzEN8TVK5jeKCnTbnGAbTBqcDapR9hM41bJT/9eAyKn9C2MDG9Qjh3MkltAYuLDoXg==} + typedoc-plugin-markdown@4.11.0: + resolution: {integrity: sha512-2iunh2ALyfyh204OF7h2u0kuQ84xB3jFZtFyUr01nThJkLvR8oGGSSDlyt2gyO4kXhvUxDcVbO0y43+qX+wFbw==} engines: {node: '>= 18'} peerDependencies: typedoc: 0.28.x @@ -11336,8 +10991,8 @@ packages: uzip-module@1.0.3: resolution: {integrity: sha512-AMqwWZaknLM77G+VPYNZLEruMGWGzyigPK3/Whg99B3S6vGHuqsyl5ZrOv1UUF3paGK1U6PM0cnayioaryg/fA==} - valibot@1.2.0: - resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + valibot@1.3.1: + resolution: {integrity: sha512-sfdRir/QFM0JaF22hqTroPc5xy4DimuGQVKFrzF1YfGwaS1nJot3Y8VqMdLO2Lg27fMzat2yD3pY5PbAYO39Gg==} peerDependencies: typescript: '>=5' peerDependenciesMeta: @@ -11426,55 +11081,21 @@ packages: yaml: optional: true - vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} + vitest@4.1.0: + resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.18 - '@vitest/browser-preview': 4.0.18 - '@vitest/browser-webdriverio': 4.0.18 - '@vitest/ui': 4.0.18 + '@vitest/browser-playwright': 4.1.0 + '@vitest/browser-preview': 4.1.0 + '@vitest/browser-webdriverio': 4.1.0 + '@vitest/ui': 4.1.0 happy-dom: '*' jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vitest@4.1.0: - resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0 - '@vitest/browser-preview': 4.1.0 - '@vitest/browser-webdriverio': 4.1.0 - '@vitest/ui': 4.1.0 - happy-dom: '*' - jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -11626,8 +11247,8 @@ packages: wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} - wkt-parser@1.5.3: - resolution: {integrity: sha512-myla+RrMj+WTlnHc8Y4HEwjBcBF9dqJ3vjff/zmlrn9V3OKOM1mZVIyNjlPEmOM9Jjr/PPut0tnaTs9NyHcK8Q==} + wkt-parser@1.5.4: + resolution: {integrity: sha512-heRp3QBynj8SAGepAkE8h2k4KhUGRqzgwlSRgqNhxjmSIeSvE5ZrV8n1uy5jk+iJO2jmfffIwjdAaTirBOOx0A==} wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -11716,9 +11337,6 @@ packages: zarrita@0.6.1: resolution: {integrity: sha512-YOMTW8FT55Rz+vadTIZeOFZ/F2h4svKizyldvPtMYSxPgSNcRkOzkxCsWpIWlWzB1I/LmISmi0bEekOhLlI+Zw==} - zeptomatch@2.1.0: - resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} - zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -11728,8 +11346,8 @@ packages: zstddec@0.2.0: resolution: {integrity: sha512-oyPnDa1X5c13+Y7mA/FDMNJrn4S8UNBe0KCqtDmor40Re7ALrPN6npFwyYVRRh+PqozZQdeg23QtbcamZnG5rA==} - zustand@5.0.11: - resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} + zustand@5.0.12: + resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -11875,8 +11493,8 @@ snapshots: '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 @@ -11891,7 +11509,7 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -11929,7 +11547,7 @@ snapshots: regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.7(@babel/core@7.29.0)': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 @@ -12010,15 +11628,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helpers@7.28.6': + '@babel/helpers@7.29.2': dependencies: '@babel/template': 7.28.6 '@babel/types': 7.29.0 - '@babel/parser@7.29.0': - dependencies: - '@babel/types': 7.29.0 - '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 @@ -12414,9 +12028,9 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-module-imports': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - babel-plugin-polyfill-corejs2: 0.4.16(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0) - babel-plugin-polyfill-regenerator: 0.6.7(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -12483,7 +12097,7 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-env@7.29.0(@babel/core@7.29.0)': + '@babel/preset-env@7.29.2(@babel/core@7.29.0)': dependencies: '@babel/compat-data': 7.29.0 '@babel/core': 7.29.0 @@ -12551,10 +12165,10 @@ snapshots: '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) - babel-plugin-polyfill-corejs2: 0.4.16(@babel/core@7.29.0) - babel-plugin-polyfill-corejs3: 0.14.1(@babel/core@7.29.0) - babel-plugin-polyfill-regenerator: 0.6.7(@babel/core@7.29.0) - core-js-compat: 3.48.0 + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -12589,18 +12203,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/runtime-corejs3@7.29.0': + '@babel/runtime-corejs3@7.29.2': dependencies: - core-js-pure: 3.48.0 - - '@babel/runtime@7.28.6': {} + core-js-pure: 3.49.0 '@babel/runtime@7.29.2': {} '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@babel/traverse@7.29.0': @@ -12608,7 +12220,7 @@ snapshots: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -12622,50 +12234,49 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)': + '@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0)': dependencies: '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 '@standard-schema/spec': 1.1.0 better-call: 1.3.2(zod@4.3.6) - jose: 6.1.3 - kysely: 0.28.11 - nanostores: 1.1.1 + jose: 6.2.2 + kysely: 0.28.13 + nanostores: 1.2.0 zod: 4.3.6 - '@better-auth/drizzle-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))': + '@better-auth/drizzle-adapter@1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8))': dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) + '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 - drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) + optionalDependencies: + drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8) - '@better-auth/kysely-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11)': + '@better-auth/kysely-adapter@1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(kysely@0.28.13)': dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) + '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 - kysely: 0.28.11 + kysely: 0.28.13 - '@better-auth/memory-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)': + '@better-auth/memory-adapter@1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)': dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) + '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 - '@better-auth/mongo-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0)': + '@better-auth/mongo-adapter@1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@7.1.0)': dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) + '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 mongodb: 7.1.0 - '@better-auth/prisma-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))': + '@better-auth/prisma-adapter@1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)': dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) + '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 - '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) - prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@better-auth/telemetry@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))': + '@better-auth/telemetry@1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))': dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) + '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 @@ -12673,42 +12284,42 @@ snapshots: '@better-fetch/fetch@1.1.21': {} - '@biomejs/biome@2.4.6': + '@biomejs/biome@2.4.8': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.6 - '@biomejs/cli-darwin-x64': 2.4.6 - '@biomejs/cli-linux-arm64': 2.4.6 - '@biomejs/cli-linux-arm64-musl': 2.4.6 - '@biomejs/cli-linux-x64': 2.4.6 - '@biomejs/cli-linux-x64-musl': 2.4.6 - '@biomejs/cli-win32-arm64': 2.4.6 - '@biomejs/cli-win32-x64': 2.4.6 - - '@biomejs/cli-darwin-arm64@2.4.6': + '@biomejs/cli-darwin-arm64': 2.4.8 + '@biomejs/cli-darwin-x64': 2.4.8 + '@biomejs/cli-linux-arm64': 2.4.8 + '@biomejs/cli-linux-arm64-musl': 2.4.8 + '@biomejs/cli-linux-x64': 2.4.8 + '@biomejs/cli-linux-x64-musl': 2.4.8 + '@biomejs/cli-win32-arm64': 2.4.8 + '@biomejs/cli-win32-x64': 2.4.8 + + '@biomejs/cli-darwin-arm64@2.4.8': optional: true - '@biomejs/cli-darwin-x64@2.4.6': + '@biomejs/cli-darwin-x64@2.4.8': optional: true - '@biomejs/cli-linux-arm64-musl@2.4.6': + '@biomejs/cli-linux-arm64-musl@2.4.8': optional: true - '@biomejs/cli-linux-arm64@2.4.6': + '@biomejs/cli-linux-arm64@2.4.8': optional: true - '@biomejs/cli-linux-x64-musl@2.4.6': + '@biomejs/cli-linux-x64-musl@2.4.8': optional: true - '@biomejs/cli-linux-x64@2.4.6': + '@biomejs/cli-linux-x64@2.4.8': optional: true - '@biomejs/cli-win32-arm64@2.4.6': + '@biomejs/cli-win32-arm64@2.4.8': optional: true - '@biomejs/cli-win32-x64@2.4.6': + '@biomejs/cli-win32-x64@2.4.8': optional: true - '@borewit/text-codec@0.2.1': {} + '@borewit/text-codec@0.2.2': {} '@changesets/apply-release-plan@7.1.0': dependencies: @@ -12747,7 +12358,7 @@ snapshots: transitivePeerDependencies: - encoding - '@changesets/cli@2.30.0(@types/node@25.4.0)': + '@changesets/cli@2.30.0(@types/node@25.5.0)': dependencies: '@changesets/apply-release-plan': 7.1.0 '@changesets/assemble-release-plan': 6.0.9 @@ -12763,7 +12374,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.4.0) + '@inquirer/external-editor': 1.0.3(@types/node@25.5.0) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -12868,21 +12479,6 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@chevrotain/cst-dts-gen@10.5.0': - dependencies: - '@chevrotain/gast': 10.5.0 - '@chevrotain/types': 10.5.0 - lodash: 4.17.21 - - '@chevrotain/gast@10.5.0': - dependencies: - '@chevrotain/types': 10.5.0 - lodash: 4.17.21 - - '@chevrotain/types@10.5.0': {} - - '@chevrotain/utils@10.5.0': {} - '@colors/colors@1.5.0': optional: true @@ -13227,11 +12823,11 @@ snapshots: '@babel/generator': 7.29.1 '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/preset-env': 7.29.0(@babel/core@7.29.0) + '@babel/preset-env': 7.29.2(@babel/core@7.29.0) '@babel/preset-react': 7.28.5(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) '@babel/runtime': 7.29.2 - '@babel/runtime-corejs3': 7.29.0 + '@babel/runtime-corejs3': 7.29.2 '@babel/traverse': 7.29.0 '@docusaurus/logger': 3.9.2 '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13247,7 +12843,7 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/bundler@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/bundler@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: '@babel/core': 7.29.0 '@docusaurus/babel': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13258,7 +12854,7 @@ snapshots: babel-loader: 9.2.1(@babel/core@7.29.0)(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) clean-css: 5.3.3 copy-webpack-plugin: 11.0.0(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) - css-loader: 6.11.0(@rspack/core@1.7.8(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) + css-loader: 6.11.0(@rspack/core@1.7.9(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) css-minimizer-webpack-plugin: 5.0.1(clean-css@5.3.3)(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) cssnano: 6.1.2(postcss@8.5.8) file-loader: 6.2.0(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) @@ -13290,10 +12886,10 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/core@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/core@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: '@docusaurus/babel': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@docusaurus/bundler': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/bundler': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/logger': 3.9.2 '@docusaurus/mdx-loader': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13306,7 +12902,7 @@ snapshots: cli-table3: 0.6.5 combine-promises: 1.2.0 commander: 5.1.0 - core-js: 3.48.0 + core-js: 3.49.0 detect-port: 1.6.1 escape-html: 1.0.3 eta: 2.2.0 @@ -13314,7 +12910,7 @@ snapshots: execa: 5.1.1 fs-extra: 11.3.4 html-tags: 3.3.1 - html-webpack-plugin: 5.6.6(@rspack/core@1.7.8(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) + html-webpack-plugin: 5.6.6(@rspack/core@1.7.9(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))) leven: 3.1.0 lodash: 4.17.23 open: 8.4.2 @@ -13364,7 +12960,7 @@ snapshots: '@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19)': dependencies: '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@rspack/core': 1.7.8(@swc/helpers@0.5.19) + '@rspack/core': 1.7.9(@swc/helpers@0.5.19) '@swc/core': 1.15.18(@swc/helpers@0.5.19) '@swc/html': 1.15.18 browserslist: 4.28.1 @@ -13436,13 +13032,13 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/plugin-content-blog@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-content-blog@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/logger': 3.9.2 '@docusaurus/mdx-loader': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-common': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13477,13 +13073,13 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/logger': 3.9.2 '@docusaurus/mdx-loader': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/module-type-aliases': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-common': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13517,9 +13113,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-content-pages@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-content-pages@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/mdx-loader': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13547,9 +13143,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-css-cascade-layers@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-css-cascade-layers@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-validation': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13574,9 +13170,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-debug@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-debug@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) fs-extra: 11.3.4 @@ -13602,9 +13198,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-analytics@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-google-analytics@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-validation': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 @@ -13628,9 +13224,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-gtag@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-google-gtag@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-validation': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@types/gtag.js': 0.0.12 @@ -13655,9 +13251,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-google-tag-manager@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-google-tag-manager@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-validation': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 @@ -13681,9 +13277,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-sitemap@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-sitemap@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/logger': 3.9.2 '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13712,9 +13308,9 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/plugin-svgr@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/plugin-svgr@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-validation': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13742,22 +13338,22 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/preset-classic@3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3)': - dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-content-blog': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-content-pages': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-css-cascade-layers': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-debug': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-google-analytics': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-google-gtag': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-google-tag-manager': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-sitemap': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-svgr': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/theme-classic': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@docusaurus/theme-search-algolia': 3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3) + '@docusaurus/preset-classic@3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3)': + dependencies: + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-content-blog': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-content-pages': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-css-cascade-layers': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-debug': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-google-analytics': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-google-gtag': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-google-tag-manager': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-sitemap': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-svgr': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/theme-classic': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@docusaurus/theme-search-algolia': 3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3) '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -13787,16 +13383,16 @@ snapshots: '@types/react': 19.2.14 react: 19.2.4 - '@docusaurus/theme-classic@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@docusaurus/theme-classic@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/logger': 3.9.2 '@docusaurus/mdx-loader': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/module-type-aliases': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@docusaurus/plugin-content-blog': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/plugin-content-pages': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@docusaurus/plugin-content-blog': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-content-pages': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/theme-translations': 3.9.2 '@docusaurus/types': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13834,11 +13430,11 @@ snapshots: - utf-8-validate - webpack-cli - '@docusaurus/theme-common@3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@docusaurus/theme-common@3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@docusaurus/mdx-loader': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/module-type-aliases': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-common': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@types/history': 4.7.11 @@ -13858,13 +13454,13 @@ snapshots: - uglify-js - webpack-cli - '@docusaurus/theme-search-algolia@3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3)': + '@docusaurus/theme-search-algolia@3.9.2(@algolia/client-search@5.49.2)(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3)(typescript@5.9.3)': dependencies: '@docsearch/react': 4.6.0(@algolia/client-search@5.49.2)(@types/react@19.2.14)(algoliasearch@5.49.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(search-insights@2.17.3) - '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/core': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) '@docusaurus/logger': 3.9.2 - '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.8(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@docusaurus/plugin-content-docs': 3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + '@docusaurus/theme-common': 3.9.2(@docusaurus/plugin-content-docs@3.9.2(@docusaurus/faster@3.9.2(@docusaurus/types@3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@swc/helpers@0.5.19))(@mdx-js/react@3.1.1(@types/react@19.2.14)(react@19.2.4))(@rspack/core@1.7.9(@swc/helpers@0.5.19))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/theme-translations': 3.9.2 '@docusaurus/utils': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@docusaurus/utils-validation': 3.9.2(@swc/core@1.15.18(@swc/helpers@0.5.19))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13991,7 +13587,7 @@ snapshots: - uglify-js - webpack-cli - '@dotenvx/dotenvx@1.54.1': + '@dotenvx/dotenvx@1.55.1': dependencies: commander: 11.1.0 dotenv: 17.3.1 @@ -14009,30 +13605,20 @@ snapshots: dependencies: '@noble/ciphers': 1.3.0 - '@electric-sql/pglite-socket@0.0.20(@electric-sql/pglite@0.3.15)': - dependencies: - '@electric-sql/pglite': 0.3.15 - - '@electric-sql/pglite-tools@0.2.20(@electric-sql/pglite@0.3.15)': - dependencies: - '@electric-sql/pglite': 0.3.15 - - '@electric-sql/pglite@0.3.15': {} - '@electric-sql/pglite@0.3.16': {} - '@emnapi/core@1.8.1': + '@emnapi/core@1.9.0': dependencies: - '@emnapi/wasi-threads': 1.1.0 + '@emnapi/wasi-threads': 1.2.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.8.1': + '@emnapi/runtime@1.9.0': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.1.0': + '@emnapi/wasi-threads@1.2.0': dependencies: tslib: 2.8.1 optional: true @@ -14050,7 +13636,7 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.27.3': + '@esbuild/aix-ppc64@0.27.4': optional: true '@esbuild/android-arm64@0.18.20': @@ -14059,7 +13645,7 @@ snapshots: '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.27.3': + '@esbuild/android-arm64@0.27.4': optional: true '@esbuild/android-arm@0.18.20': @@ -14068,7 +13654,7 @@ snapshots: '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.27.3': + '@esbuild/android-arm@0.27.4': optional: true '@esbuild/android-x64@0.18.20': @@ -14077,7 +13663,7 @@ snapshots: '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.27.3': + '@esbuild/android-x64@0.27.4': optional: true '@esbuild/darwin-arm64@0.18.20': @@ -14086,7 +13672,7 @@ snapshots: '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.27.3': + '@esbuild/darwin-arm64@0.27.4': optional: true '@esbuild/darwin-x64@0.18.20': @@ -14095,7 +13681,7 @@ snapshots: '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.27.3': + '@esbuild/darwin-x64@0.27.4': optional: true '@esbuild/freebsd-arm64@0.18.20': @@ -14104,7 +13690,7 @@ snapshots: '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.27.3': + '@esbuild/freebsd-arm64@0.27.4': optional: true '@esbuild/freebsd-x64@0.18.20': @@ -14113,7 +13699,7 @@ snapshots: '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@esbuild/freebsd-x64@0.27.4': optional: true '@esbuild/linux-arm64@0.18.20': @@ -14122,7 +13708,7 @@ snapshots: '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.27.3': + '@esbuild/linux-arm64@0.27.4': optional: true '@esbuild/linux-arm@0.18.20': @@ -14131,7 +13717,7 @@ snapshots: '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.27.3': + '@esbuild/linux-arm@0.27.4': optional: true '@esbuild/linux-ia32@0.18.20': @@ -14140,7 +13726,7 @@ snapshots: '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.27.3': + '@esbuild/linux-ia32@0.27.4': optional: true '@esbuild/linux-loong64@0.18.20': @@ -14149,7 +13735,7 @@ snapshots: '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.27.3': + '@esbuild/linux-loong64@0.27.4': optional: true '@esbuild/linux-mips64el@0.18.20': @@ -14158,7 +13744,7 @@ snapshots: '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@esbuild/linux-mips64el@0.27.4': optional: true '@esbuild/linux-ppc64@0.18.20': @@ -14167,7 +13753,7 @@ snapshots: '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/linux-ppc64@0.27.4': optional: true '@esbuild/linux-riscv64@0.18.20': @@ -14176,7 +13762,7 @@ snapshots: '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/linux-riscv64@0.27.4': optional: true '@esbuild/linux-s390x@0.18.20': @@ -14185,7 +13771,7 @@ snapshots: '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/linux-s390x@0.27.4': optional: true '@esbuild/linux-x64@0.18.20': @@ -14194,13 +13780,13 @@ snapshots: '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/linux-x64@0.27.4': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/netbsd-arm64@0.27.4': optional: true '@esbuild/netbsd-x64@0.18.20': @@ -14209,13 +13795,13 @@ snapshots: '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/netbsd-x64@0.27.4': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/openbsd-arm64@0.27.4': optional: true '@esbuild/openbsd-x64@0.18.20': @@ -14224,13 +13810,13 @@ snapshots: '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/openbsd-x64@0.27.4': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/openharmony-arm64@0.27.4': optional: true '@esbuild/sunos-x64@0.18.20': @@ -14239,7 +13825,7 @@ snapshots: '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/sunos-x64@0.27.4': optional: true '@esbuild/win32-arm64@0.18.20': @@ -14248,7 +13834,7 @@ snapshots: '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/win32-arm64@0.27.4': optional: true '@esbuild/win32-ia32@0.18.20': @@ -14257,7 +13843,7 @@ snapshots: '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/win32-ia32@0.27.4': optional: true '@esbuild/win32-x64@0.18.20': @@ -14266,14 +13852,14 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/win32-x64@0.27.4': optional: true - '@fastify/otel@0.16.0(@opentelemetry/api@1.9.0)': + '@fastify/otel@0.17.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.212.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 minimatch: 10.2.4 transitivePeerDependencies: @@ -14296,21 +13882,21 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@geomatico/maplibre-cog-protocol@https://codeload.github.com/SvenVw/maplibre-cog-protocol/tar.gz/fd6765830cd1453c3d20d290d6c40684153c5b7d(maplibre-gl@5.20.0)': + '@geomatico/maplibre-cog-protocol@https://codeload.github.com/SvenVw/maplibre-cog-protocol/tar.gz/fd6765830cd1453c3d20d290d6c40684153c5b7d(maplibre-gl@5.20.2)': dependencies: '@mapbox/sphericalmercator': 1.2.0 d3-scale: 4.0.2 geotiff: 2.1.3 - maplibre-gl: 5.20.0 + maplibre-gl: 5.20.2 proj4: 2.20.4 quick-lru: 7.3.0 - '@gerrit0/mini-shiki@3.22.0': + '@gerrit0/mini-shiki@3.23.0': dependencies: - '@shikijs/engine-oniguruma': 3.22.0 - '@shikijs/langs': 3.22.0 - '@shikijs/themes': 3.22.0 - '@shikijs/types': 3.22.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 '@hapi/hoek@9.3.0': {} @@ -14319,21 +13905,17 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@hono/node-server@1.19.9(hono@4.11.4)': - dependencies: - hono: 4.11.4 - '@hookform/resolvers@5.2.2(react-hook-form@7.71.2(react@19.2.4))': dependencies: '@standard-schema/utils': 0.3.0 react-hook-form: 7.71.2(react@19.2.4) - '@inquirer/external-editor@1.0.3(@types/node@25.4.0)': + '@inquirer/external-editor@1.0.3(@types/node@25.5.0)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@jest/schemas@29.6.3': dependencies: @@ -14344,7 +13926,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -14512,7 +14094,7 @@ snapshots: '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -14555,15 +14137,15 @@ snapshots: '@maplibre/geojson-vt@5.0.4': {} - '@maplibre/geojson-vt@6.0.1': + '@maplibre/geojson-vt@6.0.4': dependencies: kdbush: 4.0.2 - '@maplibre/maplibre-gl-geocoder@1.9.4(maplibre-gl@5.20.0)': + '@maplibre/maplibre-gl-geocoder@1.9.4(maplibre-gl@5.20.2)': dependencies: events: 3.3.0 lodash.debounce: 4.0.8 - maplibre-gl: 5.20.0 + maplibre-gl: 5.20.2 subtag: 0.5.0 suggestions-list: 0.0.2 xtend: 4.0.2 @@ -14587,7 +14169,7 @@ snapshots: rw: 1.3.3 tinyqueue: 3.0.0 - '@maplibre/mlt@1.1.7': + '@maplibre/mlt@1.1.8': dependencies: '@mapbox/point-geometry': 1.1.0 @@ -14668,15 +14250,10 @@ snapshots: dependencies: sparse-bitfield: 3.0.3 - '@mrleebo/prisma-ast@0.13.1': - dependencies: - chevrotain: 10.5.0 - lilconfig: 2.1.0 - '@napi-rs/wasm-runtime@1.0.7': dependencies: - '@emnapi/core': 1.8.1 - '@emnapi/runtime': 1.8.1 + '@emnapi/core': 1.9.0 + '@emnapi/runtime': 1.9.0 '@tybys/wasm-util': 0.10.1 optional: true @@ -14722,7 +14299,11 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs@0.211.0': + '@opentelemetry/api-logs@0.212.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api-logs@0.213.0': dependencies: '@opentelemetry/api': 1.9.0 @@ -14737,11 +14318,6 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.40.0 - '@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -14756,163 +14332,163 @@ snapshots: '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-amqplib@0.60.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-connect@0.56.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 '@types/connect': 3.4.38 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-dataloader@0.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-dataloader@0.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-express@0.61.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-fs@0.32.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-generic-pool@0.54.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-generic-pool@0.56.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-graphql@0.58.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-graphql@0.61.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-hapi@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-http@0.211.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-http@0.213.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-ioredis@0.59.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-ioredis@0.61.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-kafkajs@0.20.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-kafkajs@0.22.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-knex@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-knex@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-koa@0.61.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-lru-memoizer@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-lru-memoizer@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongodb@0.64.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongodb@0.66.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongoose@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql2@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql2@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 '@types/mysql': 2.15.27 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-pg@0.65.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) '@types/pg': 8.15.6 @@ -14920,29 +14496,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-redis@0.59.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-redis@0.61.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-tedious@0.30.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-tedious@0.32.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 '@types/tedious': 4.0.14 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-undici@0.23.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -14956,20 +14532,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.212.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/api-logs': 0.212.0 import-in-the-middle: 2.0.6 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.213.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.211.0 - import-in-the-middle: 2.0.6 + '@opentelemetry/api-logs': 0.213.0 + import-in-the-middle: 3.0.0 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -15145,94 +14721,19 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@posthog/core@1.23.3': + '@posthog/core@1.24.0': dependencies: cross-spawn: 7.0.6 - '@posthog/types@1.360.1': {} - - '@prisma/client-runtime-utils@7.4.2': {} - - '@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)': - dependencies: - '@prisma/client-runtime-utils': 7.4.2 - optionalDependencies: - prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - typescript: 5.9.3 + '@posthog/types@1.362.0': {} - '@prisma/config@7.4.2': - dependencies: - c12: 3.1.0 - deepmerge-ts: 7.1.5 - effect: 3.18.4 - empathic: 2.0.0 - transitivePeerDependencies: - - magicast - - '@prisma/debug@7.2.0': {} - - '@prisma/debug@7.4.2': {} - - '@prisma/dev@0.20.0(typescript@5.9.3)': - dependencies: - '@electric-sql/pglite': 0.3.15 - '@electric-sql/pglite-socket': 0.0.20(@electric-sql/pglite@0.3.15) - '@electric-sql/pglite-tools': 0.2.20(@electric-sql/pglite@0.3.15) - '@hono/node-server': 1.19.9(hono@4.11.4) - '@mrleebo/prisma-ast': 0.13.1 - '@prisma/get-platform': 7.2.0 - '@prisma/query-plan-executor': 7.2.0 - foreground-child: 3.3.1 - get-port-please: 3.2.0 - hono: 4.11.4 - http-status-codes: 2.3.0 - pathe: 2.0.3 - proper-lockfile: 4.1.2 - remeda: 2.33.4 - std-env: 3.10.0 - valibot: 1.2.0(typescript@5.9.3) - zeptomatch: 2.1.0 - transitivePeerDependencies: - - typescript - - '@prisma/engines-version@7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919': {} - - '@prisma/engines@7.4.2': - dependencies: - '@prisma/debug': 7.4.2 - '@prisma/engines-version': 7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919 - '@prisma/fetch-engine': 7.4.2 - '@prisma/get-platform': 7.4.2 - - '@prisma/fetch-engine@7.4.2': - dependencies: - '@prisma/debug': 7.4.2 - '@prisma/engines-version': 7.5.0-10.94a226be1cf2967af2541cca5529f0f7ba866919 - '@prisma/get-platform': 7.4.2 - - '@prisma/get-platform@7.2.0': - dependencies: - '@prisma/debug': 7.2.0 - - '@prisma/get-platform@7.4.2': - dependencies: - '@prisma/debug': 7.4.2 - - '@prisma/instrumentation@7.2.0(@opentelemetry/api@1.9.0)': + '@prisma/instrumentation@7.4.2(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.207.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@prisma/query-plan-executor@7.2.0': {} - - '@prisma/studio-core@0.13.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - '@types/react': 19.2.14 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - '@protobufjs/aspromise@1.1.2': {} '@protobufjs/base64@1.1.2': {} @@ -16019,7 +15520,7 @@ snapshots: '@radix-ui/rect@1.1.1': {} - '@react-email/body@0.2.1(react@19.2.4)': + '@react-email/body@0.3.0(react@19.2.4)': dependencies: react: 19.2.4 @@ -16040,9 +15541,9 @@ snapshots: dependencies: react: 19.2.4 - '@react-email/components@1.0.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@react-email/components@1.0.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@react-email/body': 0.2.1(react@19.2.4) + '@react-email/body': 0.3.0(react@19.2.4) '@react-email/button': 0.2.1(react@19.2.4) '@react-email/code-block': 0.2.1(react@19.2.4) '@react-email/code-inline': 0.0.6(react@19.2.4) @@ -16060,7 +15561,7 @@ snapshots: '@react-email/render': 2.0.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@react-email/row': 0.0.13(react@19.2.4) '@react-email/section': 0.0.17(react@19.2.4) - '@react-email/tailwind': 2.0.5(@react-email/body@0.2.1(react@19.2.4))(@react-email/button@0.2.1(react@19.2.4))(@react-email/code-block@0.2.1(react@19.2.4))(@react-email/code-inline@0.0.6(react@19.2.4))(@react-email/container@0.0.16(react@19.2.4))(@react-email/heading@0.0.16(react@19.2.4))(@react-email/hr@0.0.12(react@19.2.4))(@react-email/img@0.0.12(react@19.2.4))(@react-email/link@0.0.13(react@19.2.4))(@react-email/preview@0.0.14(react@19.2.4))(@react-email/text@0.1.6(react@19.2.4))(react@19.2.4) + '@react-email/tailwind': 2.0.6(@react-email/body@0.3.0(react@19.2.4))(@react-email/button@0.2.1(react@19.2.4))(@react-email/code-block@0.2.1(react@19.2.4))(@react-email/code-inline@0.0.6(react@19.2.4))(@react-email/container@0.0.16(react@19.2.4))(@react-email/heading@0.0.16(react@19.2.4))(@react-email/hr@0.0.12(react@19.2.4))(@react-email/img@0.0.12(react@19.2.4))(@react-email/link@0.0.13(react@19.2.4))(@react-email/preview@0.0.14(react@19.2.4))(@react-email/text@0.1.6(react@19.2.4))(react@19.2.4) '@react-email/text': 0.1.6(react@19.2.4) react: 19.2.4 transitivePeerDependencies: @@ -16122,13 +15623,13 @@ snapshots: dependencies: react: 19.2.4 - '@react-email/tailwind@2.0.5(@react-email/body@0.2.1(react@19.2.4))(@react-email/button@0.2.1(react@19.2.4))(@react-email/code-block@0.2.1(react@19.2.4))(@react-email/code-inline@0.0.6(react@19.2.4))(@react-email/container@0.0.16(react@19.2.4))(@react-email/heading@0.0.16(react@19.2.4))(@react-email/hr@0.0.12(react@19.2.4))(@react-email/img@0.0.12(react@19.2.4))(@react-email/link@0.0.13(react@19.2.4))(@react-email/preview@0.0.14(react@19.2.4))(@react-email/text@0.1.6(react@19.2.4))(react@19.2.4)': + '@react-email/tailwind@2.0.6(@react-email/body@0.3.0(react@19.2.4))(@react-email/button@0.2.1(react@19.2.4))(@react-email/code-block@0.2.1(react@19.2.4))(@react-email/code-inline@0.0.6(react@19.2.4))(@react-email/container@0.0.16(react@19.2.4))(@react-email/heading@0.0.16(react@19.2.4))(@react-email/hr@0.0.12(react@19.2.4))(@react-email/img@0.0.12(react@19.2.4))(@react-email/link@0.0.13(react@19.2.4))(@react-email/preview@0.0.14(react@19.2.4))(@react-email/text@0.1.6(react@19.2.4))(react@19.2.4)': dependencies: '@react-email/text': 0.1.6(react@19.2.4) react: 19.2.4 - tailwindcss: 4.2.1 + tailwindcss: 4.1.18 optionalDependencies: - '@react-email/body': 0.2.1(react@19.2.4) + '@react-email/body': 0.3.0(react@19.2.4) '@react-email/button': 0.2.1(react@19.2.4) '@react-email/code-block': 0.2.1(react@19.2.4) '@react-email/code-inline': 0.0.6(react@19.2.4) @@ -16171,7 +15672,7 @@ snapshots: '@react-pdf/pdfkit@4.1.0': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@react-pdf/png-js': 3.0.0 browserify-zlib: 0.2.0 crypto-js: 4.2.0 @@ -16194,7 +15695,7 @@ snapshots: '@react-pdf/render@4.3.2': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@react-pdf/fns': 3.1.2 '@react-pdf/primitives': 4.1.1 '@react-pdf/textkit': 6.1.0 @@ -16207,7 +15708,7 @@ snapshots: '@react-pdf/renderer@4.3.2(react@19.2.4)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@react-pdf/fns': 3.1.2 '@react-pdf/font': 4.0.4 '@react-pdf/layout': 4.4.2 @@ -16244,11 +15745,11 @@ snapshots: '@react-pdf/primitives': 4.1.1 '@react-pdf/stylesheet': 6.1.2 - '@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))(yaml@2.8.2)': + '@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) '@babel/traverse': 7.29.0 @@ -16273,9 +15774,9 @@ snapshots: react-router: 7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) semver: 7.7.4 tinyglobby: 0.2.15 - valibot: 1.2.0(typescript@5.9.3) - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + valibot: 1.3.1(typescript@5.9.3) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: '@react-router/serve': 7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) typescript: 5.9.3 @@ -16302,9 +15803,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@react-router/fs-routes@7.13.1(@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))(yaml@2.8.2))(typescript@5.9.3)': + '@react-router/fs-routes@7.13.1(@react-router/dev@7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2))(typescript@5.9.3)': dependencies: - '@react-router/dev': 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))(yaml@2.8.2) + '@react-router/dev': 7.13.1(@react-router/serve@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(terser@5.46.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2) minimatch: 9.0.9 optionalDependencies: typescript: 5.9.3 @@ -16480,55 +15981,55 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true - '@rspack/binding-darwin-arm64@1.7.8': + '@rspack/binding-darwin-arm64@1.7.9': optional: true - '@rspack/binding-darwin-x64@1.7.8': + '@rspack/binding-darwin-x64@1.7.9': optional: true - '@rspack/binding-linux-arm64-gnu@1.7.8': + '@rspack/binding-linux-arm64-gnu@1.7.9': optional: true - '@rspack/binding-linux-arm64-musl@1.7.8': + '@rspack/binding-linux-arm64-musl@1.7.9': optional: true - '@rspack/binding-linux-x64-gnu@1.7.8': + '@rspack/binding-linux-x64-gnu@1.7.9': optional: true - '@rspack/binding-linux-x64-musl@1.7.8': + '@rspack/binding-linux-x64-musl@1.7.9': optional: true - '@rspack/binding-wasm32-wasi@1.7.8': + '@rspack/binding-wasm32-wasi@1.7.9': dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rspack/binding-win32-arm64-msvc@1.7.8': + '@rspack/binding-win32-arm64-msvc@1.7.9': optional: true - '@rspack/binding-win32-ia32-msvc@1.7.8': + '@rspack/binding-win32-ia32-msvc@1.7.9': optional: true - '@rspack/binding-win32-x64-msvc@1.7.8': + '@rspack/binding-win32-x64-msvc@1.7.9': optional: true - '@rspack/binding@1.7.8': + '@rspack/binding@1.7.9': optionalDependencies: - '@rspack/binding-darwin-arm64': 1.7.8 - '@rspack/binding-darwin-x64': 1.7.8 - '@rspack/binding-linux-arm64-gnu': 1.7.8 - '@rspack/binding-linux-arm64-musl': 1.7.8 - '@rspack/binding-linux-x64-gnu': 1.7.8 - '@rspack/binding-linux-x64-musl': 1.7.8 - '@rspack/binding-wasm32-wasi': 1.7.8 - '@rspack/binding-win32-arm64-msvc': 1.7.8 - '@rspack/binding-win32-ia32-msvc': 1.7.8 - '@rspack/binding-win32-x64-msvc': 1.7.8 - - '@rspack/core@1.7.8(@swc/helpers@0.5.19)': + '@rspack/binding-darwin-arm64': 1.7.9 + '@rspack/binding-darwin-x64': 1.7.9 + '@rspack/binding-linux-arm64-gnu': 1.7.9 + '@rspack/binding-linux-arm64-musl': 1.7.9 + '@rspack/binding-linux-x64-gnu': 1.7.9 + '@rspack/binding-linux-x64-musl': 1.7.9 + '@rspack/binding-wasm32-wasi': 1.7.9 + '@rspack/binding-win32-arm64-msvc': 1.7.9 + '@rspack/binding-win32-ia32-msvc': 1.7.9 + '@rspack/binding-win32-x64-msvc': 1.7.9 + + '@rspack/core@1.7.9(@swc/helpers@0.5.19)': dependencies: '@module-federation/runtime-tools': 0.22.0 - '@rspack/binding': 1.7.8 + '@rspack/binding': 1.7.9 '@rspack/lite-tapable': 1.1.0 optionalDependencies: '@swc/helpers': 0.5.19 @@ -16540,38 +16041,38 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 - '@sentry-internal/browser-utils@10.43.0': + '@sentry-internal/browser-utils@10.44.0': dependencies: - '@sentry/core': 10.43.0 + '@sentry/core': 10.44.0 - '@sentry-internal/feedback@10.43.0': + '@sentry-internal/feedback@10.44.0': dependencies: - '@sentry/core': 10.43.0 + '@sentry/core': 10.44.0 '@sentry-internal/node-cpu-profiler@2.2.0': dependencies: detect-libc: 2.1.2 - node-abi: 3.88.0 + node-abi: 3.89.0 - '@sentry-internal/replay-canvas@10.43.0': + '@sentry-internal/replay-canvas@10.44.0': dependencies: - '@sentry-internal/replay': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry-internal/replay': 10.44.0 + '@sentry/core': 10.44.0 - '@sentry-internal/replay@10.43.0': + '@sentry-internal/replay@10.44.0': dependencies: - '@sentry-internal/browser-utils': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry-internal/browser-utils': 10.44.0 + '@sentry/core': 10.44.0 '@sentry/babel-plugin-component-annotate@5.1.1': {} - '@sentry/browser@10.43.0': + '@sentry/browser@10.44.0': dependencies: - '@sentry-internal/browser-utils': 10.43.0 - '@sentry-internal/feedback': 10.43.0 - '@sentry-internal/replay': 10.43.0 - '@sentry-internal/replay-canvas': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry-internal/browser-utils': 10.44.0 + '@sentry-internal/feedback': 10.44.0 + '@sentry-internal/replay': 10.44.0 + '@sentry-internal/replay-canvas': 10.44.0 + '@sentry/core': 10.44.0 '@sentry/bundler-plugin-core@5.1.1': dependencies: @@ -16630,91 +16131,91 @@ snapshots: - encoding - supports-color - '@sentry/core@10.43.0': {} + '@sentry/core@10.44.0': {} - '@sentry/node-core@10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': + '@sentry/node-core@10.44.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.213.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: - '@sentry/core': 10.43.0 - '@sentry/opentelemetry': 10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) - import-in-the-middle: 2.0.6 + '@sentry/core': 10.44.0 + '@sentry/opentelemetry': 10.44.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + import-in-the-middle: 3.0.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 - '@sentry/node@10.43.0': + '@sentry/node@10.44.0': dependencies: - '@fastify/otel': 0.16.0(@opentelemetry/api@1.9.0) + '@fastify/otel': 0.17.1(@opentelemetry/api@1.9.0) '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-dataloader': 0.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fs': 0.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-generic-pool': 0.54.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.58.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-kafkajs': 0.20.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-knex': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-lru-memoizer': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.64.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.63.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.60.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.56.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.61.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.32.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.56.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.61.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.213.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.61.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.22.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.61.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.66.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.65.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis': 0.61.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.32.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.23.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 - '@prisma/instrumentation': 7.2.0(@opentelemetry/api@1.9.0) - '@sentry/core': 10.43.0 - '@sentry/node-core': 10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) - '@sentry/opentelemetry': 10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) - import-in-the-middle: 2.0.6 + '@prisma/instrumentation': 7.4.2(@opentelemetry/api@1.9.0) + '@sentry/core': 10.44.0 + '@sentry/node-core': 10.44.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.213.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/opentelemetry': 10.44.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + import-in-the-middle: 3.0.0 transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@10.43.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': + '@sentry/opentelemetry@10.44.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 - '@sentry/core': 10.43.0 + '@sentry/core': 10.44.0 - '@sentry/profiling-node@10.43.0': + '@sentry/profiling-node@10.44.0': dependencies: '@sentry-internal/node-cpu-profiler': 2.2.0 - '@sentry/core': 10.43.0 - '@sentry/node': 10.43.0 + '@sentry/core': 10.44.0 + '@sentry/node': 10.44.0 transitivePeerDependencies: - supports-color - '@sentry/react-router@10.43.0(@react-router/node@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(rollup@4.59.0)': + '@sentry/react-router@10.44.0(@react-router/node@7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3))(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(rollup@4.59.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.6.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.213.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.40.0 '@react-router/node': 7.13.1(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) - '@sentry/browser': 10.43.0 + '@sentry/browser': 10.44.0 '@sentry/cli': 2.58.5 - '@sentry/core': 10.43.0 - '@sentry/node': 10.43.0 - '@sentry/react': 10.43.0(react@19.2.4) + '@sentry/core': 10.44.0 + '@sentry/node': 10.44.0 + '@sentry/react': 10.44.0(react@19.2.4) '@sentry/vite-plugin': 5.1.1(rollup@4.59.0) glob: 13.0.6 react: 19.2.4 @@ -16724,10 +16225,10 @@ snapshots: - rollup - supports-color - '@sentry/react@10.43.0(react@19.2.4)': + '@sentry/react@10.44.0(react@19.2.4)': dependencies: - '@sentry/browser': 10.43.0 - '@sentry/core': 10.43.0 + '@sentry/browser': 10.44.0 + '@sentry/core': 10.44.0 react: 19.2.4 '@sentry/rollup-plugin@5.1.1(rollup@4.59.0)': @@ -16748,20 +16249,20 @@ snapshots: - rollup - supports-color - '@shikijs/engine-oniguruma@3.22.0': + '@shikijs/engine-oniguruma@3.23.0': dependencies: - '@shikijs/types': 3.22.0 + '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.22.0': + '@shikijs/langs@3.23.0': dependencies: - '@shikijs/types': 3.22.0 + '@shikijs/types': 3.23.0 - '@shikijs/themes@3.22.0': + '@shikijs/themes@3.23.0': dependencies: - '@shikijs/types': 3.22.0 + '@shikijs/types': 3.23.0 - '@shikijs/types@3.22.0': + '@shikijs/types@3.23.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -16887,7 +16388,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.29.0) - '@babel/preset-env': 7.29.0(@babel/core@7.29.0) + '@babel/preset-env': 7.29.2(@babel/core@7.29.0) '@babel/preset-react': 7.28.5(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) '@svgr/core': 8.1.0(typescript@5.9.3) @@ -17005,81 +16506,81 @@ snapshots: '@tabby_ai/hijri-converter@1.0.5': {} - '@tailwindcss/node@4.2.1': + '@tailwindcss/node@4.2.2': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.20.0 + enhanced-resolve: 5.20.1 jiti: 2.6.1 - lightningcss: 1.31.1 + lightningcss: 1.32.0 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.2.1 + tailwindcss: 4.2.2 - '@tailwindcss/oxide-android-arm64@4.2.1': + '@tailwindcss/oxide-android-arm64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.1': + '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-x64@4.2.1': + '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.1': + '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.1': + '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.1': + '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true - '@tailwindcss/oxide@4.2.1': + '@tailwindcss/oxide@4.2.2': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-x64': 4.2.1 - '@tailwindcss/oxide-freebsd-x64': 4.2.1 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 - '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 - '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-x64-musl': 4.2.1 - '@tailwindcss/oxide-wasm32-wasi': 4.2.1 - '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 - '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - - '@tailwindcss/postcss@4.2.1': + '@tailwindcss/oxide-android-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-x64': 4.2.2 + '@tailwindcss/oxide-freebsd-x64': 4.2.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-x64-musl': 4.2.2 + '@tailwindcss/oxide-wasm32-wasi': 4.2.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + + '@tailwindcss/postcss@4.2.2': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.2.1 - '@tailwindcss/oxide': 4.2.1 + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 postcss: 8.5.8 - tailwindcss: 4.2.1 + tailwindcss: 4.2.2 - '@tailwindcss/vite@4.2.1(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + '@tailwindcss/vite@4.2.2(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@tailwindcss/node': 4.2.1 - '@tailwindcss/oxide': 4.2.1 - tailwindcss: 4.2.1 - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 + tailwindcss: 4.2.2 + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) '@tanstack/react-table@8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: @@ -17098,6 +16599,24 @@ snapshots: '@tokenizer/token@0.3.0': {} + '@turbo/darwin-64@2.8.19': + optional: true + + '@turbo/darwin-arm64@2.8.19': + optional: true + + '@turbo/linux-64@2.8.19': + optional: true + + '@turbo/linux-arm64@2.8.19': + optional: true + + '@turbo/windows-64@2.8.19': + optional: true + + '@turbo/windows-arm64@2.8.19': + optional: true + '@turf/along@7.3.4': dependencies: '@turf/bearing': 7.3.4 @@ -18230,11 +17749,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/bonjour@3.5.13': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/chai@5.2.3': dependencies: @@ -18244,11 +17763,11 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.8 - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/connect@3.4.38': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/d3-array@3.2.2': {} @@ -18276,7 +17795,7 @@ snapshots: '@types/d3-voronoi@1.1.12': {} - '@types/debug@4.1.12': + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -18300,7 +17819,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -18334,7 +17853,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/istanbul-lib-coverage@2.0.6': {} @@ -18380,13 +17899,13 @@ snapshots: '@types/mysql@2.15.27': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/node@12.20.55': {} '@types/node@17.0.45': {} - '@types/node@25.4.0': + '@types/node@25.5.0': dependencies: undici-types: 7.18.2 @@ -18396,7 +17915,7 @@ snapshots: '@types/pg@8.15.6': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 pg-protocol: 1.13.0 pg-types: 2.2.0 @@ -18447,16 +17966,16 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 25.4.0 + '@types/node': 17.0.45 '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/send@1.2.1': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/serve-index@1.9.4': dependencies: @@ -18465,12 +17984,12 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/send': 0.17.6 '@types/sockjs@0.3.36': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/supercluster@7.1.3': dependencies: @@ -18478,7 +17997,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/trusted-types@2.0.7': optional: true @@ -18501,7 +18020,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 '@types/yargs-parser@21.0.3': {} @@ -18516,29 +18035,15 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@vis.gl/react-maplibre@8.1.0(maplibre-gl@5.20.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@vis.gl/react-maplibre@8.1.0(maplibre-gl@5.20.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@maplibre/maplibre-gl-style-spec': 19.3.3 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) optionalDependencies: - maplibre-gl: 5.20.0 - - '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': - dependencies: - '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.18 - ast-v8-to-istanbul: 0.3.12 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.2.0 - magicast: 0.5.2 - obug: 2.1.1 - std-env: 3.10.0 - tinyrainbow: 3.1.0 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + maplibre-gl: 5.20.2 - '@vitest/coverage-v8@4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)))': + '@vitest/coverage-v8@4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.18 @@ -18550,16 +18055,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.1.0 - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) - - '@vitest/expect@4.0.18': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - chai: 6.2.2 - tinyrainbow: 3.0.3 + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/expect@4.1.0': dependencies: @@ -18570,46 +18066,27 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 4.0.18 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) - - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.18': dependencies: - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 '@vitest/pretty-format@4.1.0': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.0.18': - dependencies: - '@vitest/utils': 4.0.18 - pathe: 2.0.3 - '@vitest/runner@4.1.0': dependencies: '@vitest/utils': 4.1.0 pathe: 2.0.3 - '@vitest/snapshot@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - magic-string: 0.30.21 - pathe: 2.0.3 - '@vitest/snapshot@4.1.0': dependencies: '@vitest/pretty-format': 4.1.0 @@ -18617,8 +18094,6 @@ snapshots: magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.18': {} - '@vitest/spy@4.1.0': {} '@vitest/utils@4.0.18': @@ -18892,7 +18367,7 @@ snapshots: autoprefixer@10.4.27(postcss@8.5.8): dependencies: browserslist: 4.28.1 - caniuse-lite: 1.0.30001777 + caniuse-lite: 1.0.30001780 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.8 @@ -18902,8 +18377,6 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - aws-ssl-profiles@1.1.2: {} - axios@1.13.6: dependencies: follow-redirects: 1.15.11 @@ -18915,7 +18388,7 @@ snapshots: babel-dead-code-elimination@1.0.12: dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 transitivePeerDependencies: @@ -18932,11 +18405,11 @@ snapshots: dependencies: object.assign: 4.1.7 - babel-plugin-polyfill-corejs2@0.4.16(@babel/core@7.29.0): + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): dependencies: '@babel/compat-data': 7.29.0 '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -18944,23 +18417,23 @@ snapshots: babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0) - core-js-compat: 3.48.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.14.1(@babel/core@7.29.0): + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0) - core-js-compat: 3.48.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.7(@babel/core@7.29.0): + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.7(@babel/core@7.29.0) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) transitivePeerDependencies: - supports-color @@ -18974,7 +18447,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.0: {} + baseline-browser-mapping@2.10.8: {} basic-auth@2.0.1: dependencies: @@ -18982,67 +18455,32 @@ snapshots: batch@0.6.1: {} - better-auth@1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)): + better-auth@1.5.5(drizzle-kit@0.31.10)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8))(mongodb@7.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))): dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) - '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))) - '@better-auth/kysely-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11) - '@better-auth/memory-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1) - '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0) - '@better-auth/prisma-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) - '@better-auth/telemetry': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)) + '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0) + '@better-auth/drizzle-adapter': 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8)) + '@better-auth/kysely-adapter': 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(kysely@0.28.13) + '@better-auth/memory-adapter': 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1) + '@better-auth/mongo-adapter': 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@7.1.0) + '@better-auth/prisma-adapter': 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0))(@better-auth/utils@0.3.1) + '@better-auth/telemetry': 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.2.2)(kysely@0.28.13)(nanostores@1.2.0)) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 '@noble/ciphers': 2.1.1 '@noble/hashes': 2.0.1 better-call: 1.3.2(zod@4.3.6) defu: 6.1.4 - jose: 6.1.3 - kysely: 0.28.11 - nanostores: 1.1.1 + jose: 6.2.2 + kysely: 0.28.13 + nanostores: 1.2.0 zod: 4.3.6 optionalDependencies: - '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) - drizzle-kit: 0.31.9 - drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) + drizzle-kit: 0.31.10 + drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8) mongodb: 7.1.0 - mysql2: 3.15.3 - prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@cloudflare/workers-types' - - better-auth@1.5.4(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)))(mongodb@7.1.0)(mysql2@3.15.3)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2))): - dependencies: - '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) - '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))) - '@better-auth/kysely-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11) - '@better-auth/memory-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1) - '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0) - '@better-auth/prisma-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) - '@better-auth/telemetry': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)) - '@better-auth/utils': 0.3.1 - '@better-fetch/fetch': 1.1.21 - '@noble/ciphers': 2.1.1 - '@noble/hashes': 2.0.1 - better-call: 1.3.2(zod@4.3.6) - defu: 6.1.4 - jose: 6.1.3 - kysely: 0.28.11 - nanostores: 1.1.1 - zod: 4.3.6 - optionalDependencies: - '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) - drizzle-kit: 0.31.9 - drizzle-orm: 0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)) - mongodb: 7.1.0 - mysql2: 3.15.3 - prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - '@cloudflare/workers-types' @@ -19142,10 +18580,10 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001774 - electron-to-chromium: 1.5.302 - node-releases: 2.0.27 + baseline-browser-mapping: 2.10.8 + caniuse-lite: 1.0.30001780 + electron-to-chromium: 1.5.321 + node-releases: 2.0.36 update-browserslist-db: 1.2.3(browserslist@4.28.1) bson@7.2.0: {} @@ -19175,21 +18613,6 @@ snapshots: bytewise-core: 1.2.3 typewise: 1.0.3 - c12@3.1.0: - dependencies: - chokidar: 4.0.3 - confbox: 0.2.4 - defu: 6.1.4 - dotenv: 16.6.1 - exsolve: 1.0.8 - giget: 2.0.0 - jiti: 2.6.1 - ohash: 2.0.11 - pathe: 2.0.3 - perfect-debounce: 1.0.0 - pkg-types: 2.3.0 - rc9: 2.1.2 - cac@6.7.14: {} cacheable-lookup@7.0.0: {} @@ -19235,13 +18658,11 @@ snapshots: caniuse-api@3.0.0: dependencies: browserslist: 4.28.1 - caniuse-lite: 1.0.30001777 + caniuse-lite: 1.0.30001780 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001774: {} - - caniuse-lite@1.0.30001777: {} + caniuse-lite@1.0.30001780: {} ccount@2.0.1: {} @@ -19285,15 +18706,6 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 - chevrotain@10.5.0: - dependencies: - '@chevrotain/cst-dts-gen': 10.5.0 - '@chevrotain/gast': 10.5.0 - '@chevrotain/types': 10.5.0 - '@chevrotain/utils': 10.5.0 - lodash: 4.17.21 - regexp-to-ast: 0.5.0 - chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -19316,12 +18728,6 @@ snapshots: ci-info@3.9.0: {} - citty@0.1.6: - dependencies: - consola: 3.4.2 - - citty@0.2.1: {} - cjs-module-lexer@2.2.0: {} class-variance-authority@0.7.1: @@ -19475,13 +18881,13 @@ snapshots: serialize-javascript: 6.0.2 webpack: 5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19)) - core-js-compat@3.48.0: + core-js-compat@3.49.0: dependencies: browserslist: 4.28.1 - core-js-pure@3.48.0: {} + core-js-pure@3.49.0: {} - core-js@3.48.0: {} + core-js@3.49.0: {} core-util-is@1.0.3: {} @@ -19522,7 +18928,7 @@ snapshots: postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - css-loader@6.11.0(@rspack/core@1.7.8(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))): + css-loader@6.11.0(@rspack/core@1.7.9(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))): dependencies: icss-utils: 5.1.0(postcss@8.5.8) postcss: 8.5.8 @@ -19533,7 +18939,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.4 optionalDependencies: - '@rspack/core': 1.7.8(@swc/helpers@0.5.19) + '@rspack/core': 1.7.9(@swc/helpers@0.5.19) webpack: 5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19)) css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))): @@ -19741,8 +19147,6 @@ snapshots: deep-extend@0.6.0: {} - deepmerge-ts@7.1.5: {} - deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -19774,16 +19178,12 @@ snapshots: delayed-stream@1.0.0: {} - denque@2.1.0: {} - depd@1.1.2: {} depd@2.0.0: {} dequal@2.0.3: {} - destr@2.0.5: {} - destroy@1.2.0: {} detect-indent@6.1.0: {} @@ -19815,10 +19215,10 @@ snapshots: dependencies: '@leichtgewicht/ip-codec': 2.0.5 - docusaurus-plugin-typedoc@1.4.2(typedoc-plugin-markdown@4.10.0(typedoc@0.28.17(typescript@5.9.3))): + docusaurus-plugin-typedoc@1.4.2(typedoc-plugin-markdown@4.11.0(typedoc@0.28.17(typescript@5.9.3))): dependencies: - typedoc-docusaurus-theme: 1.4.2(typedoc-plugin-markdown@4.10.0(typedoc@0.28.17(typescript@5.9.3))) - typedoc-plugin-markdown: 4.10.0(typedoc@0.28.17(typescript@5.9.3)) + typedoc-docusaurus-theme: 1.4.2(typedoc-plugin-markdown@4.11.0(typedoc@0.28.17(typescript@5.9.3))) + typedoc-plugin-markdown: 4.11.0(typedoc@0.28.17(typescript@5.9.3)) dom-converter@0.2.0: dependencies: @@ -19882,25 +19282,20 @@ snapshots: dotenv@8.6.0: {} - drizzle-kit@0.31.9: + drizzle-kit@0.31.10: dependencies: '@drizzle-team/brocli': 0.10.2 '@esbuild-kit/esm-loader': 2.6.5 esbuild: 0.25.12 - esbuild-register: 3.6.0(esbuild@0.25.12) - transitivePeerDependencies: - - supports-color + tsx: 4.21.0 - drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.15.6)(kysely@0.28.11)(mysql2@3.15.3)(postgres@3.4.8)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)): + drizzle-orm@0.45.1(@electric-sql/pglite@0.3.16)(@opentelemetry/api@1.9.0)(@types/pg@8.15.6)(kysely@0.28.13)(postgres@3.4.8): optionalDependencies: '@electric-sql/pglite': 0.3.16 '@opentelemetry/api': 1.9.0 - '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) '@types/pg': 8.15.6 - kysely: 0.28.11 - mysql2: 3.15.3 + kysely: 0.28.13 postgres: 3.4.8 - prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) dunder-proto@1.0.1: dependencies: @@ -19929,12 +19324,7 @@ snapshots: ee-first@1.1.1: {} - effect@3.18.4: - dependencies: - '@standard-schema/spec': 1.1.0 - fast-check: 3.23.2 - - electron-to-chromium@1.5.302: {} + electron-to-chromium@1.5.321: {} emoji-regex-xs@1.0.0: {} @@ -19948,11 +19338,9 @@ snapshots: emoticon@4.1.0: {} - empathic@2.0.0: {} - encodeurl@2.0.0: {} - enhanced-resolve@5.20.0: + enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -20068,13 +19456,6 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild-register@3.6.0(esbuild@0.25.12): - dependencies: - debug: 4.4.3 - esbuild: 0.25.12 - transitivePeerDependencies: - - supports-color - esbuild@0.18.20: optionalDependencies: '@esbuild/android-arm': 0.18.20 @@ -20129,34 +19510,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.3: + esbuild@0.27.4: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 escalade@3.2.0: {} @@ -20232,7 +19613,7 @@ snapshots: eval@0.1.8: dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 require-like: 0.1.2 eventemitter3@4.0.7: {} @@ -20306,10 +19687,6 @@ snapshots: extendable-error@0.1.7: {} - fast-check@3.23.2: - dependencies: - pure-rand: 6.1.0 - fast-deep-equal@3.1.3: {} fast-equals@5.4.0: {} @@ -20361,7 +19738,7 @@ snapshots: schema-utils: 3.3.0 webpack: 5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19)) - file-type@21.3.1: + file-type@21.3.3: dependencies: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.4 @@ -20436,11 +19813,6 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - form-data-encoder@2.1.4: {} form-data@4.0.5: @@ -20459,10 +19831,10 @@ snapshots: fraction.js@5.3.4: {} - framer-motion@12.35.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + framer-motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - motion-dom: 12.35.2 - motion-utils: 12.29.2 + motion-dom: 12.38.0 + motion-utils: 12.36.0 tslib: 2.8.1 optionalDependencies: react: 19.2.4 @@ -20508,10 +19880,6 @@ snapshots: fuzzysort@3.1.0: {} - generate-function@2.3.1: - dependencies: - is-property: 1.0.2 - generator-function@2.0.1: {} gensync@1.0.0-beta.2: {} @@ -20541,7 +19909,7 @@ snapshots: xml-utils: 1.10.2 zstddec: 0.1.0 - geotiff@3.0.4: + geotiff@3.0.5: dependencies: '@petamoriken/float16': 3.9.3 lerc: 3.0.0 @@ -20571,8 +19939,6 @@ snapshots: get-own-enumerable-property-symbols@3.0.2: {} - get-port-please@3.2.0: {} - get-port@5.1.1: {} get-proto@1.0.1: @@ -20594,15 +19960,6 @@ snapshots: get-value@2.0.6: {} - giget@2.0.0: - dependencies: - citty: 0.1.6 - consola: 3.4.2 - defu: 6.1.4 - node-fetch-native: 1.6.7 - nypm: 0.6.5 - pathe: 2.0.3 - github-slugger@1.5.0: {} gl-matrix@3.4.4: {} @@ -20686,10 +20043,6 @@ snapshots: graceful-fs@4.2.11: {} - grammex@3.1.12: {} - - graphmatch@1.1.1: {} - gray-matter@4.0.3: dependencies: js-yaml: 3.14.2 @@ -20836,8 +20189,6 @@ snapshots: dependencies: react-is: 16.13.1 - hono@4.11.4: {} - hpack.js@2.1.6: dependencies: inherits: 2.0.4 @@ -20861,7 +20212,7 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.46.0 + terser: 5.46.1 html-minifier-terser@7.2.0: dependencies: @@ -20871,7 +20222,7 @@ snapshots: entities: 4.5.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.46.0 + terser: 5.46.1 html-tags@3.3.1: {} @@ -20887,7 +20238,7 @@ snapshots: html-void-elements@3.0.0: {} - html-webpack-plugin@5.6.6(@rspack/core@1.7.8(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))): + html-webpack-plugin@5.6.6(@rspack/core@1.7.9(@swc/helpers@0.5.19))(webpack@5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19))): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -20895,7 +20246,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.0 optionalDependencies: - '@rspack/core': 1.7.8(@swc/helpers@0.5.19) + '@rspack/core': 1.7.9(@swc/helpers@0.5.19) webpack: 5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19)) htmlparser2@6.1.0: @@ -20954,8 +20305,6 @@ snapshots: transitivePeerDependencies: - debug - http-status-codes@2.3.0: {} - http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 @@ -21008,6 +20357,13 @@ snapshots: cjs-module-lexer: 2.2.0 module-details-from-path: 1.0.4 + import-in-the-middle@3.0.0: + dependencies: + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) + cjs-module-lexer: 2.2.0 + module-details-from-path: 1.0.4 + import-lazy@4.0.0: {} imurmurhash@0.1.4: {} @@ -21180,8 +20536,6 @@ snapshots: dependencies: isobject: 3.0.1 - is-property@1.0.2: {} - is-reference@1.2.1: dependencies: '@types/estree': 1.0.8 @@ -21283,7 +20637,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.4.0 + '@types/node': 25.5.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -21291,13 +20645,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -21314,7 +20668,7 @@ snapshots: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 - jose@6.1.3: {} + jose@6.2.2: {} js-tokens@10.0.0: {} @@ -21399,7 +20753,7 @@ snapshots: kleur@3.0.3: {} - kysely@0.28.11: {} + kysely@0.28.13: {} latest-version@7.0.0: dependencies: @@ -21416,88 +20770,39 @@ snapshots: leven@3.1.0: {} - lightningcss-android-arm64@1.31.1: - optional: true - lightningcss-android-arm64@1.32.0: optional: true - lightningcss-darwin-arm64@1.31.1: - optional: true - lightningcss-darwin-arm64@1.32.0: optional: true - lightningcss-darwin-x64@1.31.1: - optional: true - lightningcss-darwin-x64@1.32.0: optional: true - lightningcss-freebsd-x64@1.31.1: - optional: true - lightningcss-freebsd-x64@1.32.0: optional: true - lightningcss-linux-arm-gnueabihf@1.31.1: - optional: true - lightningcss-linux-arm-gnueabihf@1.32.0: optional: true - lightningcss-linux-arm64-gnu@1.31.1: - optional: true - lightningcss-linux-arm64-gnu@1.32.0: optional: true - lightningcss-linux-arm64-musl@1.31.1: - optional: true - lightningcss-linux-arm64-musl@1.32.0: optional: true - lightningcss-linux-x64-gnu@1.31.1: - optional: true - lightningcss-linux-x64-gnu@1.32.0: optional: true - lightningcss-linux-x64-musl@1.31.1: - optional: true - lightningcss-linux-x64-musl@1.32.0: optional: true - lightningcss-win32-arm64-msvc@1.31.1: - optional: true - lightningcss-win32-arm64-msvc@1.32.0: optional: true - lightningcss-win32-x64-msvc@1.31.1: - optional: true - lightningcss-win32-x64-msvc@1.32.0: optional: true - lightningcss@1.31.1: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.31.1 - lightningcss-darwin-arm64: 1.31.1 - lightningcss-darwin-x64: 1.31.1 - lightningcss-freebsd-x64: 1.31.1 - lightningcss-linux-arm-gnueabihf: 1.31.1 - lightningcss-linux-arm64-gnu: 1.31.1 - lightningcss-linux-arm64-musl: 1.31.1 - lightningcss-linux-x64-gnu: 1.31.1 - lightningcss-linux-x64-musl: 1.31.1 - lightningcss-win32-arm64-msvc: 1.31.1 - lightningcss-win32-x64-msvc: 1.31.1 - lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -21514,8 +20819,6 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 - lilconfig@2.1.0: {} - lilconfig@3.1.3: {} linebreak@1.1.0: @@ -21573,8 +20876,6 @@ snapshots: lodash.uniq@4.5.0: {} - lodash@4.17.21: {} - lodash@4.17.23: {} long@5.3.2: {} @@ -21591,14 +20892,12 @@ snapshots: lowercase-keys@3.0.0: {} - lru-cache@11.2.6: {} + lru-cache@11.2.7: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lru.min@1.1.4: {} - lucide-react@0.577.0(react@19.2.4): dependencies: react: 19.2.4 @@ -21619,7 +20918,7 @@ snapshots: dependencies: semver: 7.7.4 - maplibre-gl@5.20.0: + maplibre-gl@5.20.2: dependencies: '@mapbox/jsonlint-lines-primitives': 2.0.2 '@mapbox/point-geometry': 1.1.0 @@ -21627,9 +20926,9 @@ snapshots: '@mapbox/unitbezier': 0.0.1 '@mapbox/vector-tile': 2.0.4 '@mapbox/whoots-js': 3.1.0 - '@maplibre/geojson-vt': 6.0.1 + '@maplibre/geojson-vt': 6.0.4 '@maplibre/maplibre-gl-style-spec': 24.7.0 - '@maplibre/mlt': 1.1.7 + '@maplibre/mlt': 1.1.8 '@maplibre/vt-pbf': 4.3.0 '@types/geojson': 7946.0.16 earcut: 3.0.2 @@ -22214,7 +21513,7 @@ snapshots: micromark@4.0.2: dependencies: - '@types/debug': 4.1.12 + '@types/debug': 4.1.13 debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 @@ -22312,11 +21611,11 @@ snapshots: transitivePeerDependencies: - supports-color - motion-dom@12.35.2: + motion-dom@12.38.0: dependencies: - motion-utils: 12.29.2 + motion-utils: 12.36.0 - motion-utils@12.29.2: {} + motion-utils@12.36.0: {} mri@1.2.0: {} @@ -22333,27 +21632,11 @@ snapshots: murmurhash-js@1.0.0: {} - mysql2@3.15.3: - dependencies: - aws-ssl-profiles: 1.1.2 - denque: 2.1.0 - generate-function: 2.3.1 - iconv-lite: 0.7.2 - long: 5.3.2 - lru.min: 1.1.4 - named-placeholders: 1.1.6 - seq-queue: 0.0.5 - sqlstring: 2.3.3 - - named-placeholders@1.1.6: - dependencies: - lru.min: 1.1.4 - nanoid@3.3.11: {} - nanoid@5.1.6: {} + nanoid@5.1.7: {} - nanostores@1.1.1: {} + nanostores@1.2.0: {} negotiator@0.6.3: {} @@ -22371,7 +21654,7 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 - node-abi@3.88.0: + node-abi@3.89.0: dependencies: semver: 7.7.4 @@ -22382,13 +21665,11 @@ snapshots: emojilib: 2.4.0 skin-tone: 2.0.0 - node-fetch-native@1.6.7: {} - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - node-releases@2.0.27: {} + node-releases@2.0.36: {} normalize-path@3.0.0: {} @@ -22419,12 +21700,6 @@ snapshots: fflate: 0.8.2 optional: true - nypm@0.6.5: - dependencies: - citty: 0.2.1 - pathe: 2.0.3 - tinyexec: 1.0.4 - object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -22446,13 +21721,11 @@ snapshots: obug@2.1.1: {} - ohash@2.0.11: {} - ol@10.8.0: dependencies: '@types/rbush': 4.0.0 earcut: 3.0.2 - geotiff: 3.0.4 + geotiff: 3.0.5 pbf: 4.0.1 rbush: 4.0.1 zarrita: 0.6.1 @@ -22636,7 +21909,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.2.6 + lru-cache: 11.2.7 minipass: 7.1.3 path-to-regexp@0.1.12: {} @@ -22659,8 +21932,6 @@ snapshots: peberminta@0.9.0: {} - perfect-debounce@1.0.0: {} - pg-int8@1.0.1: {} pg-protocol@1.13.0: {} @@ -22691,7 +21962,7 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 - pkijs@3.3.3: + pkijs@3.4.0: dependencies: '@noble/hashes': 1.4.0 asn1js: 3.0.7 @@ -23164,29 +22435,27 @@ snapshots: dependencies: xtend: 4.0.2 - postgres@3.4.7: {} - postgres@3.4.8: {} - posthog-js@1.360.1: + posthog-js@1.362.0: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.208.0 '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) - '@posthog/core': 1.23.3 - '@posthog/types': 1.360.1 - core-js: 3.48.0 + '@posthog/core': 1.24.0 + '@posthog/types': 1.362.0 + core-js: 3.49.0 dompurify: 3.3.3 fflate: 0.4.8 preact: 10.29.0 query-selector-shadow-dom: 1.0.1 web-vitals: 5.1.0 - posthog-node@5.28.1: + posthog-node@5.28.4: dependencies: - '@posthog/core': 1.23.3 + '@posthog/core': 1.24.0 postmark@4.0.7: dependencies: @@ -23215,22 +22484,6 @@ snapshots: clsx: 2.1.1 react: 19.2.4 - prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): - dependencies: - '@prisma/config': 7.4.2 - '@prisma/dev': 0.20.0(typescript@5.9.3) - '@prisma/engines': 7.4.2 - '@prisma/studio-core': 0.13.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - mysql2: 3.15.3 - postgres: 3.4.7 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@types/react' - - magicast - - react - - react-dom - prismjs@1.30.0: {} process-nextick-args@2.0.1: {} @@ -23240,7 +22493,7 @@ snapshots: proj4@2.20.4: dependencies: mgrs: 1.0.0 - wkt-parser: 1.5.3 + wkt-parser: 1.5.4 prompts@2.4.2: dependencies: @@ -23253,12 +22506,6 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - proper-lockfile@4.1.2: - dependencies: - graceful-fs: 4.2.11 - retry: 0.12.0 - signal-exit: 3.0.7 - property-information@7.1.0: {} proto-list@1.2.4: {} @@ -23275,7 +22522,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 25.4.0 + '@types/node': 25.5.0 long: 5.3.2 protocol-buffers-schema@3.6.0: {} @@ -23295,8 +22542,6 @@ snapshots: dependencies: escape-goat: 4.0.0 - pure-rand@6.1.0: {} - pvtsutils@1.3.6: dependencies: tslib: 2.8.1 @@ -23424,11 +22669,6 @@ snapshots: quickselect: 3.0.0 optional: true - rc9@2.1.2: - dependencies: - defu: 6.1.4 - destr: 2.0.5 - rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -23469,14 +22709,14 @@ snapshots: react-loadable: '@docusaurus/react-loadable@6.0.0(react@19.2.4)' webpack: 5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19)) - react-map-gl@8.1.0(maplibre-gl@5.20.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + react-map-gl@8.1.0(maplibre-gl@5.20.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@vis.gl/react-mapbox': 8.1.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@vis.gl/react-maplibre': 8.1.0(maplibre-gl@5.20.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@vis.gl/react-maplibre': 8.1.0(maplibre-gl@5.20.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) optionalDependencies: - maplibre-gl: 5.20.0 + maplibre-gl: 5.20.2 react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.4): dependencies: @@ -23685,8 +22925,6 @@ snapshots: regenerate@1.4.2: {} - regexp-to-ast@0.5.0: {} - regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -23802,8 +23040,6 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 - remeda@2.33.4: {} - remix-hook-form@7.1.1(react-dom@19.2.4(react@19.2.4))(react-hook-form@7.71.2(react@19.2.4))(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4): dependencies: react: 19.2.4 @@ -23816,9 +23052,9 @@ snapshots: react-router: 7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) zod: 4.3.6 - remix-utils@9.3.0(@standard-schema/spec@1.1.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4): + remix-utils@9.3.1(@standard-schema/spec@1.1.0)(react-router@7.13.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4): dependencies: - type-fest: 5.4.4 + type-fest: 5.5.0 optionalDependencies: '@standard-schema/spec': 1.1.0 react: 19.2.4 @@ -23873,8 +23109,6 @@ snapshots: restructure@3.0.2: {} - retry@0.12.0: {} - retry@0.13.1: {} reusify@1.1.0: {} @@ -23883,11 +23117,11 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-esbuild@6.2.1(esbuild@0.27.3)(rollup@4.59.0): + rollup-plugin-esbuild@6.2.1(esbuild@0.27.4)(rollup@4.59.0): dependencies: debug: 4.4.3 es-module-lexer: 1.7.0 - esbuild: 0.27.3 + esbuild: 0.27.4 get-tsconfig: 4.13.6 rollup: 4.59.0 unplugin-utils: 0.2.5 @@ -23976,8 +23210,6 @@ snapshots: safer-buffer@2.1.2: {} - sax@1.5.0: {} - sax@1.6.0: {} scheduler@0.25.0-rc-603e6108-20241029: {} @@ -24015,7 +23247,7 @@ snapshots: selfsigned@5.5.0: dependencies: '@peculiar/x509': 1.14.3 - pkijs: 3.3.3 + pkijs: 3.4.0 semver-diff@4.0.0: dependencies: @@ -24043,8 +23275,6 @@ snapshots: transitivePeerDependencies: - supports-color - seq-queue@0.0.5: {} - serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -24186,7 +23416,7 @@ snapshots: '@types/node': 17.0.45 '@types/sax': 1.2.7 arg: 5.0.2 - sax: 1.5.0 + sax: 1.6.0 skin-tone@2.0.0: dependencies: @@ -24284,8 +23514,6 @@ snapshots: sprintf-js@1.0.3: {} - sqlstring@2.3.3: {} - srcset@4.0.0: {} stackback@0.0.2: {} @@ -24447,11 +23675,13 @@ snapshots: tailwind-merge@3.5.0: {} - tailwindcss-animate@1.0.7(tailwindcss@4.2.1): + tailwindcss-animate@1.0.7(tailwindcss@4.2.2): dependencies: - tailwindcss: 4.2.1 + tailwindcss: 4.2.2 + + tailwindcss@4.1.18: {} - tailwindcss@4.2.1: {} + tailwindcss@4.2.2: {} tapable@2.3.0: {} @@ -24462,12 +23692,12 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - terser: 5.46.0 + terser: 5.46.1 webpack: 5.105.4(@swc/core@1.15.18(@swc/helpers@0.5.19)) optionalDependencies: '@swc/core': 1.15.18(@swc/helpers@0.5.19) - terser@5.46.0: + terser@5.46.1: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.16.0 @@ -24488,8 +23718,6 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.0.2: {} - tinyexec@1.0.4: {} tinyglobby@0.2.15: @@ -24503,8 +23731,6 @@ snapshots: tinyqueue@3.0.0: {} - tinyrainbow@3.0.3: {} - tinyrainbow@3.1.0: {} to-regex-range@5.0.1: @@ -24515,7 +23741,7 @@ snapshots: token-types@6.1.2: dependencies: - '@borewit/text-codec': 0.2.1 + '@borewit/text-codec': 0.2.2 '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -24557,36 +23783,25 @@ snapshots: tslib@2.8.1: {} + tsx@4.21.0: + dependencies: + esbuild: 0.27.4 + get-tsconfig: 4.13.6 + optionalDependencies: + fsevents: 2.3.3 + tsyringe@4.10.0: dependencies: tslib: 1.14.1 - turbo-darwin-64@2.8.16: - optional: true - - turbo-darwin-arm64@2.8.16: - optional: true - - turbo-linux-64@2.8.16: - optional: true - - turbo-linux-arm64@2.8.16: - optional: true - - turbo-windows-64@2.8.16: - optional: true - - turbo-windows-arm64@2.8.16: - optional: true - - turbo@2.8.16: + turbo@2.8.19: optionalDependencies: - turbo-darwin-64: 2.8.16 - turbo-darwin-arm64: 2.8.16 - turbo-linux-64: 2.8.16 - turbo-linux-arm64: 2.8.16 - turbo-windows-64: 2.8.16 - turbo-windows-arm64: 2.8.16 + '@turbo/darwin-64': 2.8.19 + '@turbo/darwin-arm64': 2.8.19 + '@turbo/linux-64': 2.8.19 + '@turbo/linux-arm64': 2.8.19 + '@turbo/windows-64': 2.8.19 + '@turbo/windows-arm64': 2.8.19 type-fest@0.21.3: {} @@ -24594,7 +23809,7 @@ snapshots: type-fest@2.19.0: {} - type-fest@5.4.4: + type-fest@5.5.0: dependencies: tagged-tag: 1.0.0 @@ -24651,11 +23866,11 @@ snapshots: typed-array-buffer: 1.0.3 typed-array-byte-offset: 1.0.4 - typedoc-docusaurus-theme@1.4.2(typedoc-plugin-markdown@4.10.0(typedoc@0.28.17(typescript@5.9.3))): + typedoc-docusaurus-theme@1.4.2(typedoc-plugin-markdown@4.11.0(typedoc@0.28.17(typescript@5.9.3))): dependencies: - typedoc-plugin-markdown: 4.10.0(typedoc@0.28.17(typescript@5.9.3)) + typedoc-plugin-markdown: 4.11.0(typedoc@0.28.17(typescript@5.9.3)) - typedoc-plugin-markdown@4.10.0(typedoc@0.28.17(typescript@5.9.3)): + typedoc-plugin-markdown@4.11.0(typedoc@0.28.17(typescript@5.9.3)): dependencies: typedoc: 0.28.17(typescript@5.9.3) @@ -24665,7 +23880,7 @@ snapshots: typedoc@0.28.17(typescript@5.9.3): dependencies: - '@gerrit0/mini-shiki': 3.22.0 + '@gerrit0/mini-shiki': 3.23.0 lunr: 2.3.9 markdown-it: 14.1.1 minimatch: 9.0.9 @@ -24854,7 +24069,7 @@ snapshots: uzip-module@1.0.3: optional: true - valibot@1.2.0(typescript@5.9.3): + valibot@1.3.1(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -24902,13 +24117,13 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - vite-node@3.2.4(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): + vite-node@3.2.4(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -24923,13 +24138,13 @@ snapshots: - tsx - yaml - vite-node@5.3.0(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): + vite-node@5.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: cac: 6.7.14 es-module-lexer: 2.0.0 obug: 2.1.1 pathe: 2.0.3 - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -24943,74 +24158,37 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)): + vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - typescript - vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.8 rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.4.0 + '@types/node': 25.5.0 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 - terser: 5.46.0 + terser: 5.46.1 + tsx: 4.21.0 yaml: 2.8.2 - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2): - dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/node': 25.4.0 - transitivePeerDependencies: - - jiti - - less - - lightningcss - - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)): + vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -25027,11 +24205,11 @@ snapshots: tinyexec: 1.0.4 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@25.4.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 - '@types/node': 25.4.0 + '@types/node': 25.5.0 transitivePeerDependencies: - msw @@ -25150,7 +24328,7 @@ snapshots: acorn-import-phases: 1.0.4(acorn@8.16.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.20.0 + enhanced-resolve: 5.20.1 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -25260,7 +24438,7 @@ snapshots: wildcard@2.0.1: {} - wkt-parser@1.5.3: {} + wkt-parser@1.5.4: {} wrap-ansi@7.0.0: dependencies: @@ -25324,18 +24502,13 @@ snapshots: numcodecs: 0.3.2 optional: true - zeptomatch@2.1.0: - dependencies: - grammex: 3.1.12 - graphmatch: 1.1.1 - zod@4.3.6: {} zstddec@0.1.0: {} zstddec@0.2.0: {} - zustand@5.0.11(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): + zustand@5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): optionalDependencies: '@types/react': 19.2.14 react: 19.2.4 From 97ee24649a85c39cf6cebefaabe8a97080a5de96 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 12:16:54 +0100 Subject: [PATCH 038/108] chore: update fdm-rvo packages --- fdm-rvo/package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ pnpm-workspace.yaml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json index 6c2c88c98..c5303c33d 100644 --- a/fdm-rvo/package.json +++ b/fdm-rvo/package.json @@ -52,7 +52,7 @@ }, "dependencies": { "@nmi-agro/fdm-core": "workspace:^", - "@nmi-agro/rvo-connector": "^2.2.0", + "@nmi-agro/rvo-connector": "^2.2.1", "@turf/area": "^7.3.4", "@turf/bbox": "^7.3.4", "@turf/helpers": "^7.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 326535998..9c0d4b6a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ catalogs: specifier: ^16.0.3 version: 16.0.3 '@types/node': - specifier: ^25.4.0 + specifier: ^25.5.0 version: 25.5.0 '@vitest/coverage-v8': specifier: 4.0.18 @@ -586,8 +586,8 @@ importers: specifier: workspace:^ version: link:../fdm-core '@nmi-agro/rvo-connector': - specifier: ^2.2.0 - version: 2.2.0 + specifier: ^2.2.1 + version: 2.2.1 '@turf/area': specifier: ^7.3.4 version: 7.3.4 @@ -2783,8 +2783,8 @@ packages: '@napi-rs/wasm-runtime@1.0.7': resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} - '@nmi-agro/rvo-connector@2.2.0': - resolution: {integrity: sha512-PAbpaB/3YKV8/EqqZn3qdGXojTTxl9BquF63WHxEKeT1Oxj/1iQyBozwRTVeUD2yOb0YfL0os8UA+hH+8HJioA==} + '@nmi-agro/rvo-connector@2.2.1': + resolution: {integrity: sha512-iAZmKp0YCbQbZk4lrc426nigIMgKLj/WMpRmzFLGTHWrX4f61Sk62jwFh0cbXc5leQR9e2f6mAw/1nG0b1oUWQ==} engines: {node: '>=18'} '@noble/ciphers@1.3.0': @@ -14257,7 +14257,7 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@nmi-agro/rvo-connector@2.2.0': + '@nmi-agro/rvo-connector@2.2.1': dependencies: jsonwebtoken: 9.0.3 proj4: 2.20.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3ce7e08b3..6492da9f7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,7 +12,7 @@ catalog: '@rollup/plugin-json': ^6.1.0 '@rollup/plugin-node-resolve': ^16.0.3 '@rollup/plugin-typescript': ^12.3.0 - '@types/node': ^25.4.0 + '@types/node': ^25.5.0 '@vitest/coverage-v8': 4.0.18 better-auth: ^1.5.4 drizzle-kit: ^0.31.9 From 7629e04424c6db006ebf51ee73e3a6ee9165d058 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:45:52 +0100 Subject: [PATCH 039/108] fix: request both services --- fdm-rvo/src/auth.test.ts | 1 + fdm-rvo/src/auth.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fdm-rvo/src/auth.test.ts b/fdm-rvo/src/auth.test.ts index 4ae7f9608..bba20bc4e 100644 --- a/fdm-rvo/src/auth.test.ts +++ b/fdm-rvo/src/auth.test.ts @@ -26,6 +26,7 @@ describe("auth", () => { const url = generateAuthUrl(mockClient, "state123") expect(mockClient.getAuthorizationUrl).toHaveBeenCalledWith({ state: "state123", + services: ["opvragenBedrijfspercelen", "opvragenRegelingspercelenMest"], }) expect(url).toBe("https://example.com/auth") }) diff --git a/fdm-rvo/src/auth.ts b/fdm-rvo/src/auth.ts index 95f3783e8..a66040e86 100644 --- a/fdm-rvo/src/auth.ts +++ b/fdm-rvo/src/auth.ts @@ -54,7 +54,10 @@ export const createRvoClient = ( * @returns The full URL to redirect the user to. */ export const generateAuthUrl = (rvoClient: RvoClient, state: string) => { - return rvoClient.getAuthorizationUrl({ state }) + return rvoClient.getAuthorizationUrl({ + state, + services: ["opvragenBedrijfspercelen", "opvragenRegelingspercelenMest"], + }) } /** From 64cc7b74383f80dcf3283169619aa7f0c463af08 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:08:32 +0100 Subject: [PATCH 040/108] feat: support collecting information about bufferstrips for a field --- fdm-rvo/src/compare.ts | 60 +--------------- fdm-rvo/src/data.test.ts | 132 ++++++++++++++++++++++++++++++++++-- fdm-rvo/src/data.ts | 90 ++++++++++++++++++++---- fdm-rvo/src/process.test.ts | 2 + fdm-rvo/src/process.ts | 20 ++++++ fdm-rvo/src/types.ts | 67 +++++++++++++++--- fdm-rvo/src/utils.ts | 65 ++++++++++++++++++ 7 files changed, 349 insertions(+), 87 deletions(-) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 8884b2f66..be65a59a4 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -1,8 +1,4 @@ import bbox from "@turf/bbox" -import intersect from "@turf/intersect" -import union from "@turf/union" -import area from "@turf/area" -import { feature, featureCollection } from "@turf/helpers" import type { Field, Cultivation, @@ -14,66 +10,12 @@ import { type RvoImportReviewItem, type FieldDiff, } from "./types" +import { calculateIoU, bboxOverlap } from "./utils" // Threshold for IoU (Intersection over Union) to consider fields "the same" spatially. // A value of 0.99 means the intersection area must be at least 99% of the union area. const IOU_THRESHOLD = 0.99 -/** - * Calculates Intersection over Union (IoU) for two geometries. - * - * IoU is a standard metric for measuring the overlap between two shapes. - * Formula: Area(Intersection) / Area(Union) - * - * @param geom1 - The first geometry (GeoJSON). - * @param geom2 - The second geometry (GeoJSON). - * @returns A number between 0 (no overlap) and 1 (perfect match). Returns 0 on error. - */ -function calculateIoU(geom1: any, geom2: any): number { - try { - // Cast to Polygon or MultiPolygon because Turf expects specific geometry types for intersect/union - const f1 = feature(geom1) - const f2 = feature(geom2) - - // Turf v7 intersect takes a FeatureCollection of polygons to intersect - const intResult = intersect(featureCollection([f1, f2])) - if (!intResult) return 0 - - // Union also takes a FeatureCollection - const unionResult = union(featureCollection([f1, f2])) - if (!unionResult) return 0 - - const areaInt = area(intResult) - const areaUnion = area(unionResult) - - if (areaUnion === 0) return 0 - return areaInt / areaUnion - } catch (e) { - console.error("Error calculating IoU", e) - return 0 - } -} - -/** - * Checks if two bounding boxes overlap. - * - * Used as a fast pre-filter before calculating expensive IoU operations. - * - * @param bbox1 - [minX, minY, maxX, maxY] - * @param bbox2 - [minX, minY, maxX, maxY] - * @returns True if boxes overlap, false otherwise. - */ -function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { - return !( - ( - bbox1[2] < bbox2[0] || // left - bbox1[0] > bbox2[2] || // right - bbox1[3] < bbox2[1] || // bottom - bbox1[1] > bbox2[3] - ) // top - ) -} - function findActiveCultivation( cultivations: Cultivation[], calendar: number, diff --git a/fdm-rvo/src/data.test.ts b/fdm-rvo/src/data.test.ts index b32752d38..4a401f959 100644 --- a/fdm-rvo/src/data.test.ts +++ b/fdm-rvo/src/data.test.ts @@ -2,12 +2,54 @@ import { describe, it, expect, vi } from "vitest" import { fetchRvoFields } from "./data" import type { RvoClient } from "@nmi-agro/rvo-connector" +// A simple square polygon used as test geometry +const POLYGON_A = { + type: "Polygon", + coordinates: [ + [ + [5.0, 52.0], + [5.0, 52.01], + [5.01, 52.01], + [5.01, 52.0], + [5.0, 52.0], + ], + ], +} + +// A nearly identical polygon (same field, minor floating point difference) +const POLYGON_A_SIMILAR = { + type: "Polygon", + coordinates: [ + [ + [5.0001, 52.0001], + [5.0001, 52.0099], + [5.0099, 52.0099], + [5.0099, 52.0001], + [5.0001, 52.0001], + ], + ], +} + +// A completely different polygon (no overlap) +const POLYGON_B = { + type: "Polygon", + coordinates: [ + [ + [6.0, 53.0], + [6.0, 53.01], + [6.01, 53.01], + [6.01, 53.0], + [6.0, 53.0], + ], + ], +} + describe("fetchRvoFields", () => { - it("should fetch and validate fields successfully", async () => { + it("should merge mestData via spatial IoU join (Tier 2)", async () => { const mockFeatures = [ { type: "Feature", - geometry: { type: "Polygon", coordinates: [] }, + geometry: POLYGON_A, properties: { CropFieldID: "123", CropFieldVersion: "1", @@ -23,9 +65,9 @@ describe("fetchRvoFields", () => { const mockMestFeatures = [ { type: "Feature", - geometry: { type: "Polygon", coordinates: [] }, + geometry: POLYGON_A_SIMILAR, properties: { - CropFieldID: "123", + MESTFieldid: "mest-1", Bufferstrook: true, Regelingsgebied: "Yes", }, @@ -57,13 +99,88 @@ describe("fetchRvoFields", () => { }) expect(result).toHaveLength(1) expect(result[0].properties.CropFieldID).toBe("123") - expect(result[0].properties.mestData).toEqual({ - CropFieldID: "123", + expect(result[0].properties.mestData).toMatchObject({ + MESTFieldid: "mest-1", Bufferstrook: true, Regelingsgebied: "Yes", }) }) + it("should merge mestData via designator match + IoU check (Tier 1)", async () => { + const mockFeatures = [ + { + type: "Feature", + geometry: POLYGON_A, + properties: { + CropFieldID: "123", + CropFieldVersion: "1", + CropFieldDesignator: "My Field", + BeginDate: "2024-01-01", + Country: "NL", + CropTypeCode: "101", + UseTitleCode: "01", + }, + }, + ] + + const mockMestFeatures = [ + { + type: "Feature", + geometry: POLYGON_A_SIMILAR, + properties: { + MESTFieldid: "mest-1", + Fielddesignator: "My Field", + Grondsoort: "1", + }, + }, + ] + + const mockClient = { + opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ features: mockFeatures }), + opvragenRegelingspercelenMest: vi.fn().mockResolvedValue({ features: mockMestFeatures }), + } as unknown as RvoClient + + const result = await fetchRvoFields(mockClient, "2024", "12345678") + expect(result[0].properties.mestData).toMatchObject({ MESTFieldid: "mest-1", Fielddesignator: "My Field" }) + }) + + it("should not merge mestData if geometries do not overlap sufficiently", async () => { + const mockFeatures = [ + { + type: "Feature", + geometry: POLYGON_A, + properties: { + CropFieldID: "123", + CropFieldVersion: "1", + CropFieldDesignator: "Field A", + BeginDate: "2024-01-01", + Country: "NL", + CropTypeCode: "101", + UseTitleCode: "01", + }, + }, + ] + + const mockMestFeatures = [ + { + type: "Feature", + geometry: POLYGON_B, + properties: { + MESTFieldid: "mest-2", + Bufferstrook: false, + }, + }, + ] + + const mockClient = { + opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ features: mockFeatures }), + opvragenRegelingspercelenMest: vi.fn().mockResolvedValue({ features: mockMestFeatures }), + } as unknown as RvoClient + + const result = await fetchRvoFields(mockClient, "2024", "12345678") + expect(result[0].properties.mestData).toBeUndefined() + }) + it("should return empty array if no features found", async () => { const mockClient = { opvragenBedrijfspercelen: vi.fn().mockResolvedValue({ @@ -96,7 +213,7 @@ describe("fetchRvoFields", () => { const mockFeatures = [ { type: "Feature", - // Missing properties + // Missing required properties properties: { CropFieldID: "123", }, @@ -117,3 +234,4 @@ describe("fetchRvoFields", () => { ).rejects.toThrow() }) }) + diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts index ffed7670e..d07fa7c26 100644 --- a/fdm-rvo/src/data.ts +++ b/fdm-rvo/src/data.ts @@ -1,6 +1,14 @@ import { RvoClient } from "@nmi-agro/rvo-connector" +import bbox from "@turf/bbox" import { RvoFieldSchema, type RvoField } from "./types" import { z } from "zod" +import { calculateIoU, bboxOverlap } from "./utils" + +// Minimum IoU to consider a MEST feature as matching a bedrijfsperceel. +// Set high (0.95) because both datasets represent the same physical parcel in RVO — +// geometries should be essentially identical, with only minor coordinate precision +// differences between the two RVO registration systems. +const MEST_IOU_THRESHOLD = 0.95 /** * Fetches agricultural fields (bedrijfspercelen) from the RVO webservice for a specific year and farm. @@ -46,23 +54,79 @@ export async function fetchRvoFields( const mestFeatures = (mestFieldsRaw as any).features || [] if (Array.isArray(features)) { - // Create a lookup for MEST fields by CropFieldID - const mestLookup = new Map() - if (Array.isArray(mestFeatures)) { - for (const mf of mestFeatures) { - if (mf?.properties?.CropFieldID) { - mestLookup.set(String(mf.properties.CropFieldID), mf.properties) + // Pre-compute bounding boxes for all MEST features (avoid recalc in inner loops) + const mestWithBbox = Array.isArray(mestFeatures) + ? mestFeatures + .filter((mf: any) => mf?.geometry) + .map((mf: any) => ({ + feature: mf, + bbox: bbox(mf.geometry) as number[], + })) + : [] + + const matchedMestIndices = new Set() + + for (const cropFeature of features) { + if (!cropFeature?.geometry) continue + + const cropBbox = bbox(cropFeature.geometry) as number[] + const cropDesignator: string = + cropFeature?.properties?.CropFieldDesignator ?? "" + + let mergedMestProps: any = null + + // ------------------------------------------------------- + // Tier 1: FieldDesignator name match + IoU sanity check + // ------------------------------------------------------- + if (cropDesignator) { + const nameMatches = mestWithBbox + .map((m, idx) => ({ ...m, idx })) + .filter( + ({ feature: mf, idx }) => + !matchedMestIndices.has(idx) && + mf?.properties?.Fielddesignator === cropDesignator, + ) + + if (nameMatches.length === 1) { + const candidate = nameMatches[0] + const iou = calculateIoU( + cropFeature.geometry, + candidate.feature.geometry, + ) + if (iou >= MEST_IOU_THRESHOLD) { + mergedMestProps = candidate.feature.properties + matchedMestIndices.add(candidate.idx) + } } } - } - // Merge MEST data into Bedrijfspercelen features - for (const feature of features) { - if (feature?.properties?.CropFieldID) { - const cropFieldId = String(feature.properties.CropFieldID) - if (mestLookup.has(cropFieldId)) { - feature.properties.mestData = mestLookup.get(cropFieldId) + // ------------------------------------------------------- + // Tier 2: Spatial IoU join (bbox pre-filter) + // ------------------------------------------------------- + if (!mergedMestProps) { + let bestIoU = 0 + let bestIdx = -1 + + for (let i = 0; i < mestWithBbox.length; i++) { + if (matchedMestIndices.has(i)) continue + const { feature: mf, bbox: mBbox } = mestWithBbox[i] + if (!bboxOverlap(cropBbox, mBbox)) continue + + const iou = calculateIoU(cropFeature.geometry, mf.geometry) + if (iou > bestIoU) { + bestIoU = iou + bestIdx = i + } } + + if (bestIdx >= 0 && bestIoU >= MEST_IOU_THRESHOLD) { + mergedMestProps = mestWithBbox[bestIdx].feature.properties + matchedMestIndices.add(bestIdx) + } + } + + if (mergedMestProps) { + cropFeature.properties.mestData = mergedMestProps } } diff --git a/fdm-rvo/src/process.test.ts b/fdm-rvo/src/process.test.ts index 644d3cacf..24fb7ae08 100644 --- a/fdm-rvo/src/process.test.ts +++ b/fdm-rvo/src/process.test.ts @@ -77,6 +77,7 @@ describe("processRvoImport", () => { expect.any(Date), // start "nl_01", undefined, // end + undefined, // b_bufferstrip (no mestData in test) ) expect(addCultivation).toHaveBeenCalled() expect(onFieldAdded).toHaveBeenCalledWith( @@ -128,6 +129,7 @@ describe("processRvoImport", () => { expect.any(Date), "nl_01", undefined, + undefined, // b_bufferstrip (no mestData in test) ) // No cultivation change implies no cultivation update call unless localCultivation differs expect(removeCultivation).not.toHaveBeenCalled() diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index 3a766e10d..13d3aea99 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -44,6 +44,15 @@ export async function processRvoImport( switch (action) { case "ADD_REMOTE": if (item.rvoField) { + const b_bufferstrip = + item.rvoField.properties.mestData?.IndBufferstrook === + "J" + ? true + : item.rvoField.properties.mestData?.IndBufferstrook === + "N" + ? false + : undefined + const b_id = await addField( fdm, principal_id, @@ -57,6 +66,7 @@ export async function processRvoImport( item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, + b_bufferstrip, ) // Add cultivation from RVO @@ -85,6 +95,15 @@ export async function processRvoImport( break case "UPDATE_FROM_REMOTE": if (item.localField && item.rvoField) { + const b_bufferstrip = + item.rvoField.properties.mestData?.IndBufferstrook === + "J" + ? true + : item.rvoField.properties.mestData?.IndBufferstrook === + "N" + ? false + : undefined + await updateField( fdm, principal_id, @@ -98,6 +117,7 @@ export async function processRvoImport( item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, + b_bufferstrip, ) // Update cultivation if different diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index bc2904a8f..9af888686 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -1,17 +1,68 @@ import { z } from "zod" +const MestCropDetailsSchema = z.object({ + Grondbedekking: z.union([z.string(), z.number()]).optional(), + Oppervlakte: z.union([z.string(), z.number()]).optional(), + Inzaaidatum: z.string().optional(), + GewasbeschermingVoorteelt: z.string().optional(), + descriptiveValues: z.record(z.string(), z.any()).optional(), +}).catchall(z.any()) + /** * Zod schema for properties returned by the RegelingspercelenMest endpoint. + * Matches `MestFieldProperties` from the rvo-connector. */ export const MestDataSchema = z.object({ - /** Unique identifier for the crop field (linked to Bedrijfspercelen) */ - CropFieldID: z.string().optional(), - /** Indicates if the field is a buffer strip */ - Bufferstrook: z.boolean().optional(), - /** Soil type code */ - Grondsoort: z.union([z.string(), z.number()]).optional(), - /** Indicates if a catch crop (vanggewas/nateelt) is required or present for manure regulations */ - IndNateeltMest: z.boolean().optional(), + /** Unique identification of the parcel (e.g. AGRONL...). */ + MESTFieldid: z.string().optional(), + /** Version number of the MEST field. */ + MESTFieldVersion: z.string().optional(), + /** Start date of the field's validity (YYYY-MM-DDTHH:mm:ss). */ + BeginDate: z.string().optional(), + /** End date of the field's validity (YYYY-MM-DDTHH:mm:ss). */ + EndDate: z.string().optional(), + /** Date of the statement. */ + OpgaveDatum: z.string().optional(), + /** User-assigned name/designator for the field. */ + Fielddesignator: z.string().optional(), + /** Calculated area in hectares (4 decimals). */ + CalculatedArea: z.union([z.string(), z.number()]).optional(), + /** Proposed area in hectares. */ + VoorgesteldeOppervlakte: z.union([z.string(), z.number()]).optional(), + /** Declared area in hectares. */ + OpgegevenOppervlakte: z.union([z.string(), z.number()]).optional(), + /** Crop Type Code. Codelist: CL263 or CL411. */ + Grondbedekking: z.union([z.string(), z.number()]).optional(), + /** Use Title Code. Codelist: CL412. */ + GebruiksTitel: z.string().optional(), + /** Regulatory Soil Type Code. Codelist: CL405. */ + Grondsoort: z.string().optional(), + /** Natural land type code. */ + TypeGrond: z.string().optional(), + /** Indicator for buffer strips. */ + IndBufferstrook: z.enum(["J", "N"]).optional(), + /** Surface area of buffer strips. */ + BufferstrookOppervlakte: z.union([z.string(), z.number()]).optional(), + /** Sampling date. */ + BemonsteringDatum: z.string().optional(), + /** Sampling protocol code. */ + BemonsteringProtocol: z.string().optional(), + /** Indicator for phosphate differentiation. */ + IndFosfaatdifferentiatie: z.enum(["J", "N"]).optional(), + /** PCA CL2 value. */ + PCACL2Waarde: z.union([z.string(), z.number()]).optional(), + /** Pal value from 2021 onwards. */ + PalWaardeVanaf2021: z.union([z.string(), z.number()]).optional(), + /** Indicator for catch crop (nateelt) as manure requirement. */ + IndNateeltMest: z.string().optional(), + /** Cause of the update/mutation. */ + MESTFieldCause: z.string().optional(), + /** Previous crop (Voorteelt) details. */ + Voorteelt: z.union([MestCropDetailsSchema, z.array(MestCropDetailsSchema)]).optional(), + /** Catch crop (Nateelt) details. */ + Nateelt: z.union([MestCropDetailsSchema, z.array(MestCropDetailsSchema)]).optional(), + /** Quality indicators for this field. */ + QualityIndicatorType: z.any().optional(), /** Human-readable labels and boolean mappings if `enrichResponse` was used */ descriptiveValues: z.record(z.string(), z.any()).optional(), }).catchall(z.any()) diff --git a/fdm-rvo/src/utils.ts b/fdm-rvo/src/utils.ts index 483c4937a..819a2d30e 100644 --- a/fdm-rvo/src/utils.ts +++ b/fdm-rvo/src/utils.ts @@ -1,3 +1,8 @@ +import bbox from "@turf/bbox" +import intersect from "@turf/intersect" +import union from "@turf/union" +import area from "@turf/area" +import { feature, featureCollection } from "@turf/helpers" import type { RvoImportReviewItem } from "./types" /** @@ -17,3 +22,63 @@ export function getItemId(item: RvoImportReviewItem): string { "unknown" ) } + +/** + * Calculates Intersection over Union (IoU) for two geometries. + * + * IoU is a standard metric for measuring the overlap between two shapes. + * Formula: Area(Intersection) / Area(Union) + * + * @param geom1 - The first geometry (GeoJSON). + * @param geom2 - The second geometry (GeoJSON). + * @returns A number between 0 (no overlap) and 1 (perfect match). Returns 0 on error. + */ +export function calculateIoU(geom1: any, geom2: any): number { + try { + const f1 = feature(geom1) + const f2 = feature(geom2) + + const intResult = intersect(featureCollection([f1, f2])) + if (!intResult) return 0 + + const unionResult = union(featureCollection([f1, f2])) + if (!unionResult) return 0 + + const areaInt = area(intResult) + const areaUnion = area(unionResult) + + if (areaUnion === 0) return 0 + return areaInt / areaUnion + } catch (e) { + console.error("Error calculating IoU", e) + return 0 + } +} + +/** + * Checks if two bounding boxes overlap. + * + * Used as a fast pre-filter before calculating expensive IoU operations. + * + * @param bbox1 - [minX, minY, maxX, maxY] + * @param bbox2 - [minX, minY, maxX, maxY] + * @returns True if boxes overlap, false otherwise. + */ +export function bboxOverlap(bbox1: number[], bbox2: number[]): boolean { + return !( + bbox1[2] < bbox2[0] || + bbox1[0] > bbox2[2] || + bbox1[3] < bbox2[1] || + bbox1[1] > bbox2[3] + ) +} + +/** + * Pre-computes the bounding box of a GeoJSON geometry. + * + * @param geometry - The GeoJSON geometry. + * @returns The bounding box as [minX, minY, maxX, maxY]. + */ +export function computeBbox(geometry: any): number[] { + return bbox(geometry) +} From 1c82ab5365bfe610f49f8b3e003d978d491ef660 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:16:18 +0100 Subject: [PATCH 041/108] feat: improve page names --- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 2 +- fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index d10fe41d1..7f452d69a 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -66,7 +66,7 @@ import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" import { clientConfig } from "../lib/config" export const meta: MetaFunction = ({ params }) => { - return [{ title: `RVO Koppeling - Bedrijf ${params.b_id_farm}` }] + return [{ title: `Percelen ophalen bij RVO - Bedrijf ${params.b_id_farm}` }] } export async function loader({ request, params }: LoaderFunctionArgs) { diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 191077f68..6f06fa723 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -55,13 +55,13 @@ import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" export const meta: MetaFunction = ({ params }) => { const b_id_farm = params.b_id_farm - return [{ title: `RVO Import - Nieuw Bedrijf ${b_id_farm}` }] + return [{ title: `Percelen ophalen bij RVO - Nieuw Bedrijf ${b_id_farm}` }] } export async function loader({ request, params }: LoaderFunctionArgs) { const { b_id_farm, calendar: yearString } = params if (!b_id_farm || !yearString) { - throw new Response("Farm ID en Kalender zijn verplicht", { + throw new Response("Farm ID en kalender zijn verplicht", { status: 400, }) } From 327b51bbda65e0c1e265684ea52c51a78d0154be Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:33:47 +0100 Subject: [PATCH 042/108] fix: show buffer strip status --- .../blocks/rvo/import-review-table.tsx | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index 23c9711c4..b98a36651 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -489,22 +489,36 @@ export const columns: ColumnDef>[] = [ ), - cell: ({ row }) => { + cell: ({ row, table }) => { const item = row.original - const isBufferstrip = item.rvoField?.properties.mestData?.Bufferstrook + // @ts-ignore + const { userChoices } = table.options.meta as any + const action = userChoices[getItemId(item)] as ImportReviewAction - if (isBufferstrip === undefined) return - + const rvoBufferstrip = + item.rvoField?.properties.mestData?.IndBufferstrook + const rvoLabel = + rvoBufferstrip === undefined + ? undefined + : rvoBufferstrip === "J" + ? "Ja" + : "Nee" + + const localLabel = + item.localField?.b_bufferstrip === undefined + ? undefined + : item.localField.b_bufferstrip + ? "Ja" + : "Nee" return ( - - {isBufferstrip ? "Ja" : "Nee"} - + val ?? "-"} + /> ) }, }, @@ -599,7 +613,7 @@ export const columns: ColumnDef>[] = [
- Gebruik + Gebruik{" "} {clientConfig.name}
From 89b67a61e1cf3c46652dd0e84e4ec7cfcef4eb56 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:34:47 +0100 Subject: [PATCH 043/108] fix: compare buffer strip state --- fdm-rvo/src/compare.ts | 9 +++++++++ fdm-rvo/src/types.ts | 1 + 2 files changed, 10 insertions(+) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index be65a59a4..98413cf0c 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -314,5 +314,14 @@ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { diffs.push("b_end") } + // 5. Buffer strip + // Only check when RVO MEST data is available (mestData present means MEST was fetched) + if (rvo.properties.mestData?.IndBufferstrook !== undefined) { + const rvoBufferstrip = rvo.properties.mestData.IndBufferstrook === "J" + if (local.b_bufferstrip !== rvoBufferstrip) { + diffs.push("b_bufferstrip") + } + } + return diffs } diff --git a/fdm-rvo/src/types.ts b/fdm-rvo/src/types.ts index 9af888686..d2857c940 100644 --- a/fdm-rvo/src/types.ts +++ b/fdm-rvo/src/types.ts @@ -150,6 +150,7 @@ export type FieldDiff = | "b_end" // End date difference | "b_acquiring_method" // Method of acquisition difference (implied) | "b_lu_catalogue" // Cultivation difference + | "b_bufferstrip" // Buffer strip status difference /** * Represents the explicit decision made by the user for a RVO Import Review item. From 09e66091db2bd8be0cae622cbe26fda850eca16b Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:03:38 +0100 Subject: [PATCH 044/108] refactor: check filepath --- fdm-rvo/src/auth.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fdm-rvo/src/auth.ts b/fdm-rvo/src/auth.ts index a66040e86..045eae7b2 100644 --- a/fdm-rvo/src/auth.ts +++ b/fdm-rvo/src/auth.ts @@ -25,7 +25,8 @@ export const createRvoClient = ( if ( pkioPrivateKey.startsWith("/") || pkioPrivateKey.startsWith("./") || - pkioPrivateKey.includes(":") + pkioPrivateKey.startsWith("../") || + /^[a-zA-Z]:\\/.test(pkioPrivateKey) ) { if (fs.existsSync(pkioPrivateKey)) { privateKey = fs.readFileSync(pkioPrivateKey, "utf8") From 654465e448c127fff0f16cda60b4505e82c458d0 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:03:55 +0100 Subject: [PATCH 045/108] refactor: improve accesibilty --- fdm-app/app/components/blocks/rvo/import-review-table.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index b98a36651..3a01000a7 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -120,7 +120,7 @@ const DiffCell = ({ className={cn( "flex items-center gap-2", - useRemote && "opacity-50 grayscale", + useRemote && "opacity-70", )} > Date: Thu, 19 Mar 2026 15:04:56 +0100 Subject: [PATCH 046/108] fix: typo --- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 7f452d69a..c1cce21df 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -348,7 +348,7 @@ export default function RvoImportReviewPage() { > - Perceleh ophalen bij RVO is niet beschikbaar + Percelen ophalen bij RVO is niet beschikbaar De RVO koppeling is nog niet ingesteld op deze @@ -548,7 +548,7 @@ export async function action({ request, params }: ActionFunctionArgs) { return { success: false, message: - "Geen data gevonden om te verwerken. Start 'percelehn ophalen bij RVO' opnieuw.", + "Geen data gevonden om te verwerken. Start 'percelen ophalen bij RVO' opnieuw.", } } From d5ff3af5e092bb78918701697d000dda6c507a54 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:24:38 +0100 Subject: [PATCH 047/108] refactor: improve state handling --- fdm-app/app/integrations/rvo.ts | 64 +++++++++++++++++++ .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 41 ++++++++---- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 34 ++++++---- 3 files changed, 112 insertions(+), 27 deletions(-) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts index 24fb03888..fdff62811 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.ts @@ -1,4 +1,68 @@ import { serverConfig } from "../lib/config.server" +import { createCookie } from "react-router" +import { nanoid } from "nanoid" + +export const rvoStateCookie = createCookie("rvo_state", { + path: "/", + httpOnly: true, + sameSite: "lax", + secure: process.env.NODE_ENV === "production", + maxAge: 3600, // 1 hour + secrets: [serverConfig.auth.fdm_session_secret], +}) + +/** + * Generates a signed OAuth state with a random nonce. + * @returns { state, cookieHeader } The base64 state string and the serialized cookie header. + */ +export async function createRvoState(farmId: string, returnUrl: string) { + const nonce = nanoid() + const state = Buffer.from( + JSON.stringify({ + farmId, + returnUrl, + nonce, + }), + ).toString("base64") + + return { + state, + cookieHeader: await rvoStateCookie.serialize(state), + } +} + +/** + * Verifies the OAuth state against the signed cookie and ensures the farm ID matches. + * @throws {Response} 403 if CSRF or farm ID validation fails. + */ +export async function verifyRvoState( + request: Request, + stateFromUrl: string, + expectedFarmId: string, +) { + const cookieHeader = request.headers.get("Cookie") + const stateFromCookie = await rvoStateCookie.parse(cookieHeader) + + if (!stateFromCookie || stateFromCookie !== stateFromUrl) { + throw new Response("Ongeldige state parameter (CSRF)", { + status: 403, + }) + } + + try { + const decodedState = JSON.parse( + Buffer.from(stateFromUrl, "base64").toString("utf-8"), + ) + if (decodedState.farmId !== expectedFarmId) { + throw new Response("Ongeldig bedrijfs-ID in state", { + status: 403, + }) + } + return decodedState + } catch (e) { + throw new Response("Ongeldig state formaat", { status: 400 }) + } +} export function getRvoCredentials(): RvoCredentials | undefined { // Check if RVO is configured diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index c1cce21df..4b591b3c4 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -8,8 +8,6 @@ import { useLoaderData, useNavigation, useParams, - Link, - useNavigate, useLocation, } from "react-router" import { getSession } from "~/lib/auth.server" @@ -51,7 +49,11 @@ import { Header } from "~/components/blocks/header/base" import { HeaderFarm } from "~/components/blocks/header/farm" import { SidebarInset } from "~/components/ui/sidebar" import { BreadcrumbItem, BreadcrumbSeparator } from "~/components/ui/breadcrumb" -import { getRvoCredentials } from "../integrations/rvo" +import { + createRvoState, + getRvoCredentials, + verifyRvoState, +} from "~/integrations/rvo" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" import { getNmiApiKey, @@ -63,7 +65,7 @@ import { getCultivationsFromCatalogue, } from "@nmi-agro/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" -import { clientConfig } from "../lib/config" +import { clientConfig } from "~/lib/config" export const meta: MetaFunction = ({ params }) => { return [{ title: `Percelen ophalen bij RVO - Bedrijf ${params.b_id_farm}` }] @@ -105,12 +107,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) { status: 500, }) } - const decodedState = JSON.parse( - Buffer.from(state, "base64").toString("utf-8"), - ) - if (decodedState.farmId !== b_id_farm) { - throw new Response("Invalid state parameter", { status: 403 }) - } + + // CSRF Verification + await verifyRvoState(request, state, b_id_farm) if (!farm || !farm.b_businessid_farm) { throw new Response("b_businessid_farm is not available", { @@ -530,11 +529,20 @@ export async function action({ request, params }: ActionFunctionArgs) { rvoCredentials.clientSecret, process.env.NODE_ENV === "production" ? "production" : "acceptance", ) - const state = Buffer.from( - JSON.stringify({ farmId: b_id_farm, returnUrl: request.url }), - ).toString("base64") + + const { state, cookieHeader } = await createRvoState( + b_id_farm, + request.url, + ) + const authUrl = generateAuthUrl(rvoClient, state) - return redirect(authUrl) + + // Set state in cookie and redirect + return redirect(authUrl, { + headers: { + "Set-Cookie": cookieHeader, + }, + }) } if (intent === "apply_changes") { @@ -556,6 +564,11 @@ export async function action({ request, params }: ActionFunctionArgs) { rvoImportReviewData = JSON.parse(String(rvoImportReviewDataJson)) userChoices = JSON.parse(String(userChoicesJson)) + // Basic validation: ensure we have an array of items + if (!Array.isArray(rvoImportReviewData)) { + throw new Error("Invalid review data format") + } + const onFieldAdded = async (b_id: string, geometry: any) => { const nmiApiKey = getNmiApiKey() if (nmiApiKey) { diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 6f06fa723..81548c6e8 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -41,7 +41,11 @@ import { BreadcrumbSeparator, BreadcrumbLink, } from "~/components/ui/breadcrumb" -import { getRvoCredentials } from "../integrations/rvo" +import { + getRvoCredentials, + createRvoState, + verifyRvoState, +} from "../integrations/rvo" import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" import { getNmiApiKey, @@ -90,17 +94,13 @@ export async function loader({ request, params }: LoaderFunctionArgs) { if (code && state) { try { if (!isRvoConfigured) { - throw new Response("RVO is niet geconfigureerd.", { + throw new Response("RVO client is not configured.", { status: 500, }) } - const decodedState = JSON.parse( - Buffer.from(state, "base64").toString("utf-8"), - ) - if (decodedState.farmId !== b_id_farm) { - throw new Response("Ongeldige state parameter", { status: 403 }) - } + // CSRF Verification + await verifyRvoState(request, state, b_id_farm) if (!farm || !farm.b_businessid_farm) { throw new Response( @@ -401,11 +401,19 @@ export async function action({ request, params }: ActionFunctionArgs) { rvoCredentials.clientSecret, process.env.NODE_ENV === "production" ? "production" : "acceptance", ) // Instantiate RvoClient - const state = Buffer.from( - JSON.stringify({ farmId: b_id_farm, returnUrl: request.url }), - ).toString("base64") - const authUrl = generateAuthUrl(rvoClient, state) // Pass rvoClient instance - return redirect(authUrl) + + const { state, cookieHeader } = await createRvoState( + b_id_farm, + request.url, + ) + + const authUrl = generateAuthUrl(rvoClient, state) + + return redirect(authUrl, { + headers: { + "Set-Cookie": cookieHeader, + }, + }) } if (intent === "save_fields") { From f25facc9e44117e42324ed173389835ac6358c5b Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:38:50 +0100 Subject: [PATCH 048/108] refactor: improve the button position --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 231 +++++++++--------- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 83 +++---- 2 files changed, 161 insertions(+), 153 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 4b591b3c4..f166a6e37 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -371,122 +371,129 @@ export default function RvoImportReviewPage() { ) : ( <> -
- -
- - - - - - - - Wijzigingen toepassen - - - U staat op het punt de volgende - wijzigingen door te voeren: - - -
-
    - {changes.add > 0 && ( -
  • - {changes.add} perceel - {changes.add !== 1 && - "en"}{" "} - toevoegen -
  • - )} - {changes.remove > 0 && ( -
  • - {changes.remove} perceel - {changes.remove !== 1 && - "en"}{" "} - verwijderen -
  • - )} - {changes.update > 0 && ( -
  • - {changes.update} perceel - {changes.update !== 1 && - "en"}{" "} - bijwerken -
  • - )} - {changes.close > 0 && ( -
  • - {changes.close} perceel - {changes.close !== 1 && - "en"}{" "} - afsluiten -
  • + + +
    +
    + + +
- {!hasChanges && ( -

- Geen wijzigingen - geselecteerd. -

- )} -
- - - - -
- - + + + + + Wijzigingen toepassen + + + U staat op het punt de + volgende wijzigingen door + te voeren: + + +
+
    + {changes.add > 0 && ( +
  • + {changes.add}{" "} + perceel + {changes.add !== + 1 && + "en"}{" "} + toevoegen +
  • )} - /> - 0 && ( +
  • + {changes.remove}{" "} + perceel + {changes.remove !== + 1 && + "en"}{" "} + verwijderen +
  • )} - /> -
+ {!hasChanges && ( +

+ Geen wijzigingen + geselecteerd. +

+ )} +
+ + + + + - Bevestigen - - - -
-
-
-
- -
+ + + + + + + + +
) : ( <> -
- -
-
- - - - -
-
-
+ -
+
+
+
+ + + + +
+
Date: Thu, 19 Mar 2026 15:59:00 +0100 Subject: [PATCH 049/108] fix: pnpm version --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8d3219e2f..2259386c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -304,7 +304,7 @@ jobs: - name: Setup pnpm 10 uses: pnpm/action-setup@v4 with: - version: 10.23.0 + version: 10.32.1 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 From 24ca2a0fae676f33bb1bbea4a0d07c63619d4d8d Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:59:20 +0100 Subject: [PATCH 050/108] fix: typo --- .changeset/tangy-ways-win.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/tangy-ways-win.md b/.changeset/tangy-ways-win.md index 8d3906472..c5a114156 100644 --- a/.changeset/tangy-ways-win.md +++ b/.changeset/tangy-ways-win.md @@ -2,4 +2,4 @@ "@nmi-agro/fdm-rvo": minor --- -Initial version of `fdm-rvo` that that provides the logic for fdm-app to connect to RVO and sync fields with fdm-core +Initial version of `fdm-rvo` that provides the logic for fdm-app to connect to RVO and sync fields with fdm-core From 903b9bd6d7bb90ff57500eebb36b0306f1921119 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:59:55 +0100 Subject: [PATCH 051/108] chore: remove blank line --- fdm-app/.env.example | 1 - 1 file changed, 1 deletion(-) diff --git a/fdm-app/.env.example b/fdm-app/.env.example index 53298d8d8..8734d7085 100644 --- a/fdm-app/.env.example +++ b/fdm-app/.env.example @@ -75,7 +75,6 @@ RVO_CLIENT_NAME= RVO_CLIENT_SECRET= RVO_REDIRECT_URI= - # ------------------------------------- # Map & Data Configuration # ------------------------------------- From 20c4623474e5cbfc78735e6195da4c739926dae9 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:00:51 +0100 Subject: [PATCH 052/108] refactor: move import to top of file --- fdm-app/app/components/blocks/rvo/import-review-table.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index 3a01000a7..8a473d10e 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -37,6 +37,8 @@ import { area } from "@turf/turf" import { format } from "date-fns" import { cn } from "~/lib/utils" import { acquiringMethodOptions } from "~/lib/constants" +import { useMemo } from "react" +import { clientConfig } from "@/app/lib/config" interface RvoImportReviewTableProps { data: RvoImportReviewItem[] @@ -626,9 +628,6 @@ export const columns: ColumnDef>[] = [ }, ] -import { useMemo } from "react" -import { clientConfig } from "@/app/lib/config" - export function RvoImportReviewTable({ data, userChoices, From 319cb08ba00b116da31caa2fd78e95d264fa6071 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:03:08 +0100 Subject: [PATCH 053/108] refactor: rename and use client side environmental --- .../blocks/rvo/{rvo-error-alert.tsx => error-alert.tsx} | 2 +- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 2 +- fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename fdm-app/app/components/blocks/rvo/{rvo-error-alert.tsx => error-alert.tsx} (98%) diff --git a/fdm-app/app/components/blocks/rvo/rvo-error-alert.tsx b/fdm-app/app/components/blocks/rvo/error-alert.tsx similarity index 98% rename from fdm-app/app/components/blocks/rvo/rvo-error-alert.tsx rename to fdm-app/app/components/blocks/rvo/error-alert.tsx index 20a382167..4a9c24b28 100644 --- a/fdm-app/app/components/blocks/rvo/rvo-error-alert.tsx +++ b/fdm-app/app/components/blocks/rvo/error-alert.tsx @@ -95,7 +95,7 @@ export function RvoErrorAlert({ ) : null}
- {process.env.NODE_ENV === "development" && ( + {import.meta.env.MODE === "development" && (
DEBUG: {rawMessage}
diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index f166a6e37..dbe69ec4b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -54,7 +54,7 @@ import { getRvoCredentials, verifyRvoState, } from "~/integrations/rvo" -import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" +import { RvoErrorAlert } from "~/components/blocks/rvo/error-alert" import { getNmiApiKey, getSoilParameterEstimates, diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index dea196b3c..27272a0fa 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -46,7 +46,7 @@ import { createRvoState, verifyRvoState, } from "../integrations/rvo" -import { RvoErrorAlert } from "~/components/blocks/rvo/rvo-error-alert" +import { RvoErrorAlert } from "~/components/blocks/rvo/error-alert" import { getNmiApiKey, getSoilParameterEstimates, From 8b489106eb95df37a2b223ff5608fb2c90420ec7 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:04:11 +0100 Subject: [PATCH 054/108] fix: Add path alias for @nmi-agro/fdm-rvo --- fdm-app/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fdm-app/tsconfig.json b/fdm-app/tsconfig.json index d94b0c50f..ea86cdefc 100644 --- a/fdm-app/tsconfig.json +++ b/fdm-app/tsconfig.json @@ -20,7 +20,8 @@ "~/*": ["./app/*"], "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"], - "@nmi-agro/fdm-calculator": ["../fdm-calculator/src/index.ts"] + "@nmi-agro/fdm-calculator": ["../fdm-calculator/src/index.ts"], + "@nmi-agro/fdm-rvo": ["../fdm-rvo/src/index.ts"] }, "rootDirs": [".", "./.react-router/types"] } From c8fb3c76be5c68d3c5bf3611f11fdd0a86721dfe Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:05:47 +0100 Subject: [PATCH 055/108] fix: pnpm version --- fdm-rvo/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-rvo/package.json b/fdm-rvo/package.json index c5303c33d..25bcb15be 100644 --- a/fdm-rvo/package.json +++ b/fdm-rvo/package.json @@ -72,7 +72,7 @@ "typescript": "catalog:", "vitest": "catalog:" }, - "packageManager": "pnpm@10.30.3", + "packageManager": "pnpm@10.32.1", "publishConfig": { "registry": "https://npm.pkg.github.com" } From 5f08e2c32b8799c8e4e5b38e7a88d34b6d25c65c Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:06:23 +0100 Subject: [PATCH 056/108] docs: fix --- fdm-rvo/src/compare.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 98413cf0c..e418c43a6 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -36,7 +36,7 @@ function findActiveCultivation( * 1. **Tier 1: ID Match**: Checks if `localField.b_id_source` matches `rvoField.CropFieldID`. * This is the most reliable method for fields that have been synced before. * 2. **Tier 2: Spatial Match**: For fields unmatched by ID, it calculates the spatial overlap (IoU). - * If the overlap exceeds `IOU_THRESHOLD` (0.9), they are considered the same field. + * If the overlap exceeds `IOU_THRESHOLD` (0.99), they are considered the same field. * * @param localFields - Array of fields currently in the local database. * @param rvoFields - Array of fields retrieved from the RVO webservice. From 47d82ece33e6c3b140db21e617a1f26702ec120f Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:11:55 +0100 Subject: [PATCH 057/108] fix: retrieving the rvo-connector package --- .github/workflows/release.yml | 2 ++ .github/workflows/tests.yml | 4 ---- .github/workflows/typecheck.yml | 1 - .npmrc | 2 -- fdm-calculator/package.json | 6 ++++++ 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a308e15ea..19cae138f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,7 @@ jobs: - name: Publish Snapshot if: github.ref == 'refs/heads/development' run: | + echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> .npmrc pnpm changeset version --snapshot pnpm changeset publish --tag development env: @@ -68,6 +69,7 @@ jobs: if: github.ref == 'refs/heads/main' shell: bash run: | + echo "//npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}" >> .npmrc # Build packages pnpm build --filter="!@nmi-agro/fdm-app" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2259386c6..58129db2f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -59,7 +59,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - registry-url: 'https://npm.pkg.github.com' cache: 'pnpm' - name: Install Dependencies @@ -152,7 +151,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - registry-url: 'https://npm.pkg.github.com' cache: 'pnpm' - name: Install Dependencies @@ -232,7 +230,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - registry-url: 'https://npm.pkg.github.com' cache: 'pnpm' - name: Install Dependencies @@ -310,7 +307,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - registry-url: 'https://npm.pkg.github.com' cache: 'pnpm' - name: Install Dependencies diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 57a18f9ac..d823162bb 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -33,7 +33,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - registry-url: 'https://npm.pkg.github.com' cache: 'pnpm' - name: Install Dependencies diff --git a/.npmrc b/.npmrc index 79eba4f62..476e68926 100644 --- a/.npmrc +++ b/.npmrc @@ -1,4 +1,2 @@ sync-injected-deps-after-scripts[]=build link-workspace-packages=true -@nmi-agro:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN} diff --git a/fdm-calculator/package.json b/fdm-calculator/package.json index e89d8e563..3b532ade8 100644 --- a/fdm-calculator/package.json +++ b/fdm-calculator/package.json @@ -61,5 +61,11 @@ "typescript": "catalog:", "vitest": "catalog:" }, + "engines": { + "node": ">=20.10" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com" + }, "packageManager": "pnpm@10.32.1" } From a9a965295f2eb7fe641f58fe32a077d79a238fb4 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:14:53 +0100 Subject: [PATCH 058/108] fix: Avoid non-unique "unknown" IDs in review-item --- fdm-rvo/src/utils.test.ts | 8 ++++++-- fdm-rvo/src/utils.ts | 30 ++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/fdm-rvo/src/utils.test.ts b/fdm-rvo/src/utils.test.ts index a262646ee..e4b6b1392 100644 --- a/fdm-rvo/src/utils.test.ts +++ b/fdm-rvo/src/utils.test.ts @@ -21,11 +21,15 @@ describe("getItemId", () => { expect(getItemId(item)).toBe("rvo-1") }) - it("should return 'unknown' if neither present", () => { + it("should return a deterministic composite when neither field is present", () => { const item = { status: RvoImportReviewStatus.NEW_REMOTE, diffs: [], } as any - expect(getItemId(item)).toBe("unknown") + const id = getItemId(item) + // Must be a non-empty string that starts with the status value + expect(id).toContain(RvoImportReviewStatus.NEW_REMOTE) + // Must not be "unknown" — no collisions across degenerate items + expect(id).not.toBe("unknown") }) }) diff --git a/fdm-rvo/src/utils.ts b/fdm-rvo/src/utils.ts index 819a2d30e..5551e0242 100644 --- a/fdm-rvo/src/utils.ts +++ b/fdm-rvo/src/utils.ts @@ -8,19 +8,33 @@ import type { RvoImportReviewItem } from "./types" /** * Generates a stable unique identifier for a review item. * - * This ID is used to key items in the UI (e.g., for React lists) and to map user choices. - * It prioritizes the local field ID (`b_id`), falling back to the RVO field ID (`CropFieldID`) - * if the item represents a new remote field. + * Priority: + * 1. Local field DB id (`b_id`) — always present when `localField` exists. + * 2. RVO crop field id (`CropFieldID`) — always present when `rvoField` exists. + * 3. Deterministic composite from `status`, `CropFieldVersion`, and `BeginDate` + * — used only in the degenerate case where neither field is set. + * + * The returned value is stable across renders for the same item, making it + * safe to use as a React key and for `UserChoiceMap` identity comparisons. * * @param item - The review item to generate an ID for. * @returns A unique string identifier for the item. */ export function getItemId(item: RvoImportReviewItem): string { - return ( - item.localField?.b_id || - item.rvoField?.properties.CropFieldID || - "unknown" - ) + if (item.localField?.b_id) return item.localField.b_id + if (item.rvoField?.properties.CropFieldID) + return item.rvoField.properties.CropFieldID + + // Degenerate fallback: build a deterministic composite from whatever + // stable data is available so multiple items don't collapse to the same key. + return [ + item.status, + item.rvoField?.properties.CropFieldVersion, + item.rvoField?.properties.BeginDate, + item.localField ? "local" : "remote", + ] + .filter(Boolean) + .join(":") } /** From 46db84ea0de5018adf94534761667e3e9c47c4ed Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:17:46 +0100 Subject: [PATCH 059/108] fix: set rvoCultivation to undefined for NEW_REMOTE when CropTypeCode is missing --- fdm-rvo/src/compare.test.ts | 9 +++++++++ fdm-rvo/src/compare.ts | 26 ++++++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/fdm-rvo/src/compare.test.ts b/fdm-rvo/src/compare.test.ts index 4cd57254c..e9ee3e18a 100644 --- a/fdm-rvo/src/compare.test.ts +++ b/fdm-rvo/src/compare.test.ts @@ -417,4 +417,13 @@ describe("compareFields Edge Cases", () => { expect(result[0].status).toBe(RvoImportReviewStatus.NEW_REMOTE) expect(result[0].rvoCultivation?.b_lu_name).toBe("nl_999") }) + + it("should set rvoCultivation to undefined for NEW_REMOTE when CropTypeCode is missing", () => { + const rvo = createRvoField({ CropFieldID: "new-no-crop", CropTypeCode: "" }) + const result = compareFields([], [rvo], calendar, []) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.NEW_REMOTE) + expect(result[0].rvoCultivation).toBeUndefined() + }) }) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index e418c43a6..6739ff0df 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -174,19 +174,25 @@ export function compareFields( results.push(processMatch(bestMatch, rvo)) } else { // No match found -> This is a NEW field from RVO - const rvoCode = `nl_${rvo.properties.CropTypeCode}` - const rvoCatalogueEntry = cultivationsCatalogue.find( - (c) => c.b_lu_catalogue === rvoCode, - ) + const rvoCode = rvo.properties.CropTypeCode + ? `nl_${rvo.properties.CropTypeCode}` + : undefined + const rvoCatalogueEntry = rvoCode + ? cultivationsCatalogue.find( + (c) => c.b_lu_catalogue === rvoCode, + ) + : undefined results.push({ status: RvoImportReviewStatus.NEW_REMOTE, rvoField: rvo, - rvoCultivation: { - b_lu_catalogue: rvoCode, - b_lu_name: rvoCatalogueEntry - ? rvoCatalogueEntry.b_lu_name - : rvoCode, - }, + rvoCultivation: rvoCode + ? { + b_lu_catalogue: rvoCode, + b_lu_name: rvoCatalogueEntry + ? rvoCatalogueEntry.b_lu_name + : rvoCode, + } + : undefined, diffs: [], }) } From daae3039576cf73245e716a5026dfc9b249459dc Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:18:55 +0100 Subject: [PATCH 060/108] fix: provide error message in case file reading fails --- fdm-rvo/src/auth.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fdm-rvo/src/auth.ts b/fdm-rvo/src/auth.ts index 045eae7b2..22bf63bca 100644 --- a/fdm-rvo/src/auth.ts +++ b/fdm-rvo/src/auth.ts @@ -30,6 +30,10 @@ export const createRvoClient = ( ) { if (fs.existsSync(pkioPrivateKey)) { privateKey = fs.readFileSync(pkioPrivateKey, "utf8") + } else { + throw new Error( + `PKIO private key file not found: ${pkioPrivateKey}`, + ) } } From ca23e2babb288bde5d2ef0b43b4182c4739b1ab4 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:25:04 +0100 Subject: [PATCH 061/108] fix: Handle the "no RVO parcels found" case explicitly --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 15 ++++++++++++++- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 17 ++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index dbe69ec4b..fbeae0861 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -183,11 +183,13 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } } + const noRvoParcelsFound = !error && rvoImportReviewData.length === 0 return { b_id_farm, rvoImportReviewData, error, - showimportButton: false, + showimportButton: noRvoParcelsFound, + noRvoParcelsFound, b_businessid_farm, isRvoConfigured, farms, @@ -206,6 +208,7 @@ export default function RvoImportReviewPage() { farms, calendar, showimportButton = false, + noRvoParcelsFound = false, } = useLoaderData() const actionData = useActionData() const navigation = useNavigation() @@ -360,6 +363,16 @@ export default function RvoImportReviewPage() { {rvoImportReviewData.length === 0 ? (
+ {noRvoParcelsFound && ( + + Geen percelen gevonden + + Er zijn geen percelen gevonden voor dit + bedrijf bij RVO. Controleer het KvK-nummer + en probeer opnieuw. + + + )} {showimportButton && ( () @@ -302,6 +305,18 @@ export default function RvoImportCreatePage() { {RvoImportReviewData.length === 0 ? (
+ {noRvoParcelsFound && ( + + + Geen percelen gevonden + + + Er zijn geen percelen gevonden voor dit + bedrijf bij RVO. Controleer het + KvK-nummer en probeer opnieuw. + + + )} {showImportButton && ( Date: Thu, 19 Mar 2026 16:33:08 +0100 Subject: [PATCH 062/108] fix: throw readable error --- fdm-app/app/lib/error.ts | 23 +++++++++++++++++++ .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 5 ++-- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 5 ++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/fdm-app/app/lib/error.ts b/fdm-app/app/lib/error.ts index 49b45d897..2841c5b61 100644 --- a/fdm-app/app/lib/error.ts +++ b/fdm-app/app/lib/error.ts @@ -9,6 +9,29 @@ const errorIdSize = 8 // Number of characters in ID export const createErrorId = customAlphabet(customErrorAlphabet, errorIdSize) +/** + * Extracts a human-readable error message from any thrown value. + * + * React Router loaders/actions can throw `Response` objects (e.g. via + * `throw new Response("msg", { status: 400 })`). These are valid `Error` + * values in a try/catch, but `.message` is `undefined` on them — only `.text()` + * returns the body string. This helper handles all three cases: + * 1. `Response` → await `.text()`, fallback to `HTTP ` + * 2. `Error` → `.message` + * 3. anything → `String(e)` + */ +export async function extractErrorMessage(e: unknown): Promise { + if (e instanceof Response) { + try { + return (await e.text()) || `HTTP ${e.status}` + } catch { + return `HTTP ${e.status}` + } + } + if (e instanceof Error) return e.message + return String(e) +} + export function reportError( error: unknown, tags: Record = {}, diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index fbeae0861..1078447e8 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -11,6 +11,7 @@ import { useLocation, } from "react-router" import { getSession } from "~/lib/auth.server" +import { extractErrorMessage } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { generateAuthUrl, @@ -168,7 +169,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) } catch (e: any) { console.error("Error with importing from RVO:", e) - error = e.message + error = await extractErrorMessage(e) } } else if (!url.searchParams.has("start_import")) { return { @@ -631,7 +632,7 @@ export async function action({ request, params }: ActionFunctionArgs) { console.error("Error with processing RVO import: ", e) return { success: false, - message: `Error with processing RVO import: ${e.message}`, + message: `Error with processing RVO import: ${await extractErrorMessage(e)}`, } } } diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index b93efffa9..9ff0a0ef4 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -10,6 +10,7 @@ import { useLocation, } from "react-router" import { getSession } from "~/lib/auth.server" +import { extractErrorMessage } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { generateAuthUrl, @@ -143,7 +144,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) } catch (e: any) { console.error("RVO Import Fout:", e) - error = e.message + error = await extractErrorMessage(e) } } else if (!url.searchParams.has("start_import")) { return { @@ -493,7 +494,7 @@ export async function action({ request, params }: ActionFunctionArgs) { console.error("Error at saving RVO fields: ", e) return { success: false, - message: `Error at saving RVO fields: ${e.message}`, + message: `Error at saving RVO fields: ${await extractErrorMessage(e)}`, } } } From 99106a50dd5164072ca4fe4e6ffa0b5815c79468 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:34:26 +0100 Subject: [PATCH 063/108] fix: Reject invalid calendar params before starting the import --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 19 +++++++++---------- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 3 +++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 1078447e8..a4267c42b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -78,6 +78,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) { throw new Response("Farm ID is required", { status: 400 }) } const year = Number(yearString) + if (!Number.isInteger(year)) { + throw new Response("Ongeldig kalenderjaar", { status: 400 }) + } const session = await getSession(request) const url = new URL(request.url) @@ -414,8 +417,8 @@ export default function RvoImportReviewPage() { U staat op het punt de - volgende wijzigingen door - te voeren: + volgende wijzigingen door te + voeren:
@@ -425,8 +428,7 @@ export default function RvoImportReviewPage() { {changes.add}{" "} perceel {changes.add !== - 1 && - "en"}{" "} + 1 && "en"}{" "} toevoegen )} @@ -435,8 +437,7 @@ export default function RvoImportReviewPage() { {changes.remove}{" "} perceel {changes.remove !== - 1 && - "en"}{" "} + 1 && "en"}{" "} verwijderen )} @@ -445,8 +446,7 @@ export default function RvoImportReviewPage() { {changes.update}{" "} perceel {changes.update !== - 1 && - "en"}{" "} + 1 && "en"}{" "} bijwerken )} @@ -455,8 +455,7 @@ export default function RvoImportReviewPage() { {changes.close}{" "} perceel {changes.close !== - 1 && - "en"}{" "} + 1 && "en"}{" "} afsluiten )} diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 9ff0a0ef4..b0c714f4e 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -71,6 +71,9 @@ export async function loader({ request, params }: LoaderFunctionArgs) { }) } const year = Number(yearString) + if (!Number.isInteger(year)) { + throw new Response("Ongeldig kalenderjaar", { status: 400 }) + } const session = await getSession(request) const url = new URL(request.url) From 1acd9f38b38890ca67ebb005874d7c5b09abfe6b Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:36:20 +0100 Subject: [PATCH 064/108] fix: disabled RVO card is still keyboard-activatable. --- fdm-app/app/routes/farm.$b_id_farm._index.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fdm-app/app/routes/farm.$b_id_farm._index.tsx b/fdm-app/app/routes/farm.$b_id_farm._index.tsx index ad7676ced..b6a14b240 100644 --- a/fdm-app/app/routes/farm.$b_id_farm._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm._index.tsx @@ -457,6 +457,15 @@ export default function FarmDashboardIndex() { !loaderData.farmWritePermission && "pointer-events-none opacity-50", )} + aria-disabled={ + !loaderData.farmWritePermission || + undefined + } + tabIndex={ + !loaderData.farmWritePermission + ? -1 + : undefined + } > From f7963a6bc1452008bf9d756692d6f7d04c1ed586 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:47:28 +0100 Subject: [PATCH 065/108] fix: overly permissive CSP --- fdm-app/app/lib/cache.server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-app/app/lib/cache.server.ts b/fdm-app/app/lib/cache.server.ts index 956b15901..24fd867f8 100644 --- a/fdm-app/app/lib/cache.server.ts +++ b/fdm-app/app/lib/cache.server.ts @@ -143,7 +143,7 @@ export function addSecurityHeaders(headers: Headers): Headers { media-src 'self' https://*.posthog.com; object-src 'none'; base-uri 'self'; - form-action 'self' https:; + form-action 'self'; frame-ancestors 'none';` // Add report-uri only if it exists From 713dfcd3c53c90dd0164d6e21c9e11205e1fe589 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:57:23 +0100 Subject: [PATCH 066/108] fix: path checking --- fdm-rvo/src/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-rvo/src/auth.ts b/fdm-rvo/src/auth.ts index 22bf63bca..6f13834ef 100644 --- a/fdm-rvo/src/auth.ts +++ b/fdm-rvo/src/auth.ts @@ -26,7 +26,7 @@ export const createRvoClient = ( pkioPrivateKey.startsWith("/") || pkioPrivateKey.startsWith("./") || pkioPrivateKey.startsWith("../") || - /^[a-zA-Z]:\\/.test(pkioPrivateKey) + /^[a-zA-Z]:[/\\]/.test(pkioPrivateKey) ) { if (fs.existsSync(pkioPrivateKey)) { privateKey = fs.readFileSync(pkioPrivateKey, "utf8") From 3596d6aca515953ef41fc7624dcb3d899d03f066 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:58:14 +0100 Subject: [PATCH 067/108] fix: error logging --- fdm-app/app/integrations/rvo.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts index fdff62811..df49691ae 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.ts @@ -60,6 +60,7 @@ export async function verifyRvoState( } return decodedState } catch (e) { + if (e instanceof Response) throw e throw new Response("Ongeldig state formaat", { status: 400 }) } } From f0b567bca9a10b1a82164e6399fbae00dbcb70b9 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:59:28 +0100 Subject: [PATCH 068/108] refactor: switch to deep check --- fdm-app/app/components/blocks/rvo/import-review-table.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index 8a473d10e..b3b1d4a46 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -102,8 +102,8 @@ const DiffCell = ({ // CONFLICT if (status === "CONFLICT") { - // If values are effectively equal (loose check), show one - if (local === remote) { + // If values are effectively equal (deep check), show one + if (JSON.stringify(local) === JSON.stringify(remote)) { return ( {formatter(local)} From 8007dadd5e65e3fd62df9224b1ea73a27d3ec0ac Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:00:38 +0100 Subject: [PATCH 069/108] fix: sync version --- pnpm-lock.yaml | 49 ++++++++++++++++----------------------------- pnpm-workspace.yaml | 2 +- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c0d4b6a6..7d64acea1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,8 +22,8 @@ catalogs: specifier: ^25.5.0 version: 25.5.0 '@vitest/coverage-v8': - specifier: 4.0.18 - version: 4.0.18 + specifier: 4.1.0 + version: 4.1.0 better-auth: specifier: ^1.5.4 version: 1.5.5 @@ -368,7 +368,7 @@ importers: version: 25.5.0 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) postgres: specifier: ^3.4.8 version: 3.4.8 @@ -447,7 +447,7 @@ importers: version: 13.15.10 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) drizzle-kit: specifier: 'catalog:' version: 0.31.10 @@ -499,7 +499,7 @@ importers: version: 16.0.3(rollup@4.59.0) '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) rollup: specifier: 'catalog:' version: 4.59.0 @@ -618,7 +618,7 @@ importers: version: 25.5.0 '@vitest/coverage-v8': specifier: 'catalog:' - version: 4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) + version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) rollup: specifier: 'catalog:' version: 4.59.0 @@ -5644,11 +5644,11 @@ packages: maplibre-gl: optional: true - '@vitest/coverage-v8@4.0.18': - resolution: {integrity: sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==} + '@vitest/coverage-v8@4.1.0': + resolution: {integrity: sha512-nDWulKeik2bL2Va/Wl4x7DLuTKAXa906iRFooIRPR+huHkcvp9QDkPQ2RJdmjOFrqOqvNfoSQLF68deE3xC3CQ==} peerDependencies: - '@vitest/browser': 4.0.18 - vitest: 4.0.18 + '@vitest/browser': 4.1.0 + vitest: 4.1.0 peerDependenciesMeta: '@vitest/browser': optional: true @@ -5667,9 +5667,6 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - '@vitest/pretty-format@4.1.0': resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} @@ -5682,9 +5679,6 @@ packages: '@vitest/spy@4.1.0': resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} - '@vitest/utils@4.1.0': resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} @@ -5903,8 +5897,8 @@ packages: resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} - ast-v8-to-istanbul@0.3.12: - resolution: {integrity: sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==} + ast-v8-to-istanbul@1.0.0: + resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} @@ -18043,17 +18037,17 @@ snapshots: optionalDependencies: maplibre-gl: 5.20.2 - '@vitest/coverage-v8@4.0.18(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))': + '@vitest/coverage-v8@4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.18 - ast-v8-to-istanbul: 0.3.12 + '@vitest/utils': 4.1.0 + ast-v8-to-istanbul: 1.0.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 magicast: 0.5.2 obug: 2.1.1 - std-env: 3.10.0 + std-env: 4.0.0 tinyrainbow: 3.1.0 vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -18074,10 +18068,6 @@ snapshots: optionalDependencies: vite: 7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/pretty-format@4.0.18': - dependencies: - tinyrainbow: 3.1.0 - '@vitest/pretty-format@4.1.0': dependencies: tinyrainbow: 3.1.0 @@ -18096,11 +18086,6 @@ snapshots: '@vitest/spy@4.1.0': {} - '@vitest/utils@4.0.18': - dependencies: - '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.1.0 - '@vitest/utils@4.1.0': dependencies: '@vitest/pretty-format': 4.1.0 @@ -18352,7 +18337,7 @@ snapshots: assign-symbols@1.0.0: {} - ast-v8-to-istanbul@0.3.12: + ast-v8-to-istanbul@1.0.0: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6492da9f7..16fde8e72 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,7 +13,7 @@ catalog: '@rollup/plugin-node-resolve': ^16.0.3 '@rollup/plugin-typescript': ^12.3.0 '@types/node': ^25.5.0 - '@vitest/coverage-v8': 4.0.18 + '@vitest/coverage-v8': 4.1.0 better-auth: ^1.5.4 drizzle-kit: ^0.31.9 drizzle-orm: ^0.45.1 From 9df9dddad3d9d549e3ed0850e36690c1511ceab3 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:01:21 +0100 Subject: [PATCH 070/108] docs: explain matching --- fdm-rvo/src/data.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fdm-rvo/src/data.ts b/fdm-rvo/src/data.ts index d07fa7c26..c9722857f 100644 --- a/fdm-rvo/src/data.ts +++ b/fdm-rvo/src/data.ts @@ -36,16 +36,18 @@ export async function fetchRvoFields( farmId: kvkNumber, outputFormat: "geojson", }), - rvoClient.opvragenRegelingspercelenMest({ - periodBeginDate: `${year}-01-01`, - periodEndDate: `${year}-12-31`, - farmId: kvkNumber, - outputFormat: "geojson", - }).catch(err => { - // Catching in case this endpoint fails independently so we don't break the main flow. - console.warn("Failed to fetch RegelingspercelenMest:", err) - return { features: [] } - }) + rvoClient + .opvragenRegelingspercelenMest({ + periodBeginDate: `${year}-01-01`, + periodEndDate: `${year}-12-31`, + farmId: kvkNumber, + outputFormat: "geojson", + }) + .catch((err) => { + // Catching in case this endpoint fails independently so we don't break the main flow. + console.warn("Failed to fetch RegelingspercelenMest:", err) + return { features: [] } + }), ]) // The raw response is expected to be a GeoJSON FeatureCollection. @@ -88,6 +90,8 @@ export async function fetchRvoFields( ) if (nameMatches.length === 1) { + // Only match when exactly one MEST feature has the same name. + // Multiple matches indicate ambiguity; fall through to Tier 2 spatial matching. const candidate = nameMatches[0] const iou = calculateIoU( cropFeature.geometry, From f8c03ad446483022bdaab9a9cc548773c198fb90 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:03:00 +0100 Subject: [PATCH 071/108] fix: improve checks --- fdm-app/app/integrations/rvo.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts index df49691ae..dbce241cf 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.ts @@ -67,11 +67,12 @@ export async function verifyRvoState( export function getRvoCredentials(): RvoCredentials | undefined { // Check if RVO is configured + const { clientId, clientSecret } = serverConfig.integrations.rvo const rvoConfigured = - serverConfig.integrations.rvo.clientId !== "undefined" && - serverConfig.integrations.rvo.clientId !== "" && - serverConfig.integrations.rvo.clientSecret !== "undefined" && - serverConfig.integrations.rvo.clientSecret !== "" + !!clientId?.trim() && + clientId !== "undefined" && + !!clientSecret?.trim() && + clientSecret !== "undefined" if (!rvoConfigured) { return undefined } From 292592edf7aa7946d0341012e5c1f7883feecded Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:04:46 +0100 Subject: [PATCH 072/108] refactor: create shared function for rvoClient creation --- fdm-app/app/integrations/rvo.ts | 14 +++++++++++++ .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 20 +++---------------- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 20 +++---------------- 3 files changed, 20 insertions(+), 34 deletions(-) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts index dbce241cf..03ac65b81 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.ts @@ -1,6 +1,7 @@ import { serverConfig } from "../lib/config.server" import { createCookie } from "react-router" import { nanoid } from "nanoid" +import { createRvoClient } from "@nmi-agro/fdm-rvo" export const rvoStateCookie = createCookie("rvo_state", { path: "/", @@ -91,3 +92,16 @@ type RvoCredentials = { redirectUri: string clientName: string } + +/** + * Creates an RvoClient configured from the given credentials and the current NODE_ENV. + */ +export function createConfiguredRvoClient(credentials: RvoCredentials) { + return createRvoClient( + credentials.clientId, + credentials.clientName, + credentials.redirectUri, + credentials.clientSecret, + process.env.NODE_ENV === "production" ? "production" : "acceptance", + ) +} diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index a4267c42b..a1b1f312c 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -17,7 +17,6 @@ import { generateAuthUrl, fetchRvoFields, compareFields, - createRvoClient, exchangeToken, } from "~/lib/rvo.server" import { @@ -51,6 +50,7 @@ import { HeaderFarm } from "~/components/blocks/header/farm" import { SidebarInset } from "~/components/ui/sidebar" import { BreadcrumbItem, BreadcrumbSeparator } from "~/components/ui/breadcrumb" import { + createConfiguredRvoClient, createRvoState, getRvoCredentials, verifyRvoState, @@ -121,15 +121,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { }) } - const rvoClient = createRvoClient( - rvoCredentials.clientId, - rvoCredentials.clientName, - rvoCredentials.redirectUri, - rvoCredentials.clientSecret, - process.env.NODE_ENV === "production" - ? "production" - : "acceptance", - ) + const rvoClient = createConfiguredRvoClient(rvoCredentials) await exchangeToken(rvoClient, code) const rvoFields = await fetchRvoFields( @@ -542,13 +534,7 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Response("RVO client is not configured.", { status: 500 }) } - const rvoClient = createRvoClient( - rvoCredentials.clientId, - rvoCredentials.clientName, - rvoCredentials.redirectUri, - rvoCredentials.clientSecret, - process.env.NODE_ENV === "production" ? "production" : "acceptance", - ) + const rvoClient = createConfiguredRvoClient(rvoCredentials) const { state, cookieHeader } = await createRvoState( b_id_farm, diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index b0c714f4e..f5867f5e3 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -16,7 +16,6 @@ import { generateAuthUrl, fetchRvoFields, compareFields, - createRvoClient, exchangeToken, } from "~/lib/rvo.server" import type { @@ -44,6 +43,7 @@ import { } from "~/components/ui/breadcrumb" import { getRvoCredentials, + createConfiguredRvoClient, createRvoState, verifyRvoState, } from "../integrations/rvo" @@ -113,15 +113,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { ) } - const rvoClient = createRvoClient( - rvoCredentials.clientId, - rvoCredentials.clientName, - rvoCredentials.redirectUri, - rvoCredentials.clientSecret, - process.env.NODE_ENV === "production" - ? "production" - : "acceptance", - ) // Instantiate RvoClient + const rvoClient = createConfiguredRvoClient(rvoCredentials) await exchangeToken(rvoClient, code) const rvoFields = await fetchRvoFields( @@ -414,13 +406,7 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Response("RVO client is not available", { status: 500 }) } - const rvoClient = createRvoClient( - rvoCredentials.clientId, - rvoCredentials.clientName, - rvoCredentials.redirectUri, - rvoCredentials.clientSecret, - process.env.NODE_ENV === "production" ? "production" : "acceptance", - ) // Instantiate RvoClient + const rvoClient = createConfiguredRvoClient(rvoCredentials) const { state, cookieHeader } = await createRvoState( b_id_farm, From 093938aa4e136d14f864d8bb20db468c7fee71b8 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:05:54 +0100 Subject: [PATCH 073/108] refactor: use shared function to parseBufferstrips --- fdm-rvo/src/process.ts | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index 13d3aea99..64f1c440f 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -10,6 +10,12 @@ import { import type { RvoImportReviewItem, UserChoiceMap } from "./types" import { getItemId } from "./utils" +function parseBufferstrip(value: string | undefined): boolean | undefined { + if (value === "J") return true + if (value === "N") return false + return undefined +} + /** * Processes the RVO import review results by applying user-selected actions. * @@ -44,14 +50,9 @@ export async function processRvoImport( switch (action) { case "ADD_REMOTE": if (item.rvoField) { - const b_bufferstrip = - item.rvoField.properties.mestData?.IndBufferstrook === - "J" - ? true - : item.rvoField.properties.mestData?.IndBufferstrook === - "N" - ? false - : undefined + const b_bufferstrip = parseBufferstrip( + item.rvoField.properties.mestData?.IndBufferstrook, + ) const b_id = await addField( fdm, @@ -95,14 +96,9 @@ export async function processRvoImport( break case "UPDATE_FROM_REMOTE": if (item.localField && item.rvoField) { - const b_bufferstrip = - item.rvoField.properties.mestData?.IndBufferstrook === - "J" - ? true - : item.rvoField.properties.mestData?.IndBufferstrook === - "N" - ? false - : undefined + const b_bufferstrip = parseBufferstrip( + item.rvoField.properties.mestData?.IndBufferstrook, + ) await updateField( fdm, From d46f986df1cd558d37a01e2937959f529857ffd0 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:07:28 +0100 Subject: [PATCH 074/108] refactor: improve types --- .../blocks/rvo/import-review-table.tsx | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index b3b1d4a46..3a219ec53 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -40,6 +40,13 @@ import { acquiringMethodOptions } from "~/lib/constants" import { useMemo } from "react" import { clientConfig } from "@/app/lib/config" +declare module "@tanstack/react-table" { + interface TableMeta { + userChoices: UserChoiceMap + onChoiceChange: (id: string, action: ImportReviewAction) => void + } +} + interface RvoImportReviewTableProps { data: RvoImportReviewItem[] userChoices: UserChoiceMap @@ -295,8 +302,7 @@ export const columns: ColumnDef>[] = [ cell: ({ row, table }) => { const item = row.original const id = getItemId(item) - // @ts-ignore - const { userChoices } = table.options.meta as any + const { userChoices } = table.options.meta! const action = userChoices[id] as ImportReviewAction return ( @@ -325,8 +331,7 @@ export const columns: ColumnDef>[] = [ cell: ({ row, table }) => { const item = row.original const id = getItemId(item) - // @ts-ignore - const { userChoices } = table.options.meta as any + const { userChoices } = table.options.meta! const action = userChoices[id] as ImportReviewAction const localArea = item.localField @@ -359,8 +364,7 @@ export const columns: ColumnDef>[] = [ cell: ({ row, table }) => { const item = row.original const id = getItemId(item) - // @ts-ignore - const { userChoices } = table.options.meta as any + const { userChoices } = table.options.meta! const action = userChoices[id] as ImportReviewAction return ( @@ -387,8 +391,7 @@ export const columns: ColumnDef>[] = [ cell: ({ row, table }) => { const item = row.original const id = getItemId(item) - // @ts-ignore - const { userChoices } = table.options.meta as any + const { userChoices } = table.options.meta! const action = userChoices[id] as ImportReviewAction return ( @@ -414,8 +417,7 @@ export const columns: ColumnDef>[] = [ cell: ({ row, table }) => { const item = row.original const id = getItemId(item) - // @ts-ignore - const { userChoices } = table.options.meta as any + const { userChoices } = table.options.meta! const action = userChoices[id] as ImportReviewAction return ( @@ -450,8 +452,7 @@ export const columns: ColumnDef>[] = [ cell: ({ row, table }) => { const item = row.original const id = getItemId(item) - // @ts-ignore - const { userChoices } = table.options.meta as any + const { userChoices } = table.options.meta! const action = userChoices[id] as ImportReviewAction // Map RVO code to label using FDM dictionary @@ -493,8 +494,7 @@ export const columns: ColumnDef>[] = [ ), cell: ({ row, table }) => { const item = row.original - // @ts-ignore - const { userChoices } = table.options.meta as any + const { userChoices } = table.options.meta! const action = userChoices[getItemId(item)] as ImportReviewAction const rvoBufferstrip = @@ -537,8 +537,7 @@ export const columns: ColumnDef>[] = [ cell: ({ row, table }) => { const item = row.original const id = getItemId(item) - // @ts-ignore - meta is not strictly typed in React Table basic usage but useful here - const { userChoices, onChoiceChange } = table.options.meta as any + const { userChoices, onChoiceChange } = table.options.meta! const currentChoice = userChoices[id] as ImportReviewAction if (item.status === "MATCH") { From 13a9378e4cdccedc7d7264ec39b010a5262fec54 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:09:16 +0100 Subject: [PATCH 075/108] refactor: use fixed types --- fdm-rvo/src/process.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index 64f1c440f..57513cce2 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -5,17 +5,28 @@ import { addCultivation, removeCultivation, getDefaultDatesOfCultivation, + acquiringMethodOptions, type FdmType, } from "@nmi-agro/fdm-core" import type { RvoImportReviewItem, UserChoiceMap } from "./types" import { getItemId } from "./utils" +type AcquiringMethod = (typeof acquiringMethodOptions)[number]["value"] + function parseBufferstrip(value: string | undefined): boolean | undefined { if (value === "J") return true if (value === "N") return false return undefined } +function parseAcquiringMethod(useTitleCode: string | undefined): AcquiringMethod { + const candidate = `nl_${useTitleCode}` + if (acquiringMethodOptions.some((opt) => opt.value === candidate)) { + return candidate as AcquiringMethod + } + return "unknown" +} + /** * Processes the RVO import review results by applying user-selected actions. * @@ -63,7 +74,7 @@ export async function processRvoImport( item.rvoField.properties.CropFieldID, item.rvoField.geometry, new Date(item.rvoField.properties.BeginDate), - `nl_${item.rvoField.properties.UseTitleCode}` as any, + parseAcquiringMethod(item.rvoField.properties.UseTitleCode), item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, @@ -109,7 +120,7 @@ export async function processRvoImport( item.rvoField.properties.CropFieldID, item.rvoField.geometry, new Date(item.rvoField.properties.BeginDate), - `nl_${item.rvoField.properties.UseTitleCode}` as any, + parseAcquiringMethod(item.rvoField.properties.UseTitleCode), item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, From 4e317d3a8978a6d4d22b99e6ae7e849d2edef5f2 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:15:41 +0100 Subject: [PATCH 076/108] refactor: add null check --- fdm-app/app/components/blocks/rvo/import-review-table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index 3a219ec53..6ec922fe0 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -335,7 +335,7 @@ export const columns: ColumnDef>[] = [ const action = userChoices[id] as ImportReviewAction const localArea = item.localField - ? `${item.localField.b_area.toFixed(2)} ha` + ? `${(item.localField.b_area ?? 0).toFixed(2)} ha` : undefined const remoteArea = item.rvoField ? formatArea(item.rvoField.geometry) From 6e066cd249ab01c5e1f0bd55f648ae9852373451 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:17:22 +0100 Subject: [PATCH 077/108] refacoctor: remove unused import --- fdm-app/app/routes/farm.$b_id_farm._index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm._index.tsx b/fdm-app/app/routes/farm.$b_id_farm._index.tsx index b6a14b240..98eaeea61 100644 --- a/fdm-app/app/routes/farm.$b_id_farm._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm._index.tsx @@ -16,7 +16,6 @@ import { Landmark, Loader2, MapIcon, - MapPin, PlusIcon, ScrollText, Shapes, From 0cb6ca81be6d3f3aa706d340a6a532a5963b08a8 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:17:48 +0100 Subject: [PATCH 078/108] fix: parameter mismatch --- fdm-app/app/integrations/rvo.ts | 4 +++- fdm-app/app/lib/config.server.ts | 1 + fdm-app/app/types/config.d.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts index 03ac65b81..845ca4051 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.ts @@ -83,6 +83,7 @@ export function getRvoCredentials(): RvoCredentials | undefined { clientSecret: serverConfig.integrations.rvo.clientSecret, redirectUri: serverConfig.integrations.rvo.redirectUri, clientName: serverConfig.integrations.rvo.clientName, + pkioPrivateKey: serverConfig.integrations.rvo.pkioPrivateKey, } } @@ -91,6 +92,7 @@ type RvoCredentials = { clientSecret: string redirectUri: string clientName: string + pkioPrivateKey: string } /** @@ -101,7 +103,7 @@ export function createConfiguredRvoClient(credentials: RvoCredentials) { credentials.clientId, credentials.clientName, credentials.redirectUri, - credentials.clientSecret, + credentials.pkioPrivateKey, process.env.NODE_ENV === "production" ? "production" : "acceptance", ) } diff --git a/fdm-app/app/lib/config.server.ts b/fdm-app/app/lib/config.server.ts index 6bc2bd2ff..90f32da2c 100644 --- a/fdm-app/app/lib/config.server.ts +++ b/fdm-app/app/lib/config.server.ts @@ -44,6 +44,7 @@ export const serverConfig: ServerConfig = { clientSecret: String(process.env.RVO_CLIENT_SECRET), redirectUri: String(process.env.RVO_REDIRECT_URI), clientName: String(process.env.RVO_CLIENT_NAME), + pkioPrivateKey: String(process.env.RVO_PKIO_PRIVATE_KEY), }, }, diff --git a/fdm-app/app/types/config.d.ts b/fdm-app/app/types/config.d.ts index 9e1acf3c8..a22473feb 100644 --- a/fdm-app/app/types/config.d.ts +++ b/fdm-app/app/types/config.d.ts @@ -39,6 +39,7 @@ export interface ServerConfig { clientSecret: string redirectUri: string clientName: string + pkioPrivateKey: string } } analytics: { From 07659a0a447cbf4d700e63872f29e502656c8d1a Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:19:03 +0100 Subject: [PATCH 079/108] fix: Treat null/undefined as equal if both are missing --- fdm-rvo/src/compare.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 6739ff0df..ee0062579 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -302,7 +302,8 @@ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { ? new Date(rvo.properties.BeginDate).toISOString().split("T")[0] : null - if (localStart !== rvoStart) { + // Treat null/undefined as equal if both are missing + if (localStart !== rvoStart && (localStart !== null || rvoStart !== null)) { diffs.push("b_start") } From 5557e77d6cc068e56429727fa8b5316c010ac2e7 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:21:33 +0100 Subject: [PATCH 080/108] refactor: add fallback in case no start date --- fdm-rvo/src/compare.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index ee0062579..3f99cec1e 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -218,9 +218,11 @@ export function compareFields( ? local.b_start : local.b_start ? new Date(local.b_start) - : new Date(0) + : null const importYearStart = new Date(calendar, 0, 1) // Jan 1st of import year - const isStartedBeforeYear = localStart < importYearStart + // A missing start date means the field has no known start, so treat as not-yet-started → NEW_LOCAL + const isStartedBeforeYear = + localStart !== null && localStart < importYearStart const localEnd = local.b_end ? local.b_end instanceof Date From 2698c45ca3d52eb86c313a0554531b43118c9996 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:22:46 +0100 Subject: [PATCH 081/108] refactor: add year validation --- fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index f5867f5e3..c0ac5407b 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -393,6 +393,9 @@ export async function action({ request, params }: ActionFunctionArgs) { }) } const year = Number(yearString) + if (!Number.isInteger(year)) { + throw new Response("Ongeldig kalenderjaar", { status: 400 }) + } const session = await getSession(request) const formData = await request.formData() From 51d7ad5020f62b44a9e1c92a98e21dc43b5803d0 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:24:45 +0100 Subject: [PATCH 082/108] fix: initial value is unused --- fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index c0ac5407b..d40d28d59 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -198,7 +198,7 @@ export default function RvoImportCreatePage() { const initialChoices: UserChoiceMap = {} RvoImportReviewData.forEach((item) => { const id = getItemId(item) - let defaultAction: ImportReviewAction = "NO_ACTION" + let defaultAction: ImportReviewAction switch (item.status) { case "NEW_REMOTE": From 5bcbb145e48d1a701af5a0cbcbc378f9ad1fac69 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:25:08 +0100 Subject: [PATCH 083/108] tests: fix --- fdm-rvo/src/process.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fdm-rvo/src/process.test.ts b/fdm-rvo/src/process.test.ts index 24fb7ae08..e9857f144 100644 --- a/fdm-rvo/src/process.test.ts +++ b/fdm-rvo/src/process.test.ts @@ -18,6 +18,21 @@ vi.mock("@nmi-agro/fdm-core", () => ({ addCultivation: vi.fn(), removeCultivation: vi.fn(), getDefaultDatesOfCultivation: vi.fn(), + acquiringMethodOptions: [ + { value: "nl_01", label: "Eigendom" }, + { value: "nl_02", label: "Reguliere pacht" }, + { value: "nl_03", label: "In gebruik van een terreinbeherende organisatie" }, + { value: "nl_04", label: "Tijdelijk gebruik in het kader van landinrichting" }, + { value: "nl_07", label: "Overige exploitatievormen" }, + { value: "nl_09", label: "Erfpacht" }, + { value: "nl_10", label: "Pacht van geringe oppervlakten" }, + { value: "nl_11", label: "Natuurpacht" }, + { value: "nl_12", label: "Geliberaliseerde pacht, langer dan 6 jaar" }, + { value: "nl_13", label: "Geliberaliseerde pacht, 6 jaar of korter" }, + { value: "nl_61", label: "Reguliere pacht kortlopend" }, + { value: "nl_63", label: "Teeltpacht" }, + { value: "unknown", label: "Onbekend" }, + ], })) describe("processRvoImport", () => { From e5b74c2bcdf828348f2b885639906303e6fe4d45 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:42:09 +0100 Subject: [PATCH 084/108] refactor: improve tsconfig --- fdm-app/vite.config.ts | 1 + fdm-calculator/tsconfig.build.json | 3 +++ fdm-calculator/tsconfig.json | 6 ++---- fdm-core/tsconfig.build.json | 3 +++ fdm-core/tsconfig.json | 6 +++--- fdm-data/tsconfig.build.json | 3 +++ fdm-data/tsconfig.json | 6 +++--- fdm-rvo/tsconfig.build.json | 3 +++ fdm-rvo/tsconfig.json | 4 ++-- 9 files changed, 23 insertions(+), 12 deletions(-) diff --git a/fdm-app/vite.config.ts b/fdm-app/vite.config.ts index 3e86083ae..409ace246 100644 --- a/fdm-app/vite.config.ts +++ b/fdm-app/vite.config.ts @@ -85,6 +85,7 @@ export default defineConfig((env) => { "@nmi-agro/fdm-core", "@nmi-agro/fdm-data", "@nmi-agro/fdm-calculator", + "@nmi-agro/fdm-rvo", ], }, } diff --git a/fdm-calculator/tsconfig.build.json b/fdm-calculator/tsconfig.build.json index 094d1f14d..13228ffbe 100644 --- a/fdm-calculator/tsconfig.build.json +++ b/fdm-calculator/tsconfig.build.json @@ -1,6 +1,9 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "noEmit": false, + "rootDir": "./src", + "outDir": "./dist", "paths": {} }, "exclude": [ diff --git a/fdm-calculator/tsconfig.json b/fdm-calculator/tsconfig.json index 3865e1a1f..4190c9046 100644 --- a/fdm-calculator/tsconfig.json +++ b/fdm-calculator/tsconfig.json @@ -1,14 +1,12 @@ { "extends": "../tsconfig.packages.json", "compilerOptions": { - "noEmit": false, - "rootDir": "./src", - "outDir": "./dist", + "noEmit": true, "types": ["vitest/globals", "node"], "paths": { "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] } }, - "include": ["src", "src/**/*.test.ts"] + "include": ["src/**/*", "vitest.config.ts"] } diff --git a/fdm-core/tsconfig.build.json b/fdm-core/tsconfig.build.json index c0c91e037..baeacb53b 100644 --- a/fdm-core/tsconfig.build.json +++ b/fdm-core/tsconfig.build.json @@ -1,6 +1,9 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "noEmit": false, + "rootDir": "./src", + "outDir": "./dist", "paths": {} }, "exclude": [ diff --git a/fdm-core/tsconfig.json b/fdm-core/tsconfig.json index b1e8dfb25..e519ae130 100644 --- a/fdm-core/tsconfig.json +++ b/fdm-core/tsconfig.json @@ -1,11 +1,11 @@ { "extends": "../tsconfig.packages.json", "compilerOptions": { - "rootDir": "./src", - "outDir": "./dist", + "noEmit": true, + "types": ["vitest/globals", "node"], "paths": { "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] } }, - "include": ["./src/**/*"] + "include": ["src/**/*", "vitest.config.ts", "drizzle.config.ts"] } diff --git a/fdm-data/tsconfig.build.json b/fdm-data/tsconfig.build.json index 094d1f14d..13228ffbe 100644 --- a/fdm-data/tsconfig.build.json +++ b/fdm-data/tsconfig.build.json @@ -1,6 +1,9 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "noEmit": false, + "rootDir": "./src", + "outDir": "./dist", "paths": {} }, "exclude": [ diff --git a/fdm-data/tsconfig.json b/fdm-data/tsconfig.json index 79ed647aa..b5bb3c0f3 100644 --- a/fdm-data/tsconfig.json +++ b/fdm-data/tsconfig.json @@ -1,9 +1,9 @@ { "extends": "../tsconfig.packages.json", "compilerOptions": { - "rootDir": "./src", - "outDir": "./dist", + "noEmit": true, + "types": ["vitest/globals", "node"], "resolveJsonModule": true }, - "include": ["./src/**/*"] + "include": ["src/**/*", "vitest.config.ts"] } diff --git a/fdm-rvo/tsconfig.build.json b/fdm-rvo/tsconfig.build.json index 094d1f14d..13228ffbe 100644 --- a/fdm-rvo/tsconfig.build.json +++ b/fdm-rvo/tsconfig.build.json @@ -1,6 +1,9 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "noEmit": false, + "rootDir": "./src", + "outDir": "./dist", "paths": {} }, "exclude": [ diff --git a/fdm-rvo/tsconfig.json b/fdm-rvo/tsconfig.json index 63b0cd986..4190c9046 100644 --- a/fdm-rvo/tsconfig.json +++ b/fdm-rvo/tsconfig.json @@ -1,8 +1,8 @@ { "extends": "../tsconfig.packages.json", "compilerOptions": { - "rootDir": "./src", - "outDir": "./dist", + "noEmit": true, + "types": ["vitest/globals", "node"], "paths": { "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] From 7eb98c812511df46720de48375c92180b8445e7a Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:59:07 +0100 Subject: [PATCH 085/108] refactor: more improvements to tsconfig --- fdm-calculator/tsconfig.build.json | 3 ++- fdm-calculator/tsconfig.json | 1 - fdm-core/tsconfig.build.json | 4 +++- fdm-core/tsconfig.json | 1 - fdm-data/package.json | 1 + fdm-data/tsconfig.json | 1 - fdm-rvo/tsconfig.json | 1 - pnpm-lock.yaml | 5 ++++- 8 files changed, 10 insertions(+), 7 deletions(-) diff --git a/fdm-calculator/tsconfig.build.json b/fdm-calculator/tsconfig.build.json index 13228ffbe..0fa75f2dc 100644 --- a/fdm-calculator/tsconfig.build.json +++ b/fdm-calculator/tsconfig.build.json @@ -10,6 +10,7 @@ "**/*.test.ts", "**/*.spec.ts", "src/setup-test.ts", - "src/test-utils.ts" + "src/test-utils.ts", + "vitest.config.ts" ] } diff --git a/fdm-calculator/tsconfig.json b/fdm-calculator/tsconfig.json index 4190c9046..fd9966745 100644 --- a/fdm-calculator/tsconfig.json +++ b/fdm-calculator/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.packages.json", "compilerOptions": { "noEmit": true, - "types": ["vitest/globals", "node"], "paths": { "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] diff --git a/fdm-core/tsconfig.build.json b/fdm-core/tsconfig.build.json index baeacb53b..701b0128b 100644 --- a/fdm-core/tsconfig.build.json +++ b/fdm-core/tsconfig.build.json @@ -11,6 +11,8 @@ "**/*.spec.ts", "src/setup-test.ts", "src/test-utils.ts", - "src/global-setup.ts" + "src/global-setup.ts", + "vitest.config.ts", + "drizzle.config.ts" ] } diff --git a/fdm-core/tsconfig.json b/fdm-core/tsconfig.json index e519ae130..447a77832 100644 --- a/fdm-core/tsconfig.json +++ b/fdm-core/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.packages.json", "compilerOptions": { "noEmit": true, - "types": ["vitest/globals", "node"], "paths": { "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] } diff --git a/fdm-data/package.json b/fdm-data/package.json index 4243b1611..75eb1e32a 100644 --- a/fdm-data/package.json +++ b/fdm-data/package.json @@ -47,6 +47,7 @@ "@rollup/plugin-commonjs": "catalog:", "@rollup/plugin-json": "catalog:", "@rollup/plugin-node-resolve": "catalog:", + "@types/node": "catalog:", "@vitest/coverage-v8": "catalog:", "rollup": "catalog:", "rollup-plugin-esbuild": "catalog:", diff --git a/fdm-data/tsconfig.json b/fdm-data/tsconfig.json index b5bb3c0f3..b1590ed52 100644 --- a/fdm-data/tsconfig.json +++ b/fdm-data/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.packages.json", "compilerOptions": { "noEmit": true, - "types": ["vitest/globals", "node"], "resolveJsonModule": true }, "include": ["src/**/*", "vitest.config.ts"] diff --git a/fdm-rvo/tsconfig.json b/fdm-rvo/tsconfig.json index 4190c9046..fd9966745 100644 --- a/fdm-rvo/tsconfig.json +++ b/fdm-rvo/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../tsconfig.packages.json", "compilerOptions": { "noEmit": true, - "types": ["vitest/globals", "node"], "paths": { "@nmi-agro/fdm-core": ["../fdm-core/src/index.ts"], "@nmi-agro/fdm-data": ["../fdm-data/src/index.ts"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d64acea1..ebebe66ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -497,6 +497,9 @@ importers: '@rollup/plugin-node-resolve': specifier: 'catalog:' version: 16.0.3(rollup@4.59.0) + '@types/node': + specifier: 'catalog:' + version: 25.5.0 '@vitest/coverage-v8': specifier: 'catalog:' version: 4.1.0(vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) @@ -17960,7 +17963,7 @@ snapshots: '@types/sax@1.2.7': dependencies: - '@types/node': 17.0.45 + '@types/node': 25.5.0 '@types/send@0.17.6': dependencies: From d53a9ac4d03e83f8f8d55a6f05dacf0d25b214a0 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:48:11 +0100 Subject: [PATCH 086/108] refactor: implement review feedback --- .../blocks/rvo/import-review-table.tsx | 23 +++++++++++------- fdm-app/app/integrations/rvo.ts | 23 ++++++++++-------- fdm-rvo/src/compare.test.ts | 2 ++ fdm-rvo/src/compare.ts | 24 +++++++++++++++---- 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/fdm-app/app/components/blocks/rvo/import-review-table.tsx b/fdm-app/app/components/blocks/rvo/import-review-table.tsx index 6ec922fe0..c296b5fbf 100644 --- a/fdm-app/app/components/blocks/rvo/import-review-table.tsx +++ b/fdm-app/app/components/blocks/rvo/import-review-table.tsx @@ -34,7 +34,7 @@ import { TooltipTrigger, } from "~/components/ui/tooltip" import { area } from "@turf/turf" -import { format } from "date-fns" +import { format, parseISO } from "date-fns" import { cn } from "~/lib/utils" import { acquiringMethodOptions } from "~/lib/constants" import { useMemo } from "react" @@ -56,7 +56,9 @@ interface RvoImportReviewTableProps { function formatDate(dateString?: string | Date) { if (!dateString) return "-" try { - return format(new Date(dateString), "dd-MM-yyyy") + const date = + typeof dateString === "string" ? parseISO(dateString) : dateString + return format(date, "dd-MM-yyyy") } catch { return dateString.toString() } @@ -634,16 +636,21 @@ export function RvoImportReviewTable({ }: RvoImportReviewTableProps) { const sortedData = useMemo(() => { return [...data].sort((a, b) => { - const getArea = (item: typeof a) => { + const getArea = (item: typeof a): number | null => { if (item.rvoField?.geometry) { - return area(item.rvoField.geometry) / 10000 // Convert m2 to ha + return area(item.rvoField.geometry) / 10000 } - if (item.localField) { - return item.localField.b_area + if (Number.isFinite(item.localField?.b_area)) { + return item.localField.b_area as number } - return 0 + return null } - return getArea(b) - getArea(a) + const areaA = getArea(a) + const areaB = getArea(b) + if (areaA === null && areaB === null) return 0 + if (areaA === null) return 1 + if (areaB === null) return -1 + return areaB - areaA }) }, [data]) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts index 845ca4051..1625b7d4e 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.ts @@ -68,22 +68,25 @@ export async function verifyRvoState( export function getRvoCredentials(): RvoCredentials | undefined { // Check if RVO is configured - const { clientId, clientSecret } = serverConfig.integrations.rvo + const { clientId, clientSecret, redirectUri, clientName, pkioPrivateKey } = + serverConfig.integrations.rvo + const isValid = (v: string) => !!v?.trim() && v !== "undefined" const rvoConfigured = - !!clientId?.trim() && - clientId !== "undefined" && - !!clientSecret?.trim() && - clientSecret !== "undefined" + isValid(clientId) && + isValid(clientSecret) && + isValid(redirectUri) && + isValid(clientName) && + isValid(pkioPrivateKey) if (!rvoConfigured) { return undefined } return { - clientId: serverConfig.integrations.rvo.clientId, - clientSecret: serverConfig.integrations.rvo.clientSecret, - redirectUri: serverConfig.integrations.rvo.redirectUri, - clientName: serverConfig.integrations.rvo.clientName, - pkioPrivateKey: serverConfig.integrations.rvo.pkioPrivateKey, + clientId, + clientSecret, + redirectUri, + clientName, + pkioPrivateKey, } } diff --git a/fdm-rvo/src/compare.test.ts b/fdm-rvo/src/compare.test.ts index e9ee3e18a..d4f902859 100644 --- a/fdm-rvo/src/compare.test.ts +++ b/fdm-rvo/src/compare.test.ts @@ -24,6 +24,7 @@ describe("compareFields", () => { }, b_start: new Date("2024-01-01"), b_end: undefined, + b_acquiring_method: "nl_01", cultivations: [], ...overrides, }) @@ -277,6 +278,7 @@ describe("compareFields Edge Cases", () => { }, b_start: new Date("2024-01-01"), b_end: undefined, + b_acquiring_method: "nl_01", cultivations: [], ...overrides, }) diff --git a/fdm-rvo/src/compare.ts b/fdm-rvo/src/compare.ts index 3f99cec1e..924f03cda 100644 --- a/fdm-rvo/src/compare.ts +++ b/fdm-rvo/src/compare.ts @@ -153,9 +153,10 @@ export function compareFields( let bestMatch: (Field & { cultivations?: Cultivation[] }) | null = null let bestIoU = 0 - // Optimization: Fast BBox overlap check before accurate IoU - const candidates = remainingLocals.filter((l) => - bboxOverlap(l.bbox, rvoBbox), + // Optimization: Fast BBox overlap check before accurate IoU; exclude already-matched locals + const candidates = remainingLocals.filter( + (l) => + !matchedLocalIds.has(l.field.b_id) && bboxOverlap(l.bbox, rvoBbox), ) // Find the best spatial match among candidates @@ -313,7 +314,9 @@ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { const localEnd = local.b_end instanceof Date ? local.b_end.toISOString().split("T")[0] - : null + : typeof local.b_end === "string" + ? local.b_end + : null const rvoEnd = rvo.properties.EndDate ? new Date(rvo.properties.EndDate).toISOString().split("T")[0] : null @@ -323,7 +326,18 @@ function detectDiffs(local: Field, rvo: RvoField): FieldDiff[] { diffs.push("b_end") } - // 5. Buffer strip + // 5. Acquiring method + const rvoAcquiringMethod = rvo.properties.UseTitleCode + ? `nl_${rvo.properties.UseTitleCode}` + : null + if ( + local.b_acquiring_method !== rvoAcquiringMethod && + (local.b_acquiring_method !== null || rvoAcquiringMethod !== null) + ) { + diffs.push("b_acquiring_method") + } + + // 6. Buffer strip // Only check when RVO MEST data is available (mestData present means MEST was fetched) if (rvo.properties.mestData?.IndBufferstrook !== undefined) { const rvoBufferstrip = rvo.properties.mestData.IndBufferstrook === "J" From b37e24fcb0d14c4f0a2cdd97abea785491dd8446 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Fri, 20 Mar 2026 11:02:12 +0100 Subject: [PATCH 087/108] fixes --- fdm-app/app/integrations/rvo.ts | 15 ++- fdm-rvo/src/compare.test.ts | 163 +++++++++++++------------------- 2 files changed, 80 insertions(+), 98 deletions(-) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.ts index 1625b7d4e..9bcc097b9 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.ts @@ -2,6 +2,14 @@ import { serverConfig } from "../lib/config.server" import { createCookie } from "react-router" import { nanoid } from "nanoid" import { createRvoClient } from "@nmi-agro/fdm-rvo" +import { isOfOrigin } from "../lib/url-utils" + +const sessionSecret = serverConfig.auth.fdm_session_secret +if (!sessionSecret?.trim() || sessionSecret === "undefined") { + throw new Error( + "FDM_SESSION_SECRET is missing or invalid. Cannot initialize RVO state cookie.", + ) +} export const rvoStateCookie = createCookie("rvo_state", { path: "/", @@ -9,7 +17,7 @@ export const rvoStateCookie = createCookie("rvo_state", { sameSite: "lax", secure: process.env.NODE_ENV === "production", maxAge: 3600, // 1 hour - secrets: [serverConfig.auth.fdm_session_secret], + secrets: [sessionSecret], }) /** @@ -17,11 +25,14 @@ export const rvoStateCookie = createCookie("rvo_state", { * @returns { state, cookieHeader } The base64 state string and the serialized cookie header. */ export async function createRvoState(farmId: string, returnUrl: string) { + const appOrigin = new URL(serverConfig.url).origin + const safeReturnUrl = isOfOrigin(returnUrl, appOrigin) ? returnUrl : "/" + const nonce = nanoid() const state = Buffer.from( JSON.stringify({ farmId, - returnUrl, + returnUrl: safeReturnUrl, nonce, }), ).toString("base64") diff --git a/fdm-rvo/src/compare.test.ts b/fdm-rvo/src/compare.test.ts index d4f902859..6fa7973ec 100644 --- a/fdm-rvo/src/compare.test.ts +++ b/fdm-rvo/src/compare.test.ts @@ -2,15 +2,35 @@ import { describe, it, expect } from "vitest" import { compareFields } from "./compare" import { RvoImportReviewStatus, type RvoField } from "./types" -describe("compareFields", () => { - const calendar = 2025 +// Shared helpers used across all describe blocks +const createLocalField = (overrides: Partial = {}): any => ({ + b_id: "local-1", + b_id_source: "rvo-1", + b_name: "Field 1", + b_geometry: { + type: "Polygon", + coordinates: [ + [ + [0, 0], + [0, 10], + [10, 10], + [10, 0], + [0, 0], + ], + ], + }, + b_start: new Date("2024-01-01"), + b_end: undefined, + b_acquiring_method: "nl_01", + cultivations: [], + ...overrides, +}) - // Helper to create a basic local field - const createLocalField = (overrides: Partial = {}): any => ({ - b_id: "local-1", - b_id_source: "rvo-1", - b_name: "Field 1", - b_geometry: { +const createRvoField = (overrides: any = {}): RvoField => { + const { geometry, ...props } = overrides + return { + type: "Feature", + geometry: geometry || { type: "Polygon", coordinates: [ [ @@ -22,43 +42,22 @@ describe("compareFields", () => { ], ], }, - b_start: new Date("2024-01-01"), - b_end: undefined, - b_acquiring_method: "nl_01", - cultivations: [], - ...overrides, - }) - - // Helper to create a basic RVO field - const createRvoField = (overrides: any = {}): RvoField => { - const { geometry, ...props } = overrides - return { - type: "Feature", - geometry: geometry || { - type: "Polygon", - coordinates: [ - [ - [0, 0], - [0, 10], - [10, 10], - [10, 0], - [0, 0], - ], - ], - }, - properties: { - CropFieldID: "rvo-1", - CropFieldVersion: "1", - CropFieldDesignator: "Field 1", - BeginDate: "2024-01-01", - EndDate: undefined, - Country: "NL", - CropTypeCode: "101", - UseTitleCode: "01", - ...props, - }, - } + properties: { + CropFieldID: "rvo-1", + CropFieldVersion: "1", + CropFieldDesignator: "Field 1", + BeginDate: "2024-01-01", + EndDate: undefined, + Country: "NL", + CropTypeCode: "101", + UseTitleCode: "01", + ...props, + }, } +} + +describe("compareFields", () => { + const calendar = 2025 describe("Tier 1: ID Match", () => { it("should MATCH fields with same ID and identical properties", () => { @@ -153,6 +152,30 @@ describe("compareFields", () => { expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) expect(result[0].diffs).toContain("b_lu_catalogue") }) + + it("should detect CONFLICT when acquiring method differs", () => { + const local = createLocalField({ b_acquiring_method: "nl_02" }) + const rvo = createRvoField({ UseTitleCode: "01" }) // nl_01 ≠ nl_02 + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) + expect(result[0].diffs).toContain("b_acquiring_method") + }) + + it("should detect CONFLICT when buffer strip status differs", () => { + const local = createLocalField({ b_bufferstrip: false }) + const rvo = createRvoField({ + mestData: { IndBufferstrook: "J" }, // true ≠ false + }) + + const result = compareFields([local], [rvo], calendar) + + expect(result).toHaveLength(1) + expect(result[0].status).toBe(RvoImportReviewStatus.CONFLICT) + expect(result[0].diffs).toContain("b_bufferstrip") + }) }) describe("Tier 2: Spatial Match", () => { @@ -260,58 +283,6 @@ describe("compareFields", () => { describe("compareFields Edge Cases", () => { const calendar = 2025 - const createLocalField = (overrides: Partial = {}): any => ({ - b_id: "local-1", - b_id_source: "rvo-1", - b_name: "Field 1", - b_geometry: { - type: "Polygon", - coordinates: [ - [ - [0, 0], - [0, 10], - [10, 10], - [10, 0], - [0, 0], - ], - ], - }, - b_start: new Date("2024-01-01"), - b_end: undefined, - b_acquiring_method: "nl_01", - cultivations: [], - ...overrides, - }) - - const createRvoField = (overrides: any = {}): RvoField => { - const { geometry, ...props } = overrides - return { - type: "Feature", - geometry: geometry || { - type: "Polygon", - coordinates: [ - [ - [0, 0], - [0, 10], - [10, 10], - [10, 0], - [0, 0], - ], - ], - }, - properties: { - CropFieldID: "rvo-1", - CropFieldVersion: "1", - CropFieldDesignator: "Field 1", - BeginDate: "2024-01-01", - Country: "NL", - CropTypeCode: "101", - UseTitleCode: "01", - ...props, - }, - } - } - it("should handle IoU calculation when polygons touch (area 0)", () => { // Two squares touching at x=10. // S1: 0,0 to 10,10 From 98207cf4ba73d24a51967ade53c9c757a7b9263b Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:02:08 +0100 Subject: [PATCH 088/108] refactor: remove unused parameter --- fdm-app/app/routes/farm.$b_id_farm.$calendar.field.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.tsx index da13b9820..bbdea4061 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.field.tsx @@ -26,7 +26,7 @@ import { BreadcrumbItem, BreadcrumbSeparator } from "~/components/ui/breadcrumb" import { Button } from "~/components/ui/button" import { SidebarInset } from "~/components/ui/sidebar" import { getSession } from "~/lib/auth.server" -import { getCalendar, getTimeframe } from "~/lib/calendar" +import { getTimeframe } from "~/lib/calendar" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" @@ -103,7 +103,6 @@ export async function loader({ request, params }: LoaderFunctionArgs) { // Get timeframe from calendar store const timeframe = getTimeframe(params) - const calendar = getCalendar(params) // Get a list of possible farms of the user const farms = await getFarms(fdm, session.principal_id) @@ -221,7 +220,6 @@ export async function loader({ request, params }: LoaderFunctionArgs) { return { shouldShowLayout: true, b_id_farm: b_id_farm, - calendar: calendar, farmOptions: farmOptions, fieldOptions: fieldOptions, fieldsExtended: fieldsExtended, From 25eb4093be65e90df93285422d3b40df3ae57dda Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:04:42 +0100 Subject: [PATCH 089/108] refactor: use consistent response --- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index a1b1f312c..75832cd02 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -9,6 +9,7 @@ import { useNavigation, useParams, useLocation, + data, } from "react-router" import { getSession } from "~/lib/auth.server" import { extractErrorMessage } from "~/lib/error" @@ -518,7 +519,10 @@ export default function RvoImportReviewPage() { export async function action({ request, params }: ActionFunctionArgs) { const { b_id_farm, calendar: yearString } = params if (!b_id_farm || !yearString) { - throw new Response("Farm ID is required", { status: 400 }) + throw data("Farm ID is required", { + status: 400, + statusText: "Farm ID is required", + }) } const year = Number(yearString) From 485193b76db418fb14ba8ef533f1745ba63107e1 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:38:18 +0100 Subject: [PATCH 090/108] fix: typo --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 75832cd02..b301e52cf 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -419,36 +419,38 @@ export default function RvoImportReviewPage() { {changes.add > 0 && (
  • {changes.add}{" "} - perceel - {changes.add !== - 1 && "en"}{" "} + {changes.add === 1 + ? "perceel" + : "percelen"}{" "} toevoegen
  • )} {changes.remove > 0 && (
  • {changes.remove}{" "} - perceel - {changes.remove !== - 1 && "en"}{" "} + {changes.remove === + 1 + ? "perceel" + : "percelen"}{" "} verwijderen
  • )} {changes.update > 0 && (
  • {changes.update}{" "} - perceel - {changes.update !== - 1 && "en"}{" "} + {changes.update === + 1 + ? "perceel" + : "percelen"}{" "} bijwerken
  • )} {changes.close > 0 && (
  • {changes.close}{" "} - perceel - {changes.close !== - 1 && "en"}{" "} + {changes.close === 1 + ? "perceel" + : "percelen"}{" "} afsluiten
  • )} From dbfce9aa394aa1dde2a8a62166a095c43c8285a4 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:55:58 +0100 Subject: [PATCH 091/108] refactor: use consistent imports --- fdm-app/app/integrations/{rvo.ts => rvo.server.ts} | 7 +++++-- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 4 ++-- fdm-app/app/routes/farm.$b_id_farm._index.tsx | 2 +- .../app/routes/farm.create.$b_id_farm.$calendar._index.tsx | 2 +- .../app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) rename fdm-app/app/integrations/{rvo.ts => rvo.server.ts} (91%) diff --git a/fdm-app/app/integrations/rvo.ts b/fdm-app/app/integrations/rvo.server.ts similarity index 91% rename from fdm-app/app/integrations/rvo.ts rename to fdm-app/app/integrations/rvo.server.ts index 9bcc097b9..19fc2b109 100644 --- a/fdm-app/app/integrations/rvo.ts +++ b/fdm-app/app/integrations/rvo.server.ts @@ -1,8 +1,8 @@ import { serverConfig } from "../lib/config.server" import { createCookie } from "react-router" import { nanoid } from "nanoid" -import { createRvoClient } from "@nmi-agro/fdm-rvo" -import { isOfOrigin } from "../lib/url-utils" +import { createRvoClient } from "~/lib/rvo.server" +import { isOfOrigin } from "~/lib/url-utils" const sessionSecret = serverConfig.auth.fdm_session_secret if (!sessionSecret?.trim() || sessionSecret === "undefined") { @@ -14,6 +14,9 @@ if (!sessionSecret?.trim() || sessionSecret === "undefined") { export const rvoStateCookie = createCookie("rvo_state", { path: "/", httpOnly: true, + // "lax" is required (not "strict"): the OAuth callback is a cross-site + // top-level redirect from RVO back to our app, and "strict" would suppress + // the cookie, breaking CSRF verification for every user. sameSite: "lax", secure: process.env.NODE_ENV === "production", maxAge: 3600, // 1 hour diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index b301e52cf..d93a10a1a 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -19,6 +19,7 @@ import { fetchRvoFields, compareFields, exchangeToken, + processRvoImport, } from "~/lib/rvo.server" import { type RvoImportReviewItem, @@ -27,7 +28,6 @@ import { type ImportReviewAction, } from "@nmi-agro/fdm-rvo/types" import { getItemId } from "@nmi-agro/fdm-rvo/utils" -import { processRvoImport } from "@nmi-agro/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" import { getFields, getFarm, getFarms } from "@nmi-agro/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" @@ -55,7 +55,7 @@ import { createRvoState, getRvoCredentials, verifyRvoState, -} from "~/integrations/rvo" +} from "~/integrations/rvo.server" import { RvoErrorAlert } from "~/components/blocks/rvo/error-alert" import { getNmiApiKey, diff --git a/fdm-app/app/routes/farm.$b_id_farm._index.tsx b/fdm-app/app/routes/farm.$b_id_farm._index.tsx index 98eaeea61..6535ace0e 100644 --- a/fdm-app/app/routes/farm.$b_id_farm._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm._index.tsx @@ -61,7 +61,7 @@ import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" import { fdm } from "~/lib/fdm.server" import { useCalendarStore } from "~/store/calendar" -import { getRvoCredentials } from "../integrations/rvo" +import { getRvoCredentials } from "~/integrations/rvo.server" import { cn } from "~/lib/utils" // Meta diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx index 552663699..ccede4fb5 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx @@ -22,7 +22,7 @@ import { SidebarInset } from "~/components/ui/sidebar" import { clientConfig } from "~/lib/config" import { getSession } from "../lib/auth.server" import { fdm } from "~/lib/fdm.server" -import { getRvoCredentials } from "~/integrations/rvo" +import { getRvoCredentials } from "~/integrations/rvo.server" import { cn } from "~/lib/utils" // Meta diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index d40d28d59..57b85b166 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -17,6 +17,7 @@ import { fetchRvoFields, compareFields, exchangeToken, + processRvoImport, } from "~/lib/rvo.server" import type { RvoImportReviewItem, @@ -24,7 +25,6 @@ import type { UserChoiceMap, } from "@nmi-agro/fdm-rvo/types" import { getItemId } from "@nmi-agro/fdm-rvo/utils" -import { processRvoImport } from "@nmi-agro/fdm-rvo" import { RvoImportReviewTable } from "~/components/blocks/rvo/import-review-table" import { type Cultivation, type Field, getFarm } from "@nmi-agro/fdm-core" import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert" @@ -46,7 +46,7 @@ import { createConfiguredRvoClient, createRvoState, verifyRvoState, -} from "../integrations/rvo" +} from "~/integrations/rvo.server" import { RvoErrorAlert } from "~/components/blocks/rvo/error-alert" import { getNmiApiKey, From d22dc688712dffa005e827c17920affc91474283 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:02:30 +0100 Subject: [PATCH 092/108] refactor: use transactions to process RVO fields --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 5 +- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 5 +- fdm-rvo/src/process.test.ts | 5 +- fdm-rvo/src/process.ts | 226 +++++++++--------- 4 files changed, 124 insertions(+), 117 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index d93a10a1a..760348c2b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -65,6 +65,7 @@ import { addSoilAnalysis, getCultivations, getCultivationsFromCatalogue, + type FdmType, } from "@nmi-agro/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" import { clientConfig } from "~/lib/config" @@ -581,7 +582,7 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Error("Invalid review data format") } - const onFieldAdded = async (b_id: string, geometry: any) => { + const onFieldAdded = async (tx: FdmType, b_id: string, geometry: any) => { const nmiApiKey = getNmiApiKey() if (nmiApiKey) { try { @@ -590,7 +591,7 @@ export async function action({ request, params }: ActionFunctionArgs) { nmiApiKey, ) await addSoilAnalysis( - fdm, + tx, session.principal_id, undefined, "nl-other-nmi", diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 57b85b166..535c034d9 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -55,6 +55,7 @@ import { import { addSoilAnalysis, getCultivationsFromCatalogue, + type FdmType, } from "@nmi-agro/fdm-core" import { RvoConnectCard } from "~/components/blocks/rvo/connect-card" @@ -444,7 +445,7 @@ export async function action({ request, params }: ActionFunctionArgs) { RvoImportReviewData = JSON.parse(String(RvoImportReviewDataJson)) userChoices = JSON.parse(String(userChoicesJson)) - const onFieldAdded = async (b_id: string, geometry: any) => { + const onFieldAdded = async (tx: FdmType, b_id: string, geometry: any) => { const nmiApiKey = getNmiApiKey() if (nmiApiKey) { try { @@ -453,7 +454,7 @@ export async function action({ request, params }: ActionFunctionArgs) { nmiApiKey, ) await addSoilAnalysis( - fdm, + tx, session.principal_id, undefined, "nl-other-nmi", diff --git a/fdm-rvo/src/process.test.ts b/fdm-rvo/src/process.test.ts index e9857f144..a25866c6e 100644 --- a/fdm-rvo/src/process.test.ts +++ b/fdm-rvo/src/process.test.ts @@ -36,7 +36,9 @@ vi.mock("@nmi-agro/fdm-core", () => ({ })) describe("processRvoImport", () => { - const mockFdm = {} as any + const mockFdm = { + transaction: vi.fn(async (cb: (tx: any) => Promise) => cb(mockFdm)), + } as any const principalId = "user-1" const farmId = "farm-1" const year = 2025 @@ -96,6 +98,7 @@ describe("processRvoImport", () => { ) expect(addCultivation).toHaveBeenCalled() expect(onFieldAdded).toHaveBeenCalledWith( + mockFdm, "new-field-id", expect.anything(), ) diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index 57513cce2..49c0a71c2 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -48,7 +48,7 @@ export async function processRvoImport( rvoImportReviewData: RvoImportReviewItem[], userChoices: UserChoiceMap, year: number, - onFieldAdded?: (b_id: string, geometry: any) => Promise, + onFieldAdded?: (tx: FdmType, b_id: string, geometry: any) => Promise, ) { for (const item of rvoImportReviewData) { const id = getItemId(item) @@ -58,92 +58,34 @@ export async function processRvoImport( continue } - switch (action) { - case "ADD_REMOTE": - if (item.rvoField) { - const b_bufferstrip = parseBufferstrip( - item.rvoField.properties.mestData?.IndBufferstrook, - ) - - const b_id = await addField( - fdm, - principal_id, - b_id_farm, - item.rvoField.properties.CropFieldDesignator || - `RVO Perceel ${item.rvoField.properties.CropFieldID}`, - item.rvoField.properties.CropFieldID, - item.rvoField.geometry, - new Date(item.rvoField.properties.BeginDate), - parseAcquiringMethod(item.rvoField.properties.UseTitleCode), - item.rvoField.properties.EndDate - ? new Date(item.rvoField.properties.EndDate) - : undefined, - b_bufferstrip, - ) - - // Add cultivation from RVO - const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` - const defaultDates = await getDefaultDatesOfCultivation( - fdm, - principal_id, - b_id_farm, - b_lu_catalogue, - year, - ) - - await addCultivation( - fdm, - principal_id, - b_lu_catalogue, - b_id, - defaultDates.b_lu_start, - defaultDates.b_lu_end, - ) - - if (onFieldAdded) { - await onFieldAdded(b_id, item.rvoField.geometry) - } - } - break - case "UPDATE_FROM_REMOTE": - if (item.localField && item.rvoField) { - const b_bufferstrip = parseBufferstrip( - item.rvoField.properties.mestData?.IndBufferstrook, - ) - - await updateField( - fdm, - principal_id, - item.localField.b_id, - item.rvoField.properties.CropFieldDesignator || - item.localField.b_name, - item.rvoField.properties.CropFieldID, - item.rvoField.geometry, - new Date(item.rvoField.properties.BeginDate), - parseAcquiringMethod(item.rvoField.properties.UseTitleCode), - item.rvoField.properties.EndDate - ? new Date(item.rvoField.properties.EndDate) - : undefined, - b_bufferstrip, - ) + await fdm.transaction(async (tx) => { + switch (action) { + case "ADD_REMOTE": + if (item.rvoField) { + const b_bufferstrip = parseBufferstrip( + item.rvoField.properties.mestData?.IndBufferstrook, + ) - // Update cultivation if different - if ( - item.localCultivation && - item.localCultivation.b_lu_catalogue !== - `nl_${item.rvoField.properties.CropTypeCode}` - ) { - // Remove old cultivation - await removeCultivation( - fdm, + const b_id = await addField( + tx, principal_id, - item.localCultivation.b_lu, + b_id_farm, + item.rvoField.properties.CropFieldDesignator || + `RVO Perceel ${item.rvoField.properties.CropFieldID}`, + item.rvoField.properties.CropFieldID, + item.rvoField.geometry, + new Date(item.rvoField.properties.BeginDate), + parseAcquiringMethod(item.rvoField.properties.UseTitleCode), + item.rvoField.properties.EndDate + ? new Date(item.rvoField.properties.EndDate) + : undefined, + b_bufferstrip, ) - // Add new RVO cultivation + // Add cultivation from RVO const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` const defaultDates = await getDefaultDatesOfCultivation( - fdm, + tx, principal_id, b_id_farm, b_lu_catalogue, @@ -151,42 +93,102 @@ export async function processRvoImport( ) await addCultivation( - fdm, + tx, principal_id, b_lu_catalogue, - item.localField.b_id, + b_id, defaultDates.b_lu_start, defaultDates.b_lu_end, ) + + if (onFieldAdded) { + await onFieldAdded(tx, b_id, item.rvoField.geometry) + } } - } - break - case "KEEP_LOCAL": // Keep Local for Conflict - break - case "REMOVE_LOCAL": - if (item.localField) { - await removeField(fdm, principal_id, item.localField.b_id) - } - break - case "CLOSE_LOCAL": - if (item.localField) { - // Close the field on Dec 31st of the previous year - const closeDate = new Date(year - 1, 11, 31) - await updateField( - fdm, - principal_id, - item.localField.b_id, - item.localField.b_name, - item.localField.b_id_source, - item.localField.b_geometry, - item.localField.b_start instanceof Date - ? item.localField.b_start - : new Date(item.localField.b_start), - item.localField.b_acquiring_method, - closeDate, - ) - } - break - } + break + case "UPDATE_FROM_REMOTE": + if (item.localField && item.rvoField) { + const b_bufferstrip = parseBufferstrip( + item.rvoField.properties.mestData?.IndBufferstrook, + ) + + await updateField( + tx, + principal_id, + item.localField.b_id, + item.rvoField.properties.CropFieldDesignator || + item.localField.b_name, + item.rvoField.properties.CropFieldID, + item.rvoField.geometry, + new Date(item.rvoField.properties.BeginDate), + parseAcquiringMethod(item.rvoField.properties.UseTitleCode), + item.rvoField.properties.EndDate + ? new Date(item.rvoField.properties.EndDate) + : undefined, + b_bufferstrip, + ) + + // Update cultivation if different + if ( + item.localCultivation && + item.localCultivation.b_lu_catalogue !== + `nl_${item.rvoField.properties.CropTypeCode}` + ) { + // Remove old cultivation + await removeCultivation( + tx, + principal_id, + item.localCultivation.b_lu, + ) + + // Add new RVO cultivation + const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` + const defaultDates = await getDefaultDatesOfCultivation( + tx, + principal_id, + b_id_farm, + b_lu_catalogue, + year, + ) + + await addCultivation( + tx, + principal_id, + b_lu_catalogue, + item.localField.b_id, + defaultDates.b_lu_start, + defaultDates.b_lu_end, + ) + } + } + break + case "KEEP_LOCAL": // Keep Local for Conflict + break + case "REMOVE_LOCAL": + if (item.localField) { + await removeField(tx, principal_id, item.localField.b_id) + } + break + case "CLOSE_LOCAL": + if (item.localField) { + // Close the field on Dec 31st of the previous year + const closeDate = new Date(year - 1, 11, 31) + await updateField( + tx, + principal_id, + item.localField.b_id, + item.localField.b_name, + item.localField.b_id_source, + item.localField.b_geometry, + item.localField.b_start instanceof Date + ? item.localField.b_start + : new Date(item.localField.b_start), + item.localField.b_acquiring_method, + closeDate, + ) + } + break + } + }) } } From 82a13e5bf20008237a22167476887064a8737aed Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:24:14 +0100 Subject: [PATCH 093/108] fix: build with code split --- .../blocks/fertilizer/utils.server.ts | 31 +++++++++++++++ .../app/components/blocks/fertilizer/utils.ts | 38 ------------------- .../farm.$b_id_farm.fertilizers.$p_id.tsx | 6 +-- .../farm.$b_id_farm.fertilizers.new.$p_id.tsx | 2 +- ...farm.$b_id_farm.fertilizers.new._index.tsx | 2 +- ...farm.$b_id_farm.fertilizers.new.custom.tsx | 6 +-- fdm-rvo/src/process.ts | 35 +++++++++++------ 7 files changed, 60 insertions(+), 60 deletions(-) create mode 100644 fdm-app/app/components/blocks/fertilizer/utils.server.ts diff --git a/fdm-app/app/components/blocks/fertilizer/utils.server.ts b/fdm-app/app/components/blocks/fertilizer/utils.server.ts new file mode 100644 index 000000000..f77db68ad --- /dev/null +++ b/fdm-app/app/components/blocks/fertilizer/utils.server.ts @@ -0,0 +1,31 @@ +import { getFertilizerParametersDescription, type Fertilizer } from "@nmi-agro/fdm-core" + +/** + * Retrieves RVO label and type mappings used across fertilizer forms and summaries. + * Centralizes the assembly of RVO metadata from the parameter descriptions and available fertilizers. + * + * @param fertilizers - Optional array of fertilizers to build the RVO-to-Type mapping for dynamic badge colors. + * @returns An object containing: + * - `rvoLabels`: A record mapping RVO codes to their descriptive labels (in Dutch). + * - `rvoToType`: A record mapping RVO codes to fertilizer types (manure, compost, etc.). + */ +export async function getRvoMappings(fertilizers: Partial[] = []) { + const fertilizerParameterDescription = + await getFertilizerParametersDescription("NL-nl") + const p_type_rvo_options = + fertilizerParameterDescription.find((x) => x.parameter === "p_type_rvo") + ?.options ?? [] + + const rvoLabels = Object.fromEntries( + p_type_rvo_options.map((opt) => [String(opt.value), opt.label]), + ) + + const rvoToType: Record = {} + for (const f of fertilizers) { + if (f.p_type_rvo && f.p_type) { + rvoToType[f.p_type_rvo] = f.p_type + } + } + + return { rvoLabels, rvoToType } +} diff --git a/fdm-app/app/components/blocks/fertilizer/utils.ts b/fdm-app/app/components/blocks/fertilizer/utils.ts index 5193b1747..fd408b4a1 100644 --- a/fdm-app/app/components/blocks/fertilizer/utils.ts +++ b/fdm-app/app/components/blocks/fertilizer/utils.ts @@ -64,44 +64,6 @@ export function buildFertilizerDefaults( } } -/** - * Retrieves RVO label and type mappings used across fertilizer forms and summaries. - * Centralizes the assembly of RVO metadata from the parameter descriptions and available fertilizers. - * - * @param fertilizers - Optional array of fertilizers to build the RVO-to-Type mapping for dynamic badge colors. - * @returns An object containing: - * - `rvoLabels`: A record mapping RVO codes to their descriptive labels (in Dutch). - * - `rvoToType`: A record mapping RVO codes to fertilizer types (manure, compost, etc.). - */ -export async function getRvoMappings(fertilizers: Partial[] = []) { - if (typeof window !== "undefined") { - return { rvoLabels: {}, rvoToType: {} } - } - - const { getFertilizerParametersDescription } = await import( - "@nmi-agro/fdm-core" - ) - - const fertilizerParameterDescription = - await getFertilizerParametersDescription("NL-nl") - const p_type_rvo_options = - fertilizerParameterDescription.find((x) => x.parameter === "p_type_rvo") - ?.options ?? [] - - const rvoLabels = Object.fromEntries( - p_type_rvo_options.map((opt) => [String(opt.value), opt.label]), - ) - - const rvoToType: Record = {} - for (const f of fertilizers) { - if (f.p_type_rvo && f.p_type) { - rvoToType[f.p_type_rvo] = f.p_type - } - } - - return { rvoLabels, rvoToType } -} - /** * Builds the payload for adding a fertilizer to the catalogue. * diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx index e58a24798..370797fb7 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.$p_id.tsx @@ -21,10 +21,8 @@ import type { z } from "zod" import { FertilizerForm } from "@/app/components/blocks/fertilizer/form" import { FarmTitle } from "~/components/blocks/farm/farm-title" import { FormSchema } from "~/components/blocks/fertilizer/formschema" -import { - buildFertilizerDefaults, - getRvoMappings, -} from "~/components/blocks/fertilizer/utils" +import { buildFertilizerDefaults } from "~/components/blocks/fertilizer/utils" +import { getRvoMappings } from "~/components/blocks/fertilizer/utils.server" import { Header } from "~/components/blocks/header/base" import { HeaderFarm } from "~/components/blocks/header/farm" import { HeaderFertilizer } from "~/components/blocks/header/fertilizer" diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx index 9711ec68f..21914dd3b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.$p_id.tsx @@ -13,7 +13,7 @@ import { } from "react-router" import { FarmNewFertilizerBlock } from "~/components/blocks/fertilizer/new-fertilizer-page" import { FarmTitle } from "~/components/blocks/farm/farm-title" -import { getRvoMappings } from "~/components/blocks/fertilizer/utils" +import { getRvoMappings } from "~/components/blocks/fertilizer/utils.server" import { getSession } from "~/lib/auth.server" import { clientConfig } from "~/lib/config" import { handleLoaderError } from "~/lib/error" diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx index 49746de80..33e647e4b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new._index.tsx @@ -15,7 +15,7 @@ import { CommandList, } from "~/components/ui/command" import { FarmTitle } from "~/components/blocks/farm/farm-title" -import { getRvoMappings } from "~/components/blocks/fertilizer/utils" +import { getRvoMappings } from "~/components/blocks/fertilizer/utils.server" import { getSession } from "~/lib/auth.server" import { fdm } from "~/lib/fdm.server" import { cn } from "~/lib/utils" diff --git a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx index f6541051b..e02ad8ef0 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.fertilizers.new.custom.tsx @@ -15,10 +15,8 @@ import { redirectWithSuccess } from "remix-toast" import { FarmNewFertilizerBlock } from "~/components/blocks/fertilizer/new-fertilizer-page" import { FarmTitle } from "~/components/blocks/farm/farm-title" import { FormSchema } from "~/components/blocks/fertilizer/formschema" -import { - buildCataloguePayload, - getRvoMappings, -} from "~/components/blocks/fertilizer/utils" +import { buildCataloguePayload } from "~/components/blocks/fertilizer/utils" +import { getRvoMappings } from "~/components/blocks/fertilizer/utils.server" import { getSession } from "~/lib/auth.server" import { clientConfig } from "~/lib/config" import { handleActionError, handleLoaderError } from "~/lib/error" diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index 49c0a71c2..d7402a3ce 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -19,7 +19,9 @@ function parseBufferstrip(value: string | undefined): boolean | undefined { return undefined } -function parseAcquiringMethod(useTitleCode: string | undefined): AcquiringMethod { +function parseAcquiringMethod( + useTitleCode: string | undefined, +): AcquiringMethod { const candidate = `nl_${useTitleCode}` if (acquiringMethodOptions.some((opt) => opt.value === candidate)) { return candidate as AcquiringMethod @@ -58,7 +60,7 @@ export async function processRvoImport( continue } - await fdm.transaction(async (tx) => { + await fdm.transaction(async (tx: FdmType) => { switch (action) { case "ADD_REMOTE": if (item.rvoField) { @@ -75,7 +77,9 @@ export async function processRvoImport( item.rvoField.properties.CropFieldID, item.rvoField.geometry, new Date(item.rvoField.properties.BeginDate), - parseAcquiringMethod(item.rvoField.properties.UseTitleCode), + parseAcquiringMethod( + item.rvoField.properties.UseTitleCode, + ), item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, @@ -121,7 +125,9 @@ export async function processRvoImport( item.rvoField.properties.CropFieldID, item.rvoField.geometry, new Date(item.rvoField.properties.BeginDate), - parseAcquiringMethod(item.rvoField.properties.UseTitleCode), + parseAcquiringMethod( + item.rvoField.properties.UseTitleCode, + ), item.rvoField.properties.EndDate ? new Date(item.rvoField.properties.EndDate) : undefined, @@ -143,13 +149,14 @@ export async function processRvoImport( // Add new RVO cultivation const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` - const defaultDates = await getDefaultDatesOfCultivation( - tx, - principal_id, - b_id_farm, - b_lu_catalogue, - year, - ) + const defaultDates = + await getDefaultDatesOfCultivation( + tx, + principal_id, + b_id_farm, + b_lu_catalogue, + year, + ) await addCultivation( tx, @@ -166,7 +173,11 @@ export async function processRvoImport( break case "REMOVE_LOCAL": if (item.localField) { - await removeField(tx, principal_id, item.localField.b_id) + await removeField( + tx, + principal_id, + item.localField.b_id, + ) } break case "CLOSE_LOCAL": From 97af0a1fa3e7f39e1511f6cb2a42b2a5eb6b8876 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 09:28:33 +0100 Subject: [PATCH 094/108] fix: add missing year validation --- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 760348c2b..0f12fe8e5 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -528,6 +528,12 @@ export async function action({ request, params }: ActionFunctionArgs) { }) } const year = Number(yearString) + if (!Number.isInteger(year)) { + throw data("Ongeldig kalenderjaar", { + status: 400, + statusText: "Ongeldig kalenderjaar", + }) + } const session = await getSession(request) const formData = await request.formData() From 0f259fc873e8f4bfa3eb4b516201eb7035b02b27 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 09:29:49 +0100 Subject: [PATCH 095/108] refactor: remove useless assignment --- fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 0f12fe8e5..1bf1d1921 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -225,7 +225,7 @@ export default function RvoImportReviewPage() { const initialChoices: UserChoiceMap = {} rvoImportReviewData.forEach((item) => { const id = getItemId(item) - let defaultAction: ImportReviewAction = "NO_ACTION" + let defaultAction: ImportReviewAction switch (item.status) { case RvoImportReviewStatus.NEW_REMOTE: From 25e74028a039fd393ae85d541b00bd22988af302 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 09:31:49 +0100 Subject: [PATCH 096/108] refactor: Simplify conditional rendering. --- ...arm.create.$b_id_farm.$calendar._index.tsx | 103 +++++++++--------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx index ccede4fb5..48259aa37 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar._index.tsx @@ -85,59 +85,56 @@ export default function ChooseFieldImportMethod() { : "md:grid-cols-2", )} > - - - - Importeren vanuit RVO - - Importeer je percelen door via eHerkenning - toestemming te geven. - - - - - - - Wat heb ik nodig om percelen te - importeren vanuit RVO? - - -
      -
    1. - U heeft een geldig - KvK-nummer gekoppeld aan uw - account. -
    2. -
    3. - U heeft een eHerkenning - account met machtiging voor - dit KvK-nummer. -
    4. -
    5. - U geeft ons toestemming om - perceelsgegevens op te - halen. -
    6. -
    -
    -
    -
    - - - -
    -
    + {isRvoConfigured && ( + + + + Importeren vanuit RVO + + Importeer je percelen door via eHerkenning + toestemming te geven. + + + + + + + Wat heb ik nodig om percelen te + importeren vanuit RVO? + + +
      +
    1. + U heeft een geldig + KvK-nummer gekoppeld aan uw + account. +
    2. +
    3. + U heeft een eHerkenning + account met machtiging voor + dit KvK-nummer. +
    4. +
    5. + U geeft ons toestemming om + perceelsgegevens op te + halen. +
    6. +
    +
    +
    +
    + + + +
    +
    + )} From 3d3762f1493067dbd1f8653192d21abea0836ac6 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:54:15 +0100 Subject: [PATCH 097/108] feat: add warning if user tries to refresh rvo page and thus data needs to be request again --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 45 ++++++++++++++++--- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 37 ++++++++++++--- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 1bf1d1921..40adda997 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -124,7 +124,22 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } const rvoClient = createConfiguredRvoClient(rvoCredentials) - await exchangeToken(rvoClient, code) + + try { + await exchangeToken(rvoClient, code) + } catch (e: any) { + // Handle token exchange errors specifically for refreshes + const originalError = e?.message || "" + if ( + originalError.includes("invalid_grant") || + originalError.includes("expired") + ) { + throw new Error( + "De eHerkenning sessie is verlopen door een paginaverversing of een verouderde link. Klik op 'Verbinden met RVO' om opnieuw te verbinden.", + ) + } + throw e + } const rvoFields = await fetchRvoFields( rvoClient, @@ -174,6 +189,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { rvoImportReviewData: [], error: null, showimportButton: true, + noRvoParcelsFound: false, b_businessid_farm, isRvoConfigured, farms, @@ -182,7 +198,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } const noRvoParcelsFound = !error && rvoImportReviewData.length === 0 - return { + return data({ b_id_farm, rvoImportReviewData, error, @@ -193,7 +209,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { farms, b_name_farm, calendar: yearString, - } + }) } export default function RvoImportReviewPage() { @@ -249,8 +265,23 @@ export default function RvoImportReviewPage() { setUserChoices(initialChoices) }, [rvoImportReviewData]) + // Warn the user before refreshing or leaving when data is present + useEffect(() => { + if (rvoImportReviewData.length > 0) { + const handleBeforeUnload = (e: BeforeUnloadEvent) => { + e.preventDefault() + e.returnValue = + "Als u de pagina ververst, wordt de verbinding met RVO verbroken en moet u opnieuw inloggen met eHerkenning. Wilt u doorgaan?" + return e.returnValue + } + window.addEventListener("beforeunload", handleBeforeUnload) + return () => + window.removeEventListener("beforeunload", handleBeforeUnload) + } + }, [rvoImportReviewData]) + const handleChoiceChange = (id: string, action: ImportReviewAction) => { - setUserChoices((prev) => ({ ...prev, [id]: action })) + setUserChoices((prev: UserChoiceMap) => ({ ...prev, [id]: action })) } const currentFarmName = @@ -588,7 +619,11 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Error("Invalid review data format") } - const onFieldAdded = async (tx: FdmType, b_id: string, geometry: any) => { + const onFieldAdded = async ( + tx: FdmType, + b_id: string, + geometry: any, + ) => { const nmiApiKey = getNmiApiKey() if (nmiApiKey) { try { diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 535c034d9..7989cb6fc 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -8,6 +8,7 @@ import { useLoaderData, useNavigation, useLocation, + data, } from "react-router" import { getSession } from "~/lib/auth.server" import { extractErrorMessage } from "~/lib/error" @@ -115,7 +116,17 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } const rvoClient = createConfiguredRvoClient(rvoCredentials) - await exchangeToken(rvoClient, code) + + try { + await exchangeToken(rvoClient, code) + } catch (e: any) { + // Handle token exchange errors specifically for refreshes + const originalError = e?.message || "" + if (originalError.includes("invalid_grant") || originalError.includes("expired")) { + throw new Error("De eHerkenning sessie is verlopen door een paginaverversing of een verouderde link. Klik op 'Verbinden met RVO' om opnieuw te verbinden.") + } + throw e + } const rvoFields = await fetchRvoFields( rvoClient, @@ -143,20 +154,21 @@ export async function loader({ request, params }: LoaderFunctionArgs) { error = await extractErrorMessage(e) } } else if (!url.searchParams.has("start_import")) { - return { + return data({ b_id_farm, b_businessid_farm, calendar: yearString, RvoImportReviewData: [], error: null, showImportButton: true, + noRvoParcelsFound: false, isRvoConfigured, b_name_farm, - } + }) } const noRvoParcelsFound = !error && RvoImportReviewData.length === 0 - return { + return data({ b_id_farm, b_businessid_farm, calendar: yearString, @@ -166,7 +178,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { noRvoParcelsFound, isRvoConfigured, b_name_farm, - } + }) } export default function RvoImportCreatePage() { @@ -215,8 +227,21 @@ export default function RvoImportCreatePage() { setUserChoices(initialChoices) }, [RvoImportReviewData]) + // Warn the user before refreshing or leaving when data is present + useEffect(() => { + if (RvoImportReviewData.length > 0) { + const handleBeforeUnload = (e: BeforeUnloadEvent) => { + e.preventDefault() + e.returnValue = "Als u de pagina ververst, wordt de verbinding met RVO verbroken en moet u opnieuw inloggen met eHerkenning. Wilt u doorgaan?" + return e.returnValue + } + window.addEventListener("beforeunload", handleBeforeUnload) + return () => window.removeEventListener("beforeunload", handleBeforeUnload) + } + }, [RvoImportReviewData]) + const handleChoiceChange = (id: string, action: ImportReviewAction) => { - setUserChoices((prev) => ({ ...prev, [id]: action })) + setUserChoices((prev: UserChoiceMap) => ({ ...prev, [id]: action })) } if (error) { From 89f208caf9f13a954bb941b0d3efbc27a1797356 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 13:23:18 +0100 Subject: [PATCH 098/108] refactor: remove duplicate env --- fdm-app/.env.example | 2 +- fdm-app/app/integrations/rvo.server.ts | 5 +---- fdm-app/app/lib/config.server.ts | 1 - fdm-app/app/types/config.d.ts | 1 - fdm-rvo/README.md | 1 - 5 files changed, 2 insertions(+), 8 deletions(-) diff --git a/fdm-app/.env.example b/fdm-app/.env.example index 8734d7085..db22f3e08 100644 --- a/fdm-app/.env.example +++ b/fdm-app/.env.example @@ -72,8 +72,8 @@ MS_CLIENT_SECRET= # Required: No RVO_CLIENT_ID= RVO_CLIENT_NAME= -RVO_CLIENT_SECRET= RVO_REDIRECT_URI= +RVO_PKIO_PRIVATE_KEY= # ------------------------------------- # Map & Data Configuration diff --git a/fdm-app/app/integrations/rvo.server.ts b/fdm-app/app/integrations/rvo.server.ts index 19fc2b109..65d4d1ec3 100644 --- a/fdm-app/app/integrations/rvo.server.ts +++ b/fdm-app/app/integrations/rvo.server.ts @@ -82,12 +82,11 @@ export async function verifyRvoState( export function getRvoCredentials(): RvoCredentials | undefined { // Check if RVO is configured - const { clientId, clientSecret, redirectUri, clientName, pkioPrivateKey } = + const { clientId, redirectUri, clientName, pkioPrivateKey } = serverConfig.integrations.rvo const isValid = (v: string) => !!v?.trim() && v !== "undefined" const rvoConfigured = isValid(clientId) && - isValid(clientSecret) && isValid(redirectUri) && isValid(clientName) && isValid(pkioPrivateKey) @@ -97,7 +96,6 @@ export function getRvoCredentials(): RvoCredentials | undefined { return { clientId, - clientSecret, redirectUri, clientName, pkioPrivateKey, @@ -106,7 +104,6 @@ export function getRvoCredentials(): RvoCredentials | undefined { type RvoCredentials = { clientId: string - clientSecret: string redirectUri: string clientName: string pkioPrivateKey: string diff --git a/fdm-app/app/lib/config.server.ts b/fdm-app/app/lib/config.server.ts index 90f32da2c..a0096e9c2 100644 --- a/fdm-app/app/lib/config.server.ts +++ b/fdm-app/app/lib/config.server.ts @@ -41,7 +41,6 @@ export const serverConfig: ServerConfig = { }, rvo: { clientId: String(process.env.RVO_CLIENT_ID), - clientSecret: String(process.env.RVO_CLIENT_SECRET), redirectUri: String(process.env.RVO_REDIRECT_URI), clientName: String(process.env.RVO_CLIENT_NAME), pkioPrivateKey: String(process.env.RVO_PKIO_PRIVATE_KEY), diff --git a/fdm-app/app/types/config.d.ts b/fdm-app/app/types/config.d.ts index a22473feb..c32ee5ec1 100644 --- a/fdm-app/app/types/config.d.ts +++ b/fdm-app/app/types/config.d.ts @@ -36,7 +36,6 @@ export interface ServerConfig { } rvo: { clientId: string - clientSecret: string redirectUri: string clientName: string pkioPrivateKey: string diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index d53550d9a..8386ae500 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -30,7 +30,6 @@ Ensure your `fdm-app` or consuming application has the following environment var ```env RVO_CLIENT_ID= -RVO_CLIENT_SECRET= RVO_REDIRECT_URI= RVO_PKIO_PRIVATE_KEY= RVO_ENVIRONMENT=production # or acceptance From 3b833e9790f5a332237a00dca267c77cd475e767 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:11:48 +0100 Subject: [PATCH 099/108] refactor: reorder envs --- fdm-app/.env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdm-app/.env.example b/fdm-app/.env.example index db22f3e08..a3fae6077 100644 --- a/fdm-app/.env.example +++ b/fdm-app/.env.example @@ -72,8 +72,8 @@ MS_CLIENT_SECRET= # Required: No RVO_CLIENT_ID= RVO_CLIENT_NAME= -RVO_REDIRECT_URI= RVO_PKIO_PRIVATE_KEY= +RVO_REDIRECT_URI= # ------------------------------------- # Map & Data Configuration From 21d8953ed2134d75faa0718c6dd0d346cb0fc6fb Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:13:44 +0100 Subject: [PATCH 100/108] refactor: guard against no kvk --- .../routes/farm.$b_id_farm.$calendar.rvo.tsx | 7 +++++ .../farm.create.$b_id_farm.$calendar.rvo.tsx | 30 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx index 40adda997..52c88751b 100644 --- a/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.$b_id_farm.$calendar.rvo.tsx @@ -578,6 +578,13 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Response("RVO client is not configured.", { status: 500 }) } + const farm = await getFarm(fdm, session.principal_id, b_id_farm) + if (!farm?.b_businessid_farm) { + throw new Response("Geen KvK-nummer gevonden voor dit bedrijf.", { + status: 400, + }) + } + const rvoClient = createConfiguredRvoClient(rvoCredentials) const { state, cookieHeader } = await createRvoState( diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 7989cb6fc..8e2040474 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -116,14 +116,19 @@ export async function loader({ request, params }: LoaderFunctionArgs) { } const rvoClient = createConfiguredRvoClient(rvoCredentials) - + try { await exchangeToken(rvoClient, code) } catch (e: any) { // Handle token exchange errors specifically for refreshes const originalError = e?.message || "" - if (originalError.includes("invalid_grant") || originalError.includes("expired")) { - throw new Error("De eHerkenning sessie is verlopen door een paginaverversing of een verouderde link. Klik op 'Verbinden met RVO' om opnieuw te verbinden.") + if ( + originalError.includes("invalid_grant") || + originalError.includes("expired") + ) { + throw new Error( + "De eHerkenning sessie is verlopen door een paginaverversing of een verouderde link. Klik op 'Verbinden met RVO' om opnieuw te verbinden.", + ) } throw e } @@ -232,11 +237,13 @@ export default function RvoImportCreatePage() { if (RvoImportReviewData.length > 0) { const handleBeforeUnload = (e: BeforeUnloadEvent) => { e.preventDefault() - e.returnValue = "Als u de pagina ververst, wordt de verbinding met RVO verbroken en moet u opnieuw inloggen met eHerkenning. Wilt u doorgaan?" + e.returnValue = + "Als u de pagina ververst, wordt de verbinding met RVO verbroken en moet u opnieuw inloggen met eHerkenning. Wilt u doorgaan?" return e.returnValue } window.addEventListener("beforeunload", handleBeforeUnload) - return () => window.removeEventListener("beforeunload", handleBeforeUnload) + return () => + window.removeEventListener("beforeunload", handleBeforeUnload) } }, [RvoImportReviewData]) @@ -435,6 +442,13 @@ export async function action({ request, params }: ActionFunctionArgs) { throw new Response("RVO client is not available", { status: 500 }) } + const farm = await getFarm(fdm, session.principal_id, b_id_farm) + if (!farm?.b_businessid_farm) { + throw new Response("Geen KvK-nummer gevonden voor dit bedrijf.", { + status: 400, + }) + } + const rvoClient = createConfiguredRvoClient(rvoCredentials) const { state, cookieHeader } = await createRvoState( @@ -470,7 +484,11 @@ export async function action({ request, params }: ActionFunctionArgs) { RvoImportReviewData = JSON.parse(String(RvoImportReviewDataJson)) userChoices = JSON.parse(String(userChoicesJson)) - const onFieldAdded = async (tx: FdmType, b_id: string, geometry: any) => { + const onFieldAdded = async ( + tx: FdmType, + b_id: string, + geometry: any, + ) => { const nmiApiKey = getNmiApiKey() if (nmiApiKey) { try { From 44c96b54b24d9def62f8d05d647e886e6be00613 Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:41:12 +0100 Subject: [PATCH 101/108] docs: update readme --- fdm-rvo/README.md | 150 ++++++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 64 deletions(-) diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index 8386ae500..2d1e5d452 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -11,7 +11,7 @@ This package provides the core logic for synchronizing agricultural field data w - **RVO Import Review Engine**: - Compares local FDM fields (`@nmi-agro/fdm-core`'s `Field` type) against RVO fields. - Utilizes a two-tier matching strategy: ID-based matching followed by spatial (IoU) matching. - - Detects and categorizes fields as `MATCH`, `NEW_REMOTE` (in RVO but not local), `NEW_LOCAL` (in local but not RVO), or `CONFLICT` (different properties in both). + - Detects and categorizes fields as `MATCH`, `NEW_REMOTE` (in RVO but not local), `NEW_LOCAL` (in local but not RVO), `CONFLICT` (different properties in both), or `EXPIRED_LOCAL` (local field started before the year but missing in RVO). - Identifies specific differing properties (`b_name`, `b_geometry`, `b_start`, `b_end`) for conflicts. - **Type Safety**: Fully typed for a seamless development experience. @@ -30,87 +30,109 @@ Ensure your `fdm-app` or consuming application has the following environment var ```env RVO_CLIENT_ID= +RVO_CLIENT_NAME= RVO_REDIRECT_URI= RVO_PKIO_PRIVATE_KEY= -RVO_ENVIRONMENT=production # or acceptance ``` #### 2. Authentication Flow +To ensure security, sensitive credentials like `RVO_PKIO_PRIVATE_KEY` and the `exchangeToken` step must **never** be exposed to or executed on the client-side (browser). Instead, use server-side routes (e.g., in React Router v7 / Remix) to handle the authentication flow. + +##### A. Initiate Authentication (Server-side Redirect) + +Create a server route (e.g., `app/routes/auth.rvo.tsx`) that initiates the redirect to RVO. + ```typescript -// In your React Router v7 component (client-side) -import { createRvoClient, generateAuthUrl, exchangeToken } from '@nmi-agro/fdm-rvo'; -// Assuming you have a way to securely access server config on the client, -// or that these are provided as build-time env vars (e.g., VITE_RVO_CLIENT_ID) -import { serverConfig } from '~/lib/config.client'; // Example client-side config - -// To initiate authentication (e.g., from a button click handler) -const handleAuthInitiation = () => { - const { clientId, redirectUri, pkioPrivateKey, environment } = serverConfig.integrations.rvo; - const clientName = serverConfig.name; // Use your app's name - - const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); - - // Encode current location to return to it after RVO login - const state = Buffer.from(JSON.stringify({ returnPath: window.location.pathname })).toString('base64'); +// app/routes/auth.rvo.tsx (Server-side) +import { redirect, type LoaderFunctionArgs } from "react-router"; +import { createRvoClient, generateAuthUrl } from "@nmi-agro/fdm-rvo"; +import { getEnv } from "~/lib/env.server"; // Your server-side env helper + +export async function loader({ request }: LoaderFunctionArgs) { + const env = getEnv(); + const { RVO_CLIENT_ID, RVO_CLIENT_NAME, RVO_REDIRECT_URI, RVO_PKIO_PRIVATE_KEY } = env; + + const rvoClient = createRvoClient( + RVO_CLIENT_ID, + RVO_CLIENT_NAME, + RVO_REDIRECT_URI, + RVO_PKIO_PRIVATE_KEY + ); + + // Use a secure, signed state or session-based state to prevent CSRF + const state = Crypto.randomUUID(); // Example simple state const authUrl = generateAuthUrl(rvoClient, state); - - window.location.href = authUrl; // Redirect the user -}; -// To handle the RVO callback in a component rendered at the redirectUri -const handleRvoCallback = async (code: string, state: string) => { - const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.integrations.rvo; - const clientName = serverConfig.name; + return redirect(authUrl); +} +``` + +##### B. Handle Callback (Server-side Token Exchange) - // Validate state to prevent CSRF - const decodedState = JSON.parse(Buffer.from(state, 'base64').toString('utf-8')); - // ... perform validation, e.g., against a session/cookie based state ... +Create a callback route (matching your `RVO_REDIRECT_URI`) to exchange the code for an access token. - const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); +```typescript +// app/routes/auth.rvo.callback.tsx (Server-side) +import { redirect, type LoaderFunctionArgs } from "react-router"; +import { createRvoClient, exchangeToken } from "@nmi-agro/fdm-rvo"; +import { commitSession, getSession } from "~/lib/session.server"; + +export async function loader({ request }: LoaderFunctionArgs) { + const url = new URL(request.url); + const code = url.searchParams.get("code"); + const state = url.searchParams.get("state"); + + if (!code) throw new Error("No authorization code received"); + + const env = process.env; // Or your env helper + const rvoClient = createRvoClient( + env.RVO_CLIENT_ID!, + env.RVO_CLIENT_NAME!, + env.RVO_REDIRECT_URI!, + env.RVO_PKIO_PRIVATE_KEY! + ); + + // 1. Exchange the code for an access token securely on the server const accessToken = await exchangeToken(rvoClient, code); - - // Now you have accessToken, you can fetch fields client-side or send it to a serverless function/backend API - return { accessToken, decodedState }; -}; + + // 2. Store the accessToken in a secure, HTTP-only session cookie + const session = await getSession(request.headers.get("Cookie")); + session.set("rvoAccessToken", accessToken); + + return redirect("/dashboard", { + headers: { "Set-Cookie": await commitSession(session) }, + }); +} ``` #### 3. Fetching and Reconciling Fields +Once the `accessToken` is stored in your server session, you can use it in loaders or actions to fetch and compare data. + ```typescript -// In your React Router v7 component (client-side), after getting accessToken -import { fetchRvoFields, compareFields } from '@nmi-agro/fdm-rvo'; -// Assuming you have an API endpoint to fetch local fields -const fetchLocalFieldsApi = async (farmId: string, principalId: string) => { - const response = await fetch(`/api/farm/${farmId}/fields?principalId=${principalId}`); - return response.json(); -}; - -const processRvoData = async (accessToken: string, farmId: string, principalId: string, year: string, kvkNumber: string) => { - const { clientId, clientSecret, redirectUri, pkioPrivateKey, environment } = serverConfig.integrations.rvo; - const clientName = serverConfig.name; - - const rvoClient = createRvoClient(clientId, clientName, redirectUri, pkioPrivateKey, environment); - - // Fetch RVO fields client-side - const rvoFields = await fetchRvoFields(rvoClient, year, kvkNumber); +// app/routes/farm.$id.sync.tsx (Server-side) +import { fetchRvoFields, compareFields } from "@nmi-agro/fdm-rvo"; +import { getRvoClient } from "~/lib/rvo.server"; // Helper that uses createRvoClient + +export async function loader({ request, params }: LoaderFunctionArgs) { + const session = await getSession(request.headers.get("Cookie")); + const accessToken = session.get("rvoAccessToken"); + if (!accessToken) return redirect("/auth/rvo"); + + const rvoClient = getRvoClient(); + const year = "2024"; + const kvkNumber = "12345678"; + + // Fetch RVO fields (server-side) + const rvoFields = await fetchRvoFields(rvoClient, year, kvkNumber, accessToken); - // Fetch local fields from your backend API - const localFields = await fetchLocalFieldsApi(farmId, principalId); - - const rvoImportReviewResults = compareFields(localFields, rvoFields); - return rvoImportReviewResults; -}; - -// To apply changes, you would send the user's decisions to a backend API endpoint -const applyChangesApi = async (farmId: string, decisions: any) => { - const response = await fetch(`/api/farm/${farmId}/apply-rvo-sync`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(decisions), - }); - return response.json(); -}; + // Fetch local fields (server-side) + const localFields = await db.query.fields.findMany({ /* ... */ }); + + const rvoImportReviewResults = compareFields(localFields, rvoFields, Number(year)); + return { rvoImportReviewResults }; +} ``` ### TypeDoc Generation From eb0fa458c951397a131570f75ce3145504618fcd Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:47:52 +0100 Subject: [PATCH 102/108] fix: update cultivation when missing --- fdm-rvo/src/process.ts | 45 +++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/fdm-rvo/src/process.ts b/fdm-rvo/src/process.ts index d7402a3ce..c01981bdb 100644 --- a/fdm-rvo/src/process.ts +++ b/fdm-rvo/src/process.ts @@ -134,20 +134,41 @@ export async function processRvoImport( b_bufferstrip, ) - // Update cultivation if different - if ( - item.localCultivation && - item.localCultivation.b_lu_catalogue !== + // Update cultivation if different or add if missing + if (item.localCultivation) { + if ( + item.localCultivation.b_lu_catalogue !== `nl_${item.rvoField.properties.CropTypeCode}` - ) { - // Remove old cultivation - await removeCultivation( - tx, - principal_id, - item.localCultivation.b_lu, - ) + ) { + // Remove old cultivation + await removeCultivation( + tx, + principal_id, + item.localCultivation.b_lu, + ) - // Add new RVO cultivation + // Add new RVO cultivation + const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` + const defaultDates = + await getDefaultDatesOfCultivation( + tx, + principal_id, + b_id_farm, + b_lu_catalogue, + year, + ) + + await addCultivation( + tx, + principal_id, + b_lu_catalogue, + item.localField.b_id, + defaultDates.b_lu_start, + defaultDates.b_lu_end, + ) + } + } else { + // Add new RVO cultivation as it was missing locally const b_lu_catalogue = `nl_${item.rvoField.properties.CropTypeCode}` const defaultDates = await getDefaultDatesOfCultivation( From a9d404575418b1c2b7484756a3210d17bc3ad23d Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:14:48 +0100 Subject: [PATCH 103/108] refactor: add validation --- fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index 8e2040474..fc5c1ab65 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -484,6 +484,10 @@ export async function action({ request, params }: ActionFunctionArgs) { RvoImportReviewData = JSON.parse(String(RvoImportReviewDataJson)) userChoices = JSON.parse(String(userChoicesJson)) + if (!Array.isArray(RvoImportReviewData)) { + throw new Error("Invalid review data format") + } + const onFieldAdded = async ( tx: FdmType, b_id: string, From 1cf04e6bd744c5cd813aa609299627b32e77e24e Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:15:24 +0100 Subject: [PATCH 104/108] docs: fix --- fdm-rvo/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fdm-rvo/README.md b/fdm-rvo/README.md index 2d1e5d452..2a49fd0ff 100644 --- a/fdm-rvo/README.md +++ b/fdm-rvo/README.md @@ -2,7 +2,7 @@ ## RVO Synchronization Logic for FDM -This package provides the core logic for synchronizing agricultural field data with the RVO (Rijksdienst voor Ondernemend Nederland) webservices. It wraps the `@svenvw/rvo-connector` to handle authentication and data fetching, and implements a robust field comparison mechanism to detect new, missing, and conflicting field data between local and RVO records. +This package provides the core logic for synchronizing agricultural field data with the RVO (Rijksdienst voor Ondernemend Nederland) webservices. It wraps the `@nmi-agro/rvo-connector` to handle authentication and data fetching, and implements a robust field comparison mechanism to detect new, missing, and conflicting field data between local and RVO records. ### Features @@ -61,7 +61,7 @@ export async function loader({ request }: LoaderFunctionArgs) { ); // Use a secure, signed state or session-based state to prevent CSRF - const state = Crypto.randomUUID(); // Example simple state + const state = crypto.randomUUID(); // Example simple state const authUrl = generateAuthUrl(rvoClient, state); return redirect(authUrl); From 99c76d00b9e92a33c2dcdb074a1565095b02f8fe Mon Sep 17 00:00:00 2001 From: SvenVw <37927107+SvenVw@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:23:04 +0100 Subject: [PATCH 105/108] refactor: use consistent naming --- .../farm.create.$b_id_farm.$calendar.rvo.tsx | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx index fc5c1ab65..ae766ca6a 100644 --- a/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx +++ b/fdm-app/app/routes/farm.create.$b_id_farm.$calendar.rvo.tsx @@ -82,7 +82,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { const code = url.searchParams.get("code") const state = url.searchParams.get("state") - let RvoImportReviewData: RvoImportReviewItem[] = [] + let rvoImportReviewData: RvoImportReviewItem[] = [] let error: string | null = null let b_businessid_farm: string | null = null let b_name_farm: string | null | undefined = null @@ -148,7 +148,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { const localFieldsExtended: (Field & { cultivations: Cultivation[] })[] = [] // No existing fields to compare against yet in create wizard, so localFields is empty - RvoImportReviewData = compareFields( + rvoImportReviewData = compareFields( localFieldsExtended, rvoFields, year, @@ -163,7 +163,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) { b_id_farm, b_businessid_farm, calendar: yearString, - RvoImportReviewData: [], + rvoImportReviewData: [], error: null, showImportButton: true, noRvoParcelsFound: false, @@ -172,12 +172,12 @@ export async function loader({ request, params }: LoaderFunctionArgs) { }) } - const noRvoParcelsFound = !error && RvoImportReviewData.length === 0 + const noRvoParcelsFound = !error && rvoImportReviewData.length === 0 return data({ b_id_farm, b_businessid_farm, calendar: yearString, - RvoImportReviewData, + rvoImportReviewData, error, showImportButton: noRvoParcelsFound, noRvoParcelsFound, @@ -191,7 +191,7 @@ export default function RvoImportCreatePage() { b_id_farm, b_businessid_farm, calendar, - RvoImportReviewData, + rvoImportReviewData, error, showImportButton, noRvoParcelsFound, @@ -214,7 +214,7 @@ export default function RvoImportCreatePage() { useEffect(() => { // Initialize user choices with defaults const initialChoices: UserChoiceMap = {} - RvoImportReviewData.forEach((item) => { + rvoImportReviewData.forEach((item) => { const id = getItemId(item) let defaultAction: ImportReviewAction @@ -230,11 +230,11 @@ export default function RvoImportCreatePage() { initialChoices[id] = defaultAction }) setUserChoices(initialChoices) - }, [RvoImportReviewData]) + }, [rvoImportReviewData]) // Warn the user before refreshing or leaving when data is present useEffect(() => { - if (RvoImportReviewData.length > 0) { + if (rvoImportReviewData.length > 0) { const handleBeforeUnload = (e: BeforeUnloadEvent) => { e.preventDefault() e.returnValue = @@ -245,7 +245,7 @@ export default function RvoImportCreatePage() { return () => window.removeEventListener("beforeunload", handleBeforeUnload) } - }, [RvoImportReviewData]) + }, [rvoImportReviewData]) const handleChoiceChange = (id: string, action: ImportReviewAction) => { setUserChoices((prev: UserChoiceMap) => ({ ...prev, [id]: action })) @@ -331,7 +331,7 @@ export default function RvoImportCreatePage() {
    )} - {RvoImportReviewData.length === 0 ? ( + {rvoImportReviewData.length === 0 ? (
    {noRvoParcelsFound && ( @@ -384,7 +384,7 @@ export default function RvoImportCreatePage() { type="hidden" name="RvoImportReviewDataJson" value={JSON.stringify( - RvoImportReviewData, + rvoImportReviewData, )} />