Skip to content

Commit

Permalink
feat(core): allow JsonType to use a stable JSON.stringify algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
rvion authored and gggdomi committed Oct 11, 2022
1 parent 9d5d457 commit 3341e63
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 0 deletions.
8 changes: 8 additions & 0 deletions docs/docs/custom-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,14 @@ and `stringify` only when needed).
object?: { foo: string; bar: number };
```

The default `stringify` will output diffs for changes in object key order.
If you want to minimize diffs, you can use a slower, more precise, serialization function that will serialize `{a: 1, b: 2}` and `{b: 2, a: 1}` in identical ways.

```ts
@Property({ type: new JsonType({ useStableStringify: true }) })
object: { foo: string; bar: number };
```

### DateType

To store dates without time information, we can use `DateType`. It does use `date`
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/types/JsonType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@ import { Type } from './Type';
import type { Platform } from '../platforms';
import type { EntityProperty } from '../typings';
import { Utils } from '../utils';
import { stableStringify } from '../utils/stableStringify';

export class JsonType extends Type<unknown, string | null> {

constructor(
private opts: { useStableStringify?: boolean } = {},
) {
super();
}

convertToDatabaseValue(value: unknown, platform: Platform): string | null {
if (platform.convertsJsonAutomatically(true) || value === null) {
return value as string;
}

if (this.opts.useStableStringify) {
return stableStringify(value);
}
return JSON.stringify(value);
}

Expand Down
41 changes: 41 additions & 0 deletions packages/core/src/utils/stableStringify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// extracted and modified from npm package "fast-json-stable-hash"
export function stableStringify(obj: any): string {
const type = typeof obj;
if (type === 'string') {
return JSON.stringify(obj);
}
if (Array.isArray(obj)) {
let str = '[';
const al = obj.length - 1;
for (let i = 0; i < obj.length; i++) {
str += stableStringify(obj[i]);
if (i !== al) {
str += ',';
}
}
return `${str}]`;
}
if (type === 'object' && obj !== null) {
let str = '{';
const keys = Object.keys(obj).sort();
const kl = keys.length - 1;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const val = (obj as any)[key];
if (val === undefined) {
continue;
}
if (i !== 0) {
str += ',';
}
str += `${JSON.stringify(key)}:${stableStringify(val)}`;
}
return `${str}}`;
}
if (type === 'number' || type === 'boolean' || obj == null) {
// bool, num, null have correct auto-coercions
return `${obj}`;
}

throw new TypeError(`Invalid JSON type of ${type}, value ${obj}. FJSH can only hash JSON objects.`);
}

0 comments on commit 3341e63

Please sign in to comment.