token-goat 2.9.14 → 2.9.15
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/README.md +15 -1
- package/dist/token-goat-chunk-2X2EBBC6.mjs +277 -0
- package/dist/{token-goat-chunk-XQF5J25J.mjs → token-goat-chunk-3NSDDTGL.mjs} +5 -4
- package/dist/token-goat-chunk-4EXFN2AW.mjs +29 -0
- package/dist/{token-goat-chunk-4SDX3QP3.mjs → token-goat-chunk-5V7DAC7V.mjs} +5 -4
- package/dist/token-goat-chunk-6DLVZDB6.mjs +34 -0
- package/dist/{token-goat-chunk-XEH6KBWW.mjs → token-goat-chunk-7ZYK25AO.mjs} +4 -3
- package/dist/{token-goat-chunk-7OGKZ7AP.mjs → token-goat-chunk-A4JYKD5H.mjs} +5 -3
- package/dist/{token-goat-chunk-2ESBO4IN.mjs → token-goat-chunk-ASYEPR3S.mjs} +10 -7
- package/dist/{token-goat-chunk-PGGDW7DZ.mjs → token-goat-chunk-ATIFTMRC.mjs} +9 -6
- package/dist/{token-goat-chunk-IT6O3PNN.mjs → token-goat-chunk-BL5LNGBG.mjs} +813 -4613
- package/dist/{token-goat-chunk-PRJVGIC5.mjs → token-goat-chunk-C5JIO6HK.mjs} +7 -5
- package/dist/{token-goat-chunk-YTQHZJXW.mjs → token-goat-chunk-DQ4J5AFF.mjs} +40 -17
- package/dist/token-goat-chunk-ERTXEKB6.mjs +228 -0
- package/dist/{token-goat-chunk-ASVVF4JV.mjs → token-goat-chunk-GIIHUSZX.mjs} +137 -19
- package/dist/{token-goat-chunk-DAMXYVIW.mjs → token-goat-chunk-GMQQA7E4.mjs} +296 -276
- package/dist/{token-goat-chunk-T2IWWTHB.mjs → token-goat-chunk-IVUQLQWN.mjs} +33 -1142
- package/dist/{token-goat-chunk-POBYR64E.mjs → token-goat-chunk-K7F2BFIK.mjs} +801 -3
- package/dist/{token-goat-chunk-RUDOKYPJ.mjs → token-goat-chunk-LCZBPOIN.mjs} +1733 -1360
- package/dist/token-goat-chunk-LMFO66YD.mjs +331 -0
- package/dist/{token-goat-chunk-EZNVAIR3.mjs → token-goat-chunk-LT7JRU6K.mjs} +4 -3
- package/dist/{token-goat-chunk-YHGTGG6K.mjs → token-goat-chunk-MZDIJJ3R.mjs} +29 -5
- package/dist/{token-goat-chunk-O5WAMISC.mjs → token-goat-chunk-NDPO7GAH.mjs} +26 -16
- package/dist/token-goat-chunk-NDRP4KJQ.mjs +4371 -0
- package/dist/{token-goat-chunk-ZFM4PWXL.mjs → token-goat-chunk-NU7TLMQK.mjs} +3 -3
- package/dist/token-goat-chunk-OSUFN2FV.mjs +326 -0
- package/dist/token-goat-chunk-OUGNPMDA.mjs +959 -0
- package/dist/{token-goat-chunk-AMYCQJX4.mjs → token-goat-chunk-PM76YS22.mjs} +49 -256
- package/dist/{token-goat-chunk-SAQ5PG4L.mjs → token-goat-chunk-S4XRY446.mjs} +5 -2
- package/dist/{token-goat-chunk-DBNY4RLN.mjs → token-goat-chunk-SFAS46RE.mjs} +5 -3
- package/dist/{token-goat-chunk-XVZ4MNQC.mjs → token-goat-chunk-SZWYESBS.mjs} +221 -78
- package/dist/{token-goat-chunk-HF6H7RNK.mjs → token-goat-chunk-XEPXYDPI.mjs} +3 -2
- package/dist/{token-goat-chunk-EG3663UT.mjs → token-goat-chunk-YKG35VHC.mjs} +4 -2
- package/dist/{token-goat-chunk-VZYD4OZB.mjs → token-goat-chunk-YQ7WI2CO.mjs} +78 -266
- package/dist/{token-goat-chunk-3XQPEJMV.mjs → token-goat-chunk-YZX7EFG4.mjs} +1 -1
- package/dist/token-goat-hook.mjs +13 -9
- package/dist/token-goat.core.mjs +21 -16
- package/docs/cli.md +8 -6
- package/package.json +1 -1
- package/dist/token-goat-chunk-ZD4EM4LR.mjs +0 -30
- package/dist/token-goat-chunk-ZYNNQ36L.mjs +0 -479
|
@@ -1,56 +1,44 @@
|
|
|
1
1
|
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
formatSqliteQueryTable,
|
|
5
|
+
formatSqliteSchema,
|
|
6
|
+
formatSqliteTables,
|
|
7
|
+
getSqliteSchema,
|
|
8
|
+
getSqliteTables,
|
|
9
|
+
runReadOnlySqliteQuery
|
|
10
|
+
} from "./token-goat-chunk-LMFO66YD.mjs";
|
|
3
11
|
import {
|
|
4
12
|
ImageDecodeError,
|
|
5
|
-
MAX_OVER_FETCH,
|
|
6
13
|
NOTEBOOK_CELL_LINES_SUFFIX,
|
|
7
|
-
OVER_FETCH_FACTOR,
|
|
8
14
|
SKIP_DIRS,
|
|
15
|
+
canShrinkFormat,
|
|
9
16
|
emptyIndexMessage,
|
|
10
17
|
enqueueDirtyPathSafe,
|
|
11
18
|
fenceUntrusted,
|
|
12
|
-
findContainingSection,
|
|
13
19
|
formatSymbolLocation,
|
|
14
20
|
getEmbeddingCoverage,
|
|
15
21
|
getTrackedFiles,
|
|
16
|
-
indexFileSync,
|
|
17
22
|
indexedSourceText,
|
|
18
|
-
isAvailable,
|
|
19
23
|
isImagePath,
|
|
20
24
|
isIndexEmptyForProject,
|
|
21
25
|
isPrivateIpv4Octets,
|
|
22
26
|
isPrivateIpv6Groups,
|
|
23
|
-
isTreeSitterAvailable,
|
|
24
27
|
isVirtualIndexedPath,
|
|
25
|
-
listSections,
|
|
26
|
-
mergeNearbyHits,
|
|
27
28
|
parseIpv6Groups,
|
|
28
29
|
probeImageMeta,
|
|
29
|
-
readSection,
|
|
30
30
|
scanAndRecord,
|
|
31
31
|
searchEvidenceSemantically,
|
|
32
|
-
searchSemantic,
|
|
33
32
|
shrinkImage,
|
|
34
33
|
urlPolicyDenialReason,
|
|
35
34
|
virtualIndexedScopeNote,
|
|
36
35
|
walkProject
|
|
37
|
-
} from "./token-goat-chunk-
|
|
36
|
+
} from "./token-goat-chunk-BL5LNGBG.mjs";
|
|
38
37
|
import {
|
|
39
|
-
DEFAULT_QUERY_LIMIT,
|
|
40
38
|
capJsonRows,
|
|
41
|
-
countRefs,
|
|
42
|
-
countSymbols,
|
|
43
|
-
distinctSymbolKinds,
|
|
44
39
|
estimateTokens,
|
|
45
|
-
getFileEntry,
|
|
46
|
-
pathEqClause,
|
|
47
|
-
queryRefCounts,
|
|
48
|
-
queryRefs,
|
|
49
|
-
queryRefsByContext,
|
|
50
|
-
querySymbols,
|
|
51
|
-
searchSymbolsFts,
|
|
52
40
|
trimToBudget
|
|
53
|
-
} from "./token-goat-chunk-
|
|
41
|
+
} from "./token-goat-chunk-YQ7WI2CO.mjs";
|
|
54
42
|
import {
|
|
55
43
|
extractPdfMeta,
|
|
56
44
|
extractPdfOutline,
|
|
@@ -64,7 +52,7 @@ import {
|
|
|
64
52
|
parseWhereSpecs,
|
|
65
53
|
profileCsv,
|
|
66
54
|
queryCsv
|
|
67
|
-
} from "./token-goat-chunk-
|
|
55
|
+
} from "./token-goat-chunk-YKG35VHC.mjs";
|
|
68
56
|
import {
|
|
69
57
|
MAX_ZIP_INPUT_BYTES,
|
|
70
58
|
MAX_ZIP_OUTPUT_BYTES,
|
|
@@ -75,46 +63,6 @@ import {
|
|
|
75
63
|
import {
|
|
76
64
|
createLazyModuleLoader
|
|
77
65
|
} from "./token-goat-chunk-AH6QILZM.mjs";
|
|
78
|
-
import {
|
|
79
|
-
isOcrEngineAvailable,
|
|
80
|
-
isTextHeavy,
|
|
81
|
-
ocrImage,
|
|
82
|
-
ocrIntegrityFailed
|
|
83
|
-
} from "./token-goat-chunk-DBNY4RLN.mjs";
|
|
84
|
-
import {
|
|
85
|
-
Database,
|
|
86
|
-
FILENAME_LANGUAGE,
|
|
87
|
-
TREE_SITTER_LANGUAGES,
|
|
88
|
-
basenameImportsExtension,
|
|
89
|
-
colorStdout,
|
|
90
|
-
detectLanguage,
|
|
91
|
-
detectLanguageOfFile,
|
|
92
|
-
getDb,
|
|
93
|
-
isBlobStale,
|
|
94
|
-
languageLabel,
|
|
95
|
-
loadBlob,
|
|
96
|
-
partialRefsReason,
|
|
97
|
-
recordStat,
|
|
98
|
-
redactIfDotenv,
|
|
99
|
-
redactSecrets,
|
|
100
|
-
savedTokensFromBytes,
|
|
101
|
-
storeBlob,
|
|
102
|
-
stripAnsi,
|
|
103
|
-
unsupportedLanguageName
|
|
104
|
-
} from "./token-goat-chunk-T2IWWTHB.mjs";
|
|
105
|
-
import {
|
|
106
|
-
fileIsAbsent,
|
|
107
|
-
findProject,
|
|
108
|
-
fingerprintContent,
|
|
109
|
-
fingerprintFile,
|
|
110
|
-
getDisplayRoot,
|
|
111
|
-
loadConfig,
|
|
112
|
-
resolveProjectRoot,
|
|
113
|
-
shortFingerprint
|
|
114
|
-
} from "./token-goat-chunk-SAQ5PG4L.mjs";
|
|
115
|
-
import {
|
|
116
|
-
registerReset
|
|
117
|
-
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
118
66
|
import {
|
|
119
67
|
IMPORT_RE,
|
|
120
68
|
extractAbap,
|
|
@@ -139,34 +87,92 @@ import {
|
|
|
139
87
|
extractSolidity,
|
|
140
88
|
extractThrift,
|
|
141
89
|
stripLeadingAttributes
|
|
142
|
-
} from "./token-goat-chunk-
|
|
90
|
+
} from "./token-goat-chunk-XEPXYDPI.mjs";
|
|
91
|
+
import {
|
|
92
|
+
MAX_OVER_FETCH,
|
|
93
|
+
OVER_FETCH_FACTOR,
|
|
94
|
+
checkEmbeddingPreflight,
|
|
95
|
+
findContainingSection,
|
|
96
|
+
indexFileSync,
|
|
97
|
+
isAvailable,
|
|
98
|
+
isTreeSitterAvailable,
|
|
99
|
+
listSections,
|
|
100
|
+
mergeNearbyHits,
|
|
101
|
+
readSection,
|
|
102
|
+
searchSemantic
|
|
103
|
+
} from "./token-goat-chunk-NDRP4KJQ.mjs";
|
|
104
|
+
import {
|
|
105
|
+
DEFAULT_QUERY_LIMIT,
|
|
106
|
+
countRefs,
|
|
107
|
+
countSymbols,
|
|
108
|
+
distinctSymbolKinds,
|
|
109
|
+
getFileEntry,
|
|
110
|
+
pathEqClause,
|
|
111
|
+
queryRefCounts,
|
|
112
|
+
queryRefs,
|
|
113
|
+
queryRefsByContext,
|
|
114
|
+
querySymbols,
|
|
115
|
+
searchSymbolsFts
|
|
116
|
+
} from "./token-goat-chunk-2X2EBBC6.mjs";
|
|
117
|
+
import {
|
|
118
|
+
isOcrEngineAvailable,
|
|
119
|
+
isTextHeavy,
|
|
120
|
+
ocrImage,
|
|
121
|
+
ocrIntegrityFailed
|
|
122
|
+
} from "./token-goat-chunk-SFAS46RE.mjs";
|
|
123
|
+
import {
|
|
124
|
+
colorStdout,
|
|
125
|
+
isBlobStale,
|
|
126
|
+
loadBlob,
|
|
127
|
+
recordStat,
|
|
128
|
+
redactSecrets,
|
|
129
|
+
savedTokensFromBytes,
|
|
130
|
+
storeBlob,
|
|
131
|
+
stripAnsi
|
|
132
|
+
} from "./token-goat-chunk-IVUQLQWN.mjs";
|
|
143
133
|
import {
|
|
134
|
+
FILENAME_LANGUAGE,
|
|
135
|
+
TREE_SITTER_LANGUAGES,
|
|
136
|
+
basenameImportsExtension,
|
|
137
|
+
detectLanguage,
|
|
138
|
+
detectLanguageOfFile,
|
|
139
|
+
getDb,
|
|
144
140
|
isAblSource,
|
|
145
141
|
isMatlabSource,
|
|
146
142
|
isObjcHeader,
|
|
147
143
|
isObjcSource,
|
|
148
144
|
isPascalSource,
|
|
149
145
|
isPerlSource,
|
|
150
|
-
isPrologSource
|
|
151
|
-
|
|
146
|
+
isPrologSource,
|
|
147
|
+
languageLabel,
|
|
148
|
+
partialRefsReason,
|
|
149
|
+
redactIfDotenv,
|
|
150
|
+
unsupportedLanguageName
|
|
151
|
+
} from "./token-goat-chunk-K7F2BFIK.mjs";
|
|
152
|
+
import {
|
|
153
|
+
fileIsAbsent,
|
|
154
|
+
findProject,
|
|
155
|
+
fingerprintContent,
|
|
156
|
+
fingerprintFile,
|
|
157
|
+
getDisplayRoot,
|
|
158
|
+
loadConfig,
|
|
159
|
+
resolveProjectRoot,
|
|
160
|
+
shortFingerprint
|
|
161
|
+
} from "./token-goat-chunk-S4XRY446.mjs";
|
|
162
|
+
import {
|
|
163
|
+
registerReset
|
|
164
|
+
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
152
165
|
import {
|
|
153
166
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
154
167
|
SKELETON_SYMBOL_CAP,
|
|
155
|
-
UNTRUSTED_FILE_TAG,
|
|
156
|
-
UNTRUSTED_GITHUB_TAG,
|
|
157
|
-
UNTRUSTED_HTML_TAG,
|
|
158
168
|
atomicWriteBytes,
|
|
159
169
|
compileGrepMatcher,
|
|
160
170
|
countNoun,
|
|
161
171
|
decodeSource,
|
|
162
|
-
displaySafeJson,
|
|
163
|
-
displaySafeText,
|
|
164
172
|
ensureNewline,
|
|
165
173
|
escapeRegExp,
|
|
166
174
|
excludeTestsHiddenNote,
|
|
167
175
|
extractErrorMessage,
|
|
168
|
-
fenceUntrustedContent,
|
|
169
|
-
fenceUntrustedFileContent,
|
|
170
176
|
filtersFilteredToEmptyNotice,
|
|
171
177
|
foldCaseForContainment,
|
|
172
178
|
foldPath,
|
|
@@ -174,20 +180,29 @@ import {
|
|
|
174
180
|
grepFilteredToEmptyNotice,
|
|
175
181
|
isInsideRoot,
|
|
176
182
|
isTestFile,
|
|
177
|
-
normalizePath,
|
|
178
183
|
pushAll,
|
|
179
184
|
redactUrlQuery,
|
|
180
185
|
requireNonNegativeStrictInt,
|
|
181
186
|
requirePositiveStrictInt,
|
|
182
|
-
resolveIndexPath,
|
|
183
187
|
resolveOnPath,
|
|
184
188
|
runGit,
|
|
185
189
|
stripBom,
|
|
186
190
|
supportRequestLine,
|
|
187
|
-
toDisplayPath,
|
|
188
191
|
windowsCmdQuoteArg,
|
|
189
192
|
withExtension
|
|
190
|
-
} from "./token-goat-chunk-
|
|
193
|
+
} from "./token-goat-chunk-PM76YS22.mjs";
|
|
194
|
+
import {
|
|
195
|
+
UNTRUSTED_FILE_TAG,
|
|
196
|
+
UNTRUSTED_GITHUB_TAG,
|
|
197
|
+
UNTRUSTED_HTML_TAG,
|
|
198
|
+
displaySafeJson,
|
|
199
|
+
displaySafeText,
|
|
200
|
+
fenceUntrustedContent,
|
|
201
|
+
fenceUntrustedFileContent,
|
|
202
|
+
normalizePath,
|
|
203
|
+
resolveIndexPath,
|
|
204
|
+
toDisplayPath
|
|
205
|
+
} from "./token-goat-chunk-ERTXEKB6.mjs";
|
|
191
206
|
import {
|
|
192
207
|
compileGuardedRegex
|
|
193
208
|
} from "./token-goat-chunk-GMOUBOX4.mjs";
|
|
@@ -195,185 +210,439 @@ import {
|
|
|
195
210
|
init_define_import_meta_env
|
|
196
211
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
197
212
|
|
|
198
|
-
// src/
|
|
199
|
-
init_define_import_meta_env();
|
|
200
|
-
import * as fs14 from "node:fs";
|
|
201
|
-
|
|
202
|
-
// src/read_commands.ts
|
|
203
|
-
init_define_import_meta_env();
|
|
204
|
-
import * as fs13 from "node:fs";
|
|
205
|
-
import * as path12 from "node:path";
|
|
206
|
-
|
|
207
|
-
// src/import_export_extract.ts
|
|
213
|
+
// src/json_query.ts
|
|
208
214
|
init_define_import_meta_env();
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
[".pli", extractPli],
|
|
214
|
-
[".pl1", extractPli],
|
|
215
|
-
[".rpgle", extractRpg],
|
|
216
|
-
[".sqlrpgle", extractRpg],
|
|
217
|
-
[".jcl", extractJcl],
|
|
218
|
-
...[".f", ".for", ".f77", ".f90", ".f95", ".f03", ".f08"].map((e) => [e, extractFortran]),
|
|
219
|
-
...[".pas", ".dpr", ".dpk", ".lpr"].map((e) => [e, extractPascal]),
|
|
220
|
-
[".cmake", extractCmake],
|
|
221
|
-
...[".s", ".asm", ".nasm"].map((e) => [e, extractAsm]),
|
|
222
|
-
...[".bat", ".cmd"].map((e) => [e, extractBatch]),
|
|
223
|
-
...[".erl", ".hrl"].map((e) => [e, extractErlang])
|
|
224
|
-
]);
|
|
225
|
-
var BRACE_ADAPTER_IMPORTS = new Map([
|
|
226
|
-
[".mm", extractObjc],
|
|
227
|
-
[".groovy", extractGroovy],
|
|
228
|
-
[".gvy", extractGroovy],
|
|
229
|
-
[".gradle", extractGroovy],
|
|
230
|
-
[".pm", extractPerl],
|
|
231
|
-
[".sol", extractSolidity],
|
|
232
|
-
[".thrift", extractThrift],
|
|
233
|
-
...[".glsl", ".vert", ".frag", ".comp", ".geom", ".tesc", ".tese", ".hlsl", ".hlsli", ".metal"].map((e) => [e, extractCShader])
|
|
234
|
-
]);
|
|
235
|
-
function braceAdapterImportsFor(e, text) {
|
|
236
|
-
if (e === ".m") return isObjcSource(text) ? extractObjc : isMatlabSource(text) ? extractMatlab : void 0;
|
|
237
|
-
if (e === ".pp") return isPascalSource(text) ? extractPascal : void 0;
|
|
238
|
-
if (e === ".h") return isObjcHeader(text) ? extractObjc : void 0;
|
|
239
|
-
if (e === ".pl") return isPrologSource(text) ? void 0 : extractPerl;
|
|
240
|
-
if (e === ".t") return isPerlSource(text) ? extractPerl : void 0;
|
|
241
|
-
return BRACE_ADAPTER_IMPORTS.get(e);
|
|
215
|
+
function jsonType(value) {
|
|
216
|
+
if (value === null) return "null";
|
|
217
|
+
if (Array.isArray(value)) return "array";
|
|
218
|
+
return typeof value;
|
|
242
219
|
}
|
|
243
|
-
function
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
for (const line of lines) {
|
|
269
|
-
const m = /^\s*pub(?:\s*\([^)]*\))?\s+(?:async\s+)?(?:fn|struct|enum|trait|type|const|mod|static)\s+([A-Za-z_]\w*)/.exec(line);
|
|
270
|
-
if (m) push(m[1]);
|
|
220
|
+
function fieldSummary(name, value) {
|
|
221
|
+
const type = jsonType(value);
|
|
222
|
+
if (type === "array") return { name, type, size: value.length };
|
|
223
|
+
if (type === "object") return { name, type, size: Object.keys(value).length };
|
|
224
|
+
return { name, type };
|
|
225
|
+
}
|
|
226
|
+
function outlineJson(data, opts = {}) {
|
|
227
|
+
const sampleSize = opts.sampleSize ?? 5;
|
|
228
|
+
const type = jsonType(data);
|
|
229
|
+
if (type === "array") {
|
|
230
|
+
const arr = data;
|
|
231
|
+
const sample = arr.slice(0, sampleSize);
|
|
232
|
+
const elementTypes = new Set(sample.map(jsonType));
|
|
233
|
+
const elementType = elementTypes.size === 0 ? "unknown" : elementTypes.size === 1 ? [...elementTypes][0] : "mixed";
|
|
234
|
+
const result = { kind: "array", length: arr.length, elementType };
|
|
235
|
+
if (elementType === "object") {
|
|
236
|
+
const objects = sample;
|
|
237
|
+
const keySets = objects.map((el) => Object.keys(el));
|
|
238
|
+
const allKeys = [...new Set(keySets.flat())];
|
|
239
|
+
result.sampleKeys = allKeys.map((k) => {
|
|
240
|
+
const owner = objects.find((el) => Object.prototype.hasOwnProperty.call(el, k));
|
|
241
|
+
return fieldSummary(k, owner?.[k]);
|
|
242
|
+
});
|
|
243
|
+
const firstKeySet = keySets[0] ?? [];
|
|
244
|
+
result.heterogeneous = keySets.some((ks) => ks.length !== firstKeySet.length || !ks.every((k) => firstKeySet.includes(k)));
|
|
271
245
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
if (type === "object") {
|
|
249
|
+
const obj = data;
|
|
250
|
+
return { kind: "object", fields: Object.keys(obj).map((k) => fieldSummary(k, obj[k])) };
|
|
251
|
+
}
|
|
252
|
+
return { kind: "primitive", type };
|
|
253
|
+
}
|
|
254
|
+
function formatJsonOutline(outline) {
|
|
255
|
+
if (outline.kind === "primitive") return `(scalar ${outline.type})`;
|
|
256
|
+
if (outline.kind === "object") {
|
|
257
|
+
if (outline.fields.length === 0) return "(empty object)";
|
|
258
|
+
return outline.fields.map((f) => `${displaySafeText(f.name)}: ${f.type}${f.size !== void 0 ? ` (${f.size})` : ""}`).join("\n");
|
|
259
|
+
}
|
|
260
|
+
const lines = [`array of ${outline.length} element${outline.length === 1 ? "" : "s"} (${outline.elementType})`];
|
|
261
|
+
if (outline.sampleKeys !== void 0) {
|
|
262
|
+
lines.push(outline.heterogeneous === true ? "keys (from first elements, shape varies across sample):" : "keys (from first elements):");
|
|
263
|
+
for (const f of outline.sampleKeys) {
|
|
264
|
+
lines.push(` ${displaySafeText(f.name)}: ${f.type}${f.size !== void 0 ? ` (${f.size})` : ""}`);
|
|
276
265
|
}
|
|
277
266
|
}
|
|
278
|
-
return
|
|
267
|
+
return lines.join("\n");
|
|
279
268
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
269
|
+
var MAX_RECURSIVE_NODES = 5e4;
|
|
270
|
+
var MAX_RECURSIVE_DEPTH = 100;
|
|
271
|
+
function collectRecursiveKey(root, keyName, out, budget) {
|
|
272
|
+
const seen = /* @__PURE__ */ new Set();
|
|
273
|
+
function walk(val, depth) {
|
|
274
|
+
if (depth > MAX_RECURSIVE_DEPTH) {
|
|
275
|
+
budget.exhausted = true;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (val === null || typeof val !== "object") return;
|
|
279
|
+
if (seen.has(val)) return;
|
|
280
|
+
seen.add(val);
|
|
281
|
+
if (budget.nodesLeft <= 0) {
|
|
282
|
+
budget.exhausted = true;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
budget.nodesLeft--;
|
|
286
|
+
if (Array.isArray(val)) {
|
|
287
|
+
for (let i = 0; i < val.length; i++) {
|
|
288
|
+
walk(val[i], depth + 1);
|
|
289
|
+
if (budget.nodesLeft <= 0) {
|
|
290
|
+
if (i < val.length - 1) budget.exhausted = true;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
290
294
|
} else {
|
|
291
|
-
|
|
295
|
+
const obj = val;
|
|
296
|
+
if (Object.prototype.hasOwnProperty.call(obj, keyName)) {
|
|
297
|
+
out.push(obj[keyName]);
|
|
298
|
+
}
|
|
299
|
+
const keys = Object.keys(obj);
|
|
300
|
+
for (let i = 0; i < keys.length; i++) {
|
|
301
|
+
walk(obj[keys[i]], depth + 1);
|
|
302
|
+
if (budget.nodesLeft <= 0) {
|
|
303
|
+
if (i < keys.length - 1) budget.exhausted = true;
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
292
307
|
}
|
|
293
308
|
}
|
|
294
|
-
|
|
295
|
-
return parts;
|
|
309
|
+
walk(root, 0);
|
|
296
310
|
}
|
|
297
|
-
function
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
311
|
+
function getNestedField(obj, fieldPath) {
|
|
312
|
+
if (obj === null || typeof obj !== "object") return void 0;
|
|
313
|
+
if (Object.prototype.hasOwnProperty.call(obj, fieldPath)) {
|
|
314
|
+
return obj[fieldPath];
|
|
315
|
+
}
|
|
316
|
+
const parts = fieldPath.split(".");
|
|
317
|
+
let cur = obj;
|
|
318
|
+
for (const part of parts) {
|
|
319
|
+
if (cur === null || typeof cur !== "object") return void 0;
|
|
320
|
+
if (!Object.prototype.hasOwnProperty.call(cur, part)) return void 0;
|
|
321
|
+
cur = cur[part];
|
|
322
|
+
}
|
|
323
|
+
return cur;
|
|
324
|
+
}
|
|
325
|
+
function parseJsonPath(spec) {
|
|
326
|
+
const ops = [];
|
|
327
|
+
const n = spec.length;
|
|
328
|
+
let i = 0;
|
|
329
|
+
while (i < n) {
|
|
330
|
+
if (spec[i] === "." && i + 1 < n && spec[i + 1] === ".") {
|
|
331
|
+
i += 2;
|
|
332
|
+
let j2 = i;
|
|
333
|
+
while (j2 < n && spec[j2] !== "." && spec[j2] !== "[") j2++;
|
|
334
|
+
if (j2 === i) throw new Error(`invalid path spec: expected property name after '..' in '${spec}'`);
|
|
335
|
+
ops.push({ kind: "recursive_key", name: spec.slice(i, j2) });
|
|
336
|
+
i = j2;
|
|
303
337
|
continue;
|
|
304
338
|
}
|
|
305
|
-
const
|
|
306
|
-
if (
|
|
307
|
-
|
|
339
|
+
const ch = spec[i];
|
|
340
|
+
if (ch === ".") {
|
|
341
|
+
i++;
|
|
308
342
|
continue;
|
|
309
343
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
344
|
+
if (ch === "[") {
|
|
345
|
+
let inQuote = null;
|
|
346
|
+
let close = -1;
|
|
347
|
+
for (let k = i + 1; k < n; k++) {
|
|
348
|
+
const c = spec[k];
|
|
349
|
+
if (inQuote !== null) {
|
|
350
|
+
if (c === "\\") {
|
|
351
|
+
k++;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (c === inQuote) {
|
|
355
|
+
inQuote = null;
|
|
356
|
+
}
|
|
357
|
+
} else if (c === '"' || c === "'") {
|
|
358
|
+
inQuote = c;
|
|
359
|
+
} else if (c === "]") {
|
|
360
|
+
close = k;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (close === -1) throw new Error(`invalid path spec: unterminated '[' in '${spec}'`);
|
|
365
|
+
const inner = spec.slice(i + 1, close);
|
|
366
|
+
if (inner === "*") {
|
|
367
|
+
ops.push({ kind: "wildcard" });
|
|
368
|
+
} else if (/^-?\d+$/.test(inner)) {
|
|
369
|
+
ops.push({ kind: "index", index: Number(inner) });
|
|
370
|
+
} else {
|
|
371
|
+
let expr = inner.trim();
|
|
372
|
+
if (expr.startsWith("?(") && expr.endsWith(")")) {
|
|
373
|
+
expr = expr.slice(2, -1).trim();
|
|
374
|
+
}
|
|
375
|
+
if (expr.startsWith("@.")) {
|
|
376
|
+
expr = expr.slice(2).trim();
|
|
377
|
+
}
|
|
378
|
+
const m = /^([^=]+)==?(.*)$/.exec(expr);
|
|
379
|
+
if (!m) {
|
|
380
|
+
const bareOperator = /[<>]/.exec(expr);
|
|
381
|
+
if (bareOperator !== null) {
|
|
382
|
+
throw new Error(`unsupported comparison operator '${bareOperator[0]}' in '[${inner}]' of path spec '${spec}': this grammar filters by equality only ([field=value])`);
|
|
383
|
+
}
|
|
384
|
+
throw new Error(`invalid bracket expression '[${inner}]' in path spec: '${spec}' (expected [n], [*], or [field=value])`);
|
|
385
|
+
}
|
|
386
|
+
let field = m[1].trim();
|
|
387
|
+
if (field.startsWith("@.")) field = field.slice(2).trim();
|
|
388
|
+
const swallowedOperator = /[!<>]$/.exec(field);
|
|
389
|
+
if (swallowedOperator !== null) {
|
|
390
|
+
throw new Error(`unsupported comparison operator '${swallowedOperator[0]}=' in '[${inner}]' of path spec '${spec}': this grammar filters by equality only ([field=value])`);
|
|
391
|
+
}
|
|
392
|
+
let rawVal = m[2].trim();
|
|
393
|
+
if (rawVal.startsWith('"') && rawVal.endsWith('"') || rawVal.startsWith("'") && rawVal.endsWith("'")) {
|
|
394
|
+
if (rawVal.length >= 2) {
|
|
395
|
+
rawVal = rawVal.slice(1, -1).replace(/\\(["'\\])/g, "$1");
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
ops.push({ kind: "filter", field, value: rawVal });
|
|
399
|
+
}
|
|
400
|
+
i = close + 1;
|
|
313
401
|
continue;
|
|
314
402
|
}
|
|
315
|
-
|
|
403
|
+
let j = i;
|
|
404
|
+
while (j < n && spec[j] !== "." && spec[j] !== "[") j++;
|
|
405
|
+
if (j === i) throw new Error(`invalid path spec: '${spec}'`);
|
|
406
|
+
ops.push({ kind: "key", name: spec.slice(i, j) });
|
|
407
|
+
i = j;
|
|
316
408
|
}
|
|
317
|
-
return
|
|
409
|
+
return ops;
|
|
318
410
|
}
|
|
319
|
-
function
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
411
|
+
function evalJsonPath(data, ops) {
|
|
412
|
+
let current = [data];
|
|
413
|
+
let fanned = false;
|
|
414
|
+
const budget = { nodesLeft: MAX_RECURSIVE_NODES, exhausted: false };
|
|
415
|
+
for (const op of ops) {
|
|
416
|
+
const next = [];
|
|
417
|
+
for (const item of current) {
|
|
418
|
+
if (op.kind === "key") {
|
|
419
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item) && Object.prototype.hasOwnProperty.call(item, op.name)) {
|
|
420
|
+
next.push(item[op.name]);
|
|
421
|
+
} else if (!fanned) {
|
|
422
|
+
throw new Error(`path not found: key '${op.name}' does not exist on ${jsonType(item)} value`);
|
|
423
|
+
}
|
|
424
|
+
} else if (op.kind === "recursive_key") {
|
|
425
|
+
fanned = true;
|
|
426
|
+
collectRecursiveKey(item, op.name, next, budget);
|
|
427
|
+
} else if (op.kind === "index") {
|
|
428
|
+
if (Array.isArray(item)) {
|
|
429
|
+
const idx = op.index < 0 ? item.length + op.index : op.index;
|
|
430
|
+
if (idx >= 0 && idx < item.length) {
|
|
431
|
+
next.push(item[idx]);
|
|
432
|
+
} else if (!fanned) {
|
|
433
|
+
throw new Error(`path not found: index [${op.index}] out of range (length ${item.length})`);
|
|
434
|
+
}
|
|
435
|
+
} else if (!fanned) {
|
|
436
|
+
throw new Error(`path not found: index [${op.index}] on non-array ${jsonType(item)} value`);
|
|
437
|
+
}
|
|
438
|
+
} else if (op.kind === "wildcard") {
|
|
439
|
+
fanned = true;
|
|
440
|
+
if (Array.isArray(item)) {
|
|
441
|
+
pushAll(next, item);
|
|
442
|
+
} else if (typeof item === "object" && item !== null) {
|
|
443
|
+
pushAll(next, Object.values(item));
|
|
444
|
+
}
|
|
445
|
+
} else {
|
|
446
|
+
fanned = true;
|
|
447
|
+
if (Array.isArray(item)) {
|
|
448
|
+
for (const el of item) {
|
|
449
|
+
if (typeof el === "object" && el !== null && !Array.isArray(el)) {
|
|
450
|
+
const val = getNestedField(el, op.field);
|
|
451
|
+
if (val !== void 0 && String(val) === op.value) {
|
|
452
|
+
next.push(el);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
364
456
|
}
|
|
365
|
-
const m = /['"]([^'"]+)['"]/.exec(line);
|
|
366
|
-
if (m) push(m[1]);
|
|
367
|
-
continue;
|
|
368
457
|
}
|
|
369
|
-
const single = /^\s*import\s+(?:[\w.]+\s+)?['"]([^'"]+)['"]/.exec(line);
|
|
370
|
-
if (single) push(single[1]);
|
|
371
458
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
459
|
+
current = next;
|
|
460
|
+
}
|
|
461
|
+
return { fanned, items: current, truncated: budget.exhausted };
|
|
462
|
+
}
|
|
463
|
+
function queryJson(data, spec) {
|
|
464
|
+
return evalJsonPath(data, parseJsonPath(spec));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/read_spec.ts
|
|
468
|
+
init_define_import_meta_env();
|
|
469
|
+
import * as fs13 from "node:fs";
|
|
470
|
+
|
|
471
|
+
// src/read_commands.ts
|
|
472
|
+
init_define_import_meta_env();
|
|
473
|
+
import * as fs12 from "node:fs";
|
|
474
|
+
import * as path12 from "node:path";
|
|
475
|
+
|
|
476
|
+
// src/import_export_extract.ts
|
|
477
|
+
init_define_import_meta_env();
|
|
478
|
+
import * as path from "node:path";
|
|
479
|
+
var STATEMENT_ADAPTER_IMPORTS = new Map([
|
|
480
|
+
[".abap", extractAbap],
|
|
481
|
+
[".sas", extractSas],
|
|
482
|
+
[".pli", extractPli],
|
|
483
|
+
[".pl1", extractPli],
|
|
484
|
+
[".rpgle", extractRpg],
|
|
485
|
+
[".sqlrpgle", extractRpg],
|
|
486
|
+
[".jcl", extractJcl],
|
|
487
|
+
...[".f", ".for", ".f77", ".f90", ".f95", ".f03", ".f08"].map((e) => [e, extractFortran]),
|
|
488
|
+
...[".pas", ".dpr", ".dpk", ".lpr"].map((e) => [e, extractPascal]),
|
|
489
|
+
[".cmake", extractCmake],
|
|
490
|
+
...[".s", ".asm", ".nasm"].map((e) => [e, extractAsm]),
|
|
491
|
+
...[".bat", ".cmd"].map((e) => [e, extractBatch]),
|
|
492
|
+
...[".erl", ".hrl"].map((e) => [e, extractErlang])
|
|
493
|
+
]);
|
|
494
|
+
var BRACE_ADAPTER_IMPORTS = new Map([
|
|
495
|
+
[".mm", extractObjc],
|
|
496
|
+
[".groovy", extractGroovy],
|
|
497
|
+
[".gvy", extractGroovy],
|
|
498
|
+
[".gradle", extractGroovy],
|
|
499
|
+
[".pm", extractPerl],
|
|
500
|
+
[".sol", extractSolidity],
|
|
501
|
+
[".thrift", extractThrift],
|
|
502
|
+
...[".glsl", ".vert", ".frag", ".comp", ".geom", ".tesc", ".tese", ".hlsl", ".hlsli", ".metal"].map((e) => [e, extractCShader])
|
|
503
|
+
]);
|
|
504
|
+
function braceAdapterImportsFor(e, text) {
|
|
505
|
+
if (e === ".m") return isObjcSource(text) ? extractObjc : isMatlabSource(text) ? extractMatlab : void 0;
|
|
506
|
+
if (e === ".pp") return isPascalSource(text) ? extractPascal : void 0;
|
|
507
|
+
if (e === ".h") return isObjcHeader(text) ? extractObjc : void 0;
|
|
508
|
+
if (e === ".pl") return isPrologSource(text) ? void 0 : extractPerl;
|
|
509
|
+
if (e === ".t") return isPerlSource(text) ? extractPerl : void 0;
|
|
510
|
+
return BRACE_ADAPTER_IMPORTS.get(e);
|
|
511
|
+
}
|
|
512
|
+
function extractExportNames(text, ext) {
|
|
513
|
+
const names = [];
|
|
514
|
+
const push = (s) => {
|
|
515
|
+
let v = (s ?? "").trim();
|
|
516
|
+
if (v.includes(" as ")) v = v.split(/\s+as\s+/).pop()?.trim() ?? v;
|
|
517
|
+
if (v !== "" && v !== "default" && !names.includes(v)) names.push(v);
|
|
518
|
+
};
|
|
519
|
+
const e = ext.toLowerCase();
|
|
520
|
+
const lines = text.split(/\r?\n/);
|
|
521
|
+
if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"].includes(e)) {
|
|
522
|
+
const declRe = /\bexport\s+(?:default\s+)?(?:declare\s+)?(?:abstract\s+)?(?:async\s+)?(?:function\*?|class|const|let|var|interface|type|enum|namespace)\s+([A-Za-z_$][\w$]*)/g;
|
|
523
|
+
const defaultRe = /\bexport\s+default\s+([A-Za-z_$][\w$]*)\s*(?:;|$)/g;
|
|
524
|
+
const namedRe = /\bexport\s+(?:type\s+)?\{([^}]*)\}/g;
|
|
525
|
+
let m;
|
|
526
|
+
while ((m = declRe.exec(text)) !== null) push(m[1]);
|
|
527
|
+
while ((m = defaultRe.exec(text)) !== null) push(m[1]);
|
|
528
|
+
while ((m = namedRe.exec(text)) !== null) {
|
|
529
|
+
for (const part of (m[1] ?? "").split(",")) push(part);
|
|
530
|
+
}
|
|
531
|
+
} else if (e === ".py") {
|
|
532
|
+
for (const line of lines) {
|
|
533
|
+
const m = /^(?:async\s+)?(?:def|class)\s+([A-Za-z_]\w*)/.exec(line);
|
|
534
|
+
if (m && !(m[1] ?? "").startsWith("_")) push(m[1]);
|
|
535
|
+
}
|
|
536
|
+
} else if (e === ".rs") {
|
|
537
|
+
for (const line of lines) {
|
|
538
|
+
const m = /^\s*pub(?:\s*\([^)]*\))?\s+(?:async\s+)?(?:fn|struct|enum|trait|type|const|mod|static)\s+([A-Za-z_]\w*)/.exec(line);
|
|
539
|
+
if (m) push(m[1]);
|
|
540
|
+
}
|
|
541
|
+
} else if (e === ".java") {
|
|
542
|
+
for (const line of lines) {
|
|
543
|
+
const m = /\bpublic\s+(?:static\s+|final\s+|abstract\s+)*(?:class|interface|enum|record)\s+([A-Za-z_]\w*)/.exec(line);
|
|
544
|
+
if (m) push(m[1]);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return names;
|
|
548
|
+
}
|
|
549
|
+
function splitTopLevelCommas(s) {
|
|
550
|
+
const parts = [];
|
|
551
|
+
let depth = 0;
|
|
552
|
+
let cur = "";
|
|
553
|
+
for (const ch of s) {
|
|
554
|
+
if (ch === "{") depth++;
|
|
555
|
+
if (ch === "}") depth--;
|
|
556
|
+
if (ch === "," && depth === 0) {
|
|
557
|
+
parts.push(cur);
|
|
558
|
+
cur = "";
|
|
559
|
+
} else {
|
|
560
|
+
cur += ch;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (cur.trim() !== "") parts.push(cur);
|
|
564
|
+
return parts;
|
|
565
|
+
}
|
|
566
|
+
function expandRustUseGroup(base, inner) {
|
|
567
|
+
const results = [];
|
|
568
|
+
for (const part of splitTopLevelCommas(inner)) {
|
|
569
|
+
const trimmed = part.trim();
|
|
570
|
+
if (trimmed === "" || trimmed === "self") {
|
|
571
|
+
if (trimmed === "self") results.push(base);
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const nested = /^([\w:]+)::\{([\s\S]*)\}$/.exec(trimmed);
|
|
575
|
+
if (nested) {
|
|
576
|
+
results.push(...expandRustUseGroup(`${base}::${nested[1] ?? ""}`, nested[2] ?? ""));
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
const name = (trimmed.split(/\s+as\s+/)[0] ?? "").trim();
|
|
580
|
+
if (name === "" || name === "self") {
|
|
581
|
+
results.push(base);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
results.push(`${base}::${name}`);
|
|
585
|
+
}
|
|
586
|
+
return results;
|
|
587
|
+
}
|
|
588
|
+
function extractImports(text, ext) {
|
|
589
|
+
const found = [];
|
|
590
|
+
const push = (s) => {
|
|
591
|
+
const v = (s ?? "").trim();
|
|
592
|
+
if (v !== "" && !found.includes(v)) found.push(v);
|
|
593
|
+
};
|
|
594
|
+
const e = ext.toLowerCase();
|
|
595
|
+
const lines = text.split(/\r?\n/);
|
|
596
|
+
if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".vue", ".svelte", ".astro"].includes(e)) {
|
|
597
|
+
const fromRe = /(?:import|export)\b[^'"]*?\bfrom\s*['"]([^'"]+)['"]/g;
|
|
598
|
+
const bareRe = /^\s*import\s*['"]([^'"]+)['"]/gm;
|
|
599
|
+
const reqRe = /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
600
|
+
const dynRe = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
601
|
+
const matches = [];
|
|
602
|
+
let m;
|
|
603
|
+
while ((m = fromRe.exec(text)) !== null) matches.push({ index: m.index, value: m[1] ?? "" });
|
|
604
|
+
while ((m = bareRe.exec(text)) !== null) matches.push({ index: m.index, value: m[1] ?? "" });
|
|
605
|
+
while ((m = reqRe.exec(text)) !== null) matches.push({ index: m.index, value: m[1] ?? "" });
|
|
606
|
+
while ((m = dynRe.exec(text)) !== null) matches.push({ index: m.index, value: m[1] ?? "" });
|
|
607
|
+
matches.sort((a, b) => a.index - b.index);
|
|
608
|
+
for (const match of matches) push(match.value);
|
|
609
|
+
} else if (e === ".py") {
|
|
610
|
+
for (const line of lines) {
|
|
611
|
+
const from = /^\s*from\s+([.\w]+)\s+import\b/.exec(line);
|
|
612
|
+
if (from) {
|
|
613
|
+
push(from[1]);
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
const imp = /^\s*import\s+(.+)$/.exec(line);
|
|
617
|
+
if (imp) {
|
|
618
|
+
const spec = (imp[1] ?? "").split("#")[0] ?? "";
|
|
619
|
+
for (const part of spec.split(",")) push(part.trim().split(/\s+as\s+/)[0]);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
} else if (e === ".go") {
|
|
623
|
+
let inBlock = false;
|
|
624
|
+
for (const line of lines) {
|
|
625
|
+
if (/^\s*import\s*\(/.test(line)) {
|
|
626
|
+
inBlock = true;
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
if (inBlock) {
|
|
630
|
+
if (/^\s*\)/.test(line)) {
|
|
631
|
+
inBlock = false;
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const m = /['"]([^'"]+)['"]/.exec(line);
|
|
635
|
+
if (m) push(m[1]);
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
const single = /^\s*import\s+(?:[\w.]+\s+)?['"]([^'"]+)['"]/.exec(line);
|
|
639
|
+
if (single) push(single[1]);
|
|
640
|
+
}
|
|
641
|
+
} else if (e === ".rs") {
|
|
642
|
+
for (const line of lines) {
|
|
643
|
+
const groupM = /^\s*(?:pub(?:\([^)]*\))?\s+)?use\s+([\w:]+)::\{([\s\S]*)\}/.exec(line);
|
|
644
|
+
if (groupM) {
|
|
645
|
+
for (const t of expandRustUseGroup(groupM[1] ?? "", groupM[2] ?? "")) push(t);
|
|
377
646
|
continue;
|
|
378
647
|
}
|
|
379
648
|
const m = /^\s*(?:pub(?:\([^)]*\))?\s+)?use\s+([^;{]+)/.exec(line);
|
|
@@ -5644,737 +5913,483 @@ function parseHtml(html) {
|
|
|
5644
5913
|
const prevLine = seenIds.get(attrVal);
|
|
5645
5914
|
if (prevLine !== void 0) {
|
|
5646
5915
|
issues.push({
|
|
5647
|
-
line: tagStartLine,
|
|
5648
|
-
type: "error",
|
|
5649
|
-
message: `Duplicate ID '#${attrVal}' previously defined on line ${prevLine}`,
|
|
5650
|
-
rule: "duplicate-id"
|
|
5651
|
-
});
|
|
5652
|
-
} else {
|
|
5653
|
-
seenIds.set(attrVal, tagStartLine);
|
|
5654
|
-
}
|
|
5655
|
-
}
|
|
5656
|
-
}
|
|
5657
|
-
const newNode = {
|
|
5658
|
-
tag: tagName,
|
|
5659
|
-
attributes,
|
|
5660
|
-
children: [],
|
|
5661
|
-
parent: currentParent,
|
|
5662
|
-
text: "",
|
|
5663
|
-
rawHtml: tagSlice,
|
|
5664
|
-
startOffset: pos,
|
|
5665
|
-
endOffset: closeBracket + 1,
|
|
5666
|
-
line: tagStartLine,
|
|
5667
|
-
endLine: tagStartLine + sliceLines,
|
|
5668
|
-
isVoid
|
|
5669
|
-
};
|
|
5670
|
-
currentParent.children.push(newNode);
|
|
5671
|
-
if (!isVoid) {
|
|
5672
|
-
currentParent = newNode;
|
|
5673
|
-
}
|
|
5674
|
-
currentLine += sliceLines;
|
|
5675
|
-
pos = closeBracket + 1;
|
|
5676
|
-
continue;
|
|
5677
|
-
}
|
|
5678
|
-
issues.push({
|
|
5679
|
-
line: tagStartLine,
|
|
5680
|
-
type: "warning",
|
|
5681
|
-
message: "Unescaped '<' character in text: consider '<'",
|
|
5682
|
-
rule: "unescaped-angle-bracket"
|
|
5683
|
-
});
|
|
5684
|
-
if (currentParent) {
|
|
5685
|
-
currentParent.text += "<";
|
|
5686
|
-
}
|
|
5687
|
-
pos++;
|
|
5688
|
-
continue;
|
|
5689
|
-
}
|
|
5690
|
-
const nextBracket = html.indexOf("<", pos);
|
|
5691
|
-
const textChunkEnd = nextBracket !== -1 ? nextBracket : len;
|
|
5692
|
-
const textChunk = html.slice(pos, textChunkEnd);
|
|
5693
|
-
if (textChunk) {
|
|
5694
|
-
if (currentParent) {
|
|
5695
|
-
currentParent.text += textChunk;
|
|
5696
|
-
}
|
|
5697
|
-
currentLine += (textChunk.match(/\n/g) || []).length;
|
|
5698
|
-
}
|
|
5699
|
-
pos = textChunkEnd;
|
|
5700
|
-
}
|
|
5701
|
-
let dangling = currentParent;
|
|
5702
|
-
while (dangling && dangling.tag !== "#root") {
|
|
5703
|
-
if (!HTML_VOID_TAGS.has(dangling.tag.toLowerCase())) {
|
|
5704
|
-
issues.push({
|
|
5705
|
-
line: dangling.line,
|
|
5706
|
-
type: "error",
|
|
5707
|
-
message: `Unclosed tag <${dangling.tag}> at end of document`,
|
|
5708
|
-
rule: "unclosed-tag"
|
|
5709
|
-
});
|
|
5710
|
-
}
|
|
5711
|
-
dangling.endOffset = len;
|
|
5712
|
-
dangling = dangling.parent;
|
|
5713
|
-
}
|
|
5714
|
-
for (const tag of Object.keys(tagCounts)) {
|
|
5715
|
-
const entry = tagCounts[tag];
|
|
5716
|
-
entry.diff = entry.open - entry.close;
|
|
5717
|
-
}
|
|
5718
|
-
root.endLine = currentLine;
|
|
5719
|
-
return { root, issues, tagCounts };
|
|
5720
|
-
}
|
|
5721
|
-
function lintHtml(htmlText) {
|
|
5722
|
-
const { issues, tagCounts } = parseHtml(htmlText);
|
|
5723
|
-
const errors = issues.filter((i) => i.type === "error");
|
|
5724
|
-
const warnings = issues.filter((i) => i.type === "warning");
|
|
5725
|
-
return {
|
|
5726
|
-
valid: errors.length === 0,
|
|
5727
|
-
errors,
|
|
5728
|
-
warnings,
|
|
5729
|
-
tagCounts
|
|
5730
|
-
};
|
|
5731
|
-
}
|
|
5732
|
-
function outlineHtml(htmlText) {
|
|
5733
|
-
const { root } = parseHtml(htmlText);
|
|
5734
|
-
let title = null;
|
|
5735
|
-
let doctype = null;
|
|
5736
|
-
const docMatch = /<!doctype\s+(\S[^>]*)>/i.exec(htmlText);
|
|
5737
|
-
if (docMatch) doctype = docMatch[1].trim();
|
|
5738
|
-
let totalElements = 0;
|
|
5739
|
-
const uniqueTags = /* @__PURE__ */ new Set();
|
|
5740
|
-
let maxDepth = 0;
|
|
5741
|
-
const headings = [];
|
|
5742
|
-
const landmarks = [];
|
|
5743
|
-
const tables = [];
|
|
5744
|
-
const forms = [];
|
|
5745
|
-
let scripts = 0;
|
|
5746
|
-
let styles = 0;
|
|
5747
|
-
const landmarkTags = /* @__PURE__ */ new Set(["header", "nav", "main", "article", "section", "footer", "aside"]);
|
|
5748
|
-
function walk(node, depth) {
|
|
5749
|
-
if (node.tag !== "#root") {
|
|
5750
|
-
totalElements++;
|
|
5751
|
-
uniqueTags.add(node.tag);
|
|
5752
|
-
if (depth > maxDepth) maxDepth = depth;
|
|
5753
|
-
const tag = node.tag.toLowerCase();
|
|
5754
|
-
if (tag === "title" && !title) {
|
|
5755
|
-
title = node.text.trim();
|
|
5756
|
-
} else if (/^h[1-6]$/.test(tag)) {
|
|
5757
|
-
const level = parseInt(tag[1], 10);
|
|
5758
|
-
headings.push({ level, text: node.text.replace(/\s+/g, " ").trim().slice(0, 80), line: node.line });
|
|
5759
|
-
} else if (landmarkTags.has(tag)) {
|
|
5760
|
-
landmarks.push({
|
|
5761
|
-
tag,
|
|
5762
|
-
id: node.attributes["id"],
|
|
5763
|
-
class: node.attributes["class"],
|
|
5764
|
-
line: node.line
|
|
5765
|
-
});
|
|
5766
|
-
} else if (tag === "table") {
|
|
5767
|
-
const rows = node.children.flatMap((c) => c.tag === "tbody" || c.tag === "thead" ? c.children : [c]).filter((c) => c.tag === "tr").length;
|
|
5768
|
-
tables.push({ id: node.attributes["id"], class: node.attributes["class"], rows, line: node.line });
|
|
5769
|
-
} else if (tag === "form") {
|
|
5770
|
-
forms.push({ id: node.attributes["id"], action: node.attributes["action"], method: node.attributes["method"], line: node.line });
|
|
5771
|
-
} else if (tag === "script") {
|
|
5772
|
-
scripts++;
|
|
5773
|
-
} else if (tag === "style") {
|
|
5774
|
-
styles++;
|
|
5775
|
-
}
|
|
5776
|
-
}
|
|
5777
|
-
for (const child of node.children) {
|
|
5778
|
-
walk(child, depth + 1);
|
|
5779
|
-
}
|
|
5780
|
-
}
|
|
5781
|
-
walk(root, 0);
|
|
5782
|
-
return {
|
|
5783
|
-
title,
|
|
5784
|
-
doctype,
|
|
5785
|
-
totalElements,
|
|
5786
|
-
uniqueTags: Array.from(uniqueTags).sort(),
|
|
5787
|
-
maxDepth,
|
|
5788
|
-
headings,
|
|
5789
|
-
landmarks,
|
|
5790
|
-
tables,
|
|
5791
|
-
forms,
|
|
5792
|
-
scripts,
|
|
5793
|
-
styles
|
|
5794
|
-
};
|
|
5795
|
-
}
|
|
5796
|
-
function formatHtmlOutline(summary) {
|
|
5797
|
-
const lines = [];
|
|
5798
|
-
lines.push(`HTML Document (${summary.totalElements} elements, ${summary.uniqueTags.length} unique tags, max depth ${summary.maxDepth})`);
|
|
5799
|
-
if (summary.title) lines.push(`Title: "${displaySafeText(summary.title)}"`);
|
|
5800
|
-
if (summary.doctype) lines.push(`DOCTYPE: ${displaySafeText(summary.doctype)}`);
|
|
5801
|
-
lines.push(`Assets: ${summary.scripts} scripts, ${summary.styles} stylesheets`);
|
|
5802
|
-
if (summary.headings.length > 0) {
|
|
5803
|
-
lines.push("", "Headings:");
|
|
5804
|
-
for (const h of summary.headings.slice(0, 30)) {
|
|
5805
|
-
lines.push(` ${" ".repeat(h.level - 1)}[h${h.level}] ${displaySafeText(h.text)} (line ${h.line})`);
|
|
5806
|
-
}
|
|
5807
|
-
if (summary.headings.length > 30) {
|
|
5808
|
-
lines.push(` ... (${summary.headings.length - 30} more headings elided)`);
|
|
5809
|
-
}
|
|
5810
|
-
}
|
|
5811
|
-
if (summary.landmarks.length > 0) {
|
|
5812
|
-
lines.push("", "Landmarks:");
|
|
5813
|
-
for (const l of summary.landmarks.slice(0, 25)) {
|
|
5814
|
-
const idStr = l.id ? `#${displaySafeText(l.id)}` : "";
|
|
5815
|
-
const classStr = l.class ? `.${displaySafeText(l.class.trim().split(/\s+/).join("."))}` : "";
|
|
5816
|
-
lines.push(` <${displaySafeText(l.tag)}${idStr}${classStr}> (line ${l.line})`);
|
|
5817
|
-
}
|
|
5818
|
-
}
|
|
5819
|
-
if (summary.tables.length > 0) {
|
|
5820
|
-
lines.push("", "Tables:");
|
|
5821
|
-
for (const t of summary.tables) {
|
|
5822
|
-
const idStr = t.id ? `#${displaySafeText(t.id)}` : "";
|
|
5823
|
-
lines.push(` <table${idStr}> with ~${t.rows} rows (line ${t.line})`);
|
|
5824
|
-
}
|
|
5825
|
-
}
|
|
5826
|
-
if (summary.forms.length > 0) {
|
|
5827
|
-
lines.push("", "Forms:");
|
|
5828
|
-
for (const f of summary.forms) {
|
|
5829
|
-
const idStr = f.id ? `#${displaySafeText(f.id)}` : "";
|
|
5830
|
-
lines.push(` <form${idStr} method="${displaySafeText(f.method || "GET")}" action="${displaySafeText(f.action || "")}"> (line ${f.line})`);
|
|
5831
|
-
}
|
|
5832
|
-
}
|
|
5833
|
-
return lines.join("\n");
|
|
5834
|
-
}
|
|
5835
|
-
function splitTopLevel(str, delimiter) {
|
|
5836
|
-
const results = [];
|
|
5837
|
-
let start = 0;
|
|
5838
|
-
let inQuote = null;
|
|
5839
|
-
let bracketDepth = 0;
|
|
5840
|
-
for (let i = 0; i < str.length; i++) {
|
|
5841
|
-
const ch = str[i];
|
|
5842
|
-
if (inQuote) {
|
|
5843
|
-
if (ch === inQuote) inQuote = null;
|
|
5844
|
-
} else {
|
|
5845
|
-
if (ch === '"' || ch === "'") {
|
|
5846
|
-
inQuote = ch;
|
|
5847
|
-
} else if (ch === "[") {
|
|
5848
|
-
bracketDepth++;
|
|
5849
|
-
} else if (ch === "]") {
|
|
5850
|
-
if (bracketDepth > 0) bracketDepth--;
|
|
5851
|
-
} else if (bracketDepth === 0 && ch === delimiter) {
|
|
5852
|
-
results.push(str.slice(start, i));
|
|
5853
|
-
start = i + 1;
|
|
5854
|
-
}
|
|
5855
|
-
}
|
|
5856
|
-
}
|
|
5857
|
-
results.push(str.slice(start));
|
|
5858
|
-
return results;
|
|
5859
|
-
}
|
|
5860
|
-
function tokenizeSelector(sel) {
|
|
5861
|
-
const tokens = [];
|
|
5862
|
-
let i = 0;
|
|
5863
|
-
const n = sel.length;
|
|
5864
|
-
while (i < n) {
|
|
5865
|
-
while (i < n && /\s/.test(sel[i])) i++;
|
|
5866
|
-
if (i >= n) break;
|
|
5867
|
-
if (sel[i] === ">") {
|
|
5868
|
-
tokens.push(">");
|
|
5869
|
-
i++;
|
|
5870
|
-
continue;
|
|
5871
|
-
}
|
|
5872
|
-
const start = i;
|
|
5873
|
-
let inQuote = null;
|
|
5874
|
-
let bracketDepth = 0;
|
|
5875
|
-
while (i < n) {
|
|
5876
|
-
const ch = sel[i];
|
|
5877
|
-
if (inQuote) {
|
|
5878
|
-
if (ch === inQuote) inQuote = null;
|
|
5879
|
-
} else {
|
|
5880
|
-
if (ch === '"' || ch === "'") {
|
|
5881
|
-
inQuote = ch;
|
|
5882
|
-
} else if (ch === "[") {
|
|
5883
|
-
bracketDepth++;
|
|
5884
|
-
} else if (ch === "]") {
|
|
5885
|
-
if (bracketDepth > 0) bracketDepth--;
|
|
5886
|
-
} else if (bracketDepth === 0) {
|
|
5887
|
-
if (/\s/.test(ch) || ch === ">") {
|
|
5888
|
-
break;
|
|
5889
|
-
}
|
|
5890
|
-
}
|
|
5891
|
-
}
|
|
5892
|
-
i++;
|
|
5893
|
-
}
|
|
5894
|
-
if (i > start) {
|
|
5895
|
-
tokens.push(sel.slice(start, i));
|
|
5896
|
-
}
|
|
5897
|
-
}
|
|
5898
|
-
return tokens;
|
|
5899
|
-
}
|
|
5900
|
-
function parseCompoundSelector(sel) {
|
|
5901
|
-
const result = {
|
|
5902
|
-
ids: [],
|
|
5903
|
-
classes: [],
|
|
5904
|
-
attributes: []
|
|
5905
|
-
};
|
|
5906
|
-
let i = 0;
|
|
5907
|
-
const n = sel.length;
|
|
5908
|
-
if (sel[0] !== "#" && sel[0] !== "." && sel[0] !== "[") {
|
|
5909
|
-
const tagMatch = /^([a-zA-Z*][a-zA-Z0-9_-]*)/.exec(sel);
|
|
5910
|
-
if (tagMatch) {
|
|
5911
|
-
result.tag = tagMatch[1];
|
|
5912
|
-
i = tagMatch[0].length;
|
|
5913
|
-
}
|
|
5914
|
-
}
|
|
5915
|
-
while (i < n) {
|
|
5916
|
-
const ch = sel[i];
|
|
5917
|
-
if (ch === "#") {
|
|
5918
|
-
i++;
|
|
5919
|
-
const idMatch = /^([a-zA-Z0-9_-]+)/.exec(sel.slice(i));
|
|
5920
|
-
if (idMatch) {
|
|
5921
|
-
result.ids.push(idMatch[1]);
|
|
5922
|
-
i += idMatch[0].length;
|
|
5923
|
-
}
|
|
5924
|
-
} else if (ch === ".") {
|
|
5925
|
-
i++;
|
|
5926
|
-
const classMatch = /^([a-zA-Z0-9_-]+)/.exec(sel.slice(i));
|
|
5927
|
-
if (classMatch) {
|
|
5928
|
-
result.classes.push(classMatch[1]);
|
|
5929
|
-
i += classMatch[0].length;
|
|
5930
|
-
}
|
|
5931
|
-
} else if (ch === "[") {
|
|
5932
|
-
let endBracket = -1;
|
|
5933
|
-
let inQuote = null;
|
|
5934
|
-
for (let j = i + 1; j < n; j++) {
|
|
5935
|
-
const c = sel[j];
|
|
5936
|
-
if (inQuote) {
|
|
5937
|
-
if (c === inQuote) inQuote = null;
|
|
5938
|
-
} else {
|
|
5939
|
-
if (c === '"' || c === "'") inQuote = c;
|
|
5940
|
-
else if (c === "]") {
|
|
5941
|
-
endBracket = j;
|
|
5942
|
-
break;
|
|
5943
|
-
}
|
|
5944
|
-
}
|
|
5945
|
-
}
|
|
5946
|
-
if (endBracket === -1) {
|
|
5947
|
-
break;
|
|
5948
|
-
}
|
|
5949
|
-
const inner = sel.slice(i + 1, endBracket).trim();
|
|
5950
|
-
i = endBracket + 1;
|
|
5951
|
-
const attrMatch = /^([a-zA-Z0-9_:-]+)(?:\s*([*^$!~]?=)\s*(?:"([^"]*)"|'([^']*)'|([^\s"'\]]+)))?$/.exec(inner);
|
|
5952
|
-
if (attrMatch) {
|
|
5953
|
-
const attrName = attrMatch[1].toLowerCase();
|
|
5954
|
-
const op = attrMatch[2];
|
|
5955
|
-
const val = attrMatch[3] ?? attrMatch[4] ?? attrMatch[5];
|
|
5956
|
-
result.attributes.push({ name: attrName, op, val });
|
|
5957
|
-
}
|
|
5958
|
-
} else {
|
|
5959
|
-
i++;
|
|
5960
|
-
}
|
|
5961
|
-
}
|
|
5962
|
-
return result;
|
|
5963
|
-
}
|
|
5964
|
-
function matchesCompoundSelector(node, sel) {
|
|
5965
|
-
if (!sel || node.tag === "#root") return false;
|
|
5966
|
-
if (sel === "*") return true;
|
|
5967
|
-
const parsed = parseCompoundSelector(sel);
|
|
5968
|
-
if (parsed.tag && parsed.tag !== "*") {
|
|
5969
|
-
if (node.tag.toLowerCase() !== parsed.tag.toLowerCase()) return false;
|
|
5970
|
-
}
|
|
5971
|
-
for (const id of parsed.ids) {
|
|
5972
|
-
if (node.attributes["id"] !== id) return false;
|
|
5973
|
-
}
|
|
5974
|
-
if (parsed.classes.length > 0) {
|
|
5975
|
-
const nodeClasses = (node.attributes["class"] || "").split(/\s+/).filter(Boolean);
|
|
5976
|
-
for (const c of parsed.classes) {
|
|
5977
|
-
if (!nodeClasses.includes(c)) return false;
|
|
5978
|
-
}
|
|
5979
|
-
}
|
|
5980
|
-
for (const attr of parsed.attributes) {
|
|
5981
|
-
const hasAttr = Object.prototype.hasOwnProperty.call(node.attributes, attr.name);
|
|
5982
|
-
if (!hasAttr) {
|
|
5983
|
-
if (attr.op === "!=") continue;
|
|
5984
|
-
return false;
|
|
5985
|
-
}
|
|
5986
|
-
if (!attr.op) continue;
|
|
5987
|
-
const actual = node.attributes[attr.name] || "";
|
|
5988
|
-
const val = attr.val ?? "";
|
|
5989
|
-
if (attr.op === "=") {
|
|
5990
|
-
if (actual !== val) return false;
|
|
5991
|
-
} else if (attr.op === "^=") {
|
|
5992
|
-
if (!actual.startsWith(val)) return false;
|
|
5993
|
-
} else if (attr.op === "$=") {
|
|
5994
|
-
if (!actual.endsWith(val)) return false;
|
|
5995
|
-
} else if (attr.op === "*=") {
|
|
5996
|
-
if (!actual.includes(val)) return false;
|
|
5997
|
-
} else if (attr.op === "~=") {
|
|
5998
|
-
if (!actual.split(/\s+/).includes(val)) return false;
|
|
5999
|
-
} else if (attr.op === "!=") {
|
|
6000
|
-
if (actual === val) return false;
|
|
6001
|
-
}
|
|
6002
|
-
}
|
|
6003
|
-
return true;
|
|
6004
|
-
}
|
|
6005
|
-
function queryHtml(htmlText, selectorStr) {
|
|
6006
|
-
const { root } = parseHtml(htmlText);
|
|
6007
|
-
let selector = selectorStr.trim();
|
|
6008
|
-
let targetAttr;
|
|
6009
|
-
const attrAtMatch = /@([a-zA-Z0-9_:-]+)$/.exec(selector);
|
|
6010
|
-
if (attrAtMatch) {
|
|
6011
|
-
targetAttr = attrAtMatch[1].toLowerCase();
|
|
6012
|
-
selector = selector.slice(0, selector.length - attrAtMatch[0].length).trim();
|
|
6013
|
-
if (!selector) selector = "*";
|
|
6014
|
-
}
|
|
6015
|
-
const rawSubSelectors = splitTopLevel(selector, ",").map((s) => s.trim()).filter(Boolean);
|
|
6016
|
-
const matchedSet = /* @__PURE__ */ new Set();
|
|
6017
|
-
for (const subSel of rawSubSelectors) {
|
|
6018
|
-
let collectAll2 = function(node) {
|
|
6019
|
-
if (node.tag !== "#root") currentCandidates.push(node);
|
|
6020
|
-
for (const c of node.children) collectAll2(c);
|
|
6021
|
-
};
|
|
6022
|
-
var collectAll = collectAll2;
|
|
6023
|
-
const tokens = tokenizeSelector(subSel);
|
|
6024
|
-
let currentCandidates = [];
|
|
6025
|
-
collectAll2(root);
|
|
6026
|
-
let i = 0;
|
|
6027
|
-
while (i < tokens.length) {
|
|
6028
|
-
const token = tokens[i];
|
|
6029
|
-
if (token === ">") {
|
|
6030
|
-
const nextTarget = tokens[i + 1];
|
|
6031
|
-
if (!nextTarget) break;
|
|
6032
|
-
const nextCandidates = [];
|
|
6033
|
-
for (const parent of currentCandidates) {
|
|
6034
|
-
for (const child of parent.children) {
|
|
6035
|
-
if (nextTarget === "*" || matchesCompoundSelector(child, nextTarget)) {
|
|
6036
|
-
nextCandidates.push(child);
|
|
6037
|
-
}
|
|
6038
|
-
}
|
|
6039
|
-
}
|
|
6040
|
-
currentCandidates = nextCandidates;
|
|
6041
|
-
i += 2;
|
|
6042
|
-
} else {
|
|
6043
|
-
if (i === 0) {
|
|
6044
|
-
currentCandidates = currentCandidates.filter((node) => token === "*" || matchesCompoundSelector(node, token));
|
|
6045
|
-
} else {
|
|
6046
|
-
const nextSet = /* @__PURE__ */ new Set();
|
|
6047
|
-
for (const ancestor of currentCandidates) {
|
|
6048
|
-
let findDescendants2 = function(node) {
|
|
6049
|
-
for (const child of node.children) {
|
|
6050
|
-
if (token === "*" || matchesCompoundSelector(child, token)) {
|
|
6051
|
-
nextSet.add(child);
|
|
6052
|
-
}
|
|
6053
|
-
findDescendants2(child);
|
|
6054
|
-
}
|
|
6055
|
-
};
|
|
6056
|
-
var findDescendants = findDescendants2;
|
|
6057
|
-
findDescendants2(ancestor);
|
|
5916
|
+
line: tagStartLine,
|
|
5917
|
+
type: "error",
|
|
5918
|
+
message: `Duplicate ID '#${attrVal}' previously defined on line ${prevLine}`,
|
|
5919
|
+
rule: "duplicate-id"
|
|
5920
|
+
});
|
|
5921
|
+
} else {
|
|
5922
|
+
seenIds.set(attrVal, tagStartLine);
|
|
5923
|
+
}
|
|
6058
5924
|
}
|
|
6059
|
-
currentCandidates = Array.from(nextSet);
|
|
6060
5925
|
}
|
|
6061
|
-
|
|
5926
|
+
const newNode = {
|
|
5927
|
+
tag: tagName,
|
|
5928
|
+
attributes,
|
|
5929
|
+
children: [],
|
|
5930
|
+
parent: currentParent,
|
|
5931
|
+
text: "",
|
|
5932
|
+
rawHtml: tagSlice,
|
|
5933
|
+
startOffset: pos,
|
|
5934
|
+
endOffset: closeBracket + 1,
|
|
5935
|
+
line: tagStartLine,
|
|
5936
|
+
endLine: tagStartLine + sliceLines,
|
|
5937
|
+
isVoid
|
|
5938
|
+
};
|
|
5939
|
+
currentParent.children.push(newNode);
|
|
5940
|
+
if (!isVoid) {
|
|
5941
|
+
currentParent = newNode;
|
|
5942
|
+
}
|
|
5943
|
+
currentLine += sliceLines;
|
|
5944
|
+
pos = closeBracket + 1;
|
|
5945
|
+
continue;
|
|
6062
5946
|
}
|
|
5947
|
+
issues.push({
|
|
5948
|
+
line: tagStartLine,
|
|
5949
|
+
type: "warning",
|
|
5950
|
+
message: "Unescaped '<' character in text: consider '<'",
|
|
5951
|
+
rule: "unescaped-angle-bracket"
|
|
5952
|
+
});
|
|
5953
|
+
if (currentParent) {
|
|
5954
|
+
currentParent.text += "<";
|
|
5955
|
+
}
|
|
5956
|
+
pos++;
|
|
5957
|
+
continue;
|
|
6063
5958
|
}
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
const attributeValues = [];
|
|
6071
|
-
for (const el of elements) {
|
|
6072
|
-
if (Object.prototype.hasOwnProperty.call(el.attributes, targetAttr)) {
|
|
6073
|
-
attributeValues.push(el.attributes[targetAttr]);
|
|
5959
|
+
const nextBracket = html.indexOf("<", pos);
|
|
5960
|
+
const textChunkEnd = nextBracket !== -1 ? nextBracket : len;
|
|
5961
|
+
const textChunk = html.slice(pos, textChunkEnd);
|
|
5962
|
+
if (textChunk) {
|
|
5963
|
+
if (currentParent) {
|
|
5964
|
+
currentParent.text += textChunk;
|
|
6074
5965
|
}
|
|
5966
|
+
currentLine += (textChunk.match(/\n/g) || []).length;
|
|
6075
5967
|
}
|
|
6076
|
-
|
|
5968
|
+
pos = textChunkEnd;
|
|
6077
5969
|
}
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
5970
|
+
let dangling = currentParent;
|
|
5971
|
+
while (dangling && dangling.tag !== "#root") {
|
|
5972
|
+
if (!HTML_VOID_TAGS.has(dangling.tag.toLowerCase())) {
|
|
5973
|
+
issues.push({
|
|
5974
|
+
line: dangling.line,
|
|
5975
|
+
type: "error",
|
|
5976
|
+
message: `Unclosed tag <${dangling.tag}> at end of document`,
|
|
5977
|
+
rule: "unclosed-tag"
|
|
5978
|
+
});
|
|
5979
|
+
}
|
|
5980
|
+
dangling.endOffset = len;
|
|
5981
|
+
dangling = dangling.parent;
|
|
6086
5982
|
}
|
|
6087
|
-
const
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
const t = extractNodeText(child);
|
|
6091
|
-
if (t) parts.push(t);
|
|
5983
|
+
for (const tag of Object.keys(tagCounts)) {
|
|
5984
|
+
const entry = tagCounts[tag];
|
|
5985
|
+
entry.diff = entry.open - entry.close;
|
|
6092
5986
|
}
|
|
6093
|
-
|
|
5987
|
+
root.endLine = currentLine;
|
|
5988
|
+
return { root, issues, tagCounts };
|
|
6094
5989
|
}
|
|
6095
|
-
function
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
}
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
5990
|
+
function lintHtml(htmlText) {
|
|
5991
|
+
const { issues, tagCounts } = parseHtml(htmlText);
|
|
5992
|
+
const errors = issues.filter((i) => i.type === "error");
|
|
5993
|
+
const warnings = issues.filter((i) => i.type === "warning");
|
|
5994
|
+
return {
|
|
5995
|
+
valid: errors.length === 0,
|
|
5996
|
+
errors,
|
|
5997
|
+
warnings,
|
|
5998
|
+
tagCounts
|
|
5999
|
+
};
|
|
6000
|
+
}
|
|
6001
|
+
function outlineHtml(htmlText) {
|
|
6002
|
+
const { root } = parseHtml(htmlText);
|
|
6003
|
+
let title = null;
|
|
6004
|
+
let doctype = null;
|
|
6005
|
+
const docMatch = /<!doctype\s+(\S[^>]*)>/i.exec(htmlText);
|
|
6006
|
+
if (docMatch) doctype = docMatch[1].trim();
|
|
6007
|
+
let totalElements = 0;
|
|
6008
|
+
const uniqueTags = /* @__PURE__ */ new Set();
|
|
6009
|
+
let maxDepth = 0;
|
|
6010
|
+
const headings = [];
|
|
6011
|
+
const landmarks = [];
|
|
6012
|
+
const tables = [];
|
|
6013
|
+
const forms = [];
|
|
6014
|
+
let scripts = 0;
|
|
6015
|
+
let styles = 0;
|
|
6016
|
+
const landmarkTags = /* @__PURE__ */ new Set(["header", "nav", "main", "article", "section", "footer", "aside"]);
|
|
6017
|
+
function walk(node, depth) {
|
|
6018
|
+
if (node.tag !== "#root") {
|
|
6019
|
+
totalElements++;
|
|
6020
|
+
uniqueTags.add(node.tag);
|
|
6021
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
6022
|
+
const tag = node.tag.toLowerCase();
|
|
6023
|
+
if (tag === "title" && !title) {
|
|
6024
|
+
title = node.text.trim();
|
|
6025
|
+
} else if (/^h[1-6]$/.test(tag)) {
|
|
6026
|
+
const level = parseInt(tag[1], 10);
|
|
6027
|
+
headings.push({ level, text: node.text.replace(/\s+/g, " ").trim().slice(0, 80), line: node.line });
|
|
6028
|
+
} else if (landmarkTags.has(tag)) {
|
|
6029
|
+
landmarks.push({
|
|
6030
|
+
tag,
|
|
6031
|
+
id: node.attributes["id"],
|
|
6032
|
+
class: node.attributes["class"],
|
|
6033
|
+
line: node.line
|
|
6034
|
+
});
|
|
6035
|
+
} else if (tag === "table") {
|
|
6036
|
+
const rows = node.children.flatMap((c) => c.tag === "tbody" || c.tag === "thead" ? c.children : [c]).filter((c) => c.tag === "tr").length;
|
|
6037
|
+
tables.push({ id: node.attributes["id"], class: node.attributes["class"], rows, line: node.line });
|
|
6038
|
+
} else if (tag === "form") {
|
|
6039
|
+
forms.push({ id: node.attributes["id"], action: node.attributes["action"], method: node.attributes["method"], line: node.line });
|
|
6040
|
+
} else if (tag === "script") {
|
|
6041
|
+
scripts++;
|
|
6042
|
+
} else if (tag === "style") {
|
|
6043
|
+
styles++;
|
|
6044
|
+
}
|
|
6112
6045
|
}
|
|
6113
|
-
|
|
6114
|
-
|
|
6046
|
+
for (const child of node.children) {
|
|
6047
|
+
walk(child, depth + 1);
|
|
6115
6048
|
}
|
|
6116
|
-
return `${pad}<${node.tag}${attrStr}>
|
|
6117
|
-
${pad} ${textTrimmed}
|
|
6118
|
-
${pad}</${node.tag}>`;
|
|
6119
6049
|
}
|
|
6120
|
-
|
|
6121
|
-
return
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
const type = jsonType(value);
|
|
6135
|
-
if (type === "array") return { name, type, size: value.length };
|
|
6136
|
-
if (type === "object") return { name, type, size: Object.keys(value).length };
|
|
6137
|
-
return { name, type };
|
|
6050
|
+
walk(root, 0);
|
|
6051
|
+
return {
|
|
6052
|
+
title,
|
|
6053
|
+
doctype,
|
|
6054
|
+
totalElements,
|
|
6055
|
+
uniqueTags: Array.from(uniqueTags).sort(),
|
|
6056
|
+
maxDepth,
|
|
6057
|
+
headings,
|
|
6058
|
+
landmarks,
|
|
6059
|
+
tables,
|
|
6060
|
+
forms,
|
|
6061
|
+
scripts,
|
|
6062
|
+
styles
|
|
6063
|
+
};
|
|
6138
6064
|
}
|
|
6139
|
-
function
|
|
6140
|
-
const
|
|
6141
|
-
|
|
6142
|
-
if (
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
const
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
result.sampleKeys = allKeys.map((k) => {
|
|
6153
|
-
const owner = objects.find((el) => Object.prototype.hasOwnProperty.call(el, k));
|
|
6154
|
-
return fieldSummary(k, owner?.[k]);
|
|
6155
|
-
});
|
|
6156
|
-
const firstKeySet = keySets[0] ?? [];
|
|
6157
|
-
result.heterogeneous = keySets.some((ks) => ks.length !== firstKeySet.length || !ks.every((k) => firstKeySet.includes(k)));
|
|
6065
|
+
function formatHtmlOutline(summary) {
|
|
6066
|
+
const lines = [];
|
|
6067
|
+
lines.push(`HTML Document (${summary.totalElements} elements, ${summary.uniqueTags.length} unique tags, max depth ${summary.maxDepth})`);
|
|
6068
|
+
if (summary.title) lines.push(`Title: "${displaySafeText(summary.title)}"`);
|
|
6069
|
+
if (summary.doctype) lines.push(`DOCTYPE: ${displaySafeText(summary.doctype)}`);
|
|
6070
|
+
lines.push(`Assets: ${summary.scripts} scripts, ${summary.styles} stylesheets`);
|
|
6071
|
+
if (summary.headings.length > 0) {
|
|
6072
|
+
lines.push("", "Headings:");
|
|
6073
|
+
for (const h of summary.headings.slice(0, 30)) {
|
|
6074
|
+
lines.push(` ${" ".repeat(h.level - 1)}[h${h.level}] ${displaySafeText(h.text)} (line ${h.line})`);
|
|
6075
|
+
}
|
|
6076
|
+
if (summary.headings.length > 30) {
|
|
6077
|
+
lines.push(` ... (${summary.headings.length - 30} more headings elided)`);
|
|
6158
6078
|
}
|
|
6159
|
-
return result;
|
|
6160
6079
|
}
|
|
6161
|
-
if (
|
|
6162
|
-
|
|
6163
|
-
|
|
6080
|
+
if (summary.landmarks.length > 0) {
|
|
6081
|
+
lines.push("", "Landmarks:");
|
|
6082
|
+
for (const l of summary.landmarks.slice(0, 25)) {
|
|
6083
|
+
const idStr = l.id ? `#${displaySafeText(l.id)}` : "";
|
|
6084
|
+
const classStr = l.class ? `.${displaySafeText(l.class.trim().split(/\s+/).join("."))}` : "";
|
|
6085
|
+
lines.push(` <${displaySafeText(l.tag)}${idStr}${classStr}> (line ${l.line})`);
|
|
6086
|
+
}
|
|
6164
6087
|
}
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
return outline.fields.map((f) => `${displaySafeText(f.name)}: ${f.type}${f.size !== void 0 ? ` (${f.size})` : ""}`).join("\n");
|
|
6088
|
+
if (summary.tables.length > 0) {
|
|
6089
|
+
lines.push("", "Tables:");
|
|
6090
|
+
for (const t of summary.tables) {
|
|
6091
|
+
const idStr = t.id ? `#${displaySafeText(t.id)}` : "";
|
|
6092
|
+
lines.push(` <table${idStr}> with ~${t.rows} rows (line ${t.line})`);
|
|
6093
|
+
}
|
|
6172
6094
|
}
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
lines.push(` ${displaySafeText(f.
|
|
6095
|
+
if (summary.forms.length > 0) {
|
|
6096
|
+
lines.push("", "Forms:");
|
|
6097
|
+
for (const f of summary.forms) {
|
|
6098
|
+
const idStr = f.id ? `#${displaySafeText(f.id)}` : "";
|
|
6099
|
+
lines.push(` <form${idStr} method="${displaySafeText(f.method || "GET")}" action="${displaySafeText(f.action || "")}"> (line ${f.line})`);
|
|
6178
6100
|
}
|
|
6179
6101
|
}
|
|
6180
6102
|
return lines.join("\n");
|
|
6181
6103
|
}
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
if (val === null || typeof val !== "object") return;
|
|
6192
|
-
if (seen.has(val)) return;
|
|
6193
|
-
seen.add(val);
|
|
6194
|
-
if (budget.nodesLeft <= 0) {
|
|
6195
|
-
budget.exhausted = true;
|
|
6196
|
-
return;
|
|
6197
|
-
}
|
|
6198
|
-
budget.nodesLeft--;
|
|
6199
|
-
if (Array.isArray(val)) {
|
|
6200
|
-
for (let i = 0; i < val.length; i++) {
|
|
6201
|
-
walk(val[i], depth + 1);
|
|
6202
|
-
if (budget.nodesLeft <= 0) {
|
|
6203
|
-
if (i < val.length - 1) budget.exhausted = true;
|
|
6204
|
-
return;
|
|
6205
|
-
}
|
|
6206
|
-
}
|
|
6104
|
+
function splitTopLevel(str, delimiter) {
|
|
6105
|
+
const results = [];
|
|
6106
|
+
let start = 0;
|
|
6107
|
+
let inQuote = null;
|
|
6108
|
+
let bracketDepth = 0;
|
|
6109
|
+
for (let i = 0; i < str.length; i++) {
|
|
6110
|
+
const ch = str[i];
|
|
6111
|
+
if (inQuote) {
|
|
6112
|
+
if (ch === inQuote) inQuote = null;
|
|
6207
6113
|
} else {
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
return;
|
|
6218
|
-
}
|
|
6114
|
+
if (ch === '"' || ch === "'") {
|
|
6115
|
+
inQuote = ch;
|
|
6116
|
+
} else if (ch === "[") {
|
|
6117
|
+
bracketDepth++;
|
|
6118
|
+
} else if (ch === "]") {
|
|
6119
|
+
if (bracketDepth > 0) bracketDepth--;
|
|
6120
|
+
} else if (bracketDepth === 0 && ch === delimiter) {
|
|
6121
|
+
results.push(str.slice(start, i));
|
|
6122
|
+
start = i + 1;
|
|
6219
6123
|
}
|
|
6220
6124
|
}
|
|
6221
6125
|
}
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
function getNestedField(obj, fieldPath) {
|
|
6225
|
-
if (obj === null || typeof obj !== "object") return void 0;
|
|
6226
|
-
if (Object.prototype.hasOwnProperty.call(obj, fieldPath)) {
|
|
6227
|
-
return obj[fieldPath];
|
|
6228
|
-
}
|
|
6229
|
-
const parts = fieldPath.split(".");
|
|
6230
|
-
let cur = obj;
|
|
6231
|
-
for (const part of parts) {
|
|
6232
|
-
if (cur === null || typeof cur !== "object") return void 0;
|
|
6233
|
-
if (!Object.prototype.hasOwnProperty.call(cur, part)) return void 0;
|
|
6234
|
-
cur = cur[part];
|
|
6235
|
-
}
|
|
6236
|
-
return cur;
|
|
6126
|
+
results.push(str.slice(start));
|
|
6127
|
+
return results;
|
|
6237
6128
|
}
|
|
6238
|
-
function
|
|
6239
|
-
const
|
|
6240
|
-
const n = spec.length;
|
|
6129
|
+
function tokenizeSelector(sel) {
|
|
6130
|
+
const tokens = [];
|
|
6241
6131
|
let i = 0;
|
|
6132
|
+
const n = sel.length;
|
|
6242
6133
|
while (i < n) {
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
if (j2 === i) throw new Error(`invalid path spec: expected property name after '..' in '${spec}'`);
|
|
6248
|
-
ops.push({ kind: "recursive_key", name: spec.slice(i, j2) });
|
|
6249
|
-
i = j2;
|
|
6250
|
-
continue;
|
|
6251
|
-
}
|
|
6252
|
-
const ch = spec[i];
|
|
6253
|
-
if (ch === ".") {
|
|
6134
|
+
while (i < n && /\s/.test(sel[i])) i++;
|
|
6135
|
+
if (i >= n) break;
|
|
6136
|
+
if (sel[i] === ">") {
|
|
6137
|
+
tokens.push(">");
|
|
6254
6138
|
i++;
|
|
6255
6139
|
continue;
|
|
6256
6140
|
}
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
k++;
|
|
6265
|
-
continue;
|
|
6266
|
-
}
|
|
6267
|
-
if (c === inQuote) {
|
|
6268
|
-
inQuote = null;
|
|
6269
|
-
}
|
|
6270
|
-
} else if (c === '"' || c === "'") {
|
|
6271
|
-
inQuote = c;
|
|
6272
|
-
} else if (c === "]") {
|
|
6273
|
-
close = k;
|
|
6274
|
-
break;
|
|
6275
|
-
}
|
|
6276
|
-
}
|
|
6277
|
-
if (close === -1) throw new Error(`invalid path spec: unterminated '[' in '${spec}'`);
|
|
6278
|
-
const inner = spec.slice(i + 1, close);
|
|
6279
|
-
if (inner === "*") {
|
|
6280
|
-
ops.push({ kind: "wildcard" });
|
|
6281
|
-
} else if (/^-?\d+$/.test(inner)) {
|
|
6282
|
-
ops.push({ kind: "index", index: Number(inner) });
|
|
6141
|
+
const start = i;
|
|
6142
|
+
let inQuote = null;
|
|
6143
|
+
let bracketDepth = 0;
|
|
6144
|
+
while (i < n) {
|
|
6145
|
+
const ch = sel[i];
|
|
6146
|
+
if (inQuote) {
|
|
6147
|
+
if (ch === inQuote) inQuote = null;
|
|
6283
6148
|
} else {
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
if (
|
|
6289
|
-
|
|
6290
|
-
}
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
const bareOperator = /[<>]/.exec(expr);
|
|
6294
|
-
if (bareOperator !== null) {
|
|
6295
|
-
throw new Error(`unsupported comparison operator '${bareOperator[0]}' in '[${inner}]' of path spec '${spec}': this grammar filters by equality only ([field=value])`);
|
|
6149
|
+
if (ch === '"' || ch === "'") {
|
|
6150
|
+
inQuote = ch;
|
|
6151
|
+
} else if (ch === "[") {
|
|
6152
|
+
bracketDepth++;
|
|
6153
|
+
} else if (ch === "]") {
|
|
6154
|
+
if (bracketDepth > 0) bracketDepth--;
|
|
6155
|
+
} else if (bracketDepth === 0) {
|
|
6156
|
+
if (/\s/.test(ch) || ch === ">") {
|
|
6157
|
+
break;
|
|
6296
6158
|
}
|
|
6297
|
-
throw new Error(`invalid bracket expression '[${inner}]' in path spec: '${spec}' (expected [n], [*], or [field=value])`);
|
|
6298
|
-
}
|
|
6299
|
-
let field = m[1].trim();
|
|
6300
|
-
if (field.startsWith("@.")) field = field.slice(2).trim();
|
|
6301
|
-
const swallowedOperator = /[!<>]$/.exec(field);
|
|
6302
|
-
if (swallowedOperator !== null) {
|
|
6303
|
-
throw new Error(`unsupported comparison operator '${swallowedOperator[0]}=' in '[${inner}]' of path spec '${spec}': this grammar filters by equality only ([field=value])`);
|
|
6304
6159
|
}
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6160
|
+
}
|
|
6161
|
+
i++;
|
|
6162
|
+
}
|
|
6163
|
+
if (i > start) {
|
|
6164
|
+
tokens.push(sel.slice(start, i));
|
|
6165
|
+
}
|
|
6166
|
+
}
|
|
6167
|
+
return tokens;
|
|
6168
|
+
}
|
|
6169
|
+
function parseCompoundSelector(sel) {
|
|
6170
|
+
const result = {
|
|
6171
|
+
ids: [],
|
|
6172
|
+
classes: [],
|
|
6173
|
+
attributes: []
|
|
6174
|
+
};
|
|
6175
|
+
let i = 0;
|
|
6176
|
+
const n = sel.length;
|
|
6177
|
+
if (sel[0] !== "#" && sel[0] !== "." && sel[0] !== "[") {
|
|
6178
|
+
const tagMatch = /^([a-zA-Z*][a-zA-Z0-9_-]*)/.exec(sel);
|
|
6179
|
+
if (tagMatch) {
|
|
6180
|
+
result.tag = tagMatch[1];
|
|
6181
|
+
i = tagMatch[0].length;
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
6184
|
+
while (i < n) {
|
|
6185
|
+
const ch = sel[i];
|
|
6186
|
+
if (ch === "#") {
|
|
6187
|
+
i++;
|
|
6188
|
+
const idMatch = /^([a-zA-Z0-9_-]+)/.exec(sel.slice(i));
|
|
6189
|
+
if (idMatch) {
|
|
6190
|
+
result.ids.push(idMatch[1]);
|
|
6191
|
+
i += idMatch[0].length;
|
|
6192
|
+
}
|
|
6193
|
+
} else if (ch === ".") {
|
|
6194
|
+
i++;
|
|
6195
|
+
const classMatch = /^([a-zA-Z0-9_-]+)/.exec(sel.slice(i));
|
|
6196
|
+
if (classMatch) {
|
|
6197
|
+
result.classes.push(classMatch[1]);
|
|
6198
|
+
i += classMatch[0].length;
|
|
6199
|
+
}
|
|
6200
|
+
} else if (ch === "[") {
|
|
6201
|
+
let endBracket = -1;
|
|
6202
|
+
let inQuote = null;
|
|
6203
|
+
for (let j = i + 1; j < n; j++) {
|
|
6204
|
+
const c = sel[j];
|
|
6205
|
+
if (inQuote) {
|
|
6206
|
+
if (c === inQuote) inQuote = null;
|
|
6207
|
+
} else {
|
|
6208
|
+
if (c === '"' || c === "'") inQuote = c;
|
|
6209
|
+
else if (c === "]") {
|
|
6210
|
+
endBracket = j;
|
|
6211
|
+
break;
|
|
6309
6212
|
}
|
|
6310
6213
|
}
|
|
6311
|
-
ops.push({ kind: "filter", field, value: rawVal });
|
|
6312
6214
|
}
|
|
6313
|
-
|
|
6314
|
-
|
|
6215
|
+
if (endBracket === -1) {
|
|
6216
|
+
break;
|
|
6217
|
+
}
|
|
6218
|
+
const inner = sel.slice(i + 1, endBracket).trim();
|
|
6219
|
+
i = endBracket + 1;
|
|
6220
|
+
const attrMatch = /^([a-zA-Z0-9_:-]+)(?:\s*([*^$!~]?=)\s*(?:"([^"]*)"|'([^']*)'|([^\s"'\]]+)))?$/.exec(inner);
|
|
6221
|
+
if (attrMatch) {
|
|
6222
|
+
const attrName = attrMatch[1].toLowerCase();
|
|
6223
|
+
const op = attrMatch[2];
|
|
6224
|
+
const val = attrMatch[3] ?? attrMatch[4] ?? attrMatch[5];
|
|
6225
|
+
result.attributes.push({ name: attrName, op, val });
|
|
6226
|
+
}
|
|
6227
|
+
} else {
|
|
6228
|
+
i++;
|
|
6229
|
+
}
|
|
6230
|
+
}
|
|
6231
|
+
return result;
|
|
6232
|
+
}
|
|
6233
|
+
function matchesCompoundSelector(node, sel) {
|
|
6234
|
+
if (!sel || node.tag === "#root") return false;
|
|
6235
|
+
if (sel === "*") return true;
|
|
6236
|
+
const parsed = parseCompoundSelector(sel);
|
|
6237
|
+
if (parsed.tag && parsed.tag !== "*") {
|
|
6238
|
+
if (node.tag.toLowerCase() !== parsed.tag.toLowerCase()) return false;
|
|
6239
|
+
}
|
|
6240
|
+
for (const id of parsed.ids) {
|
|
6241
|
+
if (node.attributes["id"] !== id) return false;
|
|
6242
|
+
}
|
|
6243
|
+
if (parsed.classes.length > 0) {
|
|
6244
|
+
const nodeClasses = (node.attributes["class"] || "").split(/\s+/).filter(Boolean);
|
|
6245
|
+
for (const c of parsed.classes) {
|
|
6246
|
+
if (!nodeClasses.includes(c)) return false;
|
|
6315
6247
|
}
|
|
6316
|
-
let j = i;
|
|
6317
|
-
while (j < n && spec[j] !== "." && spec[j] !== "[") j++;
|
|
6318
|
-
if (j === i) throw new Error(`invalid path spec: '${spec}'`);
|
|
6319
|
-
ops.push({ kind: "key", name: spec.slice(i, j) });
|
|
6320
|
-
i = j;
|
|
6321
6248
|
}
|
|
6322
|
-
|
|
6249
|
+
for (const attr of parsed.attributes) {
|
|
6250
|
+
const hasAttr = Object.prototype.hasOwnProperty.call(node.attributes, attr.name);
|
|
6251
|
+
if (!hasAttr) {
|
|
6252
|
+
if (attr.op === "!=") continue;
|
|
6253
|
+
return false;
|
|
6254
|
+
}
|
|
6255
|
+
if (!attr.op) continue;
|
|
6256
|
+
const actual = node.attributes[attr.name] || "";
|
|
6257
|
+
const val = attr.val ?? "";
|
|
6258
|
+
if (attr.op === "=") {
|
|
6259
|
+
if (actual !== val) return false;
|
|
6260
|
+
} else if (attr.op === "^=") {
|
|
6261
|
+
if (!actual.startsWith(val)) return false;
|
|
6262
|
+
} else if (attr.op === "$=") {
|
|
6263
|
+
if (!actual.endsWith(val)) return false;
|
|
6264
|
+
} else if (attr.op === "*=") {
|
|
6265
|
+
if (!actual.includes(val)) return false;
|
|
6266
|
+
} else if (attr.op === "~=") {
|
|
6267
|
+
if (!actual.split(/\s+/).includes(val)) return false;
|
|
6268
|
+
} else if (attr.op === "!=") {
|
|
6269
|
+
if (actual === val) return false;
|
|
6270
|
+
}
|
|
6271
|
+
}
|
|
6272
|
+
return true;
|
|
6323
6273
|
}
|
|
6324
|
-
function
|
|
6325
|
-
|
|
6326
|
-
let
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6274
|
+
function queryHtml(htmlText, selectorStr) {
|
|
6275
|
+
const { root } = parseHtml(htmlText);
|
|
6276
|
+
let selector = selectorStr.trim();
|
|
6277
|
+
let targetAttr;
|
|
6278
|
+
const attrAtMatch = /@([a-zA-Z0-9_:-]+)$/.exec(selector);
|
|
6279
|
+
if (attrAtMatch) {
|
|
6280
|
+
targetAttr = attrAtMatch[1].toLowerCase();
|
|
6281
|
+
selector = selector.slice(0, selector.length - attrAtMatch[0].length).trim();
|
|
6282
|
+
if (!selector) selector = "*";
|
|
6283
|
+
}
|
|
6284
|
+
const rawSubSelectors = splitTopLevel(selector, ",").map((s) => s.trim()).filter(Boolean);
|
|
6285
|
+
const matchedSet = /* @__PURE__ */ new Set();
|
|
6286
|
+
for (const subSel of rawSubSelectors) {
|
|
6287
|
+
let collectAll2 = function(node) {
|
|
6288
|
+
if (node.tag !== "#root") currentCandidates.push(node);
|
|
6289
|
+
for (const c of node.children) collectAll2(c);
|
|
6290
|
+
};
|
|
6291
|
+
var collectAll = collectAll2;
|
|
6292
|
+
const tokens = tokenizeSelector(subSel);
|
|
6293
|
+
let currentCandidates = [];
|
|
6294
|
+
collectAll2(root);
|
|
6295
|
+
let i = 0;
|
|
6296
|
+
while (i < tokens.length) {
|
|
6297
|
+
const token = tokens[i];
|
|
6298
|
+
if (token === ">") {
|
|
6299
|
+
const nextTarget = tokens[i + 1];
|
|
6300
|
+
if (!nextTarget) break;
|
|
6301
|
+
const nextCandidates = [];
|
|
6302
|
+
for (const parent of currentCandidates) {
|
|
6303
|
+
for (const child of parent.children) {
|
|
6304
|
+
if (nextTarget === "*" || matchesCompoundSelector(child, nextTarget)) {
|
|
6305
|
+
nextCandidates.push(child);
|
|
6306
|
+
}
|
|
6347
6307
|
}
|
|
6348
|
-
} else if (!fanned) {
|
|
6349
|
-
throw new Error(`path not found: index [${op.index}] on non-array ${jsonType(item)} value`);
|
|
6350
|
-
}
|
|
6351
|
-
} else if (op.kind === "wildcard") {
|
|
6352
|
-
fanned = true;
|
|
6353
|
-
if (Array.isArray(item)) {
|
|
6354
|
-
pushAll(next, item);
|
|
6355
|
-
} else if (typeof item === "object" && item !== null) {
|
|
6356
|
-
pushAll(next, Object.values(item));
|
|
6357
6308
|
}
|
|
6309
|
+
currentCandidates = nextCandidates;
|
|
6310
|
+
i += 2;
|
|
6358
6311
|
} else {
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6312
|
+
if (i === 0) {
|
|
6313
|
+
currentCandidates = currentCandidates.filter((node) => token === "*" || matchesCompoundSelector(node, token));
|
|
6314
|
+
} else {
|
|
6315
|
+
const nextSet = /* @__PURE__ */ new Set();
|
|
6316
|
+
for (const ancestor of currentCandidates) {
|
|
6317
|
+
let findDescendants2 = function(node) {
|
|
6318
|
+
for (const child of node.children) {
|
|
6319
|
+
if (token === "*" || matchesCompoundSelector(child, token)) {
|
|
6320
|
+
nextSet.add(child);
|
|
6321
|
+
}
|
|
6322
|
+
findDescendants2(child);
|
|
6366
6323
|
}
|
|
6367
|
-
}
|
|
6324
|
+
};
|
|
6325
|
+
var findDescendants = findDescendants2;
|
|
6326
|
+
findDescendants2(ancestor);
|
|
6368
6327
|
}
|
|
6328
|
+
currentCandidates = Array.from(nextSet);
|
|
6369
6329
|
}
|
|
6330
|
+
i++;
|
|
6370
6331
|
}
|
|
6371
6332
|
}
|
|
6372
|
-
|
|
6333
|
+
for (const match of currentCandidates) {
|
|
6334
|
+
matchedSet.add(match);
|
|
6335
|
+
}
|
|
6373
6336
|
}
|
|
6374
|
-
|
|
6337
|
+
const elements = Array.from(matchedSet);
|
|
6338
|
+
if (targetAttr !== void 0) {
|
|
6339
|
+
const attributeValues = [];
|
|
6340
|
+
for (const el of elements) {
|
|
6341
|
+
if (Object.prototype.hasOwnProperty.call(el.attributes, targetAttr)) {
|
|
6342
|
+
attributeValues.push(el.attributes[targetAttr]);
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
return { elements, attributeValues, fanned: attributeValues.length > 1, sourceHtml: htmlText };
|
|
6346
|
+
}
|
|
6347
|
+
return { elements, fanned: elements.length > 1, sourceHtml: htmlText };
|
|
6375
6348
|
}
|
|
6376
|
-
function
|
|
6377
|
-
|
|
6349
|
+
function extractNodeText(node, sourceHtml) {
|
|
6350
|
+
if (RAW_TEXT_TAGS.has(node.tag.toLowerCase())) return "";
|
|
6351
|
+
if (sourceHtml && node.startOffset !== void 0 && node.endOffset !== void 0) {
|
|
6352
|
+
const raw = sourceHtml.slice(node.startOffset, node.endOffset);
|
|
6353
|
+
const clean = raw.replace(/<!--[\s\S]*?-->/g, "").replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
6354
|
+
return clean.replace(/\s+/g, " ").trim();
|
|
6355
|
+
}
|
|
6356
|
+
const parts = [];
|
|
6357
|
+
if (node.text.trim()) parts.push(node.text.trim());
|
|
6358
|
+
for (const child of node.children) {
|
|
6359
|
+
const t = extractNodeText(child);
|
|
6360
|
+
if (t) parts.push(t);
|
|
6361
|
+
}
|
|
6362
|
+
return parts.join(" ");
|
|
6363
|
+
}
|
|
6364
|
+
function serializeHtmlNode(node, indent = 0, sourceHtml) {
|
|
6365
|
+
if (node.tag === "#root") {
|
|
6366
|
+
return node.children.map((c) => serializeHtmlNode(c, indent, sourceHtml)).join("\n");
|
|
6367
|
+
}
|
|
6368
|
+
if (sourceHtml && node.startOffset !== void 0 && node.endOffset !== void 0) {
|
|
6369
|
+
return sourceHtml.slice(node.startOffset, node.endOffset);
|
|
6370
|
+
}
|
|
6371
|
+
const pad = " ".repeat(indent);
|
|
6372
|
+
const attrs = Object.entries(node.attributes).map(([k, v]) => v !== "" ? `${k}="${v.replace(/"/g, """)}"` : k).join(" ");
|
|
6373
|
+
const attrStr = attrs ? ` ${attrs}` : "";
|
|
6374
|
+
if (node.isVoid) {
|
|
6375
|
+
return `${pad}<${node.tag}${attrStr}>`;
|
|
6376
|
+
}
|
|
6377
|
+
if (node.children.length === 0) {
|
|
6378
|
+
const textTrimmed = node.text.trim();
|
|
6379
|
+
if (!textTrimmed) {
|
|
6380
|
+
return `${pad}<${node.tag}${attrStr}></${node.tag}>`;
|
|
6381
|
+
}
|
|
6382
|
+
if (!textTrimmed.includes("\n") && textTrimmed.length < 80) {
|
|
6383
|
+
return `${pad}<${node.tag}${attrStr}>${textTrimmed}</${node.tag}>`;
|
|
6384
|
+
}
|
|
6385
|
+
return `${pad}<${node.tag}${attrStr}>
|
|
6386
|
+
${pad} ${textTrimmed}
|
|
6387
|
+
${pad}</${node.tag}>`;
|
|
6388
|
+
}
|
|
6389
|
+
const inner = node.children.map((c) => serializeHtmlNode(c, indent + 1, sourceHtml)).join("\n");
|
|
6390
|
+
return `${pad}<${node.tag}${attrStr}>
|
|
6391
|
+
${inner}
|
|
6392
|
+
${pad}</${node.tag}>`;
|
|
6378
6393
|
}
|
|
6379
6394
|
|
|
6380
6395
|
// src/openapi_query.ts
|
|
@@ -6572,6 +6587,7 @@ function parseXmlTree(xmlText) {
|
|
|
6572
6587
|
const namespaces = {};
|
|
6573
6588
|
let totalElements = 0;
|
|
6574
6589
|
const doctypeMatch = new RegExp(
|
|
6590
|
+
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
|
6575
6591
|
`<!DOCTYPE\\s+((?:[^>[\\]"']|"[^"]*"|'[^']*'|\\[[\\s\\S]*?\\])*)\\s*>`,
|
|
6576
6592
|
"i"
|
|
6577
6593
|
).exec(text);
|
|
@@ -6594,7 +6610,7 @@ function parseXmlTree(xmlText) {
|
|
|
6594
6610
|
return attrs;
|
|
6595
6611
|
}
|
|
6596
6612
|
const tagRegex = new RegExp(
|
|
6597
|
-
// eslint-disable-next-line no-misleading-character-class
|
|
6613
|
+
// eslint-disable-next-line no-misleading-character-class, regexp/no-super-linear-backtracking
|
|
6598
6614
|
`<(\\/)?(${XML_NAME})(${XML_ATTR_REGION})(\\/)?>|<!--[\\s\\S]*?-->|<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>|<\\?[\\s\\S]*?\\?>|${XML_DOCTYPE_TOKEN}`,
|
|
6599
6615
|
"gi"
|
|
6600
6616
|
);
|
|
@@ -6632,6 +6648,13 @@ function parseXmlTree(xmlText) {
|
|
|
6632
6648
|
lineScan++;
|
|
6633
6649
|
}
|
|
6634
6650
|
const line = lineOffset + 1;
|
|
6651
|
+
let endScan = match.index;
|
|
6652
|
+
let endLineOffset = lineOffset;
|
|
6653
|
+
while (endScan < tagRegex.lastIndex) {
|
|
6654
|
+
if (text.charCodeAt(endScan) === 10) endLineOffset++;
|
|
6655
|
+
endScan++;
|
|
6656
|
+
}
|
|
6657
|
+
const tagEndLine = endLineOffset + 1;
|
|
6635
6658
|
if (isClosing) {
|
|
6636
6659
|
if (stack.length > 0) {
|
|
6637
6660
|
let popIdx = stack.length - 1;
|
|
@@ -6640,7 +6663,8 @@ function parseXmlTree(xmlText) {
|
|
|
6640
6663
|
}
|
|
6641
6664
|
if (popIdx >= 0) {
|
|
6642
6665
|
while (stack.length > popIdx) {
|
|
6643
|
-
stack.pop();
|
|
6666
|
+
const popped = stack.pop();
|
|
6667
|
+
popped.lineEnd = tagEndLine;
|
|
6644
6668
|
}
|
|
6645
6669
|
}
|
|
6646
6670
|
}
|
|
@@ -6652,7 +6676,8 @@ function parseXmlTree(xmlText) {
|
|
|
6652
6676
|
attributes: attrs,
|
|
6653
6677
|
children: [],
|
|
6654
6678
|
text: "",
|
|
6655
|
-
line
|
|
6679
|
+
line,
|
|
6680
|
+
lineEnd: isSelfClosing ? tagEndLine : line
|
|
6656
6681
|
};
|
|
6657
6682
|
if (stack.length === 0) {
|
|
6658
6683
|
if (!rootNode) rootNode = newNode;
|
|
@@ -6665,6 +6690,12 @@ function parseXmlTree(xmlText) {
|
|
|
6665
6690
|
}
|
|
6666
6691
|
lastIndex = tagRegex.lastIndex;
|
|
6667
6692
|
}
|
|
6693
|
+
while (stack.length > 0) {
|
|
6694
|
+
const unclosed = stack.pop();
|
|
6695
|
+
if (!unclosed.lineEnd || unclosed.lineEnd < unclosed.line) {
|
|
6696
|
+
unclosed.lineEnd = unclosed.line;
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
6668
6699
|
return { root: rootNode, namespaces, doctype, totalElements };
|
|
6669
6700
|
}
|
|
6670
6701
|
function outlineXml(xmlText, opts = {}) {
|
|
@@ -6764,6 +6795,206 @@ function formatXmlOutline(summary) {
|
|
|
6764
6795
|
renderNode(summary.tree, 0);
|
|
6765
6796
|
return lines.join("\n");
|
|
6766
6797
|
}
|
|
6798
|
+
function getLocalName(tag) {
|
|
6799
|
+
const idx = tag.indexOf(":");
|
|
6800
|
+
return idx === -1 ? tag : tag.slice(idx + 1);
|
|
6801
|
+
}
|
|
6802
|
+
function matchTag(nodeTag, targetTag) {
|
|
6803
|
+
if (targetTag === "*" || targetTag === "") return true;
|
|
6804
|
+
if (nodeTag.toLowerCase() === targetTag.toLowerCase()) return true;
|
|
6805
|
+
if (!targetTag.includes(":")) {
|
|
6806
|
+
return getLocalName(nodeTag).toLowerCase() === targetTag.toLowerCase();
|
|
6807
|
+
}
|
|
6808
|
+
return false;
|
|
6809
|
+
}
|
|
6810
|
+
function getAttrValue(node, targetAttr) {
|
|
6811
|
+
const clean = targetAttr.startsWith("@") ? targetAttr.slice(1) : targetAttr;
|
|
6812
|
+
if (clean === "*") {
|
|
6813
|
+
const vals = Object.values(node.attributes);
|
|
6814
|
+
return vals.length > 0 ? vals[0] : void 0;
|
|
6815
|
+
}
|
|
6816
|
+
if (node.attributes[clean] !== void 0) return node.attributes[clean];
|
|
6817
|
+
const lowerClean = clean.toLowerCase();
|
|
6818
|
+
for (const [k, v] of Object.entries(node.attributes)) {
|
|
6819
|
+
if (k.toLowerCase() === lowerClean) return v;
|
|
6820
|
+
}
|
|
6821
|
+
if (!clean.includes(":")) {
|
|
6822
|
+
for (const [k, v] of Object.entries(node.attributes)) {
|
|
6823
|
+
if (getLocalName(k).toLowerCase() === lowerClean) return v;
|
|
6824
|
+
}
|
|
6825
|
+
}
|
|
6826
|
+
return void 0;
|
|
6827
|
+
}
|
|
6828
|
+
function splitTopLevel2(str, delimiter) {
|
|
6829
|
+
const parts = [];
|
|
6830
|
+
let current = "";
|
|
6831
|
+
let quote = null;
|
|
6832
|
+
let parenDepth = 0;
|
|
6833
|
+
for (let i = 0; i < str.length; i++) {
|
|
6834
|
+
const ch = str[i];
|
|
6835
|
+
if (!quote && (ch === '"' || ch === "'")) {
|
|
6836
|
+
quote = ch;
|
|
6837
|
+
current += ch;
|
|
6838
|
+
} else if (quote && ch === quote) {
|
|
6839
|
+
quote = null;
|
|
6840
|
+
current += ch;
|
|
6841
|
+
} else if (!quote && ch === "(") {
|
|
6842
|
+
parenDepth++;
|
|
6843
|
+
current += ch;
|
|
6844
|
+
} else if (!quote && ch === ")") {
|
|
6845
|
+
if (parenDepth > 0) parenDepth--;
|
|
6846
|
+
current += ch;
|
|
6847
|
+
} else if (!quote && parenDepth === 0 && str.startsWith(delimiter, i)) {
|
|
6848
|
+
parts.push(current.trim());
|
|
6849
|
+
current = "";
|
|
6850
|
+
i += delimiter.length - 1;
|
|
6851
|
+
} else {
|
|
6852
|
+
current += ch;
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
if (current.trim()) parts.push(current.trim());
|
|
6856
|
+
return parts;
|
|
6857
|
+
}
|
|
6858
|
+
function parseSinglePredicate(predStr) {
|
|
6859
|
+
const s = predStr.trim();
|
|
6860
|
+
if (!s) return null;
|
|
6861
|
+
const orParts = splitTopLevel2(s, " or ");
|
|
6862
|
+
if (orParts.length > 1) {
|
|
6863
|
+
const predicates = orParts.map(parseSinglePredicate);
|
|
6864
|
+
if (predicates.some((p) => p === null)) return null;
|
|
6865
|
+
return { kind: "or", predicates };
|
|
6866
|
+
}
|
|
6867
|
+
const andParts = splitTopLevel2(s, " and ");
|
|
6868
|
+
if (andParts.length > 1) {
|
|
6869
|
+
const predicates = andParts.map(parseSinglePredicate);
|
|
6870
|
+
if (predicates.some((p) => p === null)) return null;
|
|
6871
|
+
return { kind: "and", predicates };
|
|
6872
|
+
}
|
|
6873
|
+
if (/^-?\d+$/.test(s)) {
|
|
6874
|
+
const n = parseInt(s, 10);
|
|
6875
|
+
return { kind: "index", index: n };
|
|
6876
|
+
}
|
|
6877
|
+
if (s === "last()") {
|
|
6878
|
+
return { kind: "index", index: -1 };
|
|
6879
|
+
}
|
|
6880
|
+
if (s === "*") {
|
|
6881
|
+
return { kind: "all" };
|
|
6882
|
+
}
|
|
6883
|
+
const localMatch = /^local-name\(\)\s*(!?=)\s*(?:"([^"]*)"|'([^']*)'|([^\s"']+))\s*$/i.exec(s);
|
|
6884
|
+
if (localMatch) {
|
|
6885
|
+
const val = localMatch[2] !== void 0 ? localMatch[2] : localMatch[3] !== void 0 ? localMatch[3] : localMatch[4] ?? "";
|
|
6886
|
+
return {
|
|
6887
|
+
kind: "localNameEquals",
|
|
6888
|
+
value: val,
|
|
6889
|
+
...localMatch[1] === "!=" ? { notEqual: true } : {}
|
|
6890
|
+
};
|
|
6891
|
+
}
|
|
6892
|
+
const containsAttrMatch = /^contains\(\s*(@[a-zA-Z0-9_:.\\-]+|\*)\s*,\s*(?:"([^"]*)"|'([^']*)')\s*\)$/i.exec(s);
|
|
6893
|
+
if (containsAttrMatch) {
|
|
6894
|
+
const attrName = containsAttrMatch[1].replace(/^@/, "");
|
|
6895
|
+
return { kind: "attrContains", name: attrName, value: containsAttrMatch[2] ?? containsAttrMatch[3] };
|
|
6896
|
+
}
|
|
6897
|
+
const containsTextMatch = /^contains\(\s*(?:text\(\)|\.)\s*,\s*(?:"([^"]*)"|'([^']*)')\s*\)$/i.exec(s);
|
|
6898
|
+
if (containsTextMatch) {
|
|
6899
|
+
return { kind: "textContains", value: containsTextMatch[1] ?? containsTextMatch[2] };
|
|
6900
|
+
}
|
|
6901
|
+
const startsWithAttrMatch = /^starts-with\(\s*(@[a-zA-Z0-9_:.\\-]+|\*)\s*,\s*(?:"([^"]*)"|'([^']*)')\s*\)$/i.exec(s);
|
|
6902
|
+
if (startsWithAttrMatch) {
|
|
6903
|
+
const attrName = startsWithAttrMatch[1].replace(/^@/, "");
|
|
6904
|
+
return { kind: "attrStartsWith", name: attrName, value: startsWithAttrMatch[2] ?? startsWithAttrMatch[3] };
|
|
6905
|
+
}
|
|
6906
|
+
const textMatch = /^(?:text\(\)|\.)\s*(!?=)\s*(?:"([^"]*)"|'([^']*)'|([^\s"']+))\s*$/i.exec(s);
|
|
6907
|
+
if (textMatch) {
|
|
6908
|
+
const val = textMatch[2] !== void 0 ? textMatch[2] : textMatch[3] !== void 0 ? textMatch[3] : textMatch[4] ?? "";
|
|
6909
|
+
return {
|
|
6910
|
+
kind: "textEquals",
|
|
6911
|
+
value: val,
|
|
6912
|
+
...textMatch[1] === "!=" ? { notEqual: true } : {}
|
|
6913
|
+
};
|
|
6914
|
+
}
|
|
6915
|
+
const compMatch = /^(@?[a-zA-Z0-9_:.\\-]+)\s*(!?=)\s*(?:"([^"]*)"|'([^']*)'|([^\s\]]+))$/.exec(s);
|
|
6916
|
+
if (compMatch) {
|
|
6917
|
+
const name = compMatch[1];
|
|
6918
|
+
const op = compMatch[2];
|
|
6919
|
+
const val = compMatch[3] !== void 0 ? compMatch[3] : compMatch[4] !== void 0 ? compMatch[4] : compMatch[5] ?? "";
|
|
6920
|
+
if (name.startsWith("@")) {
|
|
6921
|
+
return {
|
|
6922
|
+
kind: "attrEquals",
|
|
6923
|
+
name: name.slice(1),
|
|
6924
|
+
value: val,
|
|
6925
|
+
...op === "!=" ? { notEqual: true } : {}
|
|
6926
|
+
};
|
|
6927
|
+
}
|
|
6928
|
+
return {
|
|
6929
|
+
kind: "childEquals",
|
|
6930
|
+
tag: name,
|
|
6931
|
+
value: val,
|
|
6932
|
+
...op === "!=" ? { notEqual: true } : {}
|
|
6933
|
+
};
|
|
6934
|
+
}
|
|
6935
|
+
if (s.startsWith("@")) {
|
|
6936
|
+
return { kind: "attrExists", name: s.slice(1) };
|
|
6937
|
+
}
|
|
6938
|
+
if (/^[a-zA-Z0-9_:.\\-]+$/.test(s)) {
|
|
6939
|
+
return { kind: "childExists", tag: s };
|
|
6940
|
+
}
|
|
6941
|
+
return null;
|
|
6942
|
+
}
|
|
6943
|
+
function evalPredicate(node, pred, indexInMatch, totalMatching) {
|
|
6944
|
+
switch (pred.kind) {
|
|
6945
|
+
case "index": {
|
|
6946
|
+
const targetIdx = pred.index < 0 ? totalMatching + pred.index : pred.index;
|
|
6947
|
+
return indexInMatch === targetIdx;
|
|
6948
|
+
}
|
|
6949
|
+
case "all":
|
|
6950
|
+
return true;
|
|
6951
|
+
case "and":
|
|
6952
|
+
return pred.predicates.every((p) => evalPredicate(node, p, indexInMatch, totalMatching));
|
|
6953
|
+
case "or":
|
|
6954
|
+
return pred.predicates.some((p) => evalPredicate(node, p, indexInMatch, totalMatching));
|
|
6955
|
+
case "localNameEquals": {
|
|
6956
|
+
const local = getLocalName(node.tag);
|
|
6957
|
+
const eq = local.toLowerCase() === pred.value.toLowerCase();
|
|
6958
|
+
return pred.notEqual ? !eq : eq;
|
|
6959
|
+
}
|
|
6960
|
+
case "attrExists":
|
|
6961
|
+
return getAttrValue(node, pred.name) !== void 0;
|
|
6962
|
+
case "attrEquals": {
|
|
6963
|
+
const val = getAttrValue(node, pred.name);
|
|
6964
|
+
if (val !== void 0) {
|
|
6965
|
+
return pred.notEqual ? val !== pred.value : val === pred.value;
|
|
6966
|
+
}
|
|
6967
|
+
const child = node.children.find((c) => matchTag(c.tag, pred.name));
|
|
6968
|
+
if (child !== void 0) {
|
|
6969
|
+
return pred.notEqual ? child.text.trim() !== pred.value.trim() : child.text.trim() === pred.value.trim();
|
|
6970
|
+
}
|
|
6971
|
+
return pred.notEqual === true;
|
|
6972
|
+
}
|
|
6973
|
+
case "attrContains": {
|
|
6974
|
+
const val = getAttrValue(node, pred.name);
|
|
6975
|
+
if (val === void 0) return false;
|
|
6976
|
+
return val.toLowerCase().includes(pred.value.toLowerCase());
|
|
6977
|
+
}
|
|
6978
|
+
case "attrStartsWith": {
|
|
6979
|
+
const val = getAttrValue(node, pred.name);
|
|
6980
|
+
if (val === void 0) return false;
|
|
6981
|
+
return val.toLowerCase().startsWith(pred.value.toLowerCase());
|
|
6982
|
+
}
|
|
6983
|
+
case "textEquals": {
|
|
6984
|
+
const eq = node.text.trim() === pred.value.trim();
|
|
6985
|
+
return pred.notEqual ? !eq : eq;
|
|
6986
|
+
}
|
|
6987
|
+
case "textContains":
|
|
6988
|
+
return node.text.toLowerCase().includes(pred.value.toLowerCase());
|
|
6989
|
+
case "childExists":
|
|
6990
|
+
return node.children.some((c) => matchTag(c.tag, pred.tag));
|
|
6991
|
+
case "childEquals": {
|
|
6992
|
+
const child = node.children.find((c) => matchTag(c.tag, pred.tag));
|
|
6993
|
+
if (!child) return pred.notEqual === true;
|
|
6994
|
+
return pred.notEqual ? child.text.trim() !== pred.value.trim() : child.text.trim() === pred.value.trim();
|
|
6995
|
+
}
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6767
6998
|
function parseXmlPath(pathStr) {
|
|
6768
6999
|
let normalized = pathStr.trim();
|
|
6769
7000
|
if (normalized === "" || normalized === "/") return [];
|
|
@@ -6775,16 +7006,23 @@ function parseXmlPath(pathStr) {
|
|
|
6775
7006
|
}
|
|
6776
7007
|
const segments = [];
|
|
6777
7008
|
let inBracket = false;
|
|
7009
|
+
let quoteChar = null;
|
|
6778
7010
|
let currentSegment = "";
|
|
6779
7011
|
for (let i = 0; i < normalized.length; i++) {
|
|
6780
7012
|
const ch = normalized[i];
|
|
6781
|
-
if (ch === "
|
|
7013
|
+
if (!quoteChar && (ch === '"' || ch === "'")) {
|
|
7014
|
+
quoteChar = ch;
|
|
7015
|
+
currentSegment += ch;
|
|
7016
|
+
} else if (quoteChar && ch === quoteChar) {
|
|
7017
|
+
quoteChar = null;
|
|
7018
|
+
currentSegment += ch;
|
|
7019
|
+
} else if (!quoteChar && ch === "[") {
|
|
6782
7020
|
inBracket = true;
|
|
6783
7021
|
currentSegment += ch;
|
|
6784
|
-
} else if (ch === "]") {
|
|
7022
|
+
} else if (!quoteChar && ch === "]") {
|
|
6785
7023
|
inBracket = false;
|
|
6786
7024
|
currentSegment += ch;
|
|
6787
|
-
} else if (!inBracket && (ch === "/" || ch === ".")) {
|
|
7025
|
+
} else if (!quoteChar && !inBracket && (ch === "/" || ch === ".")) {
|
|
6788
7026
|
if (currentSegment) {
|
|
6789
7027
|
segments.push(currentSegment);
|
|
6790
7028
|
currentSegment = "";
|
|
@@ -6813,54 +7051,167 @@ function parseXmlPath(pathStr) {
|
|
|
6813
7051
|
steps.push({ tag: "", isRecursive, attributeSelect: attrSelectMatch[1] });
|
|
6814
7052
|
continue;
|
|
6815
7053
|
}
|
|
6816
|
-
|
|
6817
|
-
const
|
|
6818
|
-
|
|
6819
|
-
let
|
|
7054
|
+
let tag = "";
|
|
7055
|
+
const rawPredicates = [];
|
|
7056
|
+
let inB = false;
|
|
7057
|
+
let qChar = null;
|
|
7058
|
+
let curPred = "";
|
|
7059
|
+
for (let cIdx = 0; cIdx < s.length; cIdx++) {
|
|
7060
|
+
const c = s[cIdx];
|
|
7061
|
+
if (!qChar && (c === '"' || c === "'")) {
|
|
7062
|
+
qChar = c;
|
|
7063
|
+
if (inB) curPred += c;
|
|
7064
|
+
} else if (qChar && c === qChar) {
|
|
7065
|
+
qChar = null;
|
|
7066
|
+
if (inB) curPred += c;
|
|
7067
|
+
} else if (!qChar && c === "[") {
|
|
7068
|
+
if (!inB) {
|
|
7069
|
+
inB = true;
|
|
7070
|
+
curPred = "";
|
|
7071
|
+
} else {
|
|
7072
|
+
curPred += c;
|
|
7073
|
+
}
|
|
7074
|
+
} else if (!qChar && c === "]") {
|
|
7075
|
+
if (inB) {
|
|
7076
|
+
inB = false;
|
|
7077
|
+
rawPredicates.push(curPred.trim());
|
|
7078
|
+
curPred = "";
|
|
7079
|
+
}
|
|
7080
|
+
} else if (!inB) {
|
|
7081
|
+
tag += c;
|
|
7082
|
+
} else {
|
|
7083
|
+
curPred += c;
|
|
7084
|
+
}
|
|
7085
|
+
}
|
|
7086
|
+
tag = tag.trim();
|
|
6820
7087
|
if (!tag) tag = "*";
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
7088
|
+
if (inB || qChar !== null) {
|
|
7089
|
+
steps.push({ tag: s, isRecursive });
|
|
7090
|
+
continue;
|
|
7091
|
+
}
|
|
7092
|
+
const predicates = [];
|
|
7093
|
+
let legacyIndex;
|
|
7094
|
+
let legacyAllIndices;
|
|
7095
|
+
let legacyAttrFilter;
|
|
7096
|
+
let unreadablePredicate = false;
|
|
7097
|
+
for (const rawP of rawPredicates) {
|
|
7098
|
+
const parsedP = parseSinglePredicate(rawP);
|
|
7099
|
+
if (!parsedP) {
|
|
7100
|
+
unreadablePredicate = true;
|
|
7101
|
+
break;
|
|
7102
|
+
}
|
|
7103
|
+
predicates.push(parsedP);
|
|
7104
|
+
if (parsedP.kind === "index") {
|
|
7105
|
+
legacyIndex = parsedP.index;
|
|
7106
|
+
} else if (parsedP.kind === "all") {
|
|
7107
|
+
legacyAllIndices = true;
|
|
7108
|
+
} else if (parsedP.kind === "attrEquals" || parsedP.kind === "childEquals") {
|
|
7109
|
+
legacyAttrFilter = {
|
|
7110
|
+
name: parsedP.kind === "attrEquals" ? parsedP.name : parsedP.tag,
|
|
7111
|
+
value: parsedP.value,
|
|
7112
|
+
...parsedP.notEqual ? { notEqual: true } : {}
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
6835
7115
|
}
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
7116
|
+
if (unreadablePredicate) {
|
|
7117
|
+
steps.push({ tag: s, isRecursive });
|
|
7118
|
+
continue;
|
|
7119
|
+
}
|
|
7120
|
+
const hasComplexPredicates = rawPredicates.length > 1 || predicates.some(
|
|
7121
|
+
(p) => p.kind === "and" || p.kind === "or" || p.kind === "localNameEquals" || p.kind === "attrContains" || p.kind === "textContains" || p.kind === "attrStartsWith" || p.kind === "textEquals" || p.kind === "attrExists" || p.kind === "childExists"
|
|
7122
|
+
);
|
|
7123
|
+
const step = {
|
|
7124
|
+
tag,
|
|
7125
|
+
isRecursive,
|
|
7126
|
+
...legacyIndex !== void 0 ? { index: legacyIndex } : {},
|
|
7127
|
+
...legacyAllIndices !== void 0 ? { allIndices: legacyAllIndices } : {},
|
|
7128
|
+
...legacyAttrFilter !== void 0 ? { attributeFilter: legacyAttrFilter } : {},
|
|
7129
|
+
...hasComplexPredicates ? { predicates } : {}
|
|
7130
|
+
};
|
|
6840
7131
|
steps.push(step);
|
|
6841
7132
|
}
|
|
6842
|
-
return steps;
|
|
7133
|
+
return steps;
|
|
7134
|
+
}
|
|
7135
|
+
function tryDecodeEmbeddedXml(content, opts = {}) {
|
|
7136
|
+
const maxLines = opts.maxLines ?? 60;
|
|
7137
|
+
if (!content || typeof content !== "string") return { decoded: false, text: content };
|
|
7138
|
+
let candidate = content.trim();
|
|
7139
|
+
const hadEntities = candidate.includes("<") && candidate.includes(">");
|
|
7140
|
+
if (hadEntities) {
|
|
7141
|
+
candidate = decodeXmlEntities(candidate).trim();
|
|
7142
|
+
}
|
|
7143
|
+
if (!candidate.startsWith("<") || !candidate.endsWith(">")) {
|
|
7144
|
+
return { decoded: false, text: content };
|
|
7145
|
+
}
|
|
7146
|
+
try {
|
|
7147
|
+
const { root } = parseXmlTree(candidate);
|
|
7148
|
+
if (!root) return { decoded: false, text: content };
|
|
7149
|
+
const formatted = serializeXmlNode(root, 0);
|
|
7150
|
+
const lines = formatted.split("\n");
|
|
7151
|
+
if (lines.length > maxLines) {
|
|
7152
|
+
const bounded = lines.slice(0, maxLines);
|
|
7153
|
+
bounded.push(`... (${lines.length - maxLines} more lines of decoded embedded XML elided; bounded)`);
|
|
7154
|
+
return { decoded: true, text: bounded.join("\n") };
|
|
7155
|
+
}
|
|
7156
|
+
return { decoded: true, text: formatted };
|
|
7157
|
+
} catch {
|
|
7158
|
+
return { decoded: false, text: content };
|
|
7159
|
+
}
|
|
6843
7160
|
}
|
|
6844
|
-
function serializeXmlNode(node, indent = 0) {
|
|
7161
|
+
function serializeXmlNode(node, indent = 0, opts = {}) {
|
|
6845
7162
|
const pad = " ".repeat(indent);
|
|
6846
7163
|
const attrParts = [];
|
|
6847
7164
|
for (const [k, v] of Object.entries(node.attributes)) {
|
|
7165
|
+
if (opts.decodeEmbedded) {
|
|
7166
|
+
const decodedAttr = tryDecodeEmbeddedXml(v, { maxLines: 20 });
|
|
7167
|
+
if (decodedAttr.decoded) {
|
|
7168
|
+
attrParts.push(`${k}="[Embedded XML: ${v.length} chars]"`);
|
|
7169
|
+
continue;
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
6848
7172
|
attrParts.push(`${k}="${escapeXmlAttr(v)}"`);
|
|
6849
7173
|
}
|
|
6850
7174
|
const attrStr = attrParts.length > 0 ? " " + attrParts.join(" ") : "";
|
|
7175
|
+
let textContent = node.text;
|
|
7176
|
+
let decodedTextBanner = "";
|
|
7177
|
+
if (opts.decodeEmbedded && textContent) {
|
|
7178
|
+
const decoded = tryDecodeEmbeddedXml(
|
|
7179
|
+
textContent,
|
|
7180
|
+
opts.maxLines !== void 0 ? { maxLines: opts.maxLines } : {}
|
|
7181
|
+
);
|
|
7182
|
+
if (decoded.decoded) {
|
|
7183
|
+
textContent = decoded.text.split("\n").map((l) => `${pad} ${l}`).join("\n");
|
|
7184
|
+
decodedTextBanner = `${pad} <!-- [Decoded Embedded XML] -->
|
|
7185
|
+
`;
|
|
7186
|
+
} else {
|
|
7187
|
+
textContent = `${pad} ${escapeXmlText(textContent)}`;
|
|
7188
|
+
}
|
|
7189
|
+
} else if (textContent) {
|
|
7190
|
+
textContent = `${pad} ${escapeXmlText(textContent)}`;
|
|
7191
|
+
}
|
|
6851
7192
|
if (node.children.length === 0 && !node.text) {
|
|
6852
7193
|
return `${pad}<${node.tag}${attrStr}/>`;
|
|
6853
7194
|
}
|
|
6854
7195
|
if (node.children.length === 0) {
|
|
7196
|
+
if (decodedTextBanner) {
|
|
7197
|
+
return `${pad}<${node.tag}${attrStr}>
|
|
7198
|
+
${decodedTextBanner}${textContent}
|
|
7199
|
+
${pad}</${node.tag}>`;
|
|
7200
|
+
}
|
|
6855
7201
|
return `${pad}<${node.tag}${attrStr}>${escapeXmlText(node.text)}</${node.tag}>`;
|
|
6856
7202
|
}
|
|
6857
7203
|
const lines = [];
|
|
6858
7204
|
lines.push(`${pad}<${node.tag}${attrStr}>`);
|
|
6859
7205
|
if (node.text) {
|
|
6860
|
-
|
|
7206
|
+
if (decodedTextBanner) {
|
|
7207
|
+
lines.push(decodedTextBanner.trimEnd());
|
|
7208
|
+
lines.push(textContent);
|
|
7209
|
+
} else {
|
|
7210
|
+
lines.push(`${pad} ${escapeXmlText(node.text)}`);
|
|
7211
|
+
}
|
|
6861
7212
|
}
|
|
6862
7213
|
for (const child of node.children) {
|
|
6863
|
-
lines.push(serializeXmlNode(child, indent + 1));
|
|
7214
|
+
lines.push(serializeXmlNode(child, indent + 1, opts));
|
|
6864
7215
|
}
|
|
6865
7216
|
lines.push(`${pad}</${node.tag}>`);
|
|
6866
7217
|
return lines.join("\n");
|
|
@@ -6876,23 +7227,24 @@ function getAllDescendants(node) {
|
|
|
6876
7227
|
walk(node);
|
|
6877
7228
|
return desc;
|
|
6878
7229
|
}
|
|
6879
|
-
function xmlNodeToJson(node) {
|
|
7230
|
+
function xmlNodeToJson(node, opts = {}) {
|
|
6880
7231
|
const hasAttrs = Object.keys(node.attributes).length > 0;
|
|
6881
7232
|
const hasChildren = node.children.length > 0;
|
|
6882
7233
|
if (!hasChildren && !hasAttrs) {
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
}
|
|
6890
|
-
if (node.text) {
|
|
6891
|
-
result2["#text"] = node.text;
|
|
7234
|
+
if (opts.withLines) {
|
|
7235
|
+
return {
|
|
7236
|
+
lineStart: node.line,
|
|
7237
|
+
lineEnd: node.lineEnd,
|
|
7238
|
+
value: node.text
|
|
7239
|
+
};
|
|
6892
7240
|
}
|
|
6893
|
-
return
|
|
7241
|
+
return node.text;
|
|
6894
7242
|
}
|
|
6895
7243
|
const result = {};
|
|
7244
|
+
if (opts.withLines) {
|
|
7245
|
+
result["lineStart"] = node.line;
|
|
7246
|
+
result["lineEnd"] = node.lineEnd;
|
|
7247
|
+
}
|
|
6896
7248
|
for (const [k, v] of Object.entries(node.attributes)) {
|
|
6897
7249
|
result[`@${k}`] = v;
|
|
6898
7250
|
}
|
|
@@ -6907,22 +7259,29 @@ function xmlNodeToJson(node) {
|
|
|
6907
7259
|
}
|
|
6908
7260
|
for (const [tag, group] of childGroups) {
|
|
6909
7261
|
if (group.length === 1) {
|
|
6910
|
-
result[tag] = xmlNodeToJson(group[0]);
|
|
7262
|
+
result[tag] = xmlNodeToJson(group[0], opts);
|
|
6911
7263
|
} else {
|
|
6912
|
-
result[tag] = group.map(xmlNodeToJson);
|
|
7264
|
+
result[tag] = group.map((c) => xmlNodeToJson(c, opts));
|
|
6913
7265
|
}
|
|
6914
7266
|
}
|
|
6915
7267
|
if (node.text) {
|
|
7268
|
+
if (opts.decodeEmbedded) {
|
|
7269
|
+
const decoded = tryDecodeEmbeddedXml(node.text, { maxLines: 50 });
|
|
7270
|
+
if (decoded.decoded) {
|
|
7271
|
+
result["#decodedXml"] = decoded.text;
|
|
7272
|
+
}
|
|
7273
|
+
}
|
|
6916
7274
|
result["#text"] = node.text;
|
|
6917
7275
|
}
|
|
6918
7276
|
return result;
|
|
6919
7277
|
}
|
|
6920
|
-
function queryXml(xmlText, pathStr) {
|
|
7278
|
+
function queryXml(xmlText, pathStr, opts = {}) {
|
|
6921
7279
|
const { root } = parseXmlTree(xmlText);
|
|
6922
7280
|
if (!root) {
|
|
6923
7281
|
throw new Error("No valid XML root element found");
|
|
6924
7282
|
}
|
|
6925
|
-
const
|
|
7283
|
+
const queryExpression = (opts.xpath ?? pathStr).trim();
|
|
7284
|
+
const steps = parseXmlPath(queryExpression);
|
|
6926
7285
|
if (steps.length === 0) {
|
|
6927
7286
|
return { items: [root], fanned: false };
|
|
6928
7287
|
}
|
|
@@ -6933,22 +7292,31 @@ function queryXml(xmlText, pathStr) {
|
|
|
6933
7292
|
const step = steps[sIdx];
|
|
6934
7293
|
if (step.attributeSelect !== void 0) {
|
|
6935
7294
|
const attrVals = [];
|
|
7295
|
+
const attrNodes = [];
|
|
6936
7296
|
const attrName = step.attributeSelect;
|
|
6937
7297
|
for (const cand of currentCandidates) {
|
|
6938
7298
|
if (attrName === "*") {
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
7299
|
+
for (const [k, v] of Object.entries(cand.attributes)) {
|
|
7300
|
+
attrVals.push(v);
|
|
7301
|
+
attrNodes.push({ value: v, node: cand, attrName: k });
|
|
7302
|
+
}
|
|
7303
|
+
} else {
|
|
7304
|
+
const val = getAttrValue(cand, attrName);
|
|
7305
|
+
if (val !== void 0) {
|
|
7306
|
+
attrVals.push(val);
|
|
7307
|
+
attrNodes.push({ value: val, node: cand, attrName });
|
|
7308
|
+
}
|
|
6942
7309
|
}
|
|
6943
7310
|
}
|
|
6944
7311
|
return {
|
|
6945
7312
|
items: currentCandidates,
|
|
6946
7313
|
attributeValues: attrVals,
|
|
7314
|
+
attributeNodes: attrNodes,
|
|
6947
7315
|
fanned: hasFanned || attrVals.length > 1
|
|
6948
7316
|
};
|
|
6949
7317
|
}
|
|
6950
7318
|
const nextCandidates = [];
|
|
6951
|
-
const evaluateOnCurrentNode = isAtRootLevel && !step.isRecursive && (
|
|
7319
|
+
const evaluateOnCurrentNode = isAtRootLevel && !step.isRecursive && matchTag(root.tag, step.tag);
|
|
6952
7320
|
isAtRootLevel = false;
|
|
6953
7321
|
for (const cand of currentCandidates) {
|
|
6954
7322
|
let targets;
|
|
@@ -6959,34 +7327,35 @@ function queryXml(xmlText, pathStr) {
|
|
|
6959
7327
|
} else {
|
|
6960
7328
|
targets = cand.children;
|
|
6961
7329
|
}
|
|
6962
|
-
let matching = targets.filter((c) =>
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
return af.notEqual ? attrVal !== af.value : attrVal === af.value;
|
|
6970
|
-
}
|
|
6971
|
-
const childElem = c.children.find((child) => child.tag.toLowerCase() === af.name.toLowerCase());
|
|
6972
|
-
if (childElem !== void 0) {
|
|
6973
|
-
if (af.value === void 0) return true;
|
|
6974
|
-
return af.notEqual ? childElem.text !== af.value : childElem.text === af.value;
|
|
6975
|
-
}
|
|
6976
|
-
return af.notEqual === true;
|
|
6977
|
-
});
|
|
6978
|
-
}
|
|
6979
|
-
if (step.index !== void 0) {
|
|
6980
|
-
const idx = step.index < 0 ? matching.length + step.index : step.index;
|
|
6981
|
-
if (idx >= 0 && idx < matching.length) {
|
|
6982
|
-
nextCandidates.push(matching[idx]);
|
|
7330
|
+
let matching = targets.filter((c) => matchTag(c.tag, step.tag));
|
|
7331
|
+
const preds = step.predicates && step.predicates.length > 0 ? step.predicates : step.attributeFilter ? [
|
|
7332
|
+
{
|
|
7333
|
+
kind: "attrEquals",
|
|
7334
|
+
name: step.attributeFilter.name,
|
|
7335
|
+
value: step.attributeFilter.value ?? "",
|
|
7336
|
+
...step.attributeFilter.notEqual ? { notEqual: true } : {}
|
|
6983
7337
|
}
|
|
6984
|
-
}
|
|
6985
|
-
|
|
7338
|
+
] : step.index !== void 0 ? [{ kind: "index", index: step.index }] : step.allIndices ? [{ kind: "all" }] : [];
|
|
7339
|
+
for (const pred of preds) {
|
|
7340
|
+
if (pred.kind === "index") {
|
|
7341
|
+
const total = matching.length;
|
|
7342
|
+
const idx = pred.index < 0 ? total + pred.index : pred.index;
|
|
7343
|
+
if (idx >= 0 && idx < total) {
|
|
7344
|
+
matching = [matching[idx]];
|
|
7345
|
+
} else {
|
|
7346
|
+
matching = [];
|
|
7347
|
+
}
|
|
7348
|
+
} else if (pred.kind === "all") {
|
|
6986
7349
|
hasFanned = true;
|
|
7350
|
+
} else {
|
|
7351
|
+
const total = matching.length;
|
|
7352
|
+
matching = matching.filter((node, idx) => evalPredicate(node, pred, idx, total));
|
|
6987
7353
|
}
|
|
6988
|
-
pushAll(nextCandidates, matching);
|
|
6989
7354
|
}
|
|
7355
|
+
if (matching.length > 1 || step.allIndices || step.attributeFilter !== void 0 || step.predicates && step.predicates.length > 0 && step.predicates[0]?.kind !== "index") {
|
|
7356
|
+
hasFanned = true;
|
|
7357
|
+
}
|
|
7358
|
+
pushAll(nextCandidates, matching);
|
|
6990
7359
|
}
|
|
6991
7360
|
currentCandidates = nextCandidates;
|
|
6992
7361
|
}
|
|
@@ -7220,6 +7589,11 @@ function runXmlQuery(opts) {
|
|
|
7220
7589
|
emitErr4(`Could not read: ${opts.file}`);
|
|
7221
7590
|
return 1;
|
|
7222
7591
|
}
|
|
7592
|
+
const queryPath = (opts.xpath ?? opts.path ?? "").trim();
|
|
7593
|
+
if (!queryPath) {
|
|
7594
|
+
emitErr4("Must provide a path or --xpath <expression>");
|
|
7595
|
+
return 1;
|
|
7596
|
+
}
|
|
7223
7597
|
let head;
|
|
7224
7598
|
try {
|
|
7225
7599
|
head = opts.head !== void 0 ? requireNonNegativeStrictInt("--head", opts.head) : void 0;
|
|
@@ -7228,7 +7602,7 @@ function runXmlQuery(opts) {
|
|
|
7228
7602
|
return 1;
|
|
7229
7603
|
}
|
|
7230
7604
|
try {
|
|
7231
|
-
const result = queryXml(text, opts.
|
|
7605
|
+
const result = queryXml(text, queryPath, { ...opts.xpath !== void 0 ? { xpath: opts.xpath } : {} });
|
|
7232
7606
|
const fullSourceBytes = sumFileSizes([opts.file]);
|
|
7233
7607
|
if (result.attributeValues !== void 0) {
|
|
7234
7608
|
if (result.attributeValues.length === 0) {
|
|
@@ -7237,27 +7611,86 @@ function runXmlQuery(opts) {
|
|
|
7237
7611
|
emit4(jsonText);
|
|
7238
7612
|
recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
|
|
7239
7613
|
} else {
|
|
7240
|
-
emit4(`No attributes matched path: '${displaySafeText(
|
|
7614
|
+
emit4(`No attributes matched path: '${displaySafeText(queryPath)}'`);
|
|
7241
7615
|
}
|
|
7242
7616
|
return 0;
|
|
7243
7617
|
}
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7618
|
+
const totalCount2 = result.attributeValues.length;
|
|
7619
|
+
const limitedNodes = result.attributeNodes !== void 0 ? head !== void 0 ? result.attributeNodes.slice(0, head) : result.attributeNodes : void 0;
|
|
7620
|
+
const limited2 = head !== void 0 ? result.attributeValues.slice(0, head) : result.attributeValues;
|
|
7621
|
+
const headTruncated2 = limited2.length < totalCount2;
|
|
7622
|
+
if (!result.fanned && totalCount2 === 1) {
|
|
7623
|
+
let val = limited2[0] ?? "";
|
|
7624
|
+
if (opts.decodeEmbeddedXml) {
|
|
7625
|
+
const decoded = tryDecodeEmbeddedXml(val);
|
|
7626
|
+
if (decoded.decoded) val = decoded.text;
|
|
7627
|
+
}
|
|
7628
|
+
let outText = "";
|
|
7629
|
+
if (opts.json === true) {
|
|
7630
|
+
if (opts.withLines && limitedNodes && limitedNodes[0]) {
|
|
7631
|
+
outText = displaySafeJson(
|
|
7632
|
+
{ value: val, lineStart: limitedNodes[0].node.line, lineEnd: limitedNodes[0].node.lineEnd },
|
|
7633
|
+
0
|
|
7634
|
+
);
|
|
7635
|
+
} else {
|
|
7636
|
+
outText = displaySafeJson(val, 0);
|
|
7637
|
+
}
|
|
7638
|
+
} else {
|
|
7639
|
+
if (opts.withLines && limitedNodes && limitedNodes[0]) {
|
|
7640
|
+
outText = `# Line: L${limitedNodes[0].node.line}
|
|
7641
|
+
${val}`;
|
|
7642
|
+
} else {
|
|
7643
|
+
outText = val;
|
|
7644
|
+
}
|
|
7645
|
+
}
|
|
7247
7646
|
emit4(outText);
|
|
7248
7647
|
recordReadStat("xml_query", fullSourceBytes, outText, opts.file);
|
|
7249
7648
|
return 0;
|
|
7250
7649
|
}
|
|
7251
|
-
const totalCount2 = result.attributeValues.length;
|
|
7252
|
-
const limited2 = head !== void 0 ? result.attributeValues.slice(0, head) : result.attributeValues;
|
|
7253
|
-
const headTruncated2 = limited2.length < totalCount2;
|
|
7254
7650
|
if (opts.json === true) {
|
|
7255
|
-
|
|
7256
|
-
|
|
7651
|
+
let itemsToSerialize = limited2;
|
|
7652
|
+
if (opts.withLines && limitedNodes) {
|
|
7653
|
+
itemsToSerialize = limitedNodes.map((an) => {
|
|
7654
|
+
let val = an.value;
|
|
7655
|
+
if (opts.decodeEmbeddedXml) {
|
|
7656
|
+
const decoded = tryDecodeEmbeddedXml(val);
|
|
7657
|
+
if (decoded.decoded) val = decoded.text;
|
|
7658
|
+
}
|
|
7659
|
+
return {
|
|
7660
|
+
value: val,
|
|
7661
|
+
lineStart: an.node.line,
|
|
7662
|
+
lineEnd: an.node.lineEnd
|
|
7663
|
+
};
|
|
7664
|
+
});
|
|
7665
|
+
} else if (opts.decodeEmbeddedXml) {
|
|
7666
|
+
itemsToSerialize = limited2.map((val) => {
|
|
7667
|
+
const decoded = tryDecodeEmbeddedXml(val);
|
|
7668
|
+
return decoded.decoded ? decoded.text : val;
|
|
7669
|
+
});
|
|
7670
|
+
}
|
|
7671
|
+
const capped = guardJsonRows(itemsToSerialize);
|
|
7672
|
+
const jsonText = displaySafeJson(
|
|
7673
|
+
{ items: capped.items, truncated: capped.truncated || headTruncated2, totalCount: totalCount2 },
|
|
7674
|
+
0
|
|
7675
|
+
);
|
|
7257
7676
|
emit4(jsonText);
|
|
7258
7677
|
recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
|
|
7259
7678
|
} else {
|
|
7260
|
-
const lines =
|
|
7679
|
+
const lines = [];
|
|
7680
|
+
for (let i = 0; i < limited2.length; i++) {
|
|
7681
|
+
let val = limited2[i] ?? "";
|
|
7682
|
+
if (opts.decodeEmbeddedXml) {
|
|
7683
|
+
const decoded = tryDecodeEmbeddedXml(val);
|
|
7684
|
+
if (decoded.decoded) val = decoded.text;
|
|
7685
|
+
}
|
|
7686
|
+
const nodeInfo = limitedNodes ? limitedNodes[i] : void 0;
|
|
7687
|
+
if (opts.withLines && nodeInfo) {
|
|
7688
|
+
lines.push(`# Line: L${nodeInfo.node.line}
|
|
7689
|
+
${val}`);
|
|
7690
|
+
} else {
|
|
7691
|
+
lines.push(val);
|
|
7692
|
+
}
|
|
7693
|
+
}
|
|
7261
7694
|
if (headTruncated2) {
|
|
7262
7695
|
lines.push(`...(${totalCount2 - limited2.length} more items elided; use --head to see more)`);
|
|
7263
7696
|
}
|
|
@@ -7273,19 +7706,30 @@ function runXmlQuery(opts) {
|
|
|
7273
7706
|
emit4(jsonText);
|
|
7274
7707
|
recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
|
|
7275
7708
|
} else {
|
|
7276
|
-
emit4(`No elements matched path: '${displaySafeText(
|
|
7709
|
+
emit4(`No elements matched path: '${displaySafeText(queryPath)}'`);
|
|
7277
7710
|
}
|
|
7278
7711
|
return 0;
|
|
7279
7712
|
}
|
|
7280
|
-
if (!result.fanned) {
|
|
7713
|
+
if (!result.fanned && result.items.length === 1) {
|
|
7281
7714
|
const node = result.items[0];
|
|
7282
7715
|
if (opts.json === true) {
|
|
7283
|
-
const jsonVal = xmlNodeToJson(node
|
|
7716
|
+
const jsonVal = xmlNodeToJson(node, {
|
|
7717
|
+
...opts.withLines ? { withLines: true } : {},
|
|
7718
|
+
...opts.decodeEmbeddedXml ? { decodeEmbedded: true } : {}
|
|
7719
|
+
});
|
|
7284
7720
|
const jsonText = displaySafeJson(jsonVal);
|
|
7285
7721
|
emit4(jsonText);
|
|
7286
7722
|
recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
|
|
7287
7723
|
} else {
|
|
7288
|
-
|
|
7724
|
+
let xmlText = serializeXmlNode(
|
|
7725
|
+
node,
|
|
7726
|
+
0,
|
|
7727
|
+
opts.decodeEmbeddedXml ? { decodeEmbedded: true } : {}
|
|
7728
|
+
);
|
|
7729
|
+
if (opts.withLines) {
|
|
7730
|
+
xmlText = `# Lines: L${node.line}-L${node.lineEnd}
|
|
7731
|
+
${xmlText}`;
|
|
7732
|
+
}
|
|
7289
7733
|
emitGuarded(xmlText, "xml-query");
|
|
7290
7734
|
recordReadStat("xml_query", fullSourceBytes, xmlText, opts.file);
|
|
7291
7735
|
}
|
|
@@ -7295,13 +7739,32 @@ function runXmlQuery(opts) {
|
|
|
7295
7739
|
const limited = head !== void 0 ? result.items.slice(0, head) : result.items;
|
|
7296
7740
|
const headTruncated = limited.length < totalCount;
|
|
7297
7741
|
if (opts.json === true) {
|
|
7298
|
-
const jsonItems = limited.map(
|
|
7742
|
+
const jsonItems = limited.map(
|
|
7743
|
+
(n) => xmlNodeToJson(n, {
|
|
7744
|
+
...opts.withLines ? { withLines: true } : {},
|
|
7745
|
+
...opts.decodeEmbeddedXml ? { decodeEmbedded: true } : {}
|
|
7746
|
+
})
|
|
7747
|
+
);
|
|
7299
7748
|
const capped = guardJsonRows(jsonItems);
|
|
7300
|
-
const jsonText = displaySafeJson(
|
|
7749
|
+
const jsonText = displaySafeJson(
|
|
7750
|
+
{ items: capped.items, truncated: capped.truncated || headTruncated, totalCount },
|
|
7751
|
+
0
|
|
7752
|
+
);
|
|
7301
7753
|
emit4(jsonText);
|
|
7302
7754
|
recordReadStat("xml_query", fullSourceBytes, jsonText, opts.file);
|
|
7303
7755
|
} else {
|
|
7304
|
-
const blocks = limited.map((node) =>
|
|
7756
|
+
const blocks = limited.map((node) => {
|
|
7757
|
+
let block = serializeXmlNode(
|
|
7758
|
+
node,
|
|
7759
|
+
0,
|
|
7760
|
+
opts.decodeEmbeddedXml ? { decodeEmbedded: true } : {}
|
|
7761
|
+
);
|
|
7762
|
+
if (opts.withLines) {
|
|
7763
|
+
block = `# Lines: L${node.line}-L${node.lineEnd}
|
|
7764
|
+
${block}`;
|
|
7765
|
+
}
|
|
7766
|
+
return block;
|
|
7767
|
+
});
|
|
7305
7768
|
if (headTruncated) {
|
|
7306
7769
|
blocks.push(`...(${totalCount - limited.length} more elements elided; use --head to see more)`);
|
|
7307
7770
|
}
|
|
@@ -8702,7 +9165,7 @@ function runLog(opts) {
|
|
|
8702
9165
|
|
|
8703
9166
|
// src/read_inspect.ts
|
|
8704
9167
|
init_define_import_meta_env();
|
|
8705
|
-
import * as
|
|
9168
|
+
import * as fs10 from "node:fs";
|
|
8706
9169
|
import * as path10 from "node:path";
|
|
8707
9170
|
|
|
8708
9171
|
// src/archive_query.ts
|
|
@@ -9127,317 +9590,10 @@ function isNoteStale(note, dbPath = globalDbPath()) {
|
|
|
9127
9590
|
return current === null || current !== note.fingerprint;
|
|
9128
9591
|
}
|
|
9129
9592
|
|
|
9130
|
-
// src/sqlite_query.ts
|
|
9131
|
-
init_define_import_meta_env();
|
|
9132
|
-
import * as fs10 from "node:fs";
|
|
9133
|
-
var SQLITE_QUERY_ROW_CAP = 5e3;
|
|
9134
|
-
var SQLITE_MAGIC = Buffer.from([83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0]);
|
|
9135
|
-
function readMagicBytes(filePath) {
|
|
9136
|
-
let fd;
|
|
9137
|
-
try {
|
|
9138
|
-
fd = fs10.openSync(filePath, "r");
|
|
9139
|
-
} catch {
|
|
9140
|
-
return null;
|
|
9141
|
-
}
|
|
9142
|
-
try {
|
|
9143
|
-
const buf = Buffer.alloc(SQLITE_MAGIC.length);
|
|
9144
|
-
const bytesRead = fs10.readSync(fd, buf, 0, buf.length, 0);
|
|
9145
|
-
return bytesRead === buf.length ? buf : null;
|
|
9146
|
-
} catch {
|
|
9147
|
-
return null;
|
|
9148
|
-
} finally {
|
|
9149
|
-
fs10.closeSync(fd);
|
|
9150
|
-
}
|
|
9151
|
-
}
|
|
9152
|
-
function isSqliteFile(filePath) {
|
|
9153
|
-
const magic = readMagicBytes(filePath);
|
|
9154
|
-
return magic !== null && magic.equals(SQLITE_MAGIC);
|
|
9155
|
-
}
|
|
9156
|
-
function openReadonlySqlite(filePath) {
|
|
9157
|
-
if (!fs10.existsSync(filePath)) {
|
|
9158
|
-
throw new Error(`file not found: ${displaySafeText(filePath)}`);
|
|
9159
|
-
}
|
|
9160
|
-
if (!isSqliteFile(filePath)) {
|
|
9161
|
-
throw new Error(`not a valid SQLite database (bad file header): ${displaySafeText(filePath)}`);
|
|
9162
|
-
}
|
|
9163
|
-
try {
|
|
9164
|
-
return new Database(filePath, { readonly: true, fileMustExist: true });
|
|
9165
|
-
} catch (e) {
|
|
9166
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
9167
|
-
throw new Error(`failed to open SQLite database: ${msg}`, { cause: e });
|
|
9168
|
-
}
|
|
9169
|
-
}
|
|
9170
|
-
function quoteIdent(name) {
|
|
9171
|
-
return `"${name.replace(/"/g, '""')}"`;
|
|
9172
|
-
}
|
|
9173
|
-
function getSqliteSchema(filePath) {
|
|
9174
|
-
const db = openReadonlySqlite(filePath);
|
|
9175
|
-
try {
|
|
9176
|
-
const objects = db.prepare("SELECT name, type FROM sqlite_master WHERE type IN ('table','view') AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\' ORDER BY name").all();
|
|
9177
|
-
const tables = objects.map((o) => {
|
|
9178
|
-
const columns = db.prepare(`PRAGMA table_info(${quoteIdent(o.name)})`).all().map(
|
|
9179
|
-
(c) => ({
|
|
9180
|
-
name: c.name,
|
|
9181
|
-
type: c.type || "",
|
|
9182
|
-
notNull: c.notnull === 1,
|
|
9183
|
-
primaryKey: c.pk > 0,
|
|
9184
|
-
defaultValue: c.dflt_value === null || c.dflt_value === void 0 ? null : String(c.dflt_value)
|
|
9185
|
-
})
|
|
9186
|
-
);
|
|
9187
|
-
const indexes = db.prepare(`PRAGMA index_list(${quoteIdent(o.name)})`).all().map(
|
|
9188
|
-
(idx) => {
|
|
9189
|
-
const idxColumns = db.prepare(`PRAGMA index_info(${quoteIdent(idx.name)})`).all().map((c) => c.name);
|
|
9190
|
-
return { name: idx.name, unique: idx.unique === 1, columns: idxColumns };
|
|
9191
|
-
}
|
|
9192
|
-
);
|
|
9193
|
-
const foreignKeys = db.prepare(`PRAGMA foreign_key_list(${quoteIdent(o.name)})`).all().map((fk) => ({ table: fk.table, from: fk.from, to: fk.to }));
|
|
9194
|
-
let rowCount;
|
|
9195
|
-
try {
|
|
9196
|
-
const row = db.prepare(`SELECT COUNT(*) AS c FROM ${quoteIdent(o.name)}`).get();
|
|
9197
|
-
rowCount = row.c;
|
|
9198
|
-
} catch {
|
|
9199
|
-
rowCount = null;
|
|
9200
|
-
}
|
|
9201
|
-
return { name: o.name, kind: o.type === "view" ? "view" : "table", columns, indexes, foreignKeys, rowCount };
|
|
9202
|
-
});
|
|
9203
|
-
return { tables };
|
|
9204
|
-
} finally {
|
|
9205
|
-
db.close();
|
|
9206
|
-
}
|
|
9207
|
-
}
|
|
9208
|
-
function formatSqliteSchema(result) {
|
|
9209
|
-
if (result.tables.length === 0) return "(no tables or views found)";
|
|
9210
|
-
return result.tables.map((t) => {
|
|
9211
|
-
const lines = [`${t.name} (${t.kind}${t.rowCount !== null ? `, ${t.rowCount} row${t.rowCount === 1 ? "" : "s"}` : ""})`];
|
|
9212
|
-
for (const c of t.columns) {
|
|
9213
|
-
const flags = [c.primaryKey ? "PK" : null, c.notNull ? "NOT NULL" : null, c.defaultValue !== null ? `DEFAULT ${c.defaultValue}` : null].filter((f) => f !== null).join(" ");
|
|
9214
|
-
lines.push(` ${displaySafeText(c.name)} ${displaySafeText(c.type || "(untyped)")}${flags ? ` ${flags}` : ""}`);
|
|
9215
|
-
}
|
|
9216
|
-
if (t.indexes.length > 0) {
|
|
9217
|
-
lines.push(" indexes:");
|
|
9218
|
-
for (const idx of t.indexes) {
|
|
9219
|
-
lines.push(` ${displaySafeText(idx.name)}${idx.unique ? " (unique)" : ""}: ${displaySafeText(idx.columns.join(", "))}`);
|
|
9220
|
-
}
|
|
9221
|
-
}
|
|
9222
|
-
if (t.foreignKeys.length > 0) {
|
|
9223
|
-
lines.push(" foreign keys:");
|
|
9224
|
-
for (const fk of t.foreignKeys) {
|
|
9225
|
-
lines.push(` ${fk.from} -> ${fk.table}.${fk.to}`);
|
|
9226
|
-
}
|
|
9227
|
-
}
|
|
9228
|
-
return lines.join("\n");
|
|
9229
|
-
}).join("\n\n");
|
|
9230
|
-
}
|
|
9231
|
-
function getSqliteTables(filePath) {
|
|
9232
|
-
const db = openReadonlySqlite(filePath);
|
|
9233
|
-
try {
|
|
9234
|
-
const objects = db.prepare(
|
|
9235
|
-
// ESCAPE makes every `_` literal; see getSqliteSchema above. Here it costs three tables rather than one, since `%_fts_%` and `%_vec0_%` exist to hide the shadow tables FTS5 and sqlite-vec create beside a virtual table, and unescaped they also hide anything of the shape `my_ftsx_cache`.
|
|
9236
|
-
"SELECT name, type FROM sqlite_master WHERE type IN ('table','view') AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\' AND name NOT LIKE '%\\_fts\\_%' ESCAPE '\\' AND name NOT LIKE '%\\_vec0\\_%' ESCAPE '\\' ORDER BY name"
|
|
9237
|
-
).all();
|
|
9238
|
-
return objects.map((o) => {
|
|
9239
|
-
let columnCount;
|
|
9240
|
-
try {
|
|
9241
|
-
const cols = db.prepare(`PRAGMA table_info(${quoteIdent(o.name)})`).all();
|
|
9242
|
-
columnCount = cols.length;
|
|
9243
|
-
} catch {
|
|
9244
|
-
columnCount = 0;
|
|
9245
|
-
}
|
|
9246
|
-
let rowCount;
|
|
9247
|
-
try {
|
|
9248
|
-
const row = db.prepare(`SELECT COUNT(*) AS c FROM ${quoteIdent(o.name)}`).get();
|
|
9249
|
-
rowCount = row.c;
|
|
9250
|
-
} catch {
|
|
9251
|
-
rowCount = null;
|
|
9252
|
-
}
|
|
9253
|
-
return {
|
|
9254
|
-
name: o.name,
|
|
9255
|
-
kind: o.type === "view" ? "view" : "table",
|
|
9256
|
-
rowCount,
|
|
9257
|
-
columnCount
|
|
9258
|
-
};
|
|
9259
|
-
});
|
|
9260
|
-
} finally {
|
|
9261
|
-
db.close();
|
|
9262
|
-
}
|
|
9263
|
-
}
|
|
9264
|
-
function formatSqliteTables(tables) {
|
|
9265
|
-
if (tables.length === 0) return "(no tables or views found)";
|
|
9266
|
-
return tables.map((t) => {
|
|
9267
|
-
const parts = [t.kind];
|
|
9268
|
-
if (t.rowCount !== null) {
|
|
9269
|
-
parts.push(`${t.rowCount.toLocaleString()} row${t.rowCount === 1 ? "" : "s"}`);
|
|
9270
|
-
}
|
|
9271
|
-
parts.push(`${t.columnCount} col${t.columnCount === 1 ? "" : "s"}`);
|
|
9272
|
-
return `${displaySafeText(t.name)} (${parts.join(", ")})`;
|
|
9273
|
-
}).join("\n");
|
|
9274
|
-
}
|
|
9275
|
-
var FORBIDDEN_KEYWORDS = [
|
|
9276
|
-
"INSERT",
|
|
9277
|
-
"UPDATE",
|
|
9278
|
-
"DELETE",
|
|
9279
|
-
"DROP",
|
|
9280
|
-
"ALTER",
|
|
9281
|
-
"CREATE",
|
|
9282
|
-
"ATTACH",
|
|
9283
|
-
"DETACH",
|
|
9284
|
-
"TRUNCATE",
|
|
9285
|
-
"VACUUM",
|
|
9286
|
-
"REINDEX",
|
|
9287
|
-
"PRAGMA",
|
|
9288
|
-
"BEGIN",
|
|
9289
|
-
"COMMIT",
|
|
9290
|
-
"ROLLBACK",
|
|
9291
|
-
"SAVEPOINT",
|
|
9292
|
-
"RELEASE",
|
|
9293
|
-
"GRANT",
|
|
9294
|
-
"REVOKE"
|
|
9295
|
-
];
|
|
9296
|
-
function stripSqlLiteralsAndComments(sql) {
|
|
9297
|
-
let out = "";
|
|
9298
|
-
let i = 0;
|
|
9299
|
-
const n = sql.length;
|
|
9300
|
-
while (i < n) {
|
|
9301
|
-
const c = sql[i];
|
|
9302
|
-
if (c === "'" || c === '"' || c === "`") {
|
|
9303
|
-
const quote = c;
|
|
9304
|
-
out += " ";
|
|
9305
|
-
i++;
|
|
9306
|
-
while (i < n) {
|
|
9307
|
-
if (sql[i] === quote) {
|
|
9308
|
-
if (sql[i + 1] === quote) {
|
|
9309
|
-
i += 2;
|
|
9310
|
-
continue;
|
|
9311
|
-
}
|
|
9312
|
-
i++;
|
|
9313
|
-
break;
|
|
9314
|
-
}
|
|
9315
|
-
i++;
|
|
9316
|
-
}
|
|
9317
|
-
continue;
|
|
9318
|
-
}
|
|
9319
|
-
if (c === "[") {
|
|
9320
|
-
out += " ";
|
|
9321
|
-
i++;
|
|
9322
|
-
while (i < n && sql[i] !== "]") i++;
|
|
9323
|
-
if (i < n) i++;
|
|
9324
|
-
continue;
|
|
9325
|
-
}
|
|
9326
|
-
if (c === "-" && sql[i + 1] === "-") {
|
|
9327
|
-
while (i < n && sql[i] !== "\n") i++;
|
|
9328
|
-
continue;
|
|
9329
|
-
}
|
|
9330
|
-
if (c === "/" && sql[i + 1] === "*") {
|
|
9331
|
-
i += 2;
|
|
9332
|
-
while (i < n && !(sql[i] === "*" && sql[i + 1] === "/")) i++;
|
|
9333
|
-
i += 2;
|
|
9334
|
-
continue;
|
|
9335
|
-
}
|
|
9336
|
-
out += c;
|
|
9337
|
-
i++;
|
|
9338
|
-
}
|
|
9339
|
-
return out;
|
|
9340
|
-
}
|
|
9341
|
-
function validateReadOnlySelect(sql) {
|
|
9342
|
-
const stripped = stripSqlLiteralsAndComments(sql);
|
|
9343
|
-
const trimmed = stripped.trim();
|
|
9344
|
-
if (trimmed === "") {
|
|
9345
|
-
throw new Error("empty query");
|
|
9346
|
-
}
|
|
9347
|
-
const withoutTrailingSemicolons = trimmed.replace(/;+\s*$/, "");
|
|
9348
|
-
if (withoutTrailingSemicolons.includes(";")) {
|
|
9349
|
-
throw new Error("multiple statements are not allowed (sqlite-query accepts exactly one read-only SELECT statement)");
|
|
9350
|
-
}
|
|
9351
|
-
const upper = withoutTrailingSemicolons.toUpperCase();
|
|
9352
|
-
if (!/^(SELECT|WITH)\b/.test(upper.trimStart())) {
|
|
9353
|
-
throw new Error("only SELECT statements are allowed (sqlite-query is read-only)");
|
|
9354
|
-
}
|
|
9355
|
-
for (const kw of FORBIDDEN_KEYWORDS) {
|
|
9356
|
-
if (new RegExp(`\\b${kw}\\b`).test(upper)) {
|
|
9357
|
-
throw new Error(`statement contains forbidden keyword '${kw}' (sqlite-query only allows read-only SELECT queries)`);
|
|
9358
|
-
}
|
|
9359
|
-
}
|
|
9360
|
-
if (/\bREPLACE\b(?!\s*\()/.test(upper)) {
|
|
9361
|
-
throw new Error("statement contains forbidden keyword 'REPLACE' (sqlite-query only allows read-only SELECT queries)");
|
|
9362
|
-
}
|
|
9363
|
-
}
|
|
9364
|
-
function normalizeSqliteScalar(value) {
|
|
9365
|
-
if (typeof value === "bigint") {
|
|
9366
|
-
return Number.isSafeInteger(Number(value)) ? Number(value) : value.toString();
|
|
9367
|
-
}
|
|
9368
|
-
return value;
|
|
9369
|
-
}
|
|
9370
|
-
function normalizeSqliteRow(row) {
|
|
9371
|
-
const out = {};
|
|
9372
|
-
for (const [k, v] of Object.entries(row)) out[k] = normalizeSqliteScalar(v);
|
|
9373
|
-
return out;
|
|
9374
|
-
}
|
|
9375
|
-
function runReadOnlySqliteQuery(filePath, sql, opts = {}) {
|
|
9376
|
-
validateReadOnlySelect(sql);
|
|
9377
|
-
const rowCap = opts.rowCap ?? SQLITE_QUERY_ROW_CAP;
|
|
9378
|
-
const db = openReadonlySqlite(filePath);
|
|
9379
|
-
try {
|
|
9380
|
-
let stmt;
|
|
9381
|
-
try {
|
|
9382
|
-
stmt = db.prepare(sql);
|
|
9383
|
-
} catch (e) {
|
|
9384
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
9385
|
-
throw new Error(`invalid SQL: ${msg}`, { cause: e });
|
|
9386
|
-
}
|
|
9387
|
-
if (!stmt.reader) {
|
|
9388
|
-
throw new Error("only read-only SELECT statements are allowed (sqlite-query is read-only)");
|
|
9389
|
-
}
|
|
9390
|
-
stmt.safeIntegers(true);
|
|
9391
|
-
let columns = [];
|
|
9392
|
-
try {
|
|
9393
|
-
columns = stmt.columns().map((c) => c.name);
|
|
9394
|
-
} catch {
|
|
9395
|
-
columns = [];
|
|
9396
|
-
}
|
|
9397
|
-
const rows = [];
|
|
9398
|
-
let rowCapped = false;
|
|
9399
|
-
for (const row of stmt.iterate()) {
|
|
9400
|
-
if (rows.length >= rowCap) {
|
|
9401
|
-
rowCapped = true;
|
|
9402
|
-
break;
|
|
9403
|
-
}
|
|
9404
|
-
rows.push(normalizeSqliteRow(row));
|
|
9405
|
-
}
|
|
9406
|
-
return { columns, rows, rowCapped };
|
|
9407
|
-
} finally {
|
|
9408
|
-
db.close();
|
|
9409
|
-
}
|
|
9410
|
-
}
|
|
9411
|
-
function formatSqliteCell(value) {
|
|
9412
|
-
if (value === null || value === void 0) return "";
|
|
9413
|
-
if (value instanceof Uint8Array) return `<blob ${value.length} bytes>`;
|
|
9414
|
-
return String(value);
|
|
9415
|
-
}
|
|
9416
|
-
function formatSqliteQueryTable(result, opts = {}) {
|
|
9417
|
-
if (result.columns.length === 0 && result.rows.length === 0) return "(no rows)";
|
|
9418
|
-
const lines = [
|
|
9419
|
-
result.columns.map(quoteCsvCellLocal).join(","),
|
|
9420
|
-
...result.rows.map((r) => result.columns.map((c) => quoteCsvCellLocal(formatSqliteCell(r[c]))).join(","))
|
|
9421
|
-
];
|
|
9422
|
-
if (opts.headTruncated === true) {
|
|
9423
|
-
lines.push("...(more rows elided; use --head to see more)");
|
|
9424
|
-
}
|
|
9425
|
-
if (result.rowCapped) {
|
|
9426
|
-
lines.push(`...(result truncated at the ${SQLITE_QUERY_ROW_CAP}-row safety cap; narrow the query with WHERE/LIMIT for a complete result)`);
|
|
9427
|
-
}
|
|
9428
|
-
return lines.join("\n");
|
|
9429
|
-
}
|
|
9430
|
-
function quoteCsvCellLocal(cell) {
|
|
9431
|
-
if (cell.includes(",") || cell.includes('"') || cell.includes("\n") || cell.includes("\r")) {
|
|
9432
|
-
return `"${cell.replace(/"/g, '""')}"`;
|
|
9433
|
-
}
|
|
9434
|
-
return cell;
|
|
9435
|
-
}
|
|
9436
|
-
|
|
9437
9593
|
// src/read_inspect.ts
|
|
9438
9594
|
function fileExists(p) {
|
|
9439
9595
|
try {
|
|
9440
|
-
|
|
9596
|
+
fs10.statSync(p);
|
|
9441
9597
|
return true;
|
|
9442
9598
|
} catch {
|
|
9443
9599
|
return false;
|
|
@@ -10082,7 +10238,7 @@ function runListSections(opts) {
|
|
|
10082
10238
|
|
|
10083
10239
|
// src/read_section.ts
|
|
10084
10240
|
init_define_import_meta_env();
|
|
10085
|
-
import * as
|
|
10241
|
+
import * as fs11 from "node:fs";
|
|
10086
10242
|
import * as path11 from "node:path";
|
|
10087
10243
|
var AMBIGUOUS_HEADING_LIMIT = 10;
|
|
10088
10244
|
function literalHeadingExists(filePath, heading) {
|
|
@@ -10107,7 +10263,7 @@ function runSection(opts) {
|
|
|
10107
10263
|
}
|
|
10108
10264
|
const result = readSection(filePath, heading, readFileText);
|
|
10109
10265
|
if (result === null) {
|
|
10110
|
-
if (!
|
|
10266
|
+
if (!fs11.existsSync(filePath)) {
|
|
10111
10267
|
return { text: `File not found: '${filePath}'`, code: 1 };
|
|
10112
10268
|
}
|
|
10113
10269
|
const ordSpec = /^(.*?)#(\d+)$/.exec(heading);
|
|
@@ -10162,7 +10318,39 @@ function runSectionMulti(specFilePath, resolvedFilePath, headings, opts) {
|
|
|
10162
10318
|
let anyFound = false;
|
|
10163
10319
|
const jsonOut = {};
|
|
10164
10320
|
const textBlocks = [];
|
|
10321
|
+
const includedSections = [];
|
|
10165
10322
|
for (const heading of headings) {
|
|
10323
|
+
const sectionResult = readSection(resolvedFilePath, heading, readFileText);
|
|
10324
|
+
if (sectionResult !== null) {
|
|
10325
|
+
const parent = includedSections.find(
|
|
10326
|
+
(p) => sectionResult.lineStart >= p.lineStart && sectionResult.lineEnd <= p.lineEnd
|
|
10327
|
+
);
|
|
10328
|
+
if (parent !== void 0) {
|
|
10329
|
+
anyFound = true;
|
|
10330
|
+
const notice = `(already included in section '${parent.heading}', lines ${sectionResult.lineStart}-${sectionResult.lineEnd})`;
|
|
10331
|
+
if (opts.json === true) {
|
|
10332
|
+
jsonOut[heading] = {
|
|
10333
|
+
heading: sectionResult.heading,
|
|
10334
|
+
subsumedBy: parent.heading,
|
|
10335
|
+
lineStart: sectionResult.lineStart,
|
|
10336
|
+
lineEnd: sectionResult.lineEnd,
|
|
10337
|
+
notice
|
|
10338
|
+
};
|
|
10339
|
+
continue;
|
|
10340
|
+
}
|
|
10341
|
+
textBlocks.push(
|
|
10342
|
+
`${heading}:
|
|
10343
|
+
# ${sectionResult.heading} \u2014 ${specFilePath}:${sectionResult.lineStart}-${sectionResult.lineEnd}
|
|
10344
|
+
${notice}`
|
|
10345
|
+
);
|
|
10346
|
+
continue;
|
|
10347
|
+
}
|
|
10348
|
+
includedSections.push({
|
|
10349
|
+
heading: sectionResult.heading,
|
|
10350
|
+
lineStart: sectionResult.lineStart,
|
|
10351
|
+
lineEnd: sectionResult.lineEnd
|
|
10352
|
+
});
|
|
10353
|
+
}
|
|
10166
10354
|
const sub = runSection({ ...opts, spec: `${specFilePath}::${heading}`, suppressStat: true });
|
|
10167
10355
|
if (sub.code === 0) anyFound = true;
|
|
10168
10356
|
if (opts.json === true) {
|
|
@@ -10181,12 +10369,48 @@ function runSectionCrossFile(pairs, opts) {
|
|
|
10181
10369
|
let anyFound = false;
|
|
10182
10370
|
const jsonOut = {};
|
|
10183
10371
|
const textBlocks = [];
|
|
10372
|
+
const includedByFile = /* @__PURE__ */ new Map();
|
|
10184
10373
|
const distinctFiles = new Set(pairs.map((p) => p.file));
|
|
10185
10374
|
const keyFor = (p) => distinctFiles.size === 1 ? p.symbol : `${p.file}::${p.symbol}`;
|
|
10375
|
+
const resolvePath = (f) => opts.projectRoot !== void 0 && !path11.isAbsolute(f) ? path11.resolve(opts.projectRoot, f) : f;
|
|
10186
10376
|
for (const { file, symbol: heading } of pairs) {
|
|
10377
|
+
const resolved = resolvePath(file);
|
|
10378
|
+
const sectionResult = readSection(resolved, heading, readFileText);
|
|
10379
|
+
const key = keyFor({ file, symbol: heading });
|
|
10380
|
+
const fileIncluded = includedByFile.get(resolved) ?? [];
|
|
10381
|
+
if (sectionResult !== null) {
|
|
10382
|
+
const parent = fileIncluded.find(
|
|
10383
|
+
(p) => sectionResult.lineStart >= p.lineStart && sectionResult.lineEnd <= p.lineEnd
|
|
10384
|
+
);
|
|
10385
|
+
if (parent !== void 0) {
|
|
10386
|
+
anyFound = true;
|
|
10387
|
+
const notice = `(already included in section '${parent.heading}', lines ${sectionResult.lineStart}-${sectionResult.lineEnd})`;
|
|
10388
|
+
if (opts.json === true) {
|
|
10389
|
+
jsonOut[key] = {
|
|
10390
|
+
heading: sectionResult.heading,
|
|
10391
|
+
subsumedBy: parent.heading,
|
|
10392
|
+
lineStart: sectionResult.lineStart,
|
|
10393
|
+
lineEnd: sectionResult.lineEnd,
|
|
10394
|
+
notice
|
|
10395
|
+
};
|
|
10396
|
+
continue;
|
|
10397
|
+
}
|
|
10398
|
+
textBlocks.push(
|
|
10399
|
+
`${key}:
|
|
10400
|
+
# ${sectionResult.heading} \u2014 ${file}:${sectionResult.lineStart}-${sectionResult.lineEnd}
|
|
10401
|
+
${notice}`
|
|
10402
|
+
);
|
|
10403
|
+
continue;
|
|
10404
|
+
}
|
|
10405
|
+
fileIncluded.push({
|
|
10406
|
+
heading: sectionResult.heading,
|
|
10407
|
+
lineStart: sectionResult.lineStart,
|
|
10408
|
+
lineEnd: sectionResult.lineEnd
|
|
10409
|
+
});
|
|
10410
|
+
includedByFile.set(resolved, fileIncluded);
|
|
10411
|
+
}
|
|
10187
10412
|
const sub = runSection({ ...opts, spec: `${file}::${heading}`, suppressStat: true });
|
|
10188
10413
|
if (sub.code === 0) anyFound = true;
|
|
10189
|
-
const key = keyFor({ file, symbol: heading });
|
|
10190
10414
|
if (opts.json === true) {
|
|
10191
10415
|
jsonOut[key] = sub.code === 0 ? JSON.parse(sub.text) : { error: sub.text };
|
|
10192
10416
|
continue;
|
|
@@ -10194,7 +10418,6 @@ function runSectionCrossFile(pairs, opts) {
|
|
|
10194
10418
|
textBlocks.push(`${key}:
|
|
10195
10419
|
${sub.text}`);
|
|
10196
10420
|
}
|
|
10197
|
-
const resolvePath = (f) => opts.projectRoot !== void 0 && !path11.isAbsolute(f) ? path11.resolve(opts.projectRoot, f) : f;
|
|
10198
10421
|
const text = opts.json === true ? displaySafeJson(jsonOut) : textBlocks.join("\n\n");
|
|
10199
10422
|
if (anyFound) {
|
|
10200
10423
|
const fullSourceBytes = sumFileSizes(Array.from(distinctFiles, resolvePath));
|
|
@@ -10210,7 +10433,7 @@ var FIND_SCAN_LIMIT2 = 2e4;
|
|
|
10210
10433
|
var REFS_TOP_SCAN_LIMIT = 2e4;
|
|
10211
10434
|
function fileExists2(p) {
|
|
10212
10435
|
try {
|
|
10213
|
-
|
|
10436
|
+
fs12.statSync(p);
|
|
10214
10437
|
return true;
|
|
10215
10438
|
} catch {
|
|
10216
10439
|
return false;
|
|
@@ -10241,30 +10464,30 @@ function withPinnedReads(pins, fn) {
|
|
|
10241
10464
|
}
|
|
10242
10465
|
}
|
|
10243
10466
|
function readPinnedBytes(p, pinned) {
|
|
10244
|
-
const fd =
|
|
10467
|
+
const fd = fs12.openSync(p, fs12.constants.O_RDONLY);
|
|
10245
10468
|
try {
|
|
10246
|
-
const actual = fileIdentity(
|
|
10469
|
+
const actual = fileIdentity(fs12.fstatSync(fd, { bigint: true }));
|
|
10247
10470
|
if (actual !== pinned) {
|
|
10248
10471
|
throw new ConfinementIdentityError(
|
|
10249
10472
|
`refused: "${p}" changed identity between validation and read (validated ${pinned}, opened ${actual}). The file was replaced or redirected after the confinement check, so the read was not performed.`
|
|
10250
10473
|
);
|
|
10251
10474
|
}
|
|
10252
|
-
return
|
|
10475
|
+
return fs12.readFileSync(fd);
|
|
10253
10476
|
} finally {
|
|
10254
|
-
|
|
10477
|
+
fs12.closeSync(fd);
|
|
10255
10478
|
}
|
|
10256
10479
|
}
|
|
10257
10480
|
function verifyPinnedIdentity(p, pinned) {
|
|
10258
|
-
const fd =
|
|
10481
|
+
const fd = fs12.openSync(p, fs12.constants.O_RDONLY);
|
|
10259
10482
|
try {
|
|
10260
|
-
const actual = fileIdentity(
|
|
10483
|
+
const actual = fileIdentity(fs12.fstatSync(fd, { bigint: true }));
|
|
10261
10484
|
if (actual !== pinned) {
|
|
10262
10485
|
throw new ConfinementIdentityError(
|
|
10263
10486
|
`refused: "${p}" changed identity between validation and read (validated ${pinned}, opened ${actual}). The file was replaced or redirected after the confinement check, so the read was not performed.`
|
|
10264
10487
|
);
|
|
10265
10488
|
}
|
|
10266
10489
|
} finally {
|
|
10267
|
-
|
|
10490
|
+
fs12.closeSync(fd);
|
|
10268
10491
|
}
|
|
10269
10492
|
}
|
|
10270
10493
|
function verifyStillAbsent(p) {
|
|
@@ -10311,7 +10534,7 @@ function readFileText(p) {
|
|
|
10311
10534
|
return null;
|
|
10312
10535
|
}
|
|
10313
10536
|
if (pinned !== void 0) return redactIfDotenv(p, decodeSource(readPinnedBytes(p, pinned)));
|
|
10314
|
-
return redactIfDotenv(p, decodeSource(
|
|
10537
|
+
return redactIfDotenv(p, decodeSource(fs12.readFileSync(p)));
|
|
10315
10538
|
} catch (err) {
|
|
10316
10539
|
if (err instanceof ConfinementIdentityError) throw err;
|
|
10317
10540
|
return null;
|
|
@@ -10329,9 +10552,9 @@ function readFileBytes(p) {
|
|
|
10329
10552
|
if (bytes.length > MAX_ZIP_INPUT_BYTES) throw new ZipInputTooLargeError(p, bytes.length, MAX_ZIP_INPUT_BYTES);
|
|
10330
10553
|
return bytes;
|
|
10331
10554
|
}
|
|
10332
|
-
const stat =
|
|
10555
|
+
const stat = fs12.statSync(p);
|
|
10333
10556
|
if (stat.size > MAX_ZIP_INPUT_BYTES) throw new ZipInputTooLargeError(p, stat.size, MAX_ZIP_INPUT_BYTES);
|
|
10334
|
-
return
|
|
10557
|
+
return fs12.readFileSync(p);
|
|
10335
10558
|
} catch (err) {
|
|
10336
10559
|
if (err instanceof ConfinementIdentityError || err instanceof ZipInputTooLargeError) throw err;
|
|
10337
10560
|
return null;
|
|
@@ -10349,6 +10572,8 @@ function resolveBody(entry) {
|
|
|
10349
10572
|
var STALE_WARNING = "\u26A0 STALE: index is older than the file on disk (worker hasn't reindexed yet \u2014 retry shortly, or read the file directly)";
|
|
10350
10573
|
var DELETED_WARNING = "\u26A0 DELETED: this file is no longer on disk \u2014 what follows is what the index last saw of it";
|
|
10351
10574
|
var DELETED_TAG = "\u26A0 DELETED: file no longer on disk";
|
|
10575
|
+
var STALE_TAG = "\u26A0 STALE: file changed on disk and could not be reindexed";
|
|
10576
|
+
var EMPTY_PATH_SET = /* @__PURE__ */ new Set();
|
|
10352
10577
|
function fileIsGone(absPath) {
|
|
10353
10578
|
if (!path12.isAbsolute(absPath)) return false;
|
|
10354
10579
|
return fileIsAbsent(absPath);
|
|
@@ -10388,6 +10613,21 @@ function healStaleIndex(resolvedPath) {
|
|
|
10388
10613
|
}
|
|
10389
10614
|
}
|
|
10390
10615
|
var STALE_CHECK_FILE_CAP = 25;
|
|
10616
|
+
function healStaleResultFiles(filePaths) {
|
|
10617
|
+
const checked = /* @__PURE__ */ new Set();
|
|
10618
|
+
const stillStale = /* @__PURE__ */ new Set();
|
|
10619
|
+
let healed = false;
|
|
10620
|
+
for (const raw of filePaths) {
|
|
10621
|
+
if (checked.size >= STALE_CHECK_FILE_CAP) break;
|
|
10622
|
+
if (checked.has(raw) || fileIsGone(raw)) continue;
|
|
10623
|
+
checked.add(raw);
|
|
10624
|
+
if (staleWarning(raw) === "") continue;
|
|
10625
|
+
healStaleIndex(raw);
|
|
10626
|
+
if (staleWarning(raw) === "") healed = true;
|
|
10627
|
+
else stillStale.add(raw);
|
|
10628
|
+
}
|
|
10629
|
+
return { healed, stillStale };
|
|
10630
|
+
}
|
|
10391
10631
|
function warnIfFilesStale(filePaths) {
|
|
10392
10632
|
const checked = /* @__PURE__ */ new Set();
|
|
10393
10633
|
let staleCount = 0;
|
|
@@ -10436,7 +10676,7 @@ function sumFileSizes(filePaths) {
|
|
|
10436
10676
|
let total = 0;
|
|
10437
10677
|
for (const fp of new Set(filePaths)) {
|
|
10438
10678
|
try {
|
|
10439
|
-
total += Math.min(
|
|
10679
|
+
total += Math.min(fs12.statSync(fp).size, PER_FILE_COUNTERFACTUAL_CEILING);
|
|
10440
10680
|
} catch {
|
|
10441
10681
|
}
|
|
10442
10682
|
}
|
|
@@ -10507,7 +10747,13 @@ function runSymbol(opts) {
|
|
|
10507
10747
|
}
|
|
10508
10748
|
if (opts.file === void 0 && opts.projectRoot !== void 0) queryOpts.rootDir = opts.projectRoot;
|
|
10509
10749
|
if (opts.file === void 0 && queryOpts.rootDir === void 0 && confinedRoot !== null) queryOpts.rootDir = confinedRoot;
|
|
10510
|
-
|
|
10750
|
+
let rawResults = querySymbols(queryOpts);
|
|
10751
|
+
let stillStale = EMPTY_PATH_SET;
|
|
10752
|
+
if (opts.file === void 0) {
|
|
10753
|
+
const heal = healStaleResultFiles(rawResults.map((s) => s.filePath));
|
|
10754
|
+
stillStale = heal.stillStale;
|
|
10755
|
+
if (heal.healed) rawResults = querySymbols(queryOpts);
|
|
10756
|
+
}
|
|
10511
10757
|
const preFilterCount = rawResults.length;
|
|
10512
10758
|
const effectiveLimit = opts.limit ?? 100;
|
|
10513
10759
|
const anyClientFilter = matchesGrep !== void 0 || excludeTests;
|
|
@@ -10589,6 +10835,7 @@ ${emptyIndexMessage(emptyIndexRoot)}`;
|
|
|
10589
10835
|
// Only present when true, so a result set of live files stays byte-identical to what this
|
|
10590
10836
|
// command has always emitted and only the genuinely-gone rows grow a field.
|
|
10591
10837
|
...fileIsGone(s.filePath) ? { deleted: true } : {},
|
|
10838
|
+
...stillStale.has(s.filePath) ? { stale: true } : {},
|
|
10592
10839
|
...refCounts !== void 0 ? { refCount: refCounts.get(s.name) ?? 0, hasDoc: hasRealDocstring(s.docstring) } : {}
|
|
10593
10840
|
}));
|
|
10594
10841
|
const payload = { items, truncated: truncatedFlag, totalCount: trueTotal };
|
|
@@ -10599,7 +10846,8 @@ ${emptyIndexMessage(emptyIndexRoot)}`;
|
|
|
10599
10846
|
const blocks = results.map((sym) => {
|
|
10600
10847
|
const statsStr = formatStatsSuffix(refCounts, sym);
|
|
10601
10848
|
const goneTag = fileIsGone(sym.filePath) ? ` ${DELETED_TAG}` : "";
|
|
10602
|
-
const
|
|
10849
|
+
const staleTag = stillStale.has(sym.filePath) ? ` ${STALE_TAG}` : "";
|
|
10850
|
+
const header = `# ${sym.name} (${sym.kind}) \u2014 ${formatSymbolLocation(toDisplayPath(symbolDisplayRoot, sym.filePath), sym.lineStart, sym.lineEnd)}${statsStr}${goneTag}${staleTag}`;
|
|
10603
10851
|
const body = resolveBody(sym);
|
|
10604
10852
|
const bodyLines = body.split(/\r?\n/);
|
|
10605
10853
|
const preview = bodyLines.slice(0, SYMBOL_PREVIEW_LINES).join("\n");
|
|
@@ -10676,10 +10924,42 @@ function runRead(opts) {
|
|
|
10676
10924
|
const scanned = querySymbols({ filePath: resolved, limit: FIND_SCAN_LIMIT2 }).map((s) => s.name);
|
|
10677
10925
|
const closes = rankSimilarNames(scanned, symbol);
|
|
10678
10926
|
if (closes.length > 0) messages.push(didYouMean(closes));
|
|
10679
|
-
else if (scanned.length > 0)
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10927
|
+
else if (scanned.length > 0) {
|
|
10928
|
+
if (/\.(yaml|yml)$/i.test(file)) {
|
|
10929
|
+
messages.push(`Try: token-goat yaml-outline ${file}
|
|
10930
|
+
Query subtree: token-goat yaml-query ${file} '<path>'`);
|
|
10931
|
+
} else if (/\.xml$/i.test(file)) {
|
|
10932
|
+
messages.push(`Try: token-goat xml-outline ${file}
|
|
10933
|
+
Query subtree: token-goat xml-query ${file} '<path>'`);
|
|
10934
|
+
} else if (/\.json$/i.test(file)) {
|
|
10935
|
+
messages.push(`Try: token-goat json-outline ${file}
|
|
10936
|
+
Query subtree: token-goat json-query ${file} '<path>'`);
|
|
10937
|
+
} else {
|
|
10938
|
+
messages.push(`Try: token-goat outline ${file}`);
|
|
10939
|
+
}
|
|
10940
|
+
} else if (fs12.existsSync(resolved)) {
|
|
10941
|
+
if (/\.(yaml|yml)$/i.test(file)) {
|
|
10942
|
+
messages.push(
|
|
10943
|
+
`'${file}' is a YAML file -- YAML keys below top level are not symbols; inspect structure or query values with:
|
|
10944
|
+
token-goat yaml-outline ${file}
|
|
10945
|
+
token-goat yaml-query ${file} '<path>'`
|
|
10946
|
+
);
|
|
10947
|
+
} else if (/\.xml$/i.test(file)) {
|
|
10948
|
+
messages.push(
|
|
10949
|
+
`'${file}' is an XML file -- inspect structure or query nodes with:
|
|
10950
|
+
token-goat xml-outline ${file}
|
|
10951
|
+
token-goat xml-query ${file} '<path>'`
|
|
10952
|
+
);
|
|
10953
|
+
} else if (/\.json$/i.test(file)) {
|
|
10954
|
+
messages.push(
|
|
10955
|
+
`'${file}' is a JSON file -- inspect structure or query values with:
|
|
10956
|
+
token-goat json-outline ${file}
|
|
10957
|
+
token-goat json-query ${file} '<path>'`
|
|
10958
|
+
);
|
|
10959
|
+
} else {
|
|
10960
|
+
const gap = symbolExtractorGap(file, resolved);
|
|
10961
|
+
if (gap !== void 0) messages.push(gap);
|
|
10962
|
+
}
|
|
10683
10963
|
}
|
|
10684
10964
|
return { text: messages.join("\n"), code: 1 };
|
|
10685
10965
|
}
|
|
@@ -11228,7 +11508,7 @@ async function runImageMeta(file) {
|
|
|
11228
11508
|
if (!isImagePath(file)) {
|
|
11229
11509
|
throw new Error(`Not an image file: ${file}`);
|
|
11230
11510
|
}
|
|
11231
|
-
const data =
|
|
11511
|
+
const data = fs12.readFileSync(file);
|
|
11232
11512
|
const bytes = data.length;
|
|
11233
11513
|
let probe;
|
|
11234
11514
|
try {
|
|
@@ -11240,15 +11520,17 @@ async function runImageMeta(file) {
|
|
|
11240
11520
|
throw e;
|
|
11241
11521
|
}
|
|
11242
11522
|
if (probe === null) {
|
|
11243
|
-
return { width: 0, height: 0, format: null, bytes, decodable: false, wouldShrink: false, shrunkBytes: null };
|
|
11523
|
+
return { width: 0, height: 0, format: null, bytes, decodable: false, shrinkable: false, wouldShrink: false, shrunkBytes: null };
|
|
11244
11524
|
}
|
|
11245
|
-
const
|
|
11525
|
+
const shrinkable = canShrinkFormat(probe.format);
|
|
11526
|
+
const shrink = shrinkable ? await shrinkImage(data, { sizeThresholdBytes: 0 }) : null;
|
|
11246
11527
|
return {
|
|
11247
11528
|
width: probe.width,
|
|
11248
11529
|
height: probe.height,
|
|
11249
11530
|
format: probe.format,
|
|
11250
11531
|
bytes,
|
|
11251
11532
|
decodable: true,
|
|
11533
|
+
shrinkable,
|
|
11252
11534
|
wouldShrink: shrink !== null,
|
|
11253
11535
|
shrunkBytes: shrink !== null ? shrink.shrunkBytes : null
|
|
11254
11536
|
};
|
|
@@ -11260,7 +11542,7 @@ async function runImageText(file, lang) {
|
|
|
11260
11542
|
if (!isImagePath(file)) {
|
|
11261
11543
|
throw new Error(`Not an image file: ${file}`);
|
|
11262
11544
|
}
|
|
11263
|
-
const data =
|
|
11545
|
+
const data = fs12.readFileSync(file);
|
|
11264
11546
|
const ocr = await ocrImage(data, lang);
|
|
11265
11547
|
if (ocr === null) {
|
|
11266
11548
|
if (ocrIntegrityFailed()) {
|
|
@@ -11333,7 +11615,7 @@ function runGrep(opts) {
|
|
|
11333
11615
|
if (activePins !== null) {
|
|
11334
11616
|
let realDir;
|
|
11335
11617
|
try {
|
|
11336
|
-
realDir = foldRealpath(
|
|
11618
|
+
realDir = foldRealpath(fs12.realpathSync(dir));
|
|
11337
11619
|
} catch {
|
|
11338
11620
|
return;
|
|
11339
11621
|
}
|
|
@@ -11342,12 +11624,12 @@ function runGrep(opts) {
|
|
|
11342
11624
|
visitedRealDirs.add(realDir);
|
|
11343
11625
|
}
|
|
11344
11626
|
try {
|
|
11345
|
-
for (const entry of
|
|
11627
|
+
for (const entry of fs12.readdirSync(dir)) {
|
|
11346
11628
|
if (entry.startsWith(".")) continue;
|
|
11347
11629
|
const full = path12.join(dir, entry);
|
|
11348
11630
|
let lst;
|
|
11349
11631
|
try {
|
|
11350
|
-
lst =
|
|
11632
|
+
lst = fs12.lstatSync(full);
|
|
11351
11633
|
} catch {
|
|
11352
11634
|
continue;
|
|
11353
11635
|
}
|
|
@@ -11355,7 +11637,7 @@ function runGrep(opts) {
|
|
|
11355
11637
|
if (lst.isSymbolicLink()) {
|
|
11356
11638
|
let real;
|
|
11357
11639
|
try {
|
|
11358
|
-
real =
|
|
11640
|
+
real = fs12.realpathSync(full);
|
|
11359
11641
|
} catch {
|
|
11360
11642
|
continue;
|
|
11361
11643
|
}
|
|
@@ -11364,7 +11646,7 @@ function runGrep(opts) {
|
|
|
11364
11646
|
}
|
|
11365
11647
|
let stat;
|
|
11366
11648
|
try {
|
|
11367
|
-
stat =
|
|
11649
|
+
stat = fs12.statSync(target);
|
|
11368
11650
|
} catch {
|
|
11369
11651
|
continue;
|
|
11370
11652
|
}
|
|
@@ -11384,13 +11666,13 @@ function runGrep(opts) {
|
|
|
11384
11666
|
emitErr4(`Path not found: ${searchPath}`);
|
|
11385
11667
|
return 1;
|
|
11386
11668
|
}
|
|
11387
|
-
const stat =
|
|
11669
|
+
const stat = fs12.statSync(searchPath);
|
|
11388
11670
|
if (stat.isDirectory()) {
|
|
11389
11671
|
const pinned = activePins?.get(pinKey(path12.resolve(searchPath)));
|
|
11390
11672
|
if (pinned !== void 0) verifyPin(searchPath, pinned);
|
|
11391
11673
|
let boundaryReal;
|
|
11392
11674
|
try {
|
|
11393
|
-
boundaryReal =
|
|
11675
|
+
boundaryReal = fs12.realpathSync(searchPath);
|
|
11394
11676
|
} catch (err) {
|
|
11395
11677
|
if (pinned === void 0) {
|
|
11396
11678
|
boundaryReal = path12.resolve(searchPath);
|
|
@@ -11456,7 +11738,7 @@ async function runSemantic(query, opts) {
|
|
|
11456
11738
|
}
|
|
11457
11739
|
const n = opts.limit !== void 0 && Number.isFinite(opts.limit) ? opts.limit : 20;
|
|
11458
11740
|
if (opts.projectRoot !== void 0) {
|
|
11459
|
-
if (!path12.isAbsolute(opts.projectRoot) || !
|
|
11741
|
+
if (!path12.isAbsolute(opts.projectRoot) || !fs12.existsSync(opts.projectRoot) || !fs12.statSync(opts.projectRoot).isDirectory()) {
|
|
11460
11742
|
const message = `token-goat: projectRoot must be an absolute, existing directory, got '${opts.projectRoot}'`;
|
|
11461
11743
|
if (opts.json === true) {
|
|
11462
11744
|
return { text: displaySafeJson({ error: message }), code: 1 };
|
|
@@ -11465,6 +11747,39 @@ async function runSemantic(query, opts) {
|
|
|
11465
11747
|
}
|
|
11466
11748
|
}
|
|
11467
11749
|
const rootDir = opts.projectRoot ?? resolveProjectRoot({ project: process.cwd() });
|
|
11750
|
+
let projectCoverage;
|
|
11751
|
+
try {
|
|
11752
|
+
projectCoverage = getEmbeddingCoverage(globalDbPath(), rootDir);
|
|
11753
|
+
} catch {
|
|
11754
|
+
}
|
|
11755
|
+
if (opts.preflight === true) {
|
|
11756
|
+
const preflight2 = await checkEmbeddingPreflight({
|
|
11757
|
+
...opts.warm !== void 0 ? { warm: opts.warm } : {},
|
|
11758
|
+
projectRoot: rootDir,
|
|
11759
|
+
...projectCoverage !== void 0 ? { coverage: projectCoverage } : {}
|
|
11760
|
+
});
|
|
11761
|
+
if (opts.json === true) {
|
|
11762
|
+
return { text: displaySafeJson(preflight2), code: preflight2.status === "ready" ? 0 : 1 };
|
|
11763
|
+
}
|
|
11764
|
+
const lines = [
|
|
11765
|
+
`Semantic embedding status: ${preflight2.status.toUpperCase()}`,
|
|
11766
|
+
` Summary: ${preflight2.summary}`,
|
|
11767
|
+
` Config (indexing.embeddings_enabled): ${preflight2.configEnabled ? "enabled" : "disabled"}`,
|
|
11768
|
+
` ONNX runtime (onnxruntime-node): ${preflight2.runtimeAvailable ? "available" : "missing"}`,
|
|
11769
|
+
` Model files (~34 MB): ${preflight2.modelFilesPresent ? "present" : "missing"}`,
|
|
11770
|
+
` In-memory session: ${preflight2.modelWarmed ? "ready / warmed" : "not loaded"}`,
|
|
11771
|
+
` Project coverage: ${preflight2.embeddedFiles}/${countNoun(preflight2.indexedFiles, "file")} (${preflight2.coveragePercent}%)`
|
|
11772
|
+
];
|
|
11773
|
+
if (preflight2.actionRequired) {
|
|
11774
|
+
lines.push(` Action: ${preflight2.actionRequired}`);
|
|
11775
|
+
}
|
|
11776
|
+
return { text: lines.join("\n"), code: preflight2.status === "ready" ? 0 : 1 };
|
|
11777
|
+
}
|
|
11778
|
+
const preflight = await checkEmbeddingPreflight({
|
|
11779
|
+
...opts.warm !== void 0 ? { warm: opts.warm } : {},
|
|
11780
|
+
projectRoot: rootDir,
|
|
11781
|
+
...projectCoverage !== void 0 ? { coverage: projectCoverage } : {}
|
|
11782
|
+
});
|
|
11468
11783
|
const embeddingsEnabled = loadConfig().indexing?.embeddings_enabled ?? true;
|
|
11469
11784
|
if (embeddingsEnabled && !isAvailable()) {
|
|
11470
11785
|
console.warn(
|
|
@@ -11474,9 +11789,14 @@ async function runSemantic(query, opts) {
|
|
|
11474
11789
|
console.warn(
|
|
11475
11790
|
"Matching on meaning is off (indexing.embeddings_enabled / TOKEN_GOAT_EMBEDDINGS_ENABLED is disabled); these results come from keyword search alone."
|
|
11476
11791
|
);
|
|
11792
|
+
} else if (preflight.status !== "ready") {
|
|
11793
|
+
console.warn(
|
|
11794
|
+
`Matching on meaning is degraded (${preflight.summary}); these results come from keyword search alone.${preflight.actionRequired ? ` Action: ${preflight.actionRequired}` : ""}`
|
|
11795
|
+
);
|
|
11477
11796
|
}
|
|
11478
11797
|
const overFetchForMerge = Math.min(MAX_OVER_FETCH, n * OVER_FETCH_FACTOR);
|
|
11479
11798
|
let rawHits = [];
|
|
11799
|
+
let searchSemanticError = null;
|
|
11480
11800
|
if (embeddingsEnabled) {
|
|
11481
11801
|
try {
|
|
11482
11802
|
rawHits = await searchSemantic(
|
|
@@ -11488,12 +11808,15 @@ async function runSemantic(query, opts) {
|
|
|
11488
11808
|
rootDir
|
|
11489
11809
|
);
|
|
11490
11810
|
} catch (e) {
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
11811
|
+
searchSemanticError = extractErrorMessage(e);
|
|
11812
|
+
if (preflight.status === "ready") {
|
|
11813
|
+
console.warn(
|
|
11814
|
+
`Matching on meaning is off (${searchSemanticError}); these results come from keyword search alone.`
|
|
11815
|
+
);
|
|
11816
|
+
}
|
|
11494
11817
|
}
|
|
11495
11818
|
}
|
|
11496
|
-
if (rawHits.length === 0 && isAvailable()) {
|
|
11819
|
+
if (rawHits.length === 0 && isAvailable() && preflight.status === "ready" && !searchSemanticError) {
|
|
11497
11820
|
try {
|
|
11498
11821
|
const enabled = loadConfig().indexing.embeddings_enabled;
|
|
11499
11822
|
const { indexedFiles, embeddedFiles } = enabled ? getEmbeddingCoverage(globalDbPath(), rootDir) : { indexedFiles: 0, embeddedFiles: 0 };
|
|
@@ -11571,7 +11894,21 @@ async function runSemantic(query, opts) {
|
|
|
11571
11894
|
}));
|
|
11572
11895
|
const capped = guardJsonRows(items);
|
|
11573
11896
|
const limitTruncated = hits.length < eligibleCount;
|
|
11574
|
-
const text3 = displaySafeJson({
|
|
11897
|
+
const text3 = displaySafeJson({
|
|
11898
|
+
source,
|
|
11899
|
+
...capped,
|
|
11900
|
+
truncated: capped.truncated || limitTruncated,
|
|
11901
|
+
totalCount: eligibleCount,
|
|
11902
|
+
...candidatesClipped ? { totalCountIsFloor: true } : {},
|
|
11903
|
+
...preflight.status !== "ready" ? {
|
|
11904
|
+
preflightStatus: preflight.status,
|
|
11905
|
+
warning: preflight.summary,
|
|
11906
|
+
...preflight.actionRequired ? { actionRequired: preflight.actionRequired } : {}
|
|
11907
|
+
} : searchSemanticError !== null ? {
|
|
11908
|
+
preflightStatus: "degraded",
|
|
11909
|
+
warning: `Matching on meaning failed (${searchSemanticError}); results come from keyword search alone.`
|
|
11910
|
+
} : {}
|
|
11911
|
+
});
|
|
11575
11912
|
recordReadStat("semantic_search", sumFileSizes(hits.map((h) => h.filePath)), text3, query);
|
|
11576
11913
|
return { text: text3, code: 0 };
|
|
11577
11914
|
}
|
|
@@ -11634,19 +11971,54 @@ ${previewLines(entry.text, 3)}`).join("\n\n"),
|
|
|
11634
11971
|
}
|
|
11635
11972
|
const indexEmpty = isIndexEmptyForProject(globalDbPath(), rootDir);
|
|
11636
11973
|
if (opts.json === true) {
|
|
11637
|
-
const payload = indexEmpty ? {
|
|
11974
|
+
const payload = indexEmpty ? {
|
|
11975
|
+
source: "fts",
|
|
11976
|
+
items: [],
|
|
11977
|
+
truncated: false,
|
|
11978
|
+
totalCount: 0,
|
|
11979
|
+
indexEmpty: true,
|
|
11980
|
+
hint: emptyIndexMessage(rootDir),
|
|
11981
|
+
...preflight.status !== "ready" ? {
|
|
11982
|
+
preflightStatus: preflight.status,
|
|
11983
|
+
warning: preflight.summary,
|
|
11984
|
+
...preflight.actionRequired ? { actionRequired: preflight.actionRequired } : {}
|
|
11985
|
+
} : searchSemanticError !== null ? {
|
|
11986
|
+
preflightStatus: "degraded",
|
|
11987
|
+
warning: `Matching on meaning failed (${searchSemanticError}); results come from keyword search alone.`
|
|
11988
|
+
} : {}
|
|
11989
|
+
} : {
|
|
11990
|
+
source: "fts",
|
|
11991
|
+
items: [],
|
|
11992
|
+
truncated: false,
|
|
11993
|
+
totalCount: 0,
|
|
11994
|
+
...preflight.status !== "ready" ? {
|
|
11995
|
+
preflightStatus: preflight.status,
|
|
11996
|
+
warning: preflight.summary,
|
|
11997
|
+
...preflight.actionRequired ? { actionRequired: preflight.actionRequired } : {}
|
|
11998
|
+
} : searchSemanticError !== null ? {
|
|
11999
|
+
preflightStatus: "degraded",
|
|
12000
|
+
warning: `Matching on meaning failed (${searchSemanticError}); results come from keyword search alone.`
|
|
12001
|
+
} : {}
|
|
12002
|
+
};
|
|
11638
12003
|
const text2 = displaySafeJson(payload);
|
|
11639
12004
|
return { text: text2, code: 1 };
|
|
11640
12005
|
}
|
|
11641
|
-
|
|
12006
|
+
let text = indexEmpty ? `token-goat: no matches for '${query}'
|
|
11642
12007
|
${emptyIndexMessage(rootDir)}` : `token-goat: no matches for '${query}'`;
|
|
12008
|
+
if (preflight.status !== "ready") {
|
|
12009
|
+
text += `
|
|
12010
|
+
(note: semantic indexing is degraded [${preflight.status}]: ${preflight.summary}${preflight.actionRequired ? ` \u2014 ${preflight.actionRequired}` : ""})`;
|
|
12011
|
+
} else if (searchSemanticError !== null) {
|
|
12012
|
+
text += `
|
|
12013
|
+
(note: semantic matching degraded: ${searchSemanticError}; results come from keyword search alone)`;
|
|
12014
|
+
}
|
|
11643
12015
|
return { text, code: 1 };
|
|
11644
12016
|
}
|
|
11645
12017
|
|
|
11646
12018
|
// src/read_spec.ts
|
|
11647
12019
|
function fileExists3(p) {
|
|
11648
12020
|
try {
|
|
11649
|
-
return
|
|
12021
|
+
return fs13.statSync(p).isFile();
|
|
11650
12022
|
} catch {
|
|
11651
12023
|
return false;
|
|
11652
12024
|
}
|
|
@@ -12059,6 +12431,7 @@ export {
|
|
|
12059
12431
|
runImpact,
|
|
12060
12432
|
runAsk,
|
|
12061
12433
|
load,
|
|
12434
|
+
queryJson,
|
|
12062
12435
|
runCsvQuery,
|
|
12063
12436
|
runCsvProfile,
|
|
12064
12437
|
runJsonOutline,
|