json-web3 0.1.0 → 1.0.0

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025
3
+ Copyright (c) 2025 github/haozi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -20,16 +20,18 @@ import jsonWeb3 from 'json-web3'
20
20
  const payload = {
21
21
  balance: 1234567890123456789n,
22
22
  decimals: 18n,
23
+ data: new Uint8Array([1, 2, 3, 255]),
23
24
  }
24
25
 
25
26
  const text = jsonWeb3.stringify(payload)
26
- // => {"balance":{"__@json.bigint__":"1234567890123456789"},"decimals":18n}
27
+ // => {"balance":{"__@json.bigint__":"1234567890123456789"},"decimals":{"__@json.bigint__":"18"},"data":{"__@json.bytes__":"0x010203ff"}}
27
28
 
28
29
  const restored = jsonWeb3.parse(text)
29
30
  /* =>
30
31
  {
31
32
  "balance": 1234567890123456789n,
32
- "decimals": 18n
33
+ "decimals": 18n,
34
+ "data": Uint8Array(4) [1, 2, 3, 255]
33
35
  }
34
36
  */
35
37
  ```
@@ -37,8 +39,8 @@ const restored = jsonWeb3.parse(text)
37
39
  ## API (Fully compatible with native globalThis.JSON)
38
40
 
39
41
  - `stringify(value, replacer?, space?)`
40
- - `parse(text)`
42
+ - `parse(text, reviver?)`
41
43
 
42
44
  ## Notes
43
45
 
44
- `bigint` values are encoded as objects: `{"__@json.bigint__":"<value>"}`. More types can be added later.
46
+ `bigint` values are encoded as objects: `{"__@json.bigint__":"<value>"}`. `Uint8Array`/`ArrayBuffer` (and Node `Buffer` JSON shapes) are encoded as `{"__@json.bytes__":"0x..."}` and decoded back to `Uint8Array`.
package/dist/index.cjs CHANGED
@@ -25,32 +25,97 @@ __export(index_exports, {
25
25
  stringify: () => stringify
26
26
  });
27
27
  module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/utils.ts
28
30
  var BIGINT_TAG = "__@json.bigint__";
31
+ var BYTES_TAG = "__@json.bytes__";
32
+ var undefined = void 0;
33
+ var isUndefined = (value) => value === undefined;
34
+ var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
35
+ var isArray = (value) => Array.isArray(value);
36
+ var isObject = (value) => value !== null && typeof value === "object" && !isArray(value);
37
+ var isFunction = (value) => typeof value === "function";
38
+ var isBigInt = (value) => typeof value === "bigint";
39
+ var isUint8Array = (value) => !isUndefined(Uint8Array) && value instanceof Uint8Array;
40
+ var isArrayBuffer = (value) => !isUndefined(ArrayBuffer) && value instanceof ArrayBuffer;
41
+ var toHex = (value) => {
42
+ let out = "0x";
43
+ for (const byte of value) {
44
+ out += byte.toString(16).padStart(2, "0");
45
+ }
46
+ return out;
47
+ };
48
+ var fromHex = (hex) => {
49
+ const normalized = hex.startsWith("0x") ? hex.slice(2) : hex;
50
+ if (normalized.length % 2 !== 0) {
51
+ throw new Error("Invalid hex string for bytes");
52
+ }
53
+ if (!isUndefined(Buffer)) {
54
+ return Uint8Array.from(Buffer.from(normalized, "hex"));
55
+ }
56
+ const out = new Uint8Array(normalized.length / 2);
57
+ for (let i = 0; i < normalized.length; i += 2) {
58
+ out[i / 2] = Number.parseInt(normalized.slice(i, i + 2), 16);
59
+ }
60
+ return out;
61
+ };
62
+ var toSerializable = (value) => {
63
+ if (isBigInt(value)) {
64
+ return { [BIGINT_TAG]: value.toString() };
65
+ }
66
+ if (isUint8Array(value)) {
67
+ return { [BYTES_TAG]: toHex(value) };
68
+ }
69
+ if (isArrayBuffer(value)) {
70
+ return { [BYTES_TAG]: toHex(new Uint8Array(value)) };
71
+ }
72
+ if (value && isObject(value) && value.type === "Buffer" && isArray(value.data) && Object.keys(value).length === 2 && value.data.every((entry) => Number.isInteger(entry) && entry >= 0 && entry <= 255)) {
73
+ return { [BYTES_TAG]: toHex(Uint8Array.from(value.data)) };
74
+ }
75
+ return value;
76
+ };
77
+ var fromSerializable = (value) => {
78
+ if (value && isObject(value)) {
79
+ if (hasOwnProperty(value, BIGINT_TAG)) {
80
+ return BigInt(value[BIGINT_TAG]);
81
+ }
82
+ if (hasOwnProperty(value, BYTES_TAG)) {
83
+ return fromHex(value[BYTES_TAG]);
84
+ }
85
+ if (value.type === "Buffer" && isArray(value.data) && Object.keys(value).length === 2 && value.data.every((entry) => Number.isInteger(entry) && entry >= 0 && entry <= 255)) {
86
+ return Uint8Array.from(value.data);
87
+ }
88
+ }
89
+ return value;
90
+ };
91
+
92
+ // src/index.ts
29
93
  var RAW_JSON = JSON;
30
- var applyReplacer = (key, value, replacer) => {
31
- if (typeof replacer === "function") {
94
+ var applyReplacer = (holder, key, value, replacer) => {
95
+ if (isFunction(replacer)) {
32
96
  return replacer(key, value);
33
97
  }
34
- if (Array.isArray(replacer)) {
98
+ if (isArray(replacer)) {
35
99
  if (key === "") return value;
36
100
  if (key === BIGINT_TAG) return value;
37
- return replacer.includes(key) ? value : void 0;
101
+ if (key === BYTES_TAG) return value;
102
+ if (isArray(holder)) return value;
103
+ return replacer.includes(key) ? value : undefined;
38
104
  }
39
105
  return value;
40
106
  };
41
- var toSerializable = (value) => typeof value === "bigint" ? { [BIGINT_TAG]: value.toString() } : value;
42
107
  var stringify = (value, replacer = null, space) => RAW_JSON.stringify(
43
108
  value,
44
- (key, v) => {
45
- const replaced = applyReplacer(key, v, replacer);
109
+ function replacerFn(key, v) {
110
+ const replaced = applyReplacer(this, key, v, replacer);
46
111
  return toSerializable(replaced);
47
112
  },
48
113
  space
49
114
  );
50
- var parse = (text) => RAW_JSON.parse(
51
- text,
52
- (_, v) => v && typeof v === "object" && BIGINT_TAG in v ? BigInt(v[BIGINT_TAG]) : v
53
- );
115
+ var parse = (text, reviver = null) => RAW_JSON.parse(text, (key, v) => {
116
+ const decoded = fromSerializable(v);
117
+ return typeof reviver === "function" ? reviver(key, decoded) : decoded;
118
+ });
54
119
  var index_default = {
55
120
  stringify,
56
121
  parse
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const BIGINT_TAG = '__@json.bigint__'\nconst RAW_JSON = JSON\ntype Replacer = ((this: any, key: string, value: any) => any) | Array<string | number> | null\n\nconst applyReplacer = (key: string, value: any, replacer: Replacer): any => {\n if (typeof replacer === 'function') {\n return replacer(key, value)\n }\n if (Array.isArray(replacer)) {\n if (key === '') return value\n if (key === BIGINT_TAG) return value\n return replacer.includes(key) ? value : undefined\n }\n return value\n}\n\nconst toSerializable = (value: any): any =>\n typeof value === 'bigint' ? { [BIGINT_TAG]: value.toString() } : value\n\nexport const stringify = (value: any, replacer: Replacer = null, space?: string | number): string =>\n RAW_JSON.stringify(\n value,\n (key, v) => {\n const replaced = applyReplacer(key, v, replacer)\n return toSerializable(replaced)\n },\n space,\n )\n\nexport const parse = (text: string): any =>\n RAW_JSON.parse(text, (_, v) =>\n v && typeof v === 'object' && BIGINT_TAG in v ? BigInt(v[BIGINT_TAG]) : v,\n )\n\nexport default {\n stringify,\n parse,\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAM,aAAa;AACnB,IAAM,WAAW;AAGjB,IAAM,gBAAgB,CAAC,KAAa,OAAY,aAA4B;AAC1E,MAAI,OAAO,aAAa,YAAY;AAClC,WAAO,SAAS,KAAK,KAAK;AAAA,EAC5B;AACA,MAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,QAAI,QAAQ,GAAI,QAAO;AACvB,QAAI,QAAQ,WAAY,QAAO;AAC/B,WAAO,SAAS,SAAS,GAAG,IAAI,QAAQ;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,IAAM,iBAAiB,CAAC,UACtB,OAAO,UAAU,WAAW,EAAE,CAAC,UAAU,GAAG,MAAM,SAAS,EAAE,IAAI;AAE5D,IAAM,YAAY,CAAC,OAAY,WAAqB,MAAM,UAC/D,SAAS;AAAA,EACP;AAAA,EACA,CAAC,KAAK,MAAM;AACV,UAAM,WAAW,cAAc,KAAK,GAAG,QAAQ;AAC/C,WAAO,eAAe,QAAQ;AAAA,EAChC;AAAA,EACA;AACF;AAEK,IAAM,QAAQ,CAAC,SACpB,SAAS;AAAA,EAAM;AAAA,EAAM,CAAC,GAAG,MACvB,KAAK,OAAO,MAAM,YAAY,cAAc,IAAI,OAAO,EAAE,UAAU,CAAC,IAAI;AAC1E;AAEF,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils.ts"],"sourcesContent":["import {\n BIGINT_TAG,\n BYTES_TAG,\n fromSerializable,\n isArray,\n isFunction,\n toSerializable,\n undefined,\n} from './utils'\n\nconst RAW_JSON = JSON\ntype Replacer = ((this: any, key: string, value: any) => any) | Array<string | number> | null\ntype Reviver = ((this: any, key: string, value: any) => any) | null\n\nconst applyReplacer = (holder: any, key: string, value: any, replacer: Replacer): any => {\n if (isFunction(replacer)) {\n return replacer(key, value)\n }\n if (isArray(replacer)) {\n if (key === '') return value\n if (key === BIGINT_TAG) return value\n if (key === BYTES_TAG) return value\n if (isArray(holder)) return value\n return replacer.includes(key) ? value : undefined\n }\n return value\n}\n\nexport const stringify = (value: any, replacer: Replacer = null, space?: string | number): string =>\n RAW_JSON.stringify(\n value,\n function replacerFn(key, v) {\n const replaced = applyReplacer(this, key, v, replacer)\n return toSerializable(replaced)\n },\n space,\n )\n\nexport const parse = (text: string, reviver: Reviver = null): any =>\n RAW_JSON.parse(text, (key, v) => {\n const decoded = fromSerializable(v)\n return typeof reviver === 'function' ? reviver(key, decoded) : decoded\n })\n\nexport default {\n stringify,\n parse,\n}\n","export const BIGINT_TAG = '__@json.bigint__'\nexport const BYTES_TAG = '__@json.bytes__'\n\nexport const undefined = void 0\nexport const isUndefined = (value: any): value is undefined => value === undefined\nexport const hasOwnProperty = (obj: any, prop: string): boolean =>\n Object.prototype.hasOwnProperty.call(obj, prop)\nexport const isArray = (value: any): value is Array<any> => Array.isArray(value)\nexport const isObject = (value: any): value is Record<string, any> =>\n value !== null && typeof value === 'object' && !isArray(value)\nexport const isFunction = (value: any): value is Function => typeof value === 'function'\nexport const isBigInt = (value: any): value is bigint => typeof value === 'bigint'\nexport const isBuffer = (value: any): value is Buffer =>\n !isUndefined(Buffer) && isFunction(Buffer.isBuffer) && Buffer.isBuffer(value)\nexport const isUint8Array = (value: any): value is Uint8Array =>\n !isUndefined(Uint8Array) && value instanceof Uint8Array\nexport const isArrayBuffer = (value: any): value is ArrayBuffer =>\n !isUndefined(ArrayBuffer) && value instanceof ArrayBuffer\n\nexport const toHex = (value: Uint8Array): string => {\n let out = '0x'\n for (const byte of value) {\n out += byte.toString(16).padStart(2, '0')\n }\n return out\n}\n\nexport const fromHex = (hex: string): Uint8Array => {\n const normalized = hex.startsWith('0x') ? hex.slice(2) : hex\n if (normalized.length % 2 !== 0) {\n throw new Error('Invalid hex string for bytes')\n }\n\n if (!isUndefined(Buffer)) {\n return Uint8Array.from(Buffer.from(normalized, 'hex'))\n }\n\n const out = new Uint8Array(normalized.length / 2)\n for (let i = 0; i < normalized.length; i += 2) {\n out[i / 2] = Number.parseInt(normalized.slice(i, i + 2), 16)\n }\n return out\n}\n\nexport const toSerializable = (value: any): any => {\n if (isBigInt(value)) {\n return { [BIGINT_TAG]: value.toString() }\n }\n\n if (isUint8Array(value)) {\n return { [BYTES_TAG]: toHex(value) }\n }\n\n if (isArrayBuffer(value)) {\n return { [BYTES_TAG]: toHex(new Uint8Array(value)) }\n }\n\n if (\n value &&\n isObject(value) &&\n value.type === 'Buffer' &&\n isArray(value.data) &&\n Object.keys(value).length === 2 &&\n value.data.every((entry: any) => Number.isInteger(entry) && entry >= 0 && entry <= 255)\n ) {\n return { [BYTES_TAG]: toHex(Uint8Array.from(value.data)) }\n }\n\n return value\n}\n\nexport const fromSerializable = (value: any): any => {\n if (value && isObject(value)) {\n if (hasOwnProperty(value, BIGINT_TAG)) {\n return BigInt(value[BIGINT_TAG])\n }\n if (hasOwnProperty(value, BYTES_TAG)) {\n return fromHex(value[BYTES_TAG])\n }\n if (\n value.type === 'Buffer' &&\n isArray(value.data) &&\n Object.keys(value).length === 2 &&\n value.data.every((entry: any) => Number.isInteger(entry) && entry >= 0 && entry <= 255)\n ) {\n return Uint8Array.from(value.data)\n }\n }\n return value\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
1
  type Replacer = ((this: any, key: string, value: any) => any) | Array<string | number> | null;
2
+ type Reviver = ((this: any, key: string, value: any) => any) | null;
2
3
  declare const stringify: (value: any, replacer?: Replacer, space?: string | number) => string;
3
- declare const parse: (text: string) => any;
4
+ declare const parse: (text: string, reviver?: Reviver) => any;
4
5
  declare const _default: {
5
6
  stringify: (value: any, replacer?: Replacer, space?: string | number) => string;
6
- parse: (text: string) => any;
7
+ parse: (text: string, reviver?: Reviver) => any;
7
8
  };
8
9
 
9
10
  export { _default as default, parse, stringify };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  type Replacer = ((this: any, key: string, value: any) => any) | Array<string | number> | null;
2
+ type Reviver = ((this: any, key: string, value: any) => any) | null;
2
3
  declare const stringify: (value: any, replacer?: Replacer, space?: string | number) => string;
3
- declare const parse: (text: string) => any;
4
+ declare const parse: (text: string, reviver?: Reviver) => any;
4
5
  declare const _default: {
5
6
  stringify: (value: any, replacer?: Replacer, space?: string | number) => string;
6
- parse: (text: string) => any;
7
+ parse: (text: string, reviver?: Reviver) => any;
7
8
  };
8
9
 
9
10
  export { _default as default, parse, stringify };
package/dist/index.js CHANGED
@@ -1,30 +1,93 @@
1
- // src/index.ts
1
+ // src/utils.ts
2
2
  var BIGINT_TAG = "__@json.bigint__";
3
+ var BYTES_TAG = "__@json.bytes__";
4
+ var undefined = void 0;
5
+ var isUndefined = (value) => value === undefined;
6
+ var hasOwnProperty = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
7
+ var isArray = (value) => Array.isArray(value);
8
+ var isObject = (value) => value !== null && typeof value === "object" && !isArray(value);
9
+ var isFunction = (value) => typeof value === "function";
10
+ var isBigInt = (value) => typeof value === "bigint";
11
+ var isUint8Array = (value) => !isUndefined(Uint8Array) && value instanceof Uint8Array;
12
+ var isArrayBuffer = (value) => !isUndefined(ArrayBuffer) && value instanceof ArrayBuffer;
13
+ var toHex = (value) => {
14
+ let out = "0x";
15
+ for (const byte of value) {
16
+ out += byte.toString(16).padStart(2, "0");
17
+ }
18
+ return out;
19
+ };
20
+ var fromHex = (hex) => {
21
+ const normalized = hex.startsWith("0x") ? hex.slice(2) : hex;
22
+ if (normalized.length % 2 !== 0) {
23
+ throw new Error("Invalid hex string for bytes");
24
+ }
25
+ if (!isUndefined(Buffer)) {
26
+ return Uint8Array.from(Buffer.from(normalized, "hex"));
27
+ }
28
+ const out = new Uint8Array(normalized.length / 2);
29
+ for (let i = 0; i < normalized.length; i += 2) {
30
+ out[i / 2] = Number.parseInt(normalized.slice(i, i + 2), 16);
31
+ }
32
+ return out;
33
+ };
34
+ var toSerializable = (value) => {
35
+ if (isBigInt(value)) {
36
+ return { [BIGINT_TAG]: value.toString() };
37
+ }
38
+ if (isUint8Array(value)) {
39
+ return { [BYTES_TAG]: toHex(value) };
40
+ }
41
+ if (isArrayBuffer(value)) {
42
+ return { [BYTES_TAG]: toHex(new Uint8Array(value)) };
43
+ }
44
+ if (value && isObject(value) && value.type === "Buffer" && isArray(value.data) && Object.keys(value).length === 2 && value.data.every((entry) => Number.isInteger(entry) && entry >= 0 && entry <= 255)) {
45
+ return { [BYTES_TAG]: toHex(Uint8Array.from(value.data)) };
46
+ }
47
+ return value;
48
+ };
49
+ var fromSerializable = (value) => {
50
+ if (value && isObject(value)) {
51
+ if (hasOwnProperty(value, BIGINT_TAG)) {
52
+ return BigInt(value[BIGINT_TAG]);
53
+ }
54
+ if (hasOwnProperty(value, BYTES_TAG)) {
55
+ return fromHex(value[BYTES_TAG]);
56
+ }
57
+ if (value.type === "Buffer" && isArray(value.data) && Object.keys(value).length === 2 && value.data.every((entry) => Number.isInteger(entry) && entry >= 0 && entry <= 255)) {
58
+ return Uint8Array.from(value.data);
59
+ }
60
+ }
61
+ return value;
62
+ };
63
+
64
+ // src/index.ts
3
65
  var RAW_JSON = JSON;
4
- var applyReplacer = (key, value, replacer) => {
5
- if (typeof replacer === "function") {
66
+ var applyReplacer = (holder, key, value, replacer) => {
67
+ if (isFunction(replacer)) {
6
68
  return replacer(key, value);
7
69
  }
8
- if (Array.isArray(replacer)) {
70
+ if (isArray(replacer)) {
9
71
  if (key === "") return value;
10
72
  if (key === BIGINT_TAG) return value;
11
- return replacer.includes(key) ? value : void 0;
73
+ if (key === BYTES_TAG) return value;
74
+ if (isArray(holder)) return value;
75
+ return replacer.includes(key) ? value : undefined;
12
76
  }
13
77
  return value;
14
78
  };
15
- var toSerializable = (value) => typeof value === "bigint" ? { [BIGINT_TAG]: value.toString() } : value;
16
79
  var stringify = (value, replacer = null, space) => RAW_JSON.stringify(
17
80
  value,
18
- (key, v) => {
19
- const replaced = applyReplacer(key, v, replacer);
81
+ function replacerFn(key, v) {
82
+ const replaced = applyReplacer(this, key, v, replacer);
20
83
  return toSerializable(replaced);
21
84
  },
22
85
  space
23
86
  );
24
- var parse = (text) => RAW_JSON.parse(
25
- text,
26
- (_, v) => v && typeof v === "object" && BIGINT_TAG in v ? BigInt(v[BIGINT_TAG]) : v
27
- );
87
+ var parse = (text, reviver = null) => RAW_JSON.parse(text, (key, v) => {
88
+ const decoded = fromSerializable(v);
89
+ return typeof reviver === "function" ? reviver(key, decoded) : decoded;
90
+ });
28
91
  var index_default = {
29
92
  stringify,
30
93
  parse
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["const BIGINT_TAG = '__@json.bigint__'\nconst RAW_JSON = JSON\ntype Replacer = ((this: any, key: string, value: any) => any) | Array<string | number> | null\n\nconst applyReplacer = (key: string, value: any, replacer: Replacer): any => {\n if (typeof replacer === 'function') {\n return replacer(key, value)\n }\n if (Array.isArray(replacer)) {\n if (key === '') return value\n if (key === BIGINT_TAG) return value\n return replacer.includes(key) ? value : undefined\n }\n return value\n}\n\nconst toSerializable = (value: any): any =>\n typeof value === 'bigint' ? { [BIGINT_TAG]: value.toString() } : value\n\nexport const stringify = (value: any, replacer: Replacer = null, space?: string | number): string =>\n RAW_JSON.stringify(\n value,\n (key, v) => {\n const replaced = applyReplacer(key, v, replacer)\n return toSerializable(replaced)\n },\n space,\n )\n\nexport const parse = (text: string): any =>\n RAW_JSON.parse(text, (_, v) =>\n v && typeof v === 'object' && BIGINT_TAG in v ? BigInt(v[BIGINT_TAG]) : v,\n )\n\nexport default {\n stringify,\n parse,\n}\n"],"mappings":";AAAA,IAAM,aAAa;AACnB,IAAM,WAAW;AAGjB,IAAM,gBAAgB,CAAC,KAAa,OAAY,aAA4B;AAC1E,MAAI,OAAO,aAAa,YAAY;AAClC,WAAO,SAAS,KAAK,KAAK;AAAA,EAC5B;AACA,MAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,QAAI,QAAQ,GAAI,QAAO;AACvB,QAAI,QAAQ,WAAY,QAAO;AAC/B,WAAO,SAAS,SAAS,GAAG,IAAI,QAAQ;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,IAAM,iBAAiB,CAAC,UACtB,OAAO,UAAU,WAAW,EAAE,CAAC,UAAU,GAAG,MAAM,SAAS,EAAE,IAAI;AAE5D,IAAM,YAAY,CAAC,OAAY,WAAqB,MAAM,UAC/D,SAAS;AAAA,EACP;AAAA,EACA,CAAC,KAAK,MAAM;AACV,UAAM,WAAW,cAAc,KAAK,GAAG,QAAQ;AAC/C,WAAO,eAAe,QAAQ;AAAA,EAChC;AAAA,EACA;AACF;AAEK,IAAM,QAAQ,CAAC,SACpB,SAAS;AAAA,EAAM;AAAA,EAAM,CAAC,GAAG,MACvB,KAAK,OAAO,MAAM,YAAY,cAAc,IAAI,OAAO,EAAE,UAAU,CAAC,IAAI;AAC1E;AAEF,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["export const BIGINT_TAG = '__@json.bigint__'\nexport const BYTES_TAG = '__@json.bytes__'\n\nexport const undefined = void 0\nexport const isUndefined = (value: any): value is undefined => value === undefined\nexport const hasOwnProperty = (obj: any, prop: string): boolean =>\n Object.prototype.hasOwnProperty.call(obj, prop)\nexport const isArray = (value: any): value is Array<any> => Array.isArray(value)\nexport const isObject = (value: any): value is Record<string, any> =>\n value !== null && typeof value === 'object' && !isArray(value)\nexport const isFunction = (value: any): value is Function => typeof value === 'function'\nexport const isBigInt = (value: any): value is bigint => typeof value === 'bigint'\nexport const isBuffer = (value: any): value is Buffer =>\n !isUndefined(Buffer) && isFunction(Buffer.isBuffer) && Buffer.isBuffer(value)\nexport const isUint8Array = (value: any): value is Uint8Array =>\n !isUndefined(Uint8Array) && value instanceof Uint8Array\nexport const isArrayBuffer = (value: any): value is ArrayBuffer =>\n !isUndefined(ArrayBuffer) && value instanceof ArrayBuffer\n\nexport const toHex = (value: Uint8Array): string => {\n let out = '0x'\n for (const byte of value) {\n out += byte.toString(16).padStart(2, '0')\n }\n return out\n}\n\nexport const fromHex = (hex: string): Uint8Array => {\n const normalized = hex.startsWith('0x') ? hex.slice(2) : hex\n if (normalized.length % 2 !== 0) {\n throw new Error('Invalid hex string for bytes')\n }\n\n if (!isUndefined(Buffer)) {\n return Uint8Array.from(Buffer.from(normalized, 'hex'))\n }\n\n const out = new Uint8Array(normalized.length / 2)\n for (let i = 0; i < normalized.length; i += 2) {\n out[i / 2] = Number.parseInt(normalized.slice(i, i + 2), 16)\n }\n return out\n}\n\nexport const toSerializable = (value: any): any => {\n if (isBigInt(value)) {\n return { [BIGINT_TAG]: value.toString() }\n }\n\n if (isUint8Array(value)) {\n return { [BYTES_TAG]: toHex(value) }\n }\n\n if (isArrayBuffer(value)) {\n return { [BYTES_TAG]: toHex(new Uint8Array(value)) }\n }\n\n if (\n value &&\n isObject(value) &&\n value.type === 'Buffer' &&\n isArray(value.data) &&\n Object.keys(value).length === 2 &&\n value.data.every((entry: any) => Number.isInteger(entry) && entry >= 0 && entry <= 255)\n ) {\n return { [BYTES_TAG]: toHex(Uint8Array.from(value.data)) }\n }\n\n return value\n}\n\nexport const fromSerializable = (value: any): any => {\n if (value && isObject(value)) {\n if (hasOwnProperty(value, BIGINT_TAG)) {\n return BigInt(value[BIGINT_TAG])\n }\n if (hasOwnProperty(value, BYTES_TAG)) {\n return fromHex(value[BYTES_TAG])\n }\n if (\n value.type === 'Buffer' &&\n isArray(value.data) &&\n Object.keys(value).length === 2 &&\n value.data.every((entry: any) => Number.isInteger(entry) && entry >= 0 && entry <= 255)\n ) {\n return Uint8Array.from(value.data)\n }\n }\n return value\n}\n","import {\n BIGINT_TAG,\n BYTES_TAG,\n fromSerializable,\n isArray,\n isFunction,\n toSerializable,\n undefined,\n} from './utils'\n\nconst RAW_JSON = JSON\ntype Replacer = ((this: any, key: string, value: any) => any) | Array<string | number> | null\ntype Reviver = ((this: any, key: string, value: any) => any) | null\n\nconst applyReplacer = (holder: any, key: string, value: any, replacer: Replacer): any => {\n if (isFunction(replacer)) {\n return replacer(key, value)\n }\n if (isArray(replacer)) {\n if (key === '') return value\n if (key === BIGINT_TAG) return value\n if (key === BYTES_TAG) return value\n if (isArray(holder)) return value\n return replacer.includes(key) ? value : undefined\n }\n return value\n}\n\nexport const stringify = (value: any, replacer: Replacer = null, space?: string | number): string =>\n RAW_JSON.stringify(\n value,\n function replacerFn(key, v) {\n const replaced = applyReplacer(this, key, v, replacer)\n return toSerializable(replaced)\n },\n space,\n )\n\nexport const parse = (text: string, reviver: Reviver = null): any =>\n RAW_JSON.parse(text, (key, v) => {\n const decoded = fromSerializable(v)\n return typeof reviver === 'function' ? reviver(key, decoded) : decoded\n })\n\nexport default {\n stringify,\n parse,\n}\n"],"mappings":";AAAO,IAAM,aAAa;AACnB,IAAM,YAAY;AAElB,IAAM,YAAY;AAClB,IAAM,cAAc,CAAC,UAAmC,UAAU;AAClE,IAAM,iBAAiB,CAAC,KAAU,SACvC,OAAO,UAAU,eAAe,KAAK,KAAK,IAAI;AACzC,IAAM,UAAU,CAAC,UAAoC,MAAM,QAAQ,KAAK;AACxE,IAAM,WAAW,CAAC,UACvB,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,QAAQ,KAAK;AACxD,IAAM,aAAa,CAAC,UAAkC,OAAO,UAAU;AACvE,IAAM,WAAW,CAAC,UAAgC,OAAO,UAAU;AAGnE,IAAM,eAAe,CAAC,UAC3B,CAAC,YAAY,UAAU,KAAK,iBAAiB;AACxC,IAAM,gBAAgB,CAAC,UAC5B,CAAC,YAAY,WAAW,KAAK,iBAAiB;AAEzC,IAAM,QAAQ,CAAC,UAA8B;AAClD,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,WAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,IAAM,UAAU,CAAC,QAA4B;AAClD,QAAM,aAAa,IAAI,WAAW,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI;AACzD,MAAI,WAAW,SAAS,MAAM,GAAG;AAC/B,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AAEA,MAAI,CAAC,YAAY,MAAM,GAAG;AACxB,WAAO,WAAW,KAAK,OAAO,KAAK,YAAY,KAAK,CAAC;AAAA,EACvD;AAEA,QAAM,MAAM,IAAI,WAAW,WAAW,SAAS,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,QAAI,IAAI,CAAC,IAAI,OAAO,SAAS,WAAW,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE;AAAA,EAC7D;AACA,SAAO;AACT;AAEO,IAAM,iBAAiB,CAAC,UAAoB;AACjD,MAAI,SAAS,KAAK,GAAG;AACnB,WAAO,EAAE,CAAC,UAAU,GAAG,MAAM,SAAS,EAAE;AAAA,EAC1C;AAEA,MAAI,aAAa,KAAK,GAAG;AACvB,WAAO,EAAE,CAAC,SAAS,GAAG,MAAM,KAAK,EAAE;AAAA,EACrC;AAEA,MAAI,cAAc,KAAK,GAAG;AACxB,WAAO,EAAE,CAAC,SAAS,GAAG,MAAM,IAAI,WAAW,KAAK,CAAC,EAAE;AAAA,EACrD;AAEA,MACE,SACA,SAAS,KAAK,KACd,MAAM,SAAS,YACf,QAAQ,MAAM,IAAI,KAClB,OAAO,KAAK,KAAK,EAAE,WAAW,KAC9B,MAAM,KAAK,MAAM,CAAC,UAAe,OAAO,UAAU,KAAK,KAAK,SAAS,KAAK,SAAS,GAAG,GACtF;AACA,WAAO,EAAE,CAAC,SAAS,GAAG,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,EAAE;AAAA,EAC3D;AAEA,SAAO;AACT;AAEO,IAAM,mBAAmB,CAAC,UAAoB;AACnD,MAAI,SAAS,SAAS,KAAK,GAAG;AAC5B,QAAI,eAAe,OAAO,UAAU,GAAG;AACrC,aAAO,OAAO,MAAM,UAAU,CAAC;AAAA,IACjC;AACA,QAAI,eAAe,OAAO,SAAS,GAAG;AACpC,aAAO,QAAQ,MAAM,SAAS,CAAC;AAAA,IACjC;AACA,QACE,MAAM,SAAS,YACf,QAAQ,MAAM,IAAI,KAClB,OAAO,KAAK,KAAK,EAAE,WAAW,KAC9B,MAAM,KAAK,MAAM,CAAC,UAAe,OAAO,UAAU,KAAK,KAAK,SAAS,KAAK,SAAS,GAAG,GACtF;AACA,aAAO,WAAW,KAAK,MAAM,IAAI;AAAA,IACnC;AAAA,EACF;AACA,SAAO;AACT;;;AC/EA,IAAM,WAAW;AAIjB,IAAM,gBAAgB,CAAC,QAAa,KAAa,OAAY,aAA4B;AACvF,MAAI,WAAW,QAAQ,GAAG;AACxB,WAAO,SAAS,KAAK,KAAK;AAAA,EAC5B;AACA,MAAI,QAAQ,QAAQ,GAAG;AACrB,QAAI,QAAQ,GAAI,QAAO;AACvB,QAAI,QAAQ,WAAY,QAAO;AAC/B,QAAI,QAAQ,UAAW,QAAO;AAC9B,QAAI,QAAQ,MAAM,EAAG,QAAO;AAC5B,WAAO,SAAS,SAAS,GAAG,IAAI,QAAQ;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,IAAM,YAAY,CAAC,OAAY,WAAqB,MAAM,UAC/D,SAAS;AAAA,EACP;AAAA,EACA,SAAS,WAAW,KAAK,GAAG;AAC1B,UAAM,WAAW,cAAc,MAAM,KAAK,GAAG,QAAQ;AACrD,WAAO,eAAe,QAAQ;AAAA,EAChC;AAAA,EACA;AACF;AAEK,IAAM,QAAQ,CAAC,MAAc,UAAmB,SACrD,SAAS,MAAM,MAAM,CAAC,KAAK,MAAM;AAC/B,QAAM,UAAU,iBAAiB,CAAC;AAClC,SAAO,OAAO,YAAY,aAAa,QAAQ,KAAK,OAAO,IAAI;AACjE,CAAC;AAEH,IAAO,gBAAQ;AAAA,EACb;AAAA,EACA;AACF;","names":[]}
package/package.json CHANGED
@@ -1,15 +1,22 @@
1
1
  {
2
2
  "name": "json-web3",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "BigInt-safe JSON serialization and deserialization for Web3 use cases.",
5
5
  "keywords": [
6
6
  "json",
7
7
  "bigint",
8
+ "arraybuffer",
8
9
  "serialization",
9
10
  "deserialization",
10
11
  "web3"
11
12
  ],
13
+ "homepage": "https://github.com/haozi/json-web3#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/haozi/json-web3.git"
17
+ },
12
18
  "license": "MIT",
19
+ "author": "haozi",
13
20
  "sideEffects": false,
14
21
  "type": "module",
15
22
  "exports": {
@@ -29,7 +36,12 @@
29
36
  ],
30
37
  "devDependencies": {
31
38
  "@types/node": "^25.0.9",
39
+ "@vitest/coverage-v8": "^4.0.17",
32
40
  "prettier": "^3.8.0",
41
+ "prettier-plugin-lint-md": "^1.0.1",
42
+ "prettier-plugin-organize-imports": "^4.3.0",
43
+ "prettier-plugin-sort-json": "^4.2.0",
44
+ "prettier-plugin-tailwindcss": "^0.7.2",
33
45
  "tsup": "^8.5.1",
34
46
  "typescript": "^5.9.3",
35
47
  "vitest": "^4.0.17"
@@ -39,8 +51,10 @@
39
51
  },
40
52
  "scripts": {
41
53
  "build": "tsup",
54
+ "coverage": "vitest run --coverage",
42
55
  "lint": "prettier . --write",
43
56
  "test": "vitest run",
57
+ "test:coverage": "pnpm run coverage",
44
58
  "test:watch": "vitest"
45
59
  }
46
60
  }