Skip to content

Commit

Permalink
H-4161: Improve value conversion in frontend (#6629)
Browse files Browse the repository at this point in the history
  • Loading branch information
CiaranMn authored Mar 5, 2025
1 parent 12d23c3 commit aac7dde
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ const migrate: MigrationFunction = async ({
},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["/", "self", { const: 1000, type: "number" }] },
to: { expression: ["*", "self", { const: 1000, type: "number" }] },
from: { expression: ["*", "self", { const: 1000, type: "number" }] },
to: { expression: ["/", "self", { const: 1000, type: "number" }] },
},
},
webShortname: "h",
Expand All @@ -85,8 +85,8 @@ const migrate: MigrationFunction = async ({
},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["/", "self", { const: 1000000, type: "number" }] },
to: { expression: ["*", "self", { const: 1000000, type: "number" }] },
from: { expression: ["*", "self", { const: 1000000, type: "number" }] },
to: { expression: ["/", "self", { const: 1000000, type: "number" }] },
},
},
webShortname: "h",
Expand All @@ -103,8 +103,8 @@ const migrate: MigrationFunction = async ({
},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["*", "self", { const: 60, type: "number" }] },
to: { expression: ["/", "self", { const: 60, type: "number" }] },
from: { expression: ["/", "self", { const: 60, type: "number" }] },
to: { expression: ["*", "self", { const: 60, type: "number" }] },
},
},
webShortname: "h",
Expand All @@ -121,8 +121,8 @@ const migrate: MigrationFunction = async ({
},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["*", "self", { const: 3600, type: "number" }] },
to: { expression: ["/", "self", { const: 3600, type: "number" }] },
from: { expression: ["/", "self", { const: 3600, type: "number" }] },
to: { expression: ["*", "self", { const: 3600, type: "number" }] },
},
},
webShortname: "h",
Expand All @@ -139,8 +139,8 @@ const migrate: MigrationFunction = async ({
},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["*", "self", { const: 86400, type: "number" }] },
to: { expression: ["/", "self", { const: 86400, type: "number" }] },
from: { expression: ["/", "self", { const: 86400, type: "number" }] },
to: { expression: ["*", "self", { const: 86400, type: "number" }] },
},
},
webShortname: "h",
Expand All @@ -156,8 +156,8 @@ const migrate: MigrationFunction = async ({
},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["*", "self", { const: 604800, type: "number" }] },
to: { expression: ["/", "self", { const: 604800, type: "number" }] },
from: { expression: ["/", "self", { const: 604800, type: "number" }] },
to: { expression: ["*", "self", { const: 604800, type: "number" }] },
},
},
webShortname: "h",
Expand All @@ -174,8 +174,8 @@ const migrate: MigrationFunction = async ({
},
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: { expression: ["*", "self", { const: 2629800, type: "number" }] },
to: { expression: ["/", "self", { const: 2629800, type: "number" }] },
from: { expression: ["/", "self", { const: 2629800, type: "number" }] },
to: { expression: ["*", "self", { const: 2629800, type: "number" }] },
},
},
webShortname: "h",
Expand All @@ -193,9 +193,9 @@ const migrate: MigrationFunction = async ({
conversions: {
[secondDataType.metadata.recordId.baseUrl]: {
from: {
expression: ["*", "self", { const: 31557600, type: "number" }],
expression: ["/", "self", { const: 31557600, type: "number" }],
},
to: { expression: ["/", "self", { const: 31557600, type: "number" }] },
to: { expression: ["*", "self", { const: 31557600, type: "number" }] },
},
},
webShortname: "h",
Expand Down
2 changes: 2 additions & 0 deletions libs/@local/hash-isomorphic-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@sentry/browser": "7.120.3",
"@sindresorhus/slugify": "1.1.2",
"apollo-server-express": "3.9.0",
"big.js": "6.2.2",
"fix-esm-import-path": "1.10.1",
"fractional-indexing": "2.1.0",
"immer": "9.0.21",
Expand Down Expand Up @@ -63,6 +64,7 @@
"@local/eslint": "0.0.0-private",
"@local/tsconfig": "0.0.0-private",
"@temporalio/workflow": "1.11.7",
"@types/big.js": "6.2.2",
"@types/lodash-es": "4.17.12",
"@types/node": "22.13.9",
"@types/pluralize": "0.0.33",
Expand Down
40 changes: 18 additions & 22 deletions libs/@local/hash-isomorphic-utils/src/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
} from "@blockprotocol/type-system";
import { mustHaveAtLeastOne } from "@blockprotocol/type-system";
import type { ClosedDataTypeDefinition } from "@local/hash-graph-types/ontology";
import Big from "big.js";

import { add, divide, multiply, subtract } from "./numbers.js";

Expand Down Expand Up @@ -164,63 +165,58 @@ export const formatDataValue = (
};

type Context = {
self: number;
self: Big;
};

const evaluateConversionValue = (
value: ConversionValue,
context: Context,
): number => {
): Big => {
if (Array.isArray(value)) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return evaluateExpression(value, context);
} else if (value === "self") {
return context.self;
} else {
return value.const;
return new Big(value.const);
}
};

