Skip to content

Commit 3175be7

Browse files
Merge pull request #92 from relaticle/fix/snowflake-id-precision-loss-4x
fix: cast record ID to string to prevent JS precision loss with snowflake IDs
2 parents bfe4e44 + 102ce29 commit 3175be7

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

src/Concerns/HasBoardRecords.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ public function getBatchedBoardRecordCounts(): array
154154
public function formatBoardRecord(Model $record): array
155155
{
156156
$formatted = [
157-
'id' => $record->getKey(),
157+
'id' => (string) $record->getKey(),
158158
'title' => data_get($record, $this->getRecordTitleAttribute()),
159159
'column' => data_get($record, $this->getColumnIdentifierAttribute()),
160160
'position' => data_get($record, $this->getPositionIdentifierAttribute()),
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Relaticle\Flowforge\Tests\Fixtures\Task;
6+
use Relaticle\Flowforge\Tests\Fixtures\TestBoard;
7+
8+
/**
9+
* Regression test for GitHub issue #88:
10+
* Large integer IDs (snowflakes) lose precision when passed through @js() in Blade.
11+
* JavaScript's Number type uses IEEE 754 doubles, which can only safely represent
12+
* integers up to 2^53 - 1 (9007199254740991). Snowflake IDs exceed this.
13+
*
14+
* @see https://github.com/relaticle/flowforge/issues/88
15+
*/
16+
describe('snowflake ID precision', function () {
17+
test('formatBoardRecord casts record ID to string to prevent JS precision loss', function () {
18+
$task = Task::factory()->todo()->withPosition('65535.0000000000')->create();
19+
20+
$board = app(TestBoard::class)->getBoard();
21+
$formatted = $board->formatBoardRecord($task);
22+
23+
// The ID must be a string so @js() emits a JSON string ("123") not a number (123)
24+
// This prevents JavaScript precision loss for large IDs like snowflakes
25+
expect($formatted['id'])->toBeString();
26+
});
27+
28+
test('card blade renders recordKey as string in wire:click for large IDs', function () {
29+
$task = Task::factory()->todo()->withPosition('65535.0000000000')->create();
30+
31+
// Simulate what @js() does: json_encode the record ID
32+
// If ID is an integer, json_encode produces a number literal which JS truncates
33+
$idAsInt = (int) $task->id;
34+
$jsonFromInt = json_encode(['recordKey' => $idAsInt]);
35+
36+
// If ID is a string, json_encode produces a quoted string which JS preserves
37+
$idAsString = (string) $task->id;
38+
$jsonFromString = json_encode(['recordKey' => $idAsString]);
39+
40+
// For a snowflake like 420533451316027392:
41+
// json_encode(int) -> {"recordKey":420533451316027392} <- JS reads as 420533451316027400 (WRONG)
42+
// json_encode(string) -> {"recordKey":"420533451316027392"} <- JS reads correctly
43+
$snowflakeId = 420533451316027392;
44+
$jsonSnowflakeInt = json_encode(['recordKey' => $snowflakeId]);
45+
$jsonSnowflakeStr = json_encode(['recordKey' => (string) $snowflakeId]);
46+
47+
// The string version wraps in quotes, preserving exact value
48+
expect($jsonSnowflakeStr)->toContain('"420533451316027392"');
49+
50+
// The int version does NOT wrap in quotes -- JS will lose precision
51+
expect($jsonSnowflakeInt)->not->toContain('"420533451316027392"');
52+
});
53+
});

0 commit comments

Comments
 (0)