@wrongstack/tools 0.7.7 → 0.7.8
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/builtin.js +173 -124
- package/dist/builtin.js.map +1 -1
- package/dist/codebase-index/index.js +173 -124
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/index.js +173 -124
- package/dist/index.js.map +1 -1
- package/dist/pack.js +173 -124
- package/dist/pack.js.map +1 -1
- package/package.json +2 -2
package/dist/pack.js
CHANGED
|
@@ -5,9 +5,9 @@ import { dirname } from 'node:path';
|
|
|
5
5
|
import * as os from 'node:os';
|
|
6
6
|
import * as fs11 from 'node:fs/promises';
|
|
7
7
|
import { stat } from 'node:fs/promises';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
8
9
|
import * as fs from 'node:fs';
|
|
9
10
|
import { statSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
10
|
-
import { DatabaseSync } from 'node:sqlite';
|
|
11
11
|
import * as ts from 'typescript';
|
|
12
12
|
import * as dns from 'node:dns/promises';
|
|
13
13
|
import * as net from 'node:net';
|
|
@@ -952,12 +952,39 @@ function lspKindToInternalKind(k) {
|
|
|
952
952
|
// src/codebase-index/writer.ts
|
|
953
953
|
var INDEX_DIR = ".codebase-index";
|
|
954
954
|
var DB_FILE = "index.db";
|
|
955
|
+
var warningSilenced = false;
|
|
956
|
+
function silenceSqliteExperimentalWarning() {
|
|
957
|
+
if (warningSilenced) return;
|
|
958
|
+
warningSilenced = true;
|
|
959
|
+
const original = process.emitWarning.bind(process);
|
|
960
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
961
|
+
const msg = typeof warning === "string" ? warning : warning?.message ?? "";
|
|
962
|
+
const name = typeof warning === "string" ? String(rest[0] ?? "") : warning?.name ?? "";
|
|
963
|
+
if (/sqlite/i.test(msg) && /experimental/i.test(`${name} ${msg}`)) return;
|
|
964
|
+
original(warning, ...rest);
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
var DatabaseSyncCtor;
|
|
968
|
+
function loadDatabaseSync() {
|
|
969
|
+
if (DatabaseSyncCtor) return DatabaseSyncCtor;
|
|
970
|
+
silenceSqliteExperimentalWarning();
|
|
971
|
+
try {
|
|
972
|
+
const req = createRequire(import.meta.url);
|
|
973
|
+
DatabaseSyncCtor = req("node:sqlite").DatabaseSync;
|
|
974
|
+
} catch (err) {
|
|
975
|
+
throw new Error(
|
|
976
|
+
`The codebase index needs Node's built-in SQLite (node:sqlite), available since Node 22.5. This runtime doesn't provide it: ${err instanceof Error ? err.message : String(err)}`
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
return DatabaseSyncCtor;
|
|
980
|
+
}
|
|
955
981
|
var IndexStore = class {
|
|
956
982
|
constructor(projectRoot) {
|
|
957
983
|
this.projectRoot = projectRoot;
|
|
958
984
|
const dir = path.join(projectRoot, INDEX_DIR);
|
|
959
985
|
fs.mkdirSync(dir, { recursive: true });
|
|
960
|
-
|
|
986
|
+
const Database = loadDatabaseSync();
|
|
987
|
+
this.db = new Database(path.join(dir, DB_FILE));
|
|
961
988
|
this.initSchema();
|
|
962
989
|
}
|
|
963
990
|
projectRoot;
|
|
@@ -1972,7 +1999,10 @@ function checkNativeParser() {
|
|
|
1972
1999
|
execSync("rustc --version", { stdio: "pipe" });
|
|
1973
2000
|
const toolsDir = path.join(process.cwd(), "tools");
|
|
1974
2001
|
try {
|
|
1975
|
-
execSync(
|
|
2002
|
+
execSync(
|
|
2003
|
+
"cargo metadata --no-deps --format-version 1 --manifest-path " + path.join(toolsDir, "Cargo.toml"),
|
|
2004
|
+
{ stdio: "pipe" }
|
|
2005
|
+
);
|
|
1976
2006
|
return true;
|
|
1977
2007
|
} catch {
|
|
1978
2008
|
return false;
|
|
@@ -1988,12 +2018,16 @@ function tryNativeParse(file, content) {
|
|
|
1988
2018
|
const tmpFile = path.join(crateDir, "src", "input.rs");
|
|
1989
2019
|
const { writeFileSync: writeFileSync2 } = __require("node:fs");
|
|
1990
2020
|
writeFileSync2(tmpFile, content, "utf8");
|
|
1991
|
-
const result = spawnSync(
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2021
|
+
const result = spawnSync(
|
|
2022
|
+
"cargo",
|
|
2023
|
+
["run", "--manifest-path", path.join(toolsDir, "Cargo.toml")],
|
|
2024
|
+
{
|
|
2025
|
+
cwd: process.cwd(),
|
|
2026
|
+
encoding: "utf8",
|
|
2027
|
+
timeout: 15e3,
|
|
2028
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2029
|
+
}
|
|
2030
|
+
);
|
|
1997
2031
|
if (result.status === 0 && result.stdout) {
|
|
1998
2032
|
const symbols = JSON.parse(result.stdout);
|
|
1999
2033
|
return {
|
|
@@ -2027,7 +2061,8 @@ function regexParse(opts) {
|
|
|
2027
2061
|
lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
|
|
2028
2062
|
}
|
|
2029
2063
|
function lineFromOffset(offset) {
|
|
2030
|
-
let lo = 0
|
|
2064
|
+
let lo = 0;
|
|
2065
|
+
let hi = lineOffsets.length - 1;
|
|
2031
2066
|
while (lo < hi) {
|
|
2032
2067
|
const mid = lo + hi + 1 >>> 1;
|
|
2033
2068
|
if (lineOffsets[mid] <= offset) lo = mid;
|
|
@@ -2041,8 +2076,7 @@ function regexParse(opts) {
|
|
|
2041
2076
|
}
|
|
2042
2077
|
for (const pattern of RS_PATTERNS) {
|
|
2043
2078
|
pattern.regex.lastIndex = 0;
|
|
2044
|
-
let match;
|
|
2045
|
-
while ((match = pattern.regex.exec(content)) !== null) {
|
|
2079
|
+
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
2046
2080
|
const name = match[1];
|
|
2047
2081
|
const offset = match.index;
|
|
2048
2082
|
const line = lineFromOffset(offset);
|
|
@@ -2095,7 +2129,8 @@ function regexParse2(opts) {
|
|
|
2095
2129
|
lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
|
|
2096
2130
|
}
|
|
2097
2131
|
function lineFromOffset(offset) {
|
|
2098
|
-
let lo = 0
|
|
2132
|
+
let lo = 0;
|
|
2133
|
+
let hi = lineOffsets.length - 1;
|
|
2099
2134
|
while (lo < hi) {
|
|
2100
2135
|
const mid = lo + hi + 1 >>> 1;
|
|
2101
2136
|
if (lineOffsets[mid] <= offset) lo = mid;
|
|
@@ -2107,19 +2142,20 @@ function regexParse2(opts) {
|
|
|
2107
2142
|
if (rootMatch) {
|
|
2108
2143
|
const offset = rootMatch.index;
|
|
2109
2144
|
const line = lineFromOffset(offset);
|
|
2110
|
-
symbols.push(
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2145
|
+
symbols.push(
|
|
2146
|
+
makeSymbol({
|
|
2147
|
+
name: path.basename(file),
|
|
2148
|
+
kind: "object",
|
|
2149
|
+
line,
|
|
2150
|
+
col: 0,
|
|
2151
|
+
signature: `"${path.basename(file)}" = { ... }`,
|
|
2152
|
+
file,
|
|
2153
|
+
lang
|
|
2154
|
+
})
|
|
2155
|
+
);
|
|
2119
2156
|
}
|
|
2120
2157
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
2121
|
-
let match;
|
|
2122
|
-
while ((match = topLevelKeyRegex.exec(content)) !== null) {
|
|
2158
|
+
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
2123
2159
|
const key = match[1];
|
|
2124
2160
|
const offset = match.index;
|
|
2125
2161
|
const line = lineFromOffset(offset);
|
|
@@ -2146,15 +2182,17 @@ function regexParse2(opts) {
|
|
|
2146
2182
|
signature = `"$ref": "..."`;
|
|
2147
2183
|
}
|
|
2148
2184
|
}
|
|
2149
|
-
symbols.push(
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2185
|
+
symbols.push(
|
|
2186
|
+
makeSymbol({
|
|
2187
|
+
name: key,
|
|
2188
|
+
kind,
|
|
2189
|
+
line,
|
|
2190
|
+
col,
|
|
2191
|
+
signature,
|
|
2192
|
+
file,
|
|
2193
|
+
lang
|
|
2194
|
+
})
|
|
2195
|
+
);
|
|
2158
2196
|
if (isPackageJson && key === "scripts") {
|
|
2159
2197
|
extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset);
|
|
2160
2198
|
}
|
|
@@ -2163,20 +2201,21 @@ function regexParse2(opts) {
|
|
|
2163
2201
|
}
|
|
2164
2202
|
}
|
|
2165
2203
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
2166
|
-
|
|
2167
|
-
|
|
2204
|
+
const defsMatch = defsRegex.exec(content);
|
|
2205
|
+
if (defsMatch !== null) {
|
|
2168
2206
|
const offset = defsMatch.index;
|
|
2169
2207
|
const line = lineFromOffset(offset);
|
|
2170
|
-
symbols.push(
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2208
|
+
symbols.push(
|
|
2209
|
+
makeSymbol({
|
|
2210
|
+
name: "$defs",
|
|
2211
|
+
kind: "property",
|
|
2212
|
+
line,
|
|
2213
|
+
col: offset - (lineOffsets[line - 1] ?? 0),
|
|
2214
|
+
signature: '"$defs": { ... }',
|
|
2215
|
+
file,
|
|
2216
|
+
lang
|
|
2217
|
+
})
|
|
2218
|
+
);
|
|
2180
2219
|
}
|
|
2181
2220
|
const defsPatterns = [
|
|
2182
2221
|
/"\$defs"\s*:/g,
|
|
@@ -2186,69 +2225,71 @@ function regexParse2(opts) {
|
|
|
2186
2225
|
];
|
|
2187
2226
|
for (const pat of defsPatterns) {
|
|
2188
2227
|
pat.lastIndex = 0;
|
|
2189
|
-
|
|
2228
|
+
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
2190
2229
|
const offset = match.index;
|
|
2191
2230
|
const line = lineFromOffset(offset);
|
|
2192
2231
|
const key = match[0].match(/"([^"]+)"/)?.[1] ?? match[0];
|
|
2193
|
-
symbols.push(
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2232
|
+
symbols.push(
|
|
2233
|
+
makeSymbol({
|
|
2234
|
+
name: key,
|
|
2235
|
+
kind: "property",
|
|
2236
|
+
line,
|
|
2237
|
+
col: offset - (lineOffsets[line - 1] ?? 0),
|
|
2238
|
+
signature: `"${key}": { ... }`,
|
|
2239
|
+
file,
|
|
2240
|
+
lang
|
|
2241
|
+
})
|
|
2242
|
+
);
|
|
2202
2243
|
}
|
|
2203
2244
|
}
|
|
2204
2245
|
return { file, lang, symbols, mtimeMs: Date.now() };
|
|
2205
2246
|
}
|
|
2206
2247
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
|
|
2207
2248
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
2208
|
-
let match;
|
|
2209
|
-
while ((match = scriptsBlockRegex.exec(content)) !== null) {
|
|
2249
|
+
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
2210
2250
|
const blockContent = match[0];
|
|
2211
2251
|
const blockOffset = match.index;
|
|
2212
2252
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
2213
|
-
let scriptMatch;
|
|
2214
|
-
while ((scriptMatch = scriptKeyRegex.exec(blockContent)) !== null) {
|
|
2253
|
+
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
2215
2254
|
const key = scriptMatch[1];
|
|
2216
2255
|
const keyOffset = blockOffset + scriptMatch.index;
|
|
2217
2256
|
const line = lineFromOffset(keyOffset);
|
|
2218
|
-
symbols.push(
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2257
|
+
symbols.push(
|
|
2258
|
+
makeSymbol({
|
|
2259
|
+
name: key,
|
|
2260
|
+
kind: "function",
|
|
2261
|
+
line,
|
|
2262
|
+
col: keyOffset - (lineOffsets[line - 1] ?? 0),
|
|
2263
|
+
signature: `"${key}": "..."`,
|
|
2264
|
+
file,
|
|
2265
|
+
lang
|
|
2266
|
+
})
|
|
2267
|
+
);
|
|
2227
2268
|
}
|
|
2228
2269
|
}
|
|
2229
2270
|
}
|
|
2230
2271
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
|
|
2231
2272
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
2232
|
-
let match;
|
|
2233
|
-
while ((match = optsBlockRegex.exec(content)) !== null) {
|
|
2273
|
+
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
2234
2274
|
const blockContent = match[0];
|
|
2235
2275
|
const blockOffset = match.index;
|
|
2236
2276
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
2237
|
-
let optMatch;
|
|
2238
|
-
while ((optMatch = optKeyRegex.exec(blockContent)) !== null) {
|
|
2277
|
+
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
2239
2278
|
const key = optMatch[1];
|
|
2240
2279
|
const keyOffset = blockOffset + optMatch.index;
|
|
2241
2280
|
const line = lineFromOffset(keyOffset);
|
|
2242
2281
|
if (line <= parentLine) continue;
|
|
2243
|
-
symbols.push(
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2282
|
+
symbols.push(
|
|
2283
|
+
makeSymbol({
|
|
2284
|
+
name: key,
|
|
2285
|
+
kind: "property",
|
|
2286
|
+
line,
|
|
2287
|
+
col: keyOffset - (lineOffsets[line - 1] ?? 0),
|
|
2288
|
+
signature: `"${key}": ...`,
|
|
2289
|
+
file,
|
|
2290
|
+
lang
|
|
2291
|
+
})
|
|
2292
|
+
);
|
|
2252
2293
|
}
|
|
2253
2294
|
}
|
|
2254
2295
|
}
|
|
@@ -2286,7 +2327,8 @@ function regexParse3(opts) {
|
|
|
2286
2327
|
lineOffsets.push(lineOffsets[i] + lines[i].length + 1);
|
|
2287
2328
|
}
|
|
2288
2329
|
function lineFromOffset(offset) {
|
|
2289
|
-
let lo = 0
|
|
2330
|
+
let lo = 0;
|
|
2331
|
+
let hi = lineOffsets.length - 1;
|
|
2290
2332
|
while (lo < hi) {
|
|
2291
2333
|
const mid = lo + hi + 1 >>> 1;
|
|
2292
2334
|
if (lineOffsets[mid] <= offset) lo = mid;
|
|
@@ -2295,40 +2337,43 @@ function regexParse3(opts) {
|
|
|
2295
2337
|
return lo + 1;
|
|
2296
2338
|
}
|
|
2297
2339
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
2298
|
-
let match;
|
|
2299
|
-
while ((match = anchorRegex.exec(content)) !== null) {
|
|
2340
|
+
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
2300
2341
|
const name = match[1];
|
|
2301
2342
|
const offset = match.index;
|
|
2302
2343
|
const line = lineFromOffset(offset);
|
|
2303
2344
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2304
|
-
symbols.push(
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2345
|
+
symbols.push(
|
|
2346
|
+
makeSymbol2({
|
|
2347
|
+
name,
|
|
2348
|
+
kind: "const",
|
|
2349
|
+
line,
|
|
2350
|
+
col,
|
|
2351
|
+
signature: `&${name}`,
|
|
2352
|
+
file,
|
|
2353
|
+
lang
|
|
2354
|
+
})
|
|
2355
|
+
);
|
|
2313
2356
|
}
|
|
2314
2357
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
2315
|
-
|
|
2358
|
+
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
2316
2359
|
const name = match[1];
|
|
2317
2360
|
const offset = match.index;
|
|
2318
2361
|
const line = lineFromOffset(offset);
|
|
2319
2362
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2320
|
-
symbols.push(
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2363
|
+
symbols.push(
|
|
2364
|
+
makeSymbol2({
|
|
2365
|
+
name,
|
|
2366
|
+
kind: "const",
|
|
2367
|
+
line,
|
|
2368
|
+
col,
|
|
2369
|
+
signature: `*${name}`,
|
|
2370
|
+
file,
|
|
2371
|
+
lang
|
|
2372
|
+
})
|
|
2373
|
+
);
|
|
2329
2374
|
}
|
|
2330
2375
|
const kvRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:/gm;
|
|
2331
|
-
|
|
2376
|
+
for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {
|
|
2332
2377
|
const indent = match[1].length;
|
|
2333
2378
|
const key = match[2];
|
|
2334
2379
|
const offset = match.index;
|
|
@@ -2344,38 +2389,42 @@ function regexParse3(opts) {
|
|
|
2344
2389
|
symbols.push(makeSymbol2({ name: key, kind, line, col, signature, file, lang }));
|
|
2345
2390
|
}
|
|
2346
2391
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
2347
|
-
|
|
2392
|
+
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
2348
2393
|
const key = match[2];
|
|
2349
2394
|
const offset = match.index;
|
|
2350
2395
|
const line = lineFromOffset(offset);
|
|
2351
2396
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2352
2397
|
const value = extractValue(content, offset + match[0].length);
|
|
2353
2398
|
const kind = isScalar(value) ? "literal" : "property";
|
|
2354
|
-
symbols.push(
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2399
|
+
symbols.push(
|
|
2400
|
+
makeSymbol2({
|
|
2401
|
+
name: key,
|
|
2402
|
+
kind,
|
|
2403
|
+
line,
|
|
2404
|
+
col,
|
|
2405
|
+
signature: `- ${key}: ${truncate(value, 60)}`,
|
|
2406
|
+
file,
|
|
2407
|
+
lang
|
|
2408
|
+
})
|
|
2409
|
+
);
|
|
2363
2410
|
}
|
|
2364
2411
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
2365
|
-
|
|
2412
|
+
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
2366
2413
|
const key = match[2];
|
|
2367
2414
|
const offset = match.index;
|
|
2368
2415
|
const line = lineFromOffset(offset);
|
|
2369
2416
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
2370
|
-
symbols.push(
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2417
|
+
symbols.push(
|
|
2418
|
+
makeSymbol2({
|
|
2419
|
+
name: key,
|
|
2420
|
+
kind: "property",
|
|
2421
|
+
line,
|
|
2422
|
+
col,
|
|
2423
|
+
signature: `${key}: | ...`,
|
|
2424
|
+
file,
|
|
2425
|
+
lang
|
|
2426
|
+
})
|
|
2427
|
+
);
|
|
2379
2428
|
}
|
|
2380
2429
|
return { file, lang, symbols, mtimeMs: Date.now() };
|
|
2381
2430
|
}
|