feat: use bigint instead of long (#1403)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Improved support for large integer values in the API, allowing
accurate handling of big numbers in GraphQL queries and responses.
- **Bug Fixes**
- Enhanced reliability when dealing with very large numeric fields,
reducing the risk of data loss or inaccuracies for disk, share, and
memory statistics.
- **Chores**
	- Updated internal dependencies to improve handling of big integers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Eli Bosley
2025-05-22 10:42:36 -07:00
committed by GitHub
parent 5355115af2
commit 574d572d65
8 changed files with 36 additions and 61 deletions

View File

@@ -115,6 +115,7 @@
"ini": "^5.0.0",
"ip": "^2.0.1",
"jose": "^6.0.0",
"json-bigint-patch": "^0.0.8",
"lodash-es": "^4.17.21",
"multi-ini": "^2.3.2",
"mustache": "^4.2.0",

View File

@@ -1,4 +1,5 @@
import '@app/dotenv.js';
import 'json-bigint-patch';
import { execa } from 'execa';
import { CommandFactory } from 'nest-commander';

View File

@@ -1,5 +1,6 @@
import 'reflect-metadata';
import 'global-agent/bootstrap.js';
import 'json-bigint-patch';
import '@app/dotenv.js';
import { type NestFastifyApplication } from '@nestjs/platform-fastify';

View File

@@ -4,7 +4,7 @@ import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { NoUnusedVariablesRule } from 'graphql';
import { JSONResolver, URLResolver } from 'graphql-scalars';
import { GraphQLBigInt, JSONResolver, URLResolver } from 'graphql-scalars';
import { ENVIRONMENT } from '@app/environment.js';
import { getters } from '@app/store/index.js';
@@ -14,7 +14,6 @@ import {
} from '@app/unraid-api/graph/directives/use-permissions.directive.js';
import { ResolversModule } from '@app/unraid-api/graph/resolvers/resolvers.module.js';
import { sandboxPlugin } from '@app/unraid-api/graph/sandbox-plugin.js';
import { GraphQLLong } from '@app/unraid-api/graph/scalars/graphql-type-long.js';
import { PrefixedID as PrefixedIDScalar } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
@@ -50,7 +49,7 @@ import { PluginModule } from '@app/unraid-api/plugin/plugin.module.js';
},
resolvers: {
JSON: JSONResolver,
Long: GraphQLLong,
Long: GraphQLBigInt,
URL: URLResolver,
},
buildSchemaOptions: {

View File

@@ -1,9 +1,9 @@
import { Field, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
import { IsEnum } from 'class-validator';
import { GraphQLBigInt } from 'graphql-scalars';
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
import { GraphQLLong } from '@app/unraid-api/graph/scalars/graphql-type-long.js';
import { PrefixedID } from '@app/unraid-api/graph/scalars/graphql-type-prefixed-id.js';
@ObjectType()
@@ -43,7 +43,7 @@ export class ArrayDisk extends Node {
@Field(() => String, { nullable: true })
device?: string;
@Field(() => GraphQLLong, { description: '(KB) Disk Size total', nullable: true })
@Field(() => GraphQLBigInt, { description: '(KB) Disk Size total', nullable: true })
size?: number | null;
@Field(() => ArrayDiskStatus, { nullable: true })
@@ -58,40 +58,40 @@ export class ArrayDisk extends Node {
})
temp?: number | null;
@Field(() => GraphQLLong, {
@Field(() => GraphQLBigInt, {
nullable: true,
description:
'Count of I/O read requests sent to the device I/O drivers. These statistics may be cleared at any time.',
})
numReads?: number | null;
@Field(() => GraphQLLong, {
@Field(() => GraphQLBigInt, {
nullable: true,
description:
'Count of I/O writes requests sent to the device I/O drivers. These statistics may be cleared at any time.',
})
numWrites?: number | null;
@Field(() => GraphQLLong, {
@Field(() => GraphQLBigInt, {
nullable: true,
description:
'Number of unrecoverable errors reported by the device I/O drivers. Missing data due to unrecoverable array read errors is filled in on-the-fly using parity reconstruct (and we attempt to write this data back to the sector(s) which failed). Any unrecoverable write error results in disabling the disk.',
})
numErrors?: number | null;
@Field(() => GraphQLLong, {
@Field(() => GraphQLBigInt, {
nullable: true,
description: '(KB) Total Size of the FS (Not present on Parity type drive)',
})
fsSize?: number | null;
@Field(() => GraphQLLong, {
@Field(() => GraphQLBigInt, {
nullable: true,
description: '(KB) Free Size on the FS (Not present on Parity type drive)',
})
fsFree?: number | null;
@Field(() => GraphQLLong, {
@Field(() => GraphQLBigInt, {
nullable: true,
description: '(KB) Used Size on the FS (Not present on Parity type drive)',
})
@@ -243,13 +243,13 @@ export class Share extends Node {
@Field(() => String, { description: 'Display name', nullable: true })
name?: string | null;
@Field(() => GraphQLLong, { description: '(KB) Free space', nullable: true })
@Field(() => GraphQLBigInt, { description: '(KB) Free space', nullable: true })
free?: number | null;
@Field(() => GraphQLLong, { description: '(KB) Used Size', nullable: true })
@Field(() => GraphQLBigInt, { description: '(KB) Used Size', nullable: true })
used?: number | null;
@Field(() => GraphQLLong, { description: '(KB) Total size', nullable: true })
@Field(() => GraphQLBigInt, { description: '(KB) Total size', nullable: true })
size?: number | null;
@Field(() => [String], { description: 'Disks that are included in this share', nullable: true })

View File

@@ -8,7 +8,7 @@ import {
registerEnumType,
} from '@nestjs/graphql';
import { GraphQLJSON } from 'graphql-scalars';
import { GraphQLBigInt, GraphQLJSON } from 'graphql-scalars';
import { Node } from '@app/unraid-api/graph/resolvers/base.model.js';
import { ThemeName } from '@app/unraid-api/graph/resolvers/customization/theme.model.js';
@@ -290,7 +290,7 @@ export class Display extends Node {
@ObjectType({ implements: () => Node })
export class MemoryLayout extends Node {
@Field(() => Int)
@Field(() => GraphQLBigInt)
size!: number;
@Field(() => String, { nullable: true })
@@ -326,34 +326,34 @@ export class MemoryLayout extends Node {
@ObjectType({ implements: () => Node })
export class InfoMemory extends Node {
@Field(() => Int)
@Field(() => GraphQLBigInt)
max!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
total!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
free!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
used!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
active!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
available!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
buffcache!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
swaptotal!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
swapused!: number;
@Field(() => Int)
@Field(() => GraphQLBigInt)
swapfree!: number;
@Field(() => [MemoryLayout])

View File

@@ -1,35 +0,0 @@
import type { ASTNode } from 'graphql';
import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language/index.js';
const MAX_LONG = Number.MAX_SAFE_INTEGER;
const MIN_LONG = Number.MIN_SAFE_INTEGER;
const coerceLong = (value) => {
if (value === '')
throw new TypeError('Long cannot represent non 52-bit signed integer value: (empty string)');
const num = Number(value);
if (num == num && num <= MAX_LONG && num >= MIN_LONG) {
if (num < 0) {
return Math.ceil(num);
}
return Math.floor(num);
}
throw new TypeError('Long cannot represent non 52-bit signed integer value: ' + String(value));
};
const parseLiteral = (ast: ASTNode) => {
if (ast.kind === Kind.INT) {
const num = parseInt(ast.value, 10);
if (num <= MAX_LONG && num >= MIN_LONG) return num;
}
return null;
};
export const GraphQLLong = new GraphQLScalarType({
name: 'Long',
description: 'The `Long` scalar type represents 52-bit integers',
serialize: coerceLong,
parseValue: coerceLong,
parseLiteral: parseLiteral,
});

8
pnpm-lock.yaml generated
View File

@@ -222,6 +222,9 @@ importers:
jose:
specifier: ^6.0.0
version: 6.0.10
json-bigint-patch:
specifier: ^0.0.8
version: 0.0.8
lodash-es:
specifier: ^4.17.21
version: 4.17.21
@@ -8285,6 +8288,9 @@ packages:
engines: {node: '>=6'}
hasBin: true
json-bigint-patch@0.0.8:
resolution: {integrity: sha512-xa0LTQsyaq8awYyZyuUsporWisZFiyqzxGW8CKM3t7oouf0GFAKYJnqAm6e9NLNBQOCtOLvy614DEiRX/rPbnA==}
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -20855,6 +20861,8 @@ snapshots:
jsesc@3.1.0: {}
json-bigint-patch@0.0.8: {}
json-buffer@3.0.1: {}
json-parse-better-errors@1.0.2: {}