fat-json 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +40 -0
- package/src/common/index.d.mts +11 -0
- package/src/common/index.mjs +21 -0
- package/src/index.d.mts +9 -0
- package/src/index.mjs +151 -0
- package/src/replacer/index.d.mts +5 -0
- package/src/replacer/index.mjs +39 -0
- package/src/reviver/index.d.mts +5 -0
- package/src/reviver/index.mjs +90 -0
- package/src/transformers/index.mjs +46 -0
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fat-json",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"main": "./src/index.mjs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "./src/index.d.mts",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Jonathan Perry for Sequence Media Limited",
|
|
9
|
+
"email": "sequencemedia@sequencemedia.net",
|
|
10
|
+
"url": "https://sequencemedia.net"
|
|
11
|
+
},
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/sequencemedia/fat-json.git"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"lint": "eslint .",
|
|
19
|
+
"lint:fix": "npm run lint -- --fix"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@sequencemedia/eslint-config-standard": "^0.2.82",
|
|
23
|
+
"@sequencemedia/eslint-config-typescript": "^0.1.141",
|
|
24
|
+
"@sequencemedia/sinon-chai": "^0.0.186",
|
|
25
|
+
"@types/chai": "^5.2.2",
|
|
26
|
+
"@types/mocha": "^10.0.10",
|
|
27
|
+
"@types/node": "^24.5.0",
|
|
28
|
+
"@types/sinon": "^17.0.4",
|
|
29
|
+
"chai": "^6.0.1",
|
|
30
|
+
"eslint": "^9.35.0",
|
|
31
|
+
"globals": "^16.4.0",
|
|
32
|
+
"mocha": "^11.7.2",
|
|
33
|
+
"sinon": "^21.0.0"
|
|
34
|
+
},
|
|
35
|
+
"imports": {
|
|
36
|
+
"#fat-json": "./src/index.mjs",
|
|
37
|
+
"#common": "./src/common/index.mjs",
|
|
38
|
+
"#transformers": "./src/transformers/index.mjs"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {FatJsonTypes.ValueType} ValueType
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const BIG_INT = /^(\d+)n$/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {ValueType | ValueType[]} v
|
|
9
|
+
* @returns {v is any[]}
|
|
10
|
+
*/
|
|
11
|
+
export function isArray (v) {
|
|
12
|
+
return Array.isArray(v)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {ValueType | ValueType[]} v
|
|
17
|
+
* @returns {v is object}
|
|
18
|
+
*/
|
|
19
|
+
export function isObject (v) {
|
|
20
|
+
return (v || false) instanceof Object && !Array.isArray(v)
|
|
21
|
+
}
|
package/src/index.d.mts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
namespace FatJsonTypes {
|
|
3
|
+
export type ArrayLiteralType = unknown[] | never[]
|
|
4
|
+
export type ObjectLiteralType = Record<PropertyKey, unknown> | Record<PropertyKey, never>
|
|
5
|
+
export type ValueType = string | number | boolean | null | bigint | ArrayLiteralType | ObjectLiteralType | Buffer | undefined | object
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {}
|
package/src/index.mjs
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {FatJsonTypes.ValueType} ValueType
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
BIG_INT
|
|
6
|
+
} from './common/index.mjs'
|
|
7
|
+
import getPath from './replacer/index.mjs'
|
|
8
|
+
import genPath from './reviver/index.mjs'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {(key: PropertyKey, value: ValueType, type?: string) => ValueType} replacer
|
|
12
|
+
* @returns {(key: PropertyKey, value: ValueType) => ValueType}
|
|
13
|
+
*/
|
|
14
|
+
export function useReplacerWithType (replacer) {
|
|
15
|
+
/**
|
|
16
|
+
* @this {any | any[]}
|
|
17
|
+
* @param {PropertyKey} key
|
|
18
|
+
* @param {ValueType} value
|
|
19
|
+
* @returns {ValueType}
|
|
20
|
+
*/
|
|
21
|
+
return function withType (key, value) {
|
|
22
|
+
return replacer.call(this, key, value, typeof value)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {(key: PropertyKey, value: ValueType, type?: string) => ValueType} reviver
|
|
28
|
+
* @returns {(key: PropertyKey, value: ValueType) => ValueType}
|
|
29
|
+
*/
|
|
30
|
+
export function useReviverWithType (reviver) {
|
|
31
|
+
/**
|
|
32
|
+
* @this {any | any[]}
|
|
33
|
+
* @param {PropertyKey} key
|
|
34
|
+
* @param {ValueType} value
|
|
35
|
+
* @returns {ValueType}
|
|
36
|
+
*/
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
return function withType (key, value) {
|
|
39
|
+
return this[key] = reviver.call(this, key, value, typeof value)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {(key: PropertyKey, value: ValueType, path?: string) => ValueType} replacer
|
|
45
|
+
* @returns {(key: PropertyKey, value: ValueType) => ValueType}
|
|
46
|
+
*/
|
|
47
|
+
export function useReplacerWithPath (replacer) {
|
|
48
|
+
/**
|
|
49
|
+
* @this {any | any[]}
|
|
50
|
+
* @param {PropertyKey} key
|
|
51
|
+
* @param {ValueType} value
|
|
52
|
+
* @returns {ValueType}
|
|
53
|
+
*/
|
|
54
|
+
return function withPath (key, value) {
|
|
55
|
+
return replacer.call(this, key, value, getPath(key, value, this))
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {(key: PropertyKey, value: ValueType, path?: string) => ValueType} reviver
|
|
61
|
+
* @returns {(key: PropertyKey, value: ValueType) => ValueType}
|
|
62
|
+
*/
|
|
63
|
+
export function useReviverWithPath (reviver) {
|
|
64
|
+
/**
|
|
65
|
+
* @this {any | any[]}
|
|
66
|
+
*
|
|
67
|
+
* @param {PropertyKey} key
|
|
68
|
+
* @param {ValueType} value
|
|
69
|
+
* @returns {ValueType}
|
|
70
|
+
*/
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
return function withPath (key, value) {
|
|
73
|
+
if (!key) {
|
|
74
|
+
for (const { path, ...args } of genPath(key, value, this)) {
|
|
75
|
+
if (path !== '$') {
|
|
76
|
+
const {
|
|
77
|
+
context,
|
|
78
|
+
key,
|
|
79
|
+
value
|
|
80
|
+
} = args
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
context[key] = reviver.call(context, key, value, path)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return this[key] = reviver.call(this, key, value, '$')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return value
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {PropertyKey} key
|
|
95
|
+
* @param {ValueType} value
|
|
96
|
+
* @returns {ValueType}
|
|
97
|
+
*/
|
|
98
|
+
export function replacer (key, value) {
|
|
99
|
+
if (typeof value === 'bigint') return String(value) + 'n'
|
|
100
|
+
|
|
101
|
+
return value
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {PropertyKey} key
|
|
106
|
+
* @param {ValueType} value
|
|
107
|
+
* @returns {ValueType}
|
|
108
|
+
*/
|
|
109
|
+
export function reviver (key, value) {
|
|
110
|
+
if (typeof value === 'string' && BIG_INT.test(value)) return BigInt (value.replace(BIG_INT, '$1'))
|
|
111
|
+
|
|
112
|
+
return value
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {Set<PropertyKey>} [set]
|
|
117
|
+
* @returns {(key: PropertyKey, value: ValueType) => ValueType}
|
|
118
|
+
*/
|
|
119
|
+
export function getReplacerFor (set = new Set()) {
|
|
120
|
+
/**
|
|
121
|
+
* @param {PropertyKey} key
|
|
122
|
+
* @param {ValueType} value
|
|
123
|
+
* @returns {ValueType}
|
|
124
|
+
*/
|
|
125
|
+
return function replacer (key, value) {
|
|
126
|
+
if (set.has(key)) {
|
|
127
|
+
if (typeof value === 'bigint') return String(value)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return value
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {Set<PropertyKey>} [set]
|
|
136
|
+
* @returns {(key: PropertyKey, value: ValueType) => ValueType}
|
|
137
|
+
*/
|
|
138
|
+
export function getReviverFor (set = new Set()) {
|
|
139
|
+
/**
|
|
140
|
+
* @param {PropertyKey} key
|
|
141
|
+
* @param {ValueType} value
|
|
142
|
+
* @returns {ValueType}
|
|
143
|
+
*/
|
|
144
|
+
return function reviver (key, value) {
|
|
145
|
+
if (set.has(key)) {
|
|
146
|
+
if (typeof value === 'string') return BigInt(value)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return value
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type ArrayLiteralType = FatJsonTypes.ArrayLiteralType
|
|
2
|
+
type ObjectLiteralType = FatJsonTypes.ObjectLiteralType
|
|
3
|
+
type ValueType = FatJsonTypes.ValueType
|
|
4
|
+
|
|
5
|
+
export default function getPath (key: PropertyKey, value: ValueType, context: ArrayLiteralType | ObjectLiteralType): string
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isArray,
|
|
3
|
+
isObject
|
|
4
|
+
} from '#common'
|
|
5
|
+
|
|
6
|
+
const MAP = new WeakMap()
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {PropertyKey} key
|
|
10
|
+
* @param {unknown} value
|
|
11
|
+
* @param {any} context
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
export default function getPath (key, value, context) {
|
|
15
|
+
let valuePath = String(key) || '$'
|
|
16
|
+
|
|
17
|
+
if (MAP.has(context)) {
|
|
18
|
+
const contextPath = MAP.get(context)
|
|
19
|
+
|
|
20
|
+
valuePath = (
|
|
21
|
+
contextPath + (
|
|
22
|
+
Array.isArray(context)
|
|
23
|
+
? `[${key}]`
|
|
24
|
+
: ( // valuePath.includes(SP) || valuePath.includes(ST)
|
|
25
|
+
/[ .]/.test(key)
|
|
26
|
+
? `.['${key}']`
|
|
27
|
+
: `.${key}`
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
isArray(value) ||
|
|
35
|
+
isObject(value)
|
|
36
|
+
) MAP.set(value, valuePath)
|
|
37
|
+
|
|
38
|
+
return valuePath
|
|
39
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type ArrayLiteralType = FatJsonTypes.ArrayLiteralType
|
|
2
|
+
type ObjectLiteralType = FatJsonTypes.ObjectLiteralType
|
|
3
|
+
type ValueType = FatJsonTypes.ValueType
|
|
4
|
+
|
|
5
|
+
export default function genPath (key: PropertyKey, value: ValueType, context: ArrayLiteralType | ObjectLiteralType): IterableIterator<{ key: PropertyKey; value: ValueType; path: string; context: ArrayLiteralType | ObjectLiteralType }>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {FatJsonTypes.ArrayLiteralType} ArrayLiteralType
|
|
3
|
+
* @typedef {FatJsonTypes.ObjectLiteralType} ObjectLiteralType
|
|
4
|
+
* @typedef {FatJsonTypes.ValueType} ValueType
|
|
5
|
+
* @typedef {FatJsonTypes.WeakMapType} WeakMapType
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
isArray,
|
|
10
|
+
isObject
|
|
11
|
+
} from '#common'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {any[]} context
|
|
15
|
+
* @param {string} contextPath
|
|
16
|
+
* @returns {IterableIterator<{ key: PropertyKey; value: unknown; path: string; context: any }>}
|
|
17
|
+
*/
|
|
18
|
+
function * genPathForArray (context, contextPath) {
|
|
19
|
+
for (const [index, value] of context.entries()) {
|
|
20
|
+
const valuePath = contextPath + `[${index}]`
|
|
21
|
+
|
|
22
|
+
if (isArray(value)) yield * genPathForArray(value, valuePath)
|
|
23
|
+
|
|
24
|
+
if (isObject(value)) yield * genPathForObject(value, valuePath)
|
|
25
|
+
|
|
26
|
+
yield {
|
|
27
|
+
key: String(index),
|
|
28
|
+
value,
|
|
29
|
+
path: valuePath,
|
|
30
|
+
context
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {any} context
|
|
37
|
+
* @param {string} contextPath
|
|
38
|
+
* @returns {IterableIterator<{ key: PropertyKey; value: unknown; path: string; context: any }>}
|
|
39
|
+
*/
|
|
40
|
+
function * genPathForObject (context, contextPath) {
|
|
41
|
+
for (const [key, value] of Object.entries(context)) {
|
|
42
|
+
const valuePath = (
|
|
43
|
+
contextPath + (
|
|
44
|
+
/[ .]/.test(key)
|
|
45
|
+
? `.['${key}']`
|
|
46
|
+
: `.${key}`
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if (isArray(value)) yield * genPathForArray(value, valuePath)
|
|
51
|
+
|
|
52
|
+
if (isObject(value)) yield * genPathForObject(value, valuePath)
|
|
53
|
+
|
|
54
|
+
yield {
|
|
55
|
+
key: String(key), // should already be a string but
|
|
56
|
+
value,
|
|
57
|
+
path: valuePath,
|
|
58
|
+
context
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {PropertyKey} key
|
|
65
|
+
* @param {unknown} value
|
|
66
|
+
* @param {any | any[]} context
|
|
67
|
+
* @returns {IterableIterator<{ key: PropertyKey; value: unknown; path: string; context: any }>}
|
|
68
|
+
*/
|
|
69
|
+
export default function * genPath (key, value, context) {
|
|
70
|
+
const valuePath = key
|
|
71
|
+
? typeof key === 'number'
|
|
72
|
+
? `[${key}]`
|
|
73
|
+
: (
|
|
74
|
+
/[ .]/.test(key)
|
|
75
|
+
? `.['${key}']`
|
|
76
|
+
: `.${key}`
|
|
77
|
+
)
|
|
78
|
+
: '$'
|
|
79
|
+
|
|
80
|
+
if (isArray(value)) yield * genPathForArray(value, valuePath)
|
|
81
|
+
|
|
82
|
+
if (isObject(value)) yield * genPathForObject(value, valuePath)
|
|
83
|
+
|
|
84
|
+
yield {
|
|
85
|
+
key: String(key),
|
|
86
|
+
value,
|
|
87
|
+
path: valuePath,
|
|
88
|
+
context
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {BigInt} value
|
|
3
|
+
* @returns {string}
|
|
4
|
+
*/
|
|
5
|
+
export function fromBigIntToString (value) {
|
|
6
|
+
return String(value)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} value
|
|
11
|
+
* @returns {BigInt}
|
|
12
|
+
*/
|
|
13
|
+
export function fromStringToBigInt (value) {
|
|
14
|
+
return BigInt(value)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} b
|
|
19
|
+
* @returns {number}
|
|
20
|
+
*/
|
|
21
|
+
function toCodePoint (b) {
|
|
22
|
+
const c = b.codePointAt(0)
|
|
23
|
+
|
|
24
|
+
return Number(c)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* ```javascript
|
|
29
|
+
* Buffer.from(Uint8Array.fromBase64(base64))
|
|
30
|
+
* ```
|
|
31
|
+
* @param {string} base64
|
|
32
|
+
* @returns {Buffer}
|
|
33
|
+
*/
|
|
34
|
+
export function fromBase64ToBuffer (base64) {
|
|
35
|
+
const binary = atob(base64) // base64 string to binary string
|
|
36
|
+
const uint8Array = Uint8Array.from(binary, toCodePoint) // binary string to code point integer
|
|
37
|
+
return Buffer.from(uint8Array)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {Buffer} buffer
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
export function fromBufferToBase64 (buffer) {
|
|
45
|
+
return buffer.toString('base64')
|
|
46
|
+
}
|