@suilend/sdk 6.0.3 → 7.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/client.js +7 -13
- package/package.json +1 -1
- package/lib/pythAdapter.d.ts +0 -74
- package/lib/pythAdapter.js +0 -182
package/client.js
CHANGED
|
@@ -135,24 +135,18 @@ export class SuilendClient {
|
|
|
135
135
|
constructor(lendingMarket, suiGrpcClient) {
|
|
136
136
|
this.lendingMarket = lendingMarket;
|
|
137
137
|
this.suiGrpcClient = suiGrpcClient;
|
|
138
|
-
// The grpc client goes in DIRECTLY
|
|
138
|
+
// The grpc client goes in DIRECTLY. `SuiGrpcClient` satisfies v4's
|
|
139
|
+
// `ClientWithCoreApi` natively, so nothing needs adapting.
|
|
139
140
|
//
|
|
140
141
|
// pyth v4's SuiPythClient reads through `provider.core.getObject` and
|
|
141
142
|
// `provider.core.getDynamicField`; v2 read the legacy top-level
|
|
142
143
|
// `provider.getObject` / `getDynamicFieldObject`. The class name survived the
|
|
143
|
-
// major bump, its provider contract did not — so the
|
|
144
|
-
//
|
|
145
|
-
// pythClient throw "Cannot read properties of undefined (reading
|
|
144
|
+
// major bump, its provider contract did not — so wrapping the client in
|
|
145
|
+
// anything that presents the legacy shape without a `.core` makes every call
|
|
146
|
+
// through pythClient throw "Cannot read properties of undefined (reading
|
|
146
147
|
// 'getDynamicField')". That is the on-chain price-update path, i.e. every
|
|
147
|
-
// deposit/withdraw/borrow/repay, and nothing read-only touches it
|
|
148
|
-
//
|
|
149
|
-
// `SuiGrpcClient` satisfies v4's `ClientWithCoreApi` natively, so no adapter
|
|
150
|
-
// is needed here at all. The adapter's hardcoded price-table fallback is also
|
|
151
|
-
// obsolete: `core.getDynamicField` resolves the pyth `price_info` wrapper over
|
|
152
|
-
// grpc and returns the same table id the adapter had pinned.
|
|
153
|
-
//
|
|
154
|
-
// createJsonRpcAdapter is still exported — the indexer's bluefin swapper
|
|
155
|
-
// needs the legacy shape — it just must not be used for pyth v4.
|
|
148
|
+
// deposit/withdraw/borrow/repay, and nothing read-only touches it, so a
|
|
149
|
+
// typecheck and every page-load check stay green while writes are broken.
|
|
156
150
|
this.pythClient = new SuiPythClient(suiGrpcClient, PYTH_STATE_ID, WORMHOLE_STATE_ID);
|
|
157
151
|
const pythEndpoint = PRIMARY_PYTH_ENDPOINT; // TODO: Use getWorkingPythEndpoint
|
|
158
152
|
this.pythConnection = new SuiPriceServiceConnection(pythEndpoint, {
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@suilend/sdk","version":"
|
|
1
|
+
{"name":"@suilend/sdk","version":"7.0.0","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/obligation":"./utils/obligation.js","./utils/simulate":"./utils/simulate.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/source/0x1":"./_generated/_dependencies/source/0x1/index.js","./_generated/_dependencies/source/0x2":"./_generated/_dependencies/source/0x2/index.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/index.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js","./_generated/_dependencies/source/0x1/ascii/structs":"./_generated/_dependencies/source/0x1/ascii/structs.js","./_generated/_dependencies/source/0x1/option/structs":"./_generated/_dependencies/source/0x1/option/structs.js","./_generated/_dependencies/source/0x1/type-name/structs":"./_generated/_dependencies/source/0x1/type-name/structs.js","./_generated/_dependencies/source/0x2/bag/structs":"./_generated/_dependencies/source/0x2/bag/structs.js","./_generated/_dependencies/source/0x2/balance/structs":"./_generated/_dependencies/source/0x2/balance/structs.js","./_generated/_dependencies/source/0x2/object/structs":"./_generated/_dependencies/source/0x2/object/structs.js","./_generated/_dependencies/source/0x2/object-table/structs":"./_generated/_dependencies/source/0x2/object-table/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/i64/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-feed/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-info/structs.js"},"types":"./index.d.ts","scripts":{"build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"^7.5.0","@cetusprotocol/aggregator-sdk":"^1.5.7","@flowx-finance/sdk":"^2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"^4.0.0","bignumber.js":"^9.1.2","bn.js":"^5.2.2","crypto-js":"^4.2.0","lodash":"^4.17.21","p-limit":"3.1.0","uuid":"^11.0.3"},"devDependencies":{"@mysten/bcs":"^2.0.5","@mysten/sui":"2.17.0","@suilend/sui-core":"^1.0.0","@tsconfig/recommended":"^1.0.8","@types/bn.js":"^5.2.0","@types/lodash":"^4.17.20","@types/node":"^22.9.0","prettier":"^3.3.3","ts-node":"^10.9.2","typescript":"^6.0.3","vitest":"4.1.0"},"peerDependencies":{"@mysten/bcs":"^2.0.5","@mysten/sui":"2.17.0","@suilend/sui-core":"^1.0.0"},"type":"module"}
|
package/lib/pythAdapter.d.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
2
|
-
/**
|
|
3
|
-
* A Move object as the legacy JSON-RPC shape reported it.
|
|
4
|
-
*
|
|
5
|
-
* `fields` stays `unknown` on purpose: it is a decoded Move struct whose shape
|
|
6
|
-
* depends entirely on the object being read, and the callers that consume this
|
|
7
|
-
* adapter narrow it themselves. That is a genuine unknown, not the blanket `any`
|
|
8
|
-
* this module used to return.
|
|
9
|
-
*/
|
|
10
|
-
export interface LegacyObjectResponse {
|
|
11
|
-
data: {
|
|
12
|
-
objectId: string;
|
|
13
|
-
type: string;
|
|
14
|
-
content: {
|
|
15
|
-
dataType: "moveObject";
|
|
16
|
-
fields: unknown;
|
|
17
|
-
type: string;
|
|
18
|
-
};
|
|
19
|
-
} | null;
|
|
20
|
-
}
|
|
21
|
-
/** A coin as the legacy JSON-RPC `getCoins` reported it. */
|
|
22
|
-
export interface LegacyCoin {
|
|
23
|
-
coinObjectId: string;
|
|
24
|
-
version: string | bigint | number;
|
|
25
|
-
digest: string;
|
|
26
|
-
balance: string | bigint | number;
|
|
27
|
-
coinType: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* The legacy JSON-RPC surface this adapter provides.
|
|
31
|
-
*
|
|
32
|
-
* Explicit rather than inferred, and that is the whole point: this type has NO
|
|
33
|
-
* `core` property, so handing the adapter to something expecting `@mysten/sui`'s
|
|
34
|
-
* `ClientWithCoreApi` — which is what pyth v4's `SuiPythClient` wants — is now a
|
|
35
|
-
* COMPILE error.
|
|
36
|
-
*
|
|
37
|
-
* It used to be inferred as `any` (measurably: `IsAny<ReturnType<...>>` resolved
|
|
38
|
-
* to true), which made exactly that mismatch invisible to tsc. It shipped twice:
|
|
39
|
-
* @suilend/sdk 6.0.0/6.0.1 broke every deposit/withdraw/borrow/repay, and
|
|
40
|
-
* steamm-sdk 3.5.0 broke the admin oracle flow, both with
|
|
41
|
-
* "Cannot read properties of undefined (reading 'getDynamicField')". Neither was
|
|
42
|
-
* caught by a typecheck; both were found in production.
|
|
43
|
-
*
|
|
44
|
-
* Keep this typed. A third instance should fail to compile, not to run.
|
|
45
|
-
*/
|
|
46
|
-
export interface LegacyJsonRpcAdapter {
|
|
47
|
-
getObject(args: {
|
|
48
|
-
id: string;
|
|
49
|
-
options?: unknown;
|
|
50
|
-
}): Promise<LegacyObjectResponse>;
|
|
51
|
-
getCoins(args: {
|
|
52
|
-
owner: string;
|
|
53
|
-
coinType?: string;
|
|
54
|
-
cursor?: string | null;
|
|
55
|
-
}): Promise<{
|
|
56
|
-
data: LegacyCoin[];
|
|
57
|
-
nextCursor: string | null | undefined;
|
|
58
|
-
hasNextPage: boolean;
|
|
59
|
-
}>;
|
|
60
|
-
getDynamicFieldObject(args: {
|
|
61
|
-
parentId: string;
|
|
62
|
-
name: {
|
|
63
|
-
type: string;
|
|
64
|
-
value: unknown;
|
|
65
|
-
};
|
|
66
|
-
}): Promise<LegacyObjectResponse>;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Adapter that wraps SuiGrpcClient to provide the legacy SuiClient (JSON-RPC)
|
|
70
|
-
* interface expected by third-party SDKs like @pythnetwork/pyth-sui-js.
|
|
71
|
-
*
|
|
72
|
-
* Maps old JSON-RPC method signatures to the new gRPC equivalents.
|
|
73
|
-
*/
|
|
74
|
-
export declare function createJsonRpcAdapter(suiGrpcClient: SuiGrpcClient): LegacyJsonRpcAdapter;
|
package/lib/pythAdapter.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { bcs } from "@mysten/sui/bcs";
|
|
2
|
-
const PYTH_STATE_ID = "0x1f9310238ee9298fb703c3419030b35b22bb1cc37113e3bb5007c99aec79e5b8";
|
|
3
|
-
const PYTH_PRICE_TABLE_ID = "0x234c9ffca44613ab87a2711325b6e17bad9ece0449b917c8bd9c0ad7a0506cc2";
|
|
4
|
-
const PYTH_PACKAGE = "0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e";
|
|
5
|
-
/**
|
|
6
|
-
* Adapter that wraps SuiGrpcClient to provide the legacy SuiClient (JSON-RPC)
|
|
7
|
-
* interface expected by third-party SDKs like @pythnetwork/pyth-sui-js.
|
|
8
|
-
*
|
|
9
|
-
* Maps old JSON-RPC method signatures to the new gRPC equivalents.
|
|
10
|
-
*/
|
|
11
|
-
export function createJsonRpcAdapter(suiGrpcClient) {
|
|
12
|
-
return {
|
|
13
|
-
async getObject({ id }) {
|
|
14
|
-
const { object } = await suiGrpcClient.getObject({
|
|
15
|
-
objectId: id,
|
|
16
|
-
include: { json: true },
|
|
17
|
-
});
|
|
18
|
-
return {
|
|
19
|
-
data: {
|
|
20
|
-
objectId: object.objectId,
|
|
21
|
-
type: object.type,
|
|
22
|
-
content: {
|
|
23
|
-
dataType: "moveObject",
|
|
24
|
-
fields: wrapNestedObjects(object.json),
|
|
25
|
-
type: object.type,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
},
|
|
30
|
-
async getCoins({ owner, coinType, cursor, }) {
|
|
31
|
-
const result = await suiGrpcClient.listCoins({
|
|
32
|
-
owner,
|
|
33
|
-
coinType,
|
|
34
|
-
cursor,
|
|
35
|
-
});
|
|
36
|
-
return {
|
|
37
|
-
data: result.objects.map((coin) => ({
|
|
38
|
-
coinObjectId: coin.objectId,
|
|
39
|
-
version: coin.version,
|
|
40
|
-
digest: coin.digest,
|
|
41
|
-
balance: coin.balance,
|
|
42
|
-
coinType: coin.type,
|
|
43
|
-
})),
|
|
44
|
-
nextCursor: result.cursor,
|
|
45
|
-
hasNextPage: result.hasNextPage,
|
|
46
|
-
};
|
|
47
|
-
},
|
|
48
|
-
async getDynamicFieldObject({ parentId, name, }) {
|
|
49
|
-
// The gRPC node may not have dynamic field wrapper objects indexed.
|
|
50
|
-
// For known lookups (Pyth price table), use hardcoded values.
|
|
51
|
-
if (parentId === PYTH_STATE_ID &&
|
|
52
|
-
name.type === "vector<u8>" &&
|
|
53
|
-
name.value === "price_info") {
|
|
54
|
-
return {
|
|
55
|
-
data: {
|
|
56
|
-
objectId: PYTH_PRICE_TABLE_ID,
|
|
57
|
-
type: `0x2::table::Table<${PYTH_PACKAGE}::price_identifier::PriceIdentifier, 0x2::object::ID>`,
|
|
58
|
-
content: {
|
|
59
|
-
dataType: "moveObject",
|
|
60
|
-
fields: null,
|
|
61
|
-
type: `0x2::table::Table<${PYTH_PACKAGE}::price_identifier::PriceIdentifier, 0x2::object::ID>`,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
const { dynamicField } = await suiGrpcClient.getDynamicField({
|
|
67
|
-
parentId,
|
|
68
|
-
name: {
|
|
69
|
-
type: name.type,
|
|
70
|
-
bcs: dynamicField_encodeName(name),
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
if (dynamicField.$kind === "DynamicObject" && dynamicField.childId) {
|
|
74
|
-
try {
|
|
75
|
-
const childObject = await suiGrpcClient
|
|
76
|
-
.getObject({
|
|
77
|
-
objectId: dynamicField.childId,
|
|
78
|
-
include: { json: true },
|
|
79
|
-
})
|
|
80
|
-
.then((r) => r.object);
|
|
81
|
-
return {
|
|
82
|
-
data: {
|
|
83
|
-
objectId: childObject.objectId,
|
|
84
|
-
type: childObject.type,
|
|
85
|
-
content: {
|
|
86
|
-
dataType: "moveObject",
|
|
87
|
-
fields: childObject.json,
|
|
88
|
-
type: childObject.type,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
return {
|
|
95
|
-
data: {
|
|
96
|
-
objectId: dynamicField.childId,
|
|
97
|
-
type: dynamicField.valueType,
|
|
98
|
-
content: {
|
|
99
|
-
dataType: "moveObject",
|
|
100
|
-
fields: null,
|
|
101
|
-
type: dynamicField.valueType,
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
const valueBcs = dynamicField.value?.bcs;
|
|
108
|
-
const type = dynamicField.valueType ?? dynamicField.type;
|
|
109
|
-
let fields = null;
|
|
110
|
-
if (valueBcs && valueBcs.length > 0) {
|
|
111
|
-
if (type === "0x2::object::ID" ||
|
|
112
|
-
type.endsWith("::object::ID") ||
|
|
113
|
-
valueBcs.length === 32) {
|
|
114
|
-
const id = "0x" +
|
|
115
|
-
Array.from(valueBcs)
|
|
116
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
117
|
-
.join("");
|
|
118
|
-
fields = { value: id };
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
data: {
|
|
123
|
-
objectId: dynamicField.fieldId,
|
|
124
|
-
type,
|
|
125
|
-
content: {
|
|
126
|
-
dataType: "moveObject",
|
|
127
|
-
fields,
|
|
128
|
-
type,
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function dynamicField_encodeName(name) {
|
|
136
|
-
// The pyth client passes structured names like:
|
|
137
|
-
// { type: "vector<u8>", value: "price_info" }
|
|
138
|
-
// { type: "..::PriceIdentifier", value: { bytes: number[] } }
|
|
139
|
-
// The gRPC getDynamicField expects BCS-encoded name bytes.
|
|
140
|
-
if (name.type === "vector<u8>") {
|
|
141
|
-
const bytes = typeof name.value === "string"
|
|
142
|
-
? new TextEncoder().encode(name.value)
|
|
143
|
-
: new Uint8Array(name.value);
|
|
144
|
-
return bcs.vector(bcs.u8()).serialize(Array.from(bytes)).toBytes();
|
|
145
|
-
}
|
|
146
|
-
if (name.value &&
|
|
147
|
-
typeof name.value === "object" &&
|
|
148
|
-
"bytes" in name.value &&
|
|
149
|
-
Array.isArray(name.value.bytes)) {
|
|
150
|
-
const struct = bcs.struct("PriceIdentifier", {
|
|
151
|
-
bytes: bcs.vector(bcs.u8()),
|
|
152
|
-
});
|
|
153
|
-
return struct.serialize(name.value).toBytes();
|
|
154
|
-
}
|
|
155
|
-
if (typeof name.value === "boolean") {
|
|
156
|
-
return bcs.bool().serialize(name.value).toBytes();
|
|
157
|
-
}
|
|
158
|
-
throw new Error(`jsonRpcAdapter: unsupported dynamic field name type "${name.type}"`);
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* gRPC Value.toJson() returns flat objects, but the JSON-RPC format wraps
|
|
162
|
-
* nested struct fields in { fields: { ... } }. This recursively wraps any
|
|
163
|
-
* nested plain object values so legacy SDKs (e.g. Pyth) can traverse them.
|
|
164
|
-
*/
|
|
165
|
-
function wrapNestedObjects(obj) {
|
|
166
|
-
if (obj === null || obj === undefined)
|
|
167
|
-
return obj;
|
|
168
|
-
if (Array.isArray(obj))
|
|
169
|
-
return obj.map(wrapNestedObjects);
|
|
170
|
-
if (typeof obj !== "object")
|
|
171
|
-
return obj;
|
|
172
|
-
const result = {};
|
|
173
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
174
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
175
|
-
result[key] = { fields: wrapNestedObjects(value) };
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
result[key] = value;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return result;
|
|
182
|
-
}
|