const evaluateExpression = (
expression: ConversionExpression,
context: Context,
): number => {
): Big => {
const left = evaluateConversionValue(expression[1], context);
const right = evaluateConversionValue(expression[2], context);

switch (expression[0]) {
case "+":
return add(
evaluateConversionValue(expression[1], context),
evaluateConversionValue(expression[2], context),
);
return add(left, right);
case "-":
return subtract(
evaluateConversionValue(expression[1], context),
evaluateConversionValue(expression[2], context),
);
return subtract(left, right);
case "*":
return multiply(
evaluateConversionValue(expression[1], context),
evaluateConversionValue(expression[2], context),
);
return multiply(left, right);
case "/":
return divide(
evaluateConversionValue(expression[1], context),
evaluateConversionValue(expression[2], context),
);
if (right.eq(0)) {
return new Big(0);
}
return divide(left, right);
}
};

export const createConversionFunction = (
conversions: ConversionDefinition[],
): ((value: number) => number) => {
return (value: number) => {
let evaluatedValue = value;
let evaluatedValue = new Big(value);

for (const conversion of conversions) {
evaluatedValue = evaluateExpression(conversion.expression, {
self: evaluatedValue,
});
}

return evaluatedValue;
return evaluatedValue.toNumber();
};
};

Expand Down
102 changes: 34 additions & 68 deletions libs/@local/hash-isomorphic-utils/src/numbers.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,48 @@
const greatestCommonDivisor = (
numerator: number,
denominator: number,
): number => {
while (denominator !== 0) {
const temp = denominator;
// eslint-disable-next-line no-param-reassign
denominator = numerator % denominator;
// eslint-disable-next-line no-param-reassign
numerator = temp;
}
return numerator;
};
import Big from "big.js";

const scaleValue = (value: number, scale: number) => {
return Math.round(value * scale);
};
Big.DP = 20;
Big.RM = Big.roundDown;

