@lucid-evolution/provider 0.4.0 → 0.4.2
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/dist/index.cjs +608 -548
- package/dist/index.js +104 -57
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -101,7 +101,7 @@ import {
|
|
|
101
101
|
// package.json
|
|
102
102
|
var package_default = {
|
|
103
103
|
name: "@lucid-evolution/provider",
|
|
104
|
-
version: "0.4.
|
|
104
|
+
version: "0.4.2",
|
|
105
105
|
description: "",
|
|
106
106
|
main: "./dist/index.js",
|
|
107
107
|
types: "./dist/index.d.ts",
|
|
@@ -146,7 +146,8 @@ var package_default = {
|
|
|
146
146
|
};
|
|
147
147
|
|
|
148
148
|
// src/internal/blockfrost.ts
|
|
149
|
-
import
|
|
149
|
+
import * as Record from "effect/Record";
|
|
150
|
+
import { pipe } from "effect/Function";
|
|
150
151
|
import { applySingleCborEncoding } from "@lucid-evolution/utils";
|
|
151
152
|
var toAditionalUTXOs = (utxos) => (utxos || []).map((utxo) => [
|
|
152
153
|
{
|
|
@@ -217,16 +218,16 @@ var Blockfrost = class {
|
|
|
217
218
|
constructor(url, projectId, options = {}) {
|
|
218
219
|
this.url = url;
|
|
219
220
|
this.projectId = projectId || "";
|
|
220
|
-
const
|
|
221
|
-
if (!Number.isFinite(
|
|
221
|
+
const timeout3 = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
222
|
+
if (!Number.isFinite(timeout3) || timeout3 <= 0) {
|
|
222
223
|
throw new TypeError("requestTimeoutMs must be a positive finite number.");
|
|
223
224
|
}
|
|
224
|
-
this.requestTimeoutMs =
|
|
225
|
+
this.requestTimeoutMs = timeout3;
|
|
225
226
|
}
|
|
226
227
|
/** `fetch` bounded by the provider's request timeout, combined with any caller-supplied signal. */
|
|
227
228
|
fetch(input, init = {}) {
|
|
228
|
-
const
|
|
229
|
-
const signal = init.signal == null ?
|
|
229
|
+
const timeout3 = AbortSignal.timeout(this.requestTimeoutMs);
|
|
230
|
+
const signal = init.signal == null ? timeout3 : typeof AbortSignal.any === "function" ? AbortSignal.any([init.signal, timeout3]) : init.signal;
|
|
230
231
|
return fetch(input, { ...init, signal });
|
|
231
232
|
}
|
|
232
233
|
async getProtocolParameters() {
|
|
@@ -557,19 +558,17 @@ var lucid = package_default.version;
|
|
|
557
558
|
|
|
558
559
|
// src/kupmios.ts
|
|
559
560
|
import { applyDoubleCborEncoding as applyDoubleCborEncoding2, fromUnit as fromUnit2 } from "@lucid-evolution/utils";
|
|
560
|
-
import
|
|
561
|
-
import
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
Schedule
|
|
569
|
-
} from "effect";
|
|
561
|
+
import * as S3 from "effect/Schema";
|
|
562
|
+
import * as Cause from "effect/Cause";
|
|
563
|
+
import * as Effect2 from "effect/Effect";
|
|
564
|
+
import * as Exit from "effect/Exit";
|
|
565
|
+
import * as Layer from "effect/Layer";
|
|
566
|
+
import * as _Array from "effect/Array";
|
|
567
|
+
import * as Schedule from "effect/Schedule";
|
|
568
|
+
import { pipe as pipe3 } from "effect/Function";
|
|
570
569
|
|
|
571
570
|
// src/internal/ogmios.ts
|
|
572
|
-
import
|
|
571
|
+
import * as S from "effect/Schema";
|
|
573
572
|
import { applySingleCborEncoding as applySingleCborEncoding2, fromUnit } from "@lucid-evolution/utils";
|
|
574
573
|
var JSONRPCSchema = (schema) => S.Struct({
|
|
575
574
|
jsonrpc: S.String,
|
|
@@ -850,7 +849,7 @@ var toOgmiosUTxOs = (utxos) => {
|
|
|
850
849
|
};
|
|
851
850
|
|
|
852
851
|
// src/internal/kupo.ts
|
|
853
|
-
import
|
|
852
|
+
import * as S2 from "effect/Schema";
|
|
854
853
|
var QuantitySchema = S2.Union(
|
|
855
854
|
S2.Number.pipe(S2.filter(Number.isSafeInteger)),
|
|
856
855
|
S2.String.pipe(S2.pattern(/^\d+$/))
|
|
@@ -900,13 +899,12 @@ var DelegationSchema = S2.NullOr(
|
|
|
900
899
|
);
|
|
901
900
|
|
|
902
901
|
// src/internal/HttpUtils.ts
|
|
903
|
-
import
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
} from "
|
|
909
|
-
import { Effect, identity, pipe as pipe2 } from "effect";
|
|
902
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
903
|
+
import * as HttpClientError from "@effect/platform/HttpClientError";
|
|
904
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
|
|
905
|
+
import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
|
|
906
|
+
import * as Effect from "effect/Effect";
|
|
907
|
+
import { identity, pipe as pipe2 } from "effect/Function";
|
|
910
908
|
var filterStatusOk = (self) => self.status >= 200 && self.status < 300 ? Effect.succeed(self) : self.text.pipe(
|
|
911
909
|
Effect.flatMap(
|
|
912
910
|
(text) => Effect.fail(
|
|
@@ -924,15 +922,12 @@ var makeGet = (url, schema, headers) => pipe2(
|
|
|
924
922
|
HttpClientRequest.get(url),
|
|
925
923
|
// Conditional bearer token authentication
|
|
926
924
|
headers ? HttpClientRequest.setHeaders(headers) : identity,
|
|
927
|
-
// Execute the HTTP request and retrieve the response
|
|
928
925
|
HttpClient.execute,
|
|
929
926
|
// Ensure only 2xx responses are treated as successes.
|
|
930
927
|
Effect.flatMap(filterStatusOk),
|
|
931
928
|
// Parse the response body using the provided JSON schema
|
|
932
929
|
// This validates and transforms the response to the expected type
|
|
933
930
|
Effect.flatMap(HttpClientResponse.schemaBodyJson(schema)),
|
|
934
|
-
// Ensure request is aborted if the program is interrupted
|
|
935
|
-
// Prevents resource leaks and ensures clean request termination
|
|
936
931
|
Effect.scoped
|
|
937
932
|
);
|
|
938
933
|
var makePostAsJson = (url, data, schema, headers) => pipe2(
|
|
@@ -949,8 +944,6 @@ var makePostAsJson = (url, data, schema, headers) => pipe2(
|
|
|
949
944
|
// Parse the response body using the provided JSON schema
|
|
950
945
|
// This validates and transforms the response to the expected type
|
|
951
946
|
Effect.flatMap(HttpClientResponse.schemaBodyJson(schema)),
|
|
952
|
-
// Ensure request is aborted if the program is interrupted
|
|
953
|
-
// Prevents resource leaks and ensures clean request termination
|
|
954
947
|
Effect.scoped
|
|
955
948
|
);
|
|
956
949
|
var makePostAsJsonResponse = (url, data, schema, headers) => pipe2(
|
|
@@ -987,27 +980,24 @@ var makePostAsUint8Array = (url, data, schema, headers) => pipe2(
|
|
|
987
980
|
headers ? HttpClientRequest.setHeaders(headers) : identity,
|
|
988
981
|
// Set the request body as a Uint8Array with CBOR content type
|
|
989
982
|
HttpClientRequest.bodyUint8Array(data, "application/cbor"),
|
|
990
|
-
// Execute the HTTP request and retrieve the response
|
|
991
983
|
HttpClient.execute,
|
|
992
984
|
// Ensure only 2xx responses are treated as successes.
|
|
993
985
|
Effect.flatMap(filterStatusOk),
|
|
994
986
|
// Parse the response body using the provided JSON schema
|
|
995
987
|
// This validates and transforms the response to the expected type
|
|
996
988
|
Effect.flatMap(HttpClientResponse.schemaBodyJson(schema)),
|
|
997
|
-
// Ensure request is aborted if the program is interrupted
|
|
998
|
-
// Prevents resource leaks and ensures clean request termination
|
|
999
989
|
Effect.scoped
|
|
1000
990
|
);
|
|
1001
991
|
|
|
1002
992
|
// src/kupmios.ts
|
|
1003
|
-
import
|
|
993
|
+
import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
|
|
1004
994
|
var DEFAULT_REQUEST_TIMEOUT_MS2 = 3e4;
|
|
1005
995
|
var DEFAULT_AWAIT_TX_TIMEOUT_MS = 16e4;
|
|
1006
|
-
var validateTimeout = (name,
|
|
1007
|
-
if (!Number.isFinite(
|
|
996
|
+
var validateTimeout = (name, timeout3) => {
|
|
997
|
+
if (!Number.isFinite(timeout3) || timeout3 <= 0) {
|
|
1008
998
|
throw new TypeError(`${name} must be a positive finite number.`);
|
|
1009
999
|
}
|
|
1010
|
-
return
|
|
1000
|
+
return timeout3;
|
|
1011
1001
|
};
|
|
1012
1002
|
var catchProviderError = (protocol, operation) => (effect) => Effect2.catchAll(
|
|
1013
1003
|
effect,
|
|
@@ -1121,11 +1111,11 @@ var Kupmios = class {
|
|
|
1121
1111
|
async getUtxos(addressOrCredential) {
|
|
1122
1112
|
const isAddress = typeof addressOrCredential === "string";
|
|
1123
1113
|
const queryPredicate = isAddress ? addressOrCredential : addressOrCredential.hash;
|
|
1124
|
-
const
|
|
1114
|
+
const pattern2 = `${this.kupoUrl}/matches/${queryPredicate}${isAddress ? "" : "/*"}?unspent&resolve_hashes`;
|
|
1125
1115
|
const schema = S3.Array(ResolvedUTxOSchema);
|
|
1126
1116
|
const utxos = await runProviderEffect(
|
|
1127
1117
|
pipe3(
|
|
1128
|
-
makeGet(
|
|
1118
|
+
makeGet(pattern2, schema, this.options.kupoHeader),
|
|
1129
1119
|
Effect2.map(kupmiosUtxosToUtxos),
|
|
1130
1120
|
Effect2.timeout(this.requestTimeoutMs),
|
|
1131
1121
|
catchProviderError("kupo", "getUtxos"),
|
|
@@ -1140,11 +1130,11 @@ var Kupmios = class {
|
|
|
1140
1130
|
const isAddress = typeof addressOrCredential === "string";
|
|
1141
1131
|
const queryPredicate = isAddress ? addressOrCredential : addressOrCredential.hash;
|
|
1142
1132
|
const { policyId, assetName } = fromUnit2(unit);
|
|
1143
|
-
const
|
|
1133
|
+
const pattern2 = `${this.kupoUrl}/matches/${queryPredicate}${isAddress ? "" : "/*"}?unspent&resolve_hashes&policy_id=${policyId}${assetName ? `&asset_name=${assetName}` : ""}`;
|
|
1144
1134
|
const schema = S3.Array(ResolvedUTxOSchema);
|
|
1145
1135
|
const utxos = await runProviderEffect(
|
|
1146
1136
|
pipe3(
|
|
1147
|
-
makeGet(
|
|
1137
|
+
makeGet(pattern2, schema, this.options.kupoHeader),
|
|
1148
1138
|
Effect2.map(kupmiosUtxosToUtxos),
|
|
1149
1139
|
Effect2.timeout(this.requestTimeoutMs),
|
|
1150
1140
|
catchProviderError("kupo", "getUtxosWithUnit"),
|
|
@@ -1164,11 +1154,11 @@ var Kupmios = class {
|
|
|
1164
1154
|
const normalizedPolicyId = policyId.toLowerCase();
|
|
1165
1155
|
const isAddress = typeof addressOrCredential === "string";
|
|
1166
1156
|
const queryPredicate = isAddress ? addressOrCredential : addressOrCredential.hash;
|
|
1167
|
-
const
|
|
1157
|
+
const pattern2 = `${this.kupoUrl}/matches/${queryPredicate}${isAddress ? "" : "/*"}?unspent&resolve_hashes&policy_id=${normalizedPolicyId}`;
|
|
1168
1158
|
const schema = S3.Array(ResolvedUTxOSchema);
|
|
1169
1159
|
return runProviderEffect(
|
|
1170
1160
|
pipe3(
|
|
1171
|
-
makeGet(
|
|
1161
|
+
makeGet(pattern2, schema, this.options.kupoHeader),
|
|
1172
1162
|
Effect2.map(kupmiosUtxosToUtxos),
|
|
1173
1163
|
Effect2.timeout(this.requestTimeoutMs),
|
|
1174
1164
|
catchProviderError("kupo", "getUtxosWithPolicy"),
|
|
@@ -1180,11 +1170,11 @@ var Kupmios = class {
|
|
|
1180
1170
|
}
|
|
1181
1171
|
async getUtxoByUnit(unit) {
|
|
1182
1172
|
const { policyId, assetName } = fromUnit2(unit);
|
|
1183
|
-
const
|
|
1173
|
+
const pattern2 = `${this.kupoUrl}/matches/${policyId}.${assetName ?? ""}?unspent&resolve_hashes`;
|
|
1184
1174
|
const schema = S3.Array(ResolvedUTxOSchema);
|
|
1185
1175
|
const utxos = await runProviderEffect(
|
|
1186
1176
|
pipe3(
|
|
1187
|
-
makeGet(
|
|
1177
|
+
makeGet(pattern2, schema, this.options.kupoHeader),
|
|
1188
1178
|
Effect2.map(kupmiosUtxosToUtxos),
|
|
1189
1179
|
Effect2.timeout(this.requestTimeoutMs),
|
|
1190
1180
|
catchProviderError("kupo", "getUtxoByUnit"),
|
|
@@ -1262,11 +1252,11 @@ var Kupmios = class {
|
|
|
1262
1252
|
return { poolId, rewards };
|
|
1263
1253
|
}
|
|
1264
1254
|
async getDatum(datumHash) {
|
|
1265
|
-
const
|
|
1255
|
+
const pattern2 = `${this.kupoUrl}/datums/${datumHash}`;
|
|
1266
1256
|
const schema = DatumSchema;
|
|
1267
1257
|
const result = await runProviderEffect(
|
|
1268
1258
|
pipe3(
|
|
1269
|
-
makeGet(
|
|
1259
|
+
makeGet(pattern2, schema, this.options.kupoHeader),
|
|
1270
1260
|
Effect2.provide(this.httpLayer),
|
|
1271
1261
|
Effect2.timeout(this.requestTimeoutMs),
|
|
1272
1262
|
Effect2.flatMap(Effect2.fromNullable),
|
|
@@ -1278,13 +1268,13 @@ var Kupmios = class {
|
|
|
1278
1268
|
return result.datum;
|
|
1279
1269
|
}
|
|
1280
1270
|
async getTransactionStatus(txHash, options = {}) {
|
|
1281
|
-
const
|
|
1271
|
+
const pattern2 = `${this.kupoUrl}/matches/*@${txHash}`;
|
|
1282
1272
|
const schema = S3.Array(UTxOSchema).annotations({
|
|
1283
1273
|
identifier: "Array<KupmiosSchema.KupoUTxO>"
|
|
1284
1274
|
});
|
|
1285
1275
|
const matches = await runProviderEffect(
|
|
1286
1276
|
pipe3(
|
|
1287
|
-
makeGet(
|
|
1277
|
+
makeGet(pattern2, schema, this.options.kupoHeader),
|
|
1288
1278
|
Effect2.timeout(this.requestTimeoutMs),
|
|
1289
1279
|
catchProviderError("kupo", "getTransactionStatus"),
|
|
1290
1280
|
Effect2.provide(this.httpLayer)
|
|
@@ -1306,13 +1296,13 @@ var Kupmios = class {
|
|
|
1306
1296
|
};
|
|
1307
1297
|
}
|
|
1308
1298
|
async awaitTx(txHash, checkInterval = 2e4) {
|
|
1309
|
-
const
|
|
1299
|
+
const pattern2 = `${this.kupoUrl}/matches/*@${txHash}`;
|
|
1310
1300
|
const schema = S3.Array(UTxOSchema).annotations({
|
|
1311
1301
|
identifier: "Array<KupmiosSchema.KupoUTxO>"
|
|
1312
1302
|
});
|
|
1313
1303
|
const result = await runProviderEffect(
|
|
1314
1304
|
pipe3(
|
|
1315
|
-
makeGet(
|
|
1305
|
+
makeGet(pattern2, schema, this.options.kupoHeader),
|
|
1316
1306
|
Effect2.provide(this.httpLayer),
|
|
1317
1307
|
Effect2.repeat({
|
|
1318
1308
|
schedule: Schedule.exponential(checkInterval),
|
|
@@ -1504,6 +1494,53 @@ import {
|
|
|
1504
1494
|
import { fromHex as fromHex2, withCMLScope } from "@lucid-evolution/core-utils";
|
|
1505
1495
|
import { walletFromSeed } from "@lucid-evolution/wallet";
|
|
1506
1496
|
|
|
1497
|
+
// src/internal/ed25519.ts
|
|
1498
|
+
var nodeCrypto = (() => {
|
|
1499
|
+
try {
|
|
1500
|
+
return globalThis.process?.getBuiltinModule?.("node:crypto");
|
|
1501
|
+
} catch {
|
|
1502
|
+
return void 0;
|
|
1503
|
+
}
|
|
1504
|
+
})();
|
|
1505
|
+
var SPKI_PREFIX = [
|
|
1506
|
+
48,
|
|
1507
|
+
42,
|
|
1508
|
+
48,
|
|
1509
|
+
5,
|
|
1510
|
+
6,
|
|
1511
|
+
3,
|
|
1512
|
+
43,
|
|
1513
|
+
101,
|
|
1514
|
+
112,
|
|
1515
|
+
3,
|
|
1516
|
+
33,
|
|
1517
|
+
0
|
|
1518
|
+
];
|
|
1519
|
+
var nativeVerifyEd25519 = (publicKey, message, signature) => {
|
|
1520
|
+
if (!nodeCrypto || publicKey.length !== 32 || signature.length !== 64) {
|
|
1521
|
+
return false;
|
|
1522
|
+
}
|
|
1523
|
+
if (publicKey.every((byte) => byte === 0)) return false;
|
|
1524
|
+
try {
|
|
1525
|
+
const spki = Buffer.alloc(SPKI_PREFIX.length + 32);
|
|
1526
|
+
spki.set(SPKI_PREFIX);
|
|
1527
|
+
spki.set(publicKey, SPKI_PREFIX.length);
|
|
1528
|
+
const key = nodeCrypto.createPublicKey({
|
|
1529
|
+
key: spki,
|
|
1530
|
+
format: "der",
|
|
1531
|
+
type: "spki"
|
|
1532
|
+
});
|
|
1533
|
+
return nodeCrypto.verify(null, message, key, signature);
|
|
1534
|
+
} catch {
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
var verifyEd25519 = (publicKey, message, signature) => nativeVerifyEd25519(
|
|
1539
|
+
publicKey.to_raw_bytes(),
|
|
1540
|
+
message,
|
|
1541
|
+
signature.to_raw_bytes()
|
|
1542
|
+
) || publicKey.verify(message, signature);
|
|
1543
|
+
|
|
1507
1544
|
// src/internal/emulator-ledger.ts
|
|
1508
1545
|
import { getAddressDetails } from "@lucid-evolution/utils";
|
|
1509
1546
|
var isUnorderedKey = (key) => {
|
|
@@ -2015,7 +2052,11 @@ var Emulator = class {
|
|
|
2015
2052
|
const witness = own3(vkeyWitnesses.get(i));
|
|
2016
2053
|
const publicKey = own3(witness.vkey());
|
|
2017
2054
|
const keyHash = own3(publicKey.hash()).to_hex();
|
|
2018
|
-
if (!
|
|
2055
|
+
if (!verifyEd25519(
|
|
2056
|
+
publicKey,
|
|
2057
|
+
fromHex2(txHash),
|
|
2058
|
+
own3(witness.ed25519_signature())
|
|
2059
|
+
)) {
|
|
2019
2060
|
throw new Error(`Invalid vkey witness. Key hash: ${keyHash}`);
|
|
2020
2061
|
}
|
|
2021
2062
|
keyHashes2.push(keyHash);
|
|
@@ -2856,8 +2897,10 @@ import { fromHex as fromHex3 } from "@lucid-evolution/core-utils";
|
|
|
2856
2897
|
import { fromUnit as fromUnit3 } from "@lucid-evolution/utils";
|
|
2857
2898
|
|
|
2858
2899
|
// src/internal/koios.ts
|
|
2859
|
-
import
|
|
2860
|
-
import
|
|
2900
|
+
import * as FetchHttpClient2 from "@effect/platform/FetchHttpClient";
|
|
2901
|
+
import * as Effect3 from "effect/Effect";
|
|
2902
|
+
import * as S4 from "effect/Schema";
|
|
2903
|
+
import { pipe as pipe4 } from "effect/Function";
|
|
2861
2904
|
import { applyDoubleCborEncoding as applyDoubleCborEncoding3 } from "@lucid-evolution/utils";
|
|
2862
2905
|
var ProtocolParametersSchema2 = S4.Struct({
|
|
2863
2906
|
pvt_motion_no_confidence: S4.Number,
|
|
@@ -3151,8 +3194,12 @@ var getUtxosEffect = (baseUrl, addressOrCredential, headers) => {
|
|
|
3151
3194
|
};
|
|
3152
3195
|
|
|
3153
3196
|
// src/koios.ts
|
|
3154
|
-
import
|
|
3155
|
-
import
|
|
3197
|
+
import * as Data from "effect/Data";
|
|
3198
|
+
import * as Effect4 from "effect/Effect";
|
|
3199
|
+
import * as Schedule2 from "effect/Schedule";
|
|
3200
|
+
import * as S5 from "effect/Schema";
|
|
3201
|
+
import { pipe as pipe5 } from "effect/Function";
|
|
3202
|
+
import * as FetchHttpClient3 from "@effect/platform/FetchHttpClient";
|
|
3156
3203
|
var KoiosError = class extends Data.TaggedError("KoiosError") {
|
|
3157
3204
|
get message() {
|
|
3158
3205
|
return `${this.cause}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucid-evolution/provider",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"@effect/platform": "^0.71.3",
|
|
26
26
|
"@effect/schema": "^0.68.26",
|
|
27
27
|
"effect": "^3.11.7",
|
|
28
|
-
"@lucid-evolution/cml": "0.1.
|
|
29
|
-
"@lucid-evolution/core-
|
|
30
|
-
"@lucid-evolution/core-
|
|
31
|
-
"@lucid-evolution/utils": "0.2.
|
|
32
|
-
"@lucid-evolution/wallet": "0.4.
|
|
28
|
+
"@lucid-evolution/cml": "0.1.1",
|
|
29
|
+
"@lucid-evolution/core-utils": "0.2.1",
|
|
30
|
+
"@lucid-evolution/core-types": "0.5.1",
|
|
31
|
+
"@lucid-evolution/utils": "0.2.2",
|
|
32
|
+
"@lucid-evolution/wallet": "0.4.2"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^20.12.8",
|