const maxDivisionPrecision = 16;
export const divide = (numerator: Big, denominator: Big): Big => {
try {
if (denominator.eq(0)) {
return new Big(0);
}

export const divide = (numerator: number, denominator: number): number => {
if (Number.isNaN(numerator) || Number.isNaN(denominator)) {
return numerator.div(denominator);
} catch (error: unknown) {
throw new Error(
`Arguments cannot be NaN, got numerator: ${numerator}, denominator: ${denominator}`,
`Division error: ${error instanceof Error ? error.message : String(error)}`,
);
}

if (denominator === 0) {
return 0;
}

const commonDivisor = greatestCommonDivisor(numerator, denominator);
const simplifiedNumerator = numerator / commonDivisor;
const simplifiedDenominator = denominator / commonDivisor;

const result = simplifiedNumerator / simplifiedDenominator;

const scale = 10 ** maxDivisionPrecision;
const truncatedResult = scaleValue(result, scale) / scale;

return truncatedResult;
};

const countDecimals = (number: number) => {
if (Math.floor(number) === number) {
return 0;
export const add = (a: Big, b: Big): Big => {
try {
return a.plus(b);
} catch (error: unknown) {
throw new Error(
`Addition error: ${error instanceof Error ? error.message : String(error)}`,
);
}
return number.toString().split(".")[1]?.length ?? 0;
};

export const add = (a: number, b: number) => {
const precisionA = countDecimals(a);
const precisionB = countDecimals(b);

const maxPrecision = Math.max(precisionA, precisionB);

const scale = 10 ** maxPrecision;

return (scaleValue(a, scale) + scaleValue(b, scale)) / scale;
};

export const subtract = (a: number, b: number) => {
const precisionA = countDecimals(a);
const precisionB = countDecimals(b);

const maxPrecision = Math.max(precisionA, precisionB);

const scale = 10 ** maxPrecision;

return (scaleValue(a, scale) - scaleValue(b, scale)) / scale;
export const subtract = (a: Big, b: Big): Big => {
try {
return a.minus(b);
} catch (error: unknown) {
throw new Error(
`Subtraction error: ${error instanceof Error ? error.message : String(error)}`,
);
}
};

export const multiply = (a: number, b: number) => {
const precisionA = countDecimals(a);
const precisionB = countDecimals(b);

const totalPrecision = precisionA + precisionB;

const scale = 10 ** totalPrecision;

return scaleValue(a * b, scale) / scale;
export const multiply = (a: Big, b: Big): Big => {
try {
return a.times(b);
} catch (error: unknown) {
throw new Error(
`Multiplication error: ${error instanceof Error ? error.message : String(error)}`,
);
}
};
16 changes: 16 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8996,11 +8996,13 @@ __metadata:
"@sentry/browser": "npm:7.120.3"
"@sindresorhus/slugify": "npm:1.1.2"
"@temporalio/workflow": "npm:1.11.7"
"@types/big.js": "npm:6.2.2"
"@types/lodash-es": "npm:4.17.12"
"@types/node": "npm:22.13.9"
"@types/pluralize": "npm:0.0.33"
"@vitest/coverage-istanbul": "npm:3.0.7"
apollo-server-express: "npm:3.9.0"
big.js: "npm:6.2.2"
eslint: "npm:9.20.1"
fix-esm-import-path: "npm:1.10.1"
fractional-indexing: "npm:2.1.0"
Expand Down Expand Up @@ -17314,6 +17316,13 @@ __metadata:
languageName: node
linkType: hard

"@types/big.js@npm:6.2.2":
version: 6.2.2
resolution: "@types/big.js@npm:6.2.2"
checksum: 10c0/8f8472dfc1ef61c492e6841e86f8b9b97e5b024136bf7964e582a6a80ba73d4dbfd6cc23ed3b9d8fea69c7f30834fffd1c88e7fb981811f5c6ca608380b5ad67
languageName: node
linkType: hard

"@types/body-parser@npm:*":
version: 1.19.5
resolution: "@types/body-parser@npm:1.19.5"
Expand Down Expand Up @@ -21353,6 +21362,13 @@ __metadata:
languageName: node
linkType: hard

"big.js@npm:6.2.2":
version: 6.2.2
resolution: "big.js@npm:6.2.2"
checksum: 10c0/58d204f6a1a92508dc2eb98d964e2cc6dabb37a3d9fc8a1f0b77a34dead7c11e17b173d9a6df2d5a7a0f78d5c80853a9ce6df29852da59ab10b088e981195165
languageName: node
linkType: hard

"big.js@npm:^5.2.2":
version: 5.2.2
resolution: "big.js@npm:5.2.2"
Expand Down

0 comments on commit aac7dde

Please sign in to comment.