@futpib/parser 1.0.2 → 1.0.4

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.
Files changed (311) hide show
  1. package/.github/copilot-instructions.md +149 -0
  2. package/.github/workflows/copilot-setup-steps.yml +18 -0
  3. package/.github/workflows/main.yml +29 -8
  4. package/.yarn/releases/yarn-4.9.4.cjs +942 -0
  5. package/.yarnrc.yml +1 -1
  6. package/build/allSettledStream.js +1 -1
  7. package/build/allSettledStream.test.js +2 -2
  8. package/build/androidPackageParser.d.ts +1 -1
  9. package/build/androidPackageParser.js +5 -3
  10. package/build/androidPackageParser.test.js +7 -7
  11. package/build/androidPackageUnparser.d.ts +2 -2
  12. package/build/androidPackageUnparser.js +18 -14
  13. package/build/androidPackageUnparser.test.js +7 -7
  14. package/build/arbitrarilySlicedAsyncInterator.js +2 -1
  15. package/build/arbitraryDalvikBytecode.d.ts +4 -0
  16. package/build/arbitraryDalvikBytecode.js +640 -0
  17. package/build/arbitraryDalvikExecutable.d.ts +3 -0
  18. package/build/arbitraryDalvikExecutable.js +282 -0
  19. package/build/arbitraryDosDateTime.js +1 -0
  20. package/build/arbitraryZipStream.js +1 -1
  21. package/build/arrayParser.js +2 -2
  22. package/build/arrayUnparser.d.ts +1 -1
  23. package/build/backsmali.d.ts +3 -1
  24. package/build/backsmali.js +31 -3
  25. package/build/bash.d.ts +84 -0
  26. package/build/bash.js +1 -0
  27. package/build/bashParser.d.ts +6 -0
  28. package/build/bashParser.js +294 -0
  29. package/build/bashParser.test.d.ts +1 -0
  30. package/build/bashParser.test.js +181 -0
  31. package/build/customInvariant.d.ts +2 -1
  32. package/build/customInvariant.js +4 -6
  33. package/build/dalvikBytecodeParser/formatParsers.d.ts +76 -2
  34. package/build/dalvikBytecodeParser/formatParsers.js +146 -11
  35. package/build/dalvikBytecodeParser/formatSizes.d.ts +34 -0
  36. package/build/dalvikBytecodeParser/formatSizes.js +34 -0
  37. package/build/dalvikBytecodeParser/operationFormats.d.ts +225 -0
  38. package/build/dalvikBytecodeParser/operationFormats.js +225 -0
  39. package/build/dalvikBytecodeParser.d.ts +1105 -5
  40. package/build/dalvikBytecodeParser.js +658 -205
  41. package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +152 -0
  42. package/build/dalvikBytecodeUnparser/formatUnparsers.js +225 -0
  43. package/build/dalvikBytecodeUnparser.d.ts +3 -0
  44. package/build/dalvikBytecodeUnparser.js +642 -0
  45. package/build/dalvikBytecodeUnparser.test.d.ts +1 -0
  46. package/build/dalvikBytecodeUnparser.test.js +25 -0
  47. package/build/dalvikExecutable.d.ts +65 -8
  48. package/build/dalvikExecutable.js +36 -0
  49. package/build/dalvikExecutableParser/stringSyntaxParser.d.ts +1 -1
  50. package/build/dalvikExecutableParser/stringSyntaxParser.js +17 -17
  51. package/build/dalvikExecutableParser/typeParsers.d.ts +2 -1
  52. package/build/dalvikExecutableParser/typeParsers.js +16 -11
  53. package/build/dalvikExecutableParser/typedNumbers.d.ts +85 -69
  54. package/build/dalvikExecutableParser/typedNumbers.js +0 -1
  55. package/build/dalvikExecutableParser.d.ts +2 -2
  56. package/build/dalvikExecutableParser.js +655 -337
  57. package/build/dalvikExecutableParser.test.js +24 -22
  58. package/build/dalvikExecutableParserAgainstSmaliParser.test.js +223 -246
  59. package/build/dalvikExecutableUnparser/annotationUnparsers.d.ts +14 -0
  60. package/build/dalvikExecutableUnparser/annotationUnparsers.js +97 -0
  61. package/build/dalvikExecutableUnparser/poolBuilders.d.ts +49 -0
  62. package/build/dalvikExecutableUnparser/poolBuilders.js +140 -0
  63. package/build/dalvikExecutableUnparser/poolScanners.d.ts +4 -0
  64. package/build/dalvikExecutableUnparser/poolScanners.js +220 -0
  65. package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +25 -0
  66. package/build/dalvikExecutableUnparser/sectionUnparsers.js +581 -0
  67. package/build/dalvikExecutableUnparser/utils.d.ts +10 -0
  68. package/build/dalvikExecutableUnparser/utils.js +108 -0
  69. package/build/dalvikExecutableUnparser.d.ts +4 -0
  70. package/build/dalvikExecutableUnparser.js +406 -0
  71. package/build/dalvikExecutableUnparser.test.d.ts +1 -0
  72. package/build/dalvikExecutableUnparser.test.js +31 -0
  73. package/build/debugLogInputParser.js +1 -1
  74. package/build/disjunctionParser.d.ts +2 -2
  75. package/build/disjunctionParser.js +2 -2
  76. package/build/elementTerminatedArrayParser.d.ts +2 -2
  77. package/build/elementTerminatedArrayParser.js +1 -1
  78. package/build/elementTerminatedArrayParser.test.js +5 -5
  79. package/build/elementTerminatedSequenceArrayParser.d.ts +2 -2
  80. package/build/elementTerminatedSequenceArrayParser.js +1 -1
  81. package/build/elementTerminatedSequenceArrayParser.test.js +2 -2
  82. package/build/elementTerminatedSequenceParser.d.ts +2 -2
  83. package/build/elementTerminatedSequenceParser.js +1 -1
  84. package/build/elementTerminatedSequenceParser.test.js +2 -2
  85. package/build/endOfInputParser.d.ts +1 -1
  86. package/build/exactElementSwitchParser.d.ts +3 -0
  87. package/build/exactElementSwitchParser.js +22 -0
  88. package/build/fetchCid.js +2 -6
  89. package/build/fetchCid.test.d.ts +1 -0
  90. package/build/fetchCid.test.js +16 -0
  91. package/build/fixedLengthSequenceParser.test.js +2 -2
  92. package/build/hasExecutable.js +2 -2
  93. package/build/highResolutionTimer.js +1 -1
  94. package/build/index.d.ts +24 -2
  95. package/build/index.js +22 -1
  96. package/build/inputReader.d.ts +1 -1
  97. package/build/inputReader.test.js +33 -45
  98. package/build/javaKeyStoreParser.test.js +6 -6
  99. package/build/jsonParser.js +8 -8
  100. package/build/lazyMessageError.d.ts +48 -0
  101. package/build/lazyMessageError.js +53 -0
  102. package/build/lazyMessageError.test.d.ts +1 -0
  103. package/build/lazyMessageError.test.js +15 -0
  104. package/build/leb128Parser.d.ts +1 -1
  105. package/build/leb128Parser.js +10 -10
  106. package/build/leb128Parser.test.js +7 -7
  107. package/build/negativeLookaheadParser.js +2 -2
  108. package/build/negativeLookaheadParser.test.js +4 -4
  109. package/build/noStackCaptureOverheadError.d.ts +4 -0
  110. package/build/noStackCaptureOverheadError.js +9 -0
  111. package/build/noStackCaptureOverheadError.test.d.ts +1 -0
  112. package/build/noStackCaptureOverheadError.test.js +15 -0
  113. package/build/nonEmptyArrayParser.js +2 -2
  114. package/build/nonEmptyArrayParser.test.js +2 -1
  115. package/build/optionalParser.js +2 -2
  116. package/build/parser.d.ts +2 -1
  117. package/build/parser.js +23 -8
  118. package/build/parser.test.js +78 -29
  119. package/build/parserConsumedSequenceParser.d.ts +1 -1
  120. package/build/parserConsumedSequenceParser.js +2 -2
  121. package/build/parserContext.d.ts +8 -6
  122. package/build/parserContext.js +60 -33
  123. package/build/parserContext.test.js +7 -3
  124. package/build/parserError.d.ts +603 -44
  125. package/build/parserError.js +98 -53
  126. package/build/parserImplementationInvariant.d.ts +1 -1
  127. package/build/parserImplementationInvariant.js +2 -2
  128. package/build/parserInputCompanion.js +2 -2
  129. package/build/promiseCompose.js +1 -2
  130. package/build/regexpParser.d.ts +2 -0
  131. package/build/regexpParser.js +71 -0
  132. package/build/regexpParser.test.d.ts +1 -0
  133. package/build/regexpParser.test.js +83 -0
  134. package/build/regularExpression.d.ts +63 -0
  135. package/build/regularExpression.js +1 -0
  136. package/build/regularExpressionParser.d.ts +3 -0
  137. package/build/regularExpressionParser.js +580 -0
  138. package/build/regularExpressionParser.test.d.ts +1 -0
  139. package/build/regularExpressionParser.test.js +89 -0
  140. package/build/separatedArrayParser.js +2 -2
  141. package/build/separatedNonEmptyArrayParser.d.ts +2 -0
  142. package/build/separatedNonEmptyArrayParser.js +40 -0
  143. package/build/separatedNonEmptyArrayParser.test.d.ts +1 -0
  144. package/build/separatedNonEmptyArrayParser.test.js +66 -0
  145. package/build/sequenceBuffer.js +1 -1
  146. package/build/sequenceTerminatedSequenceParser.d.ts +2 -2
  147. package/build/sequenceTerminatedSequenceParser.js +3 -3
  148. package/build/sequenceTerminatedSequenceParser.test.js +1 -1
  149. package/build/sequenceUnparser.d.ts +1 -1
  150. package/build/skipToParser.d.ts +1 -1
  151. package/build/skipToParser.js +2 -2
  152. package/build/sliceBoundedParser.test.js +4 -9
  153. package/build/smali.d.ts +1 -1
  154. package/build/smali.js +6 -2
  155. package/build/smaliParser.d.ts +62 -6
  156. package/build/smaliParser.js +1721 -296
  157. package/build/smaliParser.test.js +338 -43
  158. package/build/stringFromAsyncIterable.d.ts +1 -0
  159. package/build/stringFromAsyncIterable.js +7 -0
  160. package/build/terminatedArrayParser.js +4 -4
  161. package/build/terminatedArrayParser.test.js +7 -7
  162. package/build/toAsyncIterator.js +4 -4
  163. package/build/unionParser.d.ts +1 -1
  164. package/build/unionParser.js +2 -2
  165. package/build/unionParser.test.js +3 -3
  166. package/build/unparser.d.ts +3 -3
  167. package/build/unparser.js +6 -4
  168. package/build/unparser.test.js +7 -19
  169. package/build/unparserContext.d.ts +2 -2
  170. package/build/unparserContext.js +2 -3
  171. package/build/unparserError.d.ts +2 -1
  172. package/build/unparserError.js +2 -1
  173. package/build/unparserImplementationInvariant.d.ts +1 -1
  174. package/build/unparserOutputCompanion.d.ts +1 -1
  175. package/build/unparserOutputCompanion.js +1 -1
  176. package/build/zipParser.js +1 -1
  177. package/build/zipUnparser.d.ts +3 -3
  178. package/build/zipUnparser.js +9 -19
  179. package/build/zipUnparser.test.js +1 -1
  180. package/package.json +20 -26
  181. package/src/allSettledStream.test.ts +2 -2
  182. package/src/allSettledStream.ts +3 -3
  183. package/src/androidPackageParser.test.ts +17 -19
  184. package/src/androidPackageParser.ts +129 -171
  185. package/src/androidPackageUnparser.test.ts +19 -21
  186. package/src/androidPackageUnparser.ts +23 -17
  187. package/src/arbitrarilySlicedAsyncInterable.ts +1 -1
  188. package/src/arbitrarilySlicedAsyncInterator.ts +4 -4
  189. package/src/arbitraryDalvikBytecode.ts +992 -0
  190. package/src/arbitraryDalvikExecutable.ts +434 -0
  191. package/src/arbitraryDosDateTime.ts +1 -0
  192. package/src/arbitraryZipStream.ts +1 -1
  193. package/src/arrayParser.ts +2 -2
  194. package/src/arrayUnparser.ts +2 -2
  195. package/src/backsmali.ts +48 -4
  196. package/src/bash.ts +120 -0
  197. package/src/bashParser.test.ts +332 -0
  198. package/src/bashParser.ts +461 -0
  199. package/src/bsonParser.test.ts +12 -14
  200. package/src/customInvariant.ts +8 -12
  201. package/src/dalvikBytecodeParser/formatParsers.ts +376 -17
  202. package/src/dalvikBytecodeParser/formatSizes.ts +35 -0
  203. package/src/dalvikBytecodeParser/operationFormats.ts +226 -0
  204. package/src/dalvikBytecodeParser.ts +1042 -243
  205. package/src/dalvikBytecodeUnparser/formatUnparsers.ts +442 -0
  206. package/src/dalvikBytecodeUnparser.test.ts +44 -0
  207. package/src/dalvikBytecodeUnparser.ts +758 -0
  208. package/src/dalvikExecutable.ts +110 -48
  209. package/src/dalvikExecutableParser/stringSyntaxParser.ts +33 -33
  210. package/src/dalvikExecutableParser/typeParsers.ts +23 -14
  211. package/src/dalvikExecutableParser/typedNumbers.ts +19 -19
  212. package/src/dalvikExecutableParser.test.ts +60 -60
  213. package/src/dalvikExecutableParser.test.ts.md +6 -6
  214. package/src/dalvikExecutableParser.test.ts.snap +0 -0
  215. package/src/dalvikExecutableParser.ts +911 -434
  216. package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +256 -239
  217. package/src/dalvikExecutableUnparser/annotationUnparsers.ts +135 -0
  218. package/src/dalvikExecutableUnparser/poolBuilders.ts +189 -0
  219. package/src/dalvikExecutableUnparser/poolScanners.ts +297 -0
  220. package/src/dalvikExecutableUnparser/sectionUnparsers.ts +683 -0
  221. package/src/dalvikExecutableUnparser/utils.ts +149 -0
  222. package/src/dalvikExecutableUnparser.test.ts +57 -0
  223. package/src/dalvikExecutableUnparser.ts +581 -0
  224. package/src/debugLogInputParser.ts +1 -1
  225. package/src/disjunctionParser.ts +5 -5
  226. package/src/elementTerminatedArrayParser.test.ts +8 -8
  227. package/src/elementTerminatedArrayParser.ts +2 -2
  228. package/src/elementTerminatedSequenceArrayParser.test.ts +4 -6
  229. package/src/elementTerminatedSequenceArrayParser.ts +2 -2
  230. package/src/elementTerminatedSequenceParser.test.ts +4 -6
  231. package/src/elementTerminatedSequenceParser.ts +2 -2
  232. package/src/endOfInputParser.ts +1 -1
  233. package/src/exactElementSwitchParser.ts +41 -0
  234. package/src/fetchCid.test.ts +20 -0
  235. package/src/fetchCid.ts +3 -7
  236. package/src/fixedLengthSequenceParser.test.ts +10 -12
  237. package/src/hasExecutable.ts +2 -2
  238. package/src/highResolutionTimer.ts +1 -1
  239. package/src/index.ts +113 -2
  240. package/src/inputReader.test.ts +39 -52
  241. package/src/inputReader.ts +2 -4
  242. package/src/inputReaderState.ts +1 -1
  243. package/src/inspect.ts +1 -1
  244. package/src/javaKeyStoreParser.test.ts +12 -14
  245. package/src/javaKeyStoreParser.ts +2 -6
  246. package/src/jsonParser.test.ts +2 -4
  247. package/src/jsonParser.ts +34 -38
  248. package/src/lazyMessageError.test.ts +21 -0
  249. package/src/lazyMessageError.ts +88 -0
  250. package/src/leb128Parser.test.ts +25 -23
  251. package/src/leb128Parser.ts +19 -19
  252. package/src/negativeLookaheadParser.test.ts +7 -11
  253. package/src/negativeLookaheadParser.ts +2 -2
  254. package/src/noStackCaptureOverheadError.test.ts +17 -0
  255. package/src/noStackCaptureOverheadError.ts +12 -0
  256. package/src/nonEmptyArrayParser.test.ts +3 -2
  257. package/src/nonEmptyArrayParser.ts +2 -2
  258. package/src/optionalParser.ts +2 -2
  259. package/src/parser.test.ts +96 -43
  260. package/src/parser.test.ts.md +13 -6
  261. package/src/parser.test.ts.snap +0 -0
  262. package/src/parser.ts +35 -12
  263. package/src/parserAccessorParser.ts +1 -1
  264. package/src/parserConsumedSequenceParser.ts +3 -3
  265. package/src/parserContext.test.ts +7 -3
  266. package/src/parserContext.ts +82 -48
  267. package/src/parserError.ts +143 -63
  268. package/src/parserImplementationInvariant.ts +3 -3
  269. package/src/parserInputCompanion.ts +2 -2
  270. package/src/promiseCompose.ts +2 -2
  271. package/src/regexpParser.test.ts +186 -0
  272. package/src/regexpParser.ts +94 -0
  273. package/src/regularExpression.ts +24 -0
  274. package/src/regularExpressionParser.test.ts +102 -0
  275. package/src/regularExpressionParser.ts +921 -0
  276. package/src/separatedArrayParser.ts +3 -3
  277. package/src/separatedNonEmptyArrayParser.test.ts +117 -0
  278. package/src/separatedNonEmptyArrayParser.ts +61 -0
  279. package/src/sequenceBuffer.test.ts +9 -9
  280. package/src/sequenceBuffer.ts +4 -4
  281. package/src/sequenceTerminatedSequenceParser.test.ts +3 -5
  282. package/src/sequenceTerminatedSequenceParser.ts +4 -4
  283. package/src/sequenceUnparser.ts +2 -2
  284. package/src/skipToParser.ts +2 -2
  285. package/src/sliceBoundedParser.test.ts +4 -12
  286. package/src/sliceBoundedParser.ts +2 -2
  287. package/src/smali.ts +8 -3
  288. package/src/smaliParser.test.ts +377 -66
  289. package/src/smaliParser.test.ts.md +1635 -48
  290. package/src/smaliParser.test.ts.snap +0 -0
  291. package/src/smaliParser.ts +2751 -569
  292. package/src/stringFromAsyncIterable.ts +9 -0
  293. package/src/terminatedArrayParser.test.ts +11 -11
  294. package/src/terminatedArrayParser.ts +5 -7
  295. package/src/toAsyncIterator.ts +8 -8
  296. package/src/uint8Array.ts +2 -3
  297. package/src/unionParser.test.ts +22 -23
  298. package/src/unionParser.ts +6 -8
  299. package/src/unparser.test.ts +18 -34
  300. package/src/unparser.ts +13 -9
  301. package/src/unparserContext.ts +9 -13
  302. package/src/unparserError.ts +2 -1
  303. package/src/unparserImplementationInvariant.ts +1 -1
  304. package/src/unparserOutputCompanion.ts +1 -1
  305. package/src/zip.ts +2 -6
  306. package/src/zipParser.ts +10 -18
  307. package/src/zipUnparser.test.ts +1 -1
  308. package/src/zipUnparser.ts +52 -64
  309. package/tsconfig.json +7 -1
  310. package/xo.config.ts +15 -0
  311. package/.yarn/releases/yarn-4.5.3.cjs +0 -934
@@ -0,0 +1,434 @@
1
+ import * as fc from 'fast-check';
2
+ import {
3
+ type DalvikExecutable,
4
+ type DalvikExecutableClassDefinition,
5
+ type DalvikExecutableAccessFlags,
6
+ type DalvikExecutableClassData,
7
+ type DalvikExecutableField,
8
+ type DalvikExecutableFieldWithAccess,
9
+ type DalvikExecutableMethod,
10
+ type DalvikExecutableMethodWithAccess,
11
+ type DalvikExecutablePrototype,
12
+ type DalvikExecutableCode,
13
+ type DalvikExecutableDebugInfo,
14
+ type DalvikExecutableAnnotation,
15
+ type DalvikExecutableClassAnnotations,
16
+ type DalvikExecutableClassFieldAnnotation,
17
+ type DalvikExecutableClassMethodAnnotation,
18
+ type DalvikExecutableClassParameterAnnotation,
19
+ type DalvikExecutableEncodedValue,
20
+ } from './dalvikExecutable.js';
21
+
22
+ // Local type definitions for non-exported types
23
+
24
+ type DalvikExecutableAnnotationItemVisibility = 'build' | 'runtime' | 'system';
25
+
26
+ type DalvikExecutableAnnotationElement = {
27
+ name: string;
28
+ value: DalvikExecutableEncodedValue;
29
+ };
30
+
31
+ type DalvikExecutableDebugByteCodeValue =
32
+ | { type: 'advancePc'; addressDiff: number }
33
+ | { type: 'advanceLine'; lineDiff: number }
34
+ | { type: 'startLocal'; registerNum: number; name: undefined | string; type_: undefined | string }
35
+ | { type: 'startLocalExtended'; registerNum: number; name: undefined | string; type_: undefined | string; signature: undefined | string }
36
+ | { type: 'endLocal'; registerNum: number }
37
+ | { type: 'restartLocal'; registerNum: number }
38
+ | { type: 'setPrologueEnd' }
39
+ | { type: 'setEpilogueBegin' }
40
+ | { type: 'setFile'; name: undefined | string }
41
+ | { type: 'special'; value: number };
42
+
43
+ // String generators for valid Dalvik identifiers and type descriptors
44
+
45
+ const arbitraryJavaIdentifier = fc.stringMatching(/^[a-zA-Z_][a-zA-Z0-9_]*$/);
46
+
47
+ const arbitraryPrimitiveType = fc.oneof(
48
+ fc.constant('V'), // void
49
+ fc.constant('Z'), // boolean
50
+ fc.constant('B'), // byte
51
+ fc.constant('S'), // short
52
+ fc.constant('C'), // char
53
+ fc.constant('I'), // int
54
+ fc.constant('J'), // long
55
+ fc.constant('F'), // float
56
+ fc.constant('D'), // double
57
+ );
58
+
59
+ const arbitraryDalvikClassName: fc.Arbitrary<string> = fc.tuple(
60
+ fc.array(arbitraryJavaIdentifier, { minLength: 1, maxLength: 3 }),
61
+ arbitraryJavaIdentifier,
62
+ ).map(([packages, className]) => `L${packages.join('/')}/${className};`);
63
+
64
+ const arbitraryDalvikTypeName: fc.Arbitrary<string> = fc.oneof(
65
+ arbitraryPrimitiveType,
66
+ arbitraryDalvikClassName,
67
+ arbitraryPrimitiveType.map(p => `[${p}`), // array of primitive
68
+ arbitraryDalvikClassName.map(c => `[${c}`), // array of object
69
+ );
70
+
71
+ const arbitraryDalvikMethodName = fc.oneof(
72
+ arbitraryJavaIdentifier,
73
+ fc.constant('<init>'),
74
+ fc.constant('<clinit>'),
75
+ );
76
+
77
+ const arbitraryDalvikFieldName = arbitraryJavaIdentifier;
78
+
79
+ // Access flags generator - common flags for all contexts
80
+ const arbitraryDalvikExecutableAccessFlagsCommon: fc.Arbitrary<DalvikExecutableAccessFlags> = fc.record({
81
+ public: fc.boolean(),
82
+ private: fc.boolean(),
83
+ protected: fc.boolean(),
84
+ static: fc.boolean(),
85
+ final: fc.boolean(),
86
+ synchronized: fc.boolean(),
87
+ volatile: fc.constant(false),
88
+ bridge: fc.constant(false),
89
+ transient: fc.constant(false),
90
+ varargs: fc.constant(false),
91
+ native: fc.boolean(),
92
+ interface: fc.boolean(),
93
+ abstract: fc.boolean(),
94
+ strict: fc.boolean(),
95
+ synthetic: fc.boolean(),
96
+ annotation: fc.boolean(),
97
+ enum: fc.boolean(),
98
+ constructor: fc.boolean(),
99
+ declaredSynchronized: fc.boolean(),
100
+ });
101
+
102
+ // Access flags for fields - volatile and transient are valid (0x40 and 0x80)
103
+ const arbitraryDalvikExecutableFieldAccessFlags: fc.Arbitrary<DalvikExecutableAccessFlags> = fc.record({
104
+ public: fc.boolean(),
105
+ private: fc.boolean(),
106
+ protected: fc.boolean(),
107
+ static: fc.boolean(),
108
+ final: fc.boolean(),
109
+ synchronized: fc.boolean(),
110
+ volatile: fc.boolean(),
111
+ bridge: fc.constant(false),
112
+ transient: fc.boolean(),
113
+ varargs: fc.constant(false),
114
+ native: fc.boolean(),
115
+ interface: fc.boolean(),
116
+ abstract: fc.boolean(),
117
+ strict: fc.boolean(),
118
+ synthetic: fc.boolean(),
119
+ annotation: fc.boolean(),
120
+ enum: fc.boolean(),
121
+ constructor: fc.boolean(),
122
+ declaredSynchronized: fc.boolean(),
123
+ });
124
+
125
+ // Access flags for methods - bridge and varargs are valid (0x40 and 0x80)
126
+ const arbitraryDalvikExecutableMethodAccessFlags: fc.Arbitrary<DalvikExecutableAccessFlags> = fc.record({
127
+ public: fc.boolean(),
128
+ private: fc.boolean(),
129
+ protected: fc.boolean(),
130
+ static: fc.boolean(),
131
+ final: fc.boolean(),
132
+ synchronized: fc.boolean(),
133
+ volatile: fc.constant(false),
134
+ bridge: fc.boolean(),
135
+ transient: fc.constant(false),
136
+ varargs: fc.boolean(),
137
+ native: fc.boolean(),
138
+ interface: fc.boolean(),
139
+ abstract: fc.boolean(),
140
+ strict: fc.boolean(),
141
+ synthetic: fc.boolean(),
142
+ annotation: fc.boolean(),
143
+ enum: fc.boolean(),
144
+ constructor: fc.boolean(),
145
+ declaredSynchronized: fc.boolean(),
146
+ });
147
+
148
+ // Generic access flags for class-level (uses common)
149
+ const arbitraryDalvikExecutableAccessFlags: fc.Arbitrary<DalvikExecutableAccessFlags> = arbitraryDalvikExecutableAccessFlagsCommon;
150
+
151
+ // Field generator
152
+ const arbitraryDalvikExecutableField: fc.Arbitrary<DalvikExecutableField> = fc.record({
153
+ class: arbitraryDalvikClassName,
154
+ type: arbitraryDalvikTypeName,
155
+ name: arbitraryDalvikFieldName,
156
+ });
157
+
158
+ const arbitraryDalvikExecutableFieldWithAccess: fc.Arbitrary<DalvikExecutableFieldWithAccess> = fc.record({
159
+ field: arbitraryDalvikExecutableField,
160
+ accessFlags: arbitraryDalvikExecutableFieldAccessFlags,
161
+ });
162
+
163
+ // Prototype generator
164
+ const arbitraryShorty: fc.Arbitrary<string> = fc.tuple(
165
+ arbitraryPrimitiveType, // return type
166
+ fc.array(fc.oneof(fc.constant('L'), arbitraryPrimitiveType), { maxLength: 5 }), // parameters
167
+ ).map(([returnType, params]) => returnType + params.join(''));
168
+
169
+ const arbitraryDalvikExecutablePrototype: fc.Arbitrary<DalvikExecutablePrototype> = arbitraryShorty.chain(shorty => {
170
+ const returnTypeChar = shorty[0];
171
+ const paramChars = shorty.slice(1).split('');
172
+
173
+ const returnType = returnTypeChar === 'L' ? arbitraryDalvikClassName : fc.constant(returnTypeChar);
174
+ const parameters = fc.tuple(
175
+ ...paramChars.map(char => char === 'L' ? arbitraryDalvikClassName : fc.constant(char))
176
+ );
177
+
178
+ return fc.record({
179
+ shorty: fc.constant(shorty),
180
+ returnType,
181
+ parameters,
182
+ });
183
+ });
184
+
185
+ // Method generator
186
+ const arbitraryDalvikExecutableMethod: fc.Arbitrary<DalvikExecutableMethod> = fc.record({
187
+ class: arbitraryDalvikClassName,
188
+ prototype: arbitraryDalvikExecutablePrototype,
189
+ name: arbitraryDalvikMethodName,
190
+ });
191
+
192
+ // Encoded value generator (recursive)
193
+ // DalvikExecutableEncodedValue is now a tagged union with type information
194
+ const arbitraryDalvikExecutableEncodedValue: fc.Arbitrary<DalvikExecutableEncodedValue> = fc.letrec(tie => ({
195
+ value: fc.oneof(
196
+ fc.record({ type: fc.constant('byte' as const), value: fc.integer({ min: -128, max: 127 }) }),
197
+ fc.record({ type: fc.constant('short' as const), value: fc.integer({ min: -32768, max: 32767 }) }),
198
+ fc.record({ type: fc.constant('char' as const), value: fc.integer({ min: 0, max: 65535 }) }),
199
+ fc.record({ type: fc.constant('int' as const), value: fc.integer({ min: -2147483648, max: 2147483647 }) }),
200
+ fc.record({ type: fc.constant('long' as const), value: fc.bigInt({ min: -9223372036854775808n, max: 9223372036854775807n }) }),
201
+ fc.record({ type: fc.constant('float' as const), value: fc.float() }),
202
+ fc.record({ type: fc.constant('double' as const), value: fc.double() }),
203
+ fc.record({ type: fc.constant('methodType' as const), value: arbitraryDalvikExecutablePrototype }),
204
+ fc.record({ type: fc.constant('methodHandle' as const), value: fc.nat(255) }),
205
+ fc.record({ type: fc.constant('string' as const), value: fc.string() }),
206
+ fc.record({ type: fc.constant('type' as const), value: arbitraryDalvikClassName }),
207
+ fc.record({ type: fc.constant('field' as const), value: arbitraryDalvikExecutableField }),
208
+ fc.record({ type: fc.constant('method' as const), value: arbitraryDalvikExecutableMethod }),
209
+ fc.record({ type: fc.constant('enum' as const), value: arbitraryDalvikExecutableField }),
210
+ fc.record({ type: fc.constant('array' as const), value: fc.array(tie('value') as fc.Arbitrary<DalvikExecutableEncodedValue>, { maxLength: 3 }) }),
211
+ // Note: We skip 'annotation' type here to avoid deep recursion complexity
212
+ fc.record({ type: fc.constant('null' as const), value: fc.constant(null) }),
213
+ fc.record({ type: fc.constant('boolean' as const), value: fc.boolean() }),
214
+ ),
215
+ })).value;
216
+
217
+ // Annotation generators
218
+ const arbitraryAnnotationVisibility: fc.Arbitrary<DalvikExecutableAnnotationItemVisibility> = fc.oneof(
219
+ fc.constant('build' as const),
220
+ fc.constant('runtime' as const),
221
+ fc.constant('system' as const),
222
+ );
223
+
224
+ const arbitraryDalvikExecutableAnnotationElement: fc.Arbitrary<DalvikExecutableAnnotationElement> = fc.record({
225
+ name: arbitraryJavaIdentifier,
226
+ value: arbitraryDalvikExecutableEncodedValue,
227
+ });
228
+
229
+ const arbitraryDalvikExecutableAnnotation: fc.Arbitrary<DalvikExecutableAnnotation> = fc.record({
230
+ visibility: arbitraryAnnotationVisibility,
231
+ type: arbitraryDalvikClassName,
232
+ elements: fc.array(arbitraryDalvikExecutableAnnotationElement, { maxLength: 3 }),
233
+ });
234
+
235
+ // Debug info generators
236
+ const arbitraryDalvikExecutableDebugByteCodeValue: fc.Arbitrary<DalvikExecutableDebugByteCodeValue> = fc.oneof(
237
+ fc.record({
238
+ type: fc.constant('advancePc' as const),
239
+ addressDiff: fc.nat({ max: 255 }),
240
+ }),
241
+ fc.record({
242
+ type: fc.constant('advanceLine' as const),
243
+ lineDiff: fc.integer({ min: -128, max: 127 }),
244
+ }),
245
+ fc.record({
246
+ type: fc.constant('startLocal' as const),
247
+ registerNum: fc.nat({ max: 255 }),
248
+ name: fc.option(arbitraryJavaIdentifier, { nil: undefined }),
249
+ type_: fc.option(arbitraryDalvikTypeName, { nil: undefined }),
250
+ }),
251
+ fc.record({
252
+ type: fc.constant('startLocalExtended' as const),
253
+ registerNum: fc.nat({ max: 255 }),
254
+ name: fc.option(arbitraryJavaIdentifier, { nil: undefined }),
255
+ type_: fc.option(arbitraryDalvikTypeName, { nil: undefined }),
256
+ signature: fc.option(fc.string(), { nil: undefined }),
257
+ }),
258
+ fc.record({
259
+ type: fc.constant('endLocal' as const),
260
+ registerNum: fc.nat({ max: 255 }),
261
+ }),
262
+ fc.record({
263
+ type: fc.constant('restartLocal' as const),
264
+ registerNum: fc.nat({ max: 255 }),
265
+ }),
266
+ fc.record({
267
+ type: fc.constant('setPrologueEnd' as const),
268
+ }),
269
+ fc.record({
270
+ type: fc.constant('setEpilogueBegin' as const),
271
+ }),
272
+ fc.record({
273
+ type: fc.constant('setFile' as const),
274
+ name: fc.option(fc.string(), { nil: undefined }),
275
+ }),
276
+ fc.record({
277
+ type: fc.constant('special' as const),
278
+ // Special opcodes must be >= 0x0A (values 0x00-0x09 are specific debug opcodes)
279
+ value: fc.integer({ min: 0x0A, max: 255 }),
280
+ }),
281
+ );
282
+
283
+ const arbitraryDalvikExecutableDebugInfo: fc.Arbitrary<DalvikExecutableDebugInfo> = fc.record({
284
+ lineStart: fc.nat({ max: 65535 }),
285
+ parameterNames: fc.array(fc.option(arbitraryJavaIdentifier, { nil: undefined }), { maxLength: 5 }),
286
+ bytecode: fc.array(arbitraryDalvikExecutableDebugByteCodeValue, { maxLength: 10 }),
287
+ });
288
+
289
+ // Try-catch handler generators
290
+ interface DalvikExecutableTry {
291
+ startAddress: number;
292
+ instructionCount: number;
293
+ handler: DalvikExecutableEncodedCatchHandler;
294
+ }
295
+
296
+ interface DalvikExecutableEncodedCatchHandler {
297
+ handlers: DalvikExecutableEncodedTypeAddressPair[];
298
+ catchAllAddress: undefined | number;
299
+ }
300
+
301
+ interface DalvikExecutableEncodedTypeAddressPair {
302
+ type: string;
303
+ address: number;
304
+ }
305
+
306
+ const arbitraryDalvikExecutableEncodedTypeAddressPair: fc.Arbitrary<DalvikExecutableEncodedTypeAddressPair> = fc.record({
307
+ type: arbitraryDalvikClassName,
308
+ address: fc.nat({ max: 65535 }),
309
+ });
310
+
311
+ const arbitraryDalvikExecutableEncodedCatchHandler: fc.Arbitrary<DalvikExecutableEncodedCatchHandler> = fc.record({
312
+ handlers: fc.array(arbitraryDalvikExecutableEncodedTypeAddressPair, { maxLength: 3 }),
313
+ catchAllAddress: fc.option(fc.nat({ max: 65535 }), { nil: undefined }),
314
+ }).filter(handler => {
315
+ // A handler must have at least one typed handler OR a catch-all address
316
+ return handler.handlers.length > 0 || handler.catchAllAddress !== undefined;
317
+ });
318
+
319
+ const arbitraryDalvikExecutableTry: fc.Arbitrary<DalvikExecutableTry> = fc.record({
320
+ startAddress: fc.nat({ max: 65535 }),
321
+ instructionCount: fc.nat({ max: 255 }),
322
+ handler: arbitraryDalvikExecutableEncodedCatchHandler,
323
+ });
324
+
325
+ // Generic factory function for DalvikExecutable
326
+ export const createArbitraryDalvikExecutable = <Instructions>(
327
+ arbitraryInstructions: fc.Arbitrary<Instructions>,
328
+ ): fc.Arbitrary<DalvikExecutable<Instructions>> => {
329
+ // Code generator using provided instructions arbitrary
330
+ const arbitraryDalvikExecutableCode: fc.Arbitrary<DalvikExecutableCode<Instructions>> = fc.record({
331
+ registersSize: fc.nat({ max: 65535 }),
332
+ insSize: fc.nat({ max: 255 }),
333
+ outsSize: fc.nat({ max: 255 }),
334
+ debugInfo: fc.option(arbitraryDalvikExecutableDebugInfo, { nil: undefined }),
335
+ instructions: arbitraryInstructions,
336
+ tries: fc.array(arbitraryDalvikExecutableTry, { maxLength: 2 }),
337
+ });
338
+
339
+ // Method with access and code
340
+ const arbitraryDalvikExecutableMethodWithAccess: fc.Arbitrary<DalvikExecutableMethodWithAccess<Instructions>> = fc.record({
341
+ method: arbitraryDalvikExecutableMethod,
342
+ accessFlags: arbitraryDalvikExecutableMethodAccessFlags,
343
+ code: fc.option(arbitraryDalvikExecutableCode, { nil: undefined }),
344
+ });
345
+
346
+ // Annotation collections
347
+ const arbitraryDalvikExecutableClassFieldAnnotation: fc.Arbitrary<DalvikExecutableClassFieldAnnotation> = fc.record({
348
+ field: arbitraryDalvikExecutableField,
349
+ annotations: fc.option(fc.array(arbitraryDalvikExecutableAnnotation, { maxLength: 2 }), { nil: undefined }),
350
+ });
351
+
352
+ const arbitraryDalvikExecutableClassMethodAnnotation: fc.Arbitrary<DalvikExecutableClassMethodAnnotation> = fc.record({
353
+ method: arbitraryDalvikExecutableMethod,
354
+ annotations: fc.array(arbitraryDalvikExecutableAnnotation, { maxLength: 2 }),
355
+ });
356
+
357
+ const arbitraryDalvikExecutableClassParameterAnnotation: fc.Arbitrary<DalvikExecutableClassParameterAnnotation> = fc.record({
358
+ method: arbitraryDalvikExecutableMethod,
359
+ annotations: fc.array(
360
+ fc.array(arbitraryDalvikExecutableAnnotation, { maxLength: 2 }),
361
+ { maxLength: 3 }
362
+ ),
363
+ });
364
+
365
+ const arbitraryDalvikExecutableClassAnnotations: fc.Arbitrary<DalvikExecutableClassAnnotations> = fc.record({
366
+ classAnnotations: fc.array(arbitraryDalvikExecutableAnnotation, { maxLength: 2 }),
367
+ fieldAnnotations: fc.array(arbitraryDalvikExecutableClassFieldAnnotation, { maxLength: 2 }),
368
+ methodAnnotations: fc.array(arbitraryDalvikExecutableClassMethodAnnotation, { maxLength: 2 }),
369
+ parameterAnnotations: fc.array(arbitraryDalvikExecutableClassParameterAnnotation, { maxLength: 2 }),
370
+ }).map(annotations => ({
371
+ ...annotations,
372
+ // Filter out field/method/parameter annotations with undefined or empty annotations array
373
+ // In DEX format, fields/methods/parameters with no annotations should not appear in the annotations directory at all
374
+ fieldAnnotations: annotations.fieldAnnotations.filter(fa => fa.annotations !== undefined && fa.annotations.length > 0),
375
+ methodAnnotations: annotations.methodAnnotations.filter(ma => ma.annotations.length > 0),
376
+ parameterAnnotations: annotations.parameterAnnotations.filter(pa => pa.annotations.length > 0 && pa.annotations.some(paramAnnots => paramAnnots.length > 0)),
377
+ }));
378
+
379
+ // Class data
380
+ const arbitraryDalvikExecutableClassData: fc.Arbitrary<DalvikExecutableClassData<Instructions>> = fc.record({
381
+ staticFields: fc.array(arbitraryDalvikExecutableFieldWithAccess, { maxLength: 3 }),
382
+ instanceFields: fc.array(arbitraryDalvikExecutableFieldWithAccess, { maxLength: 3 }),
383
+ directMethods: fc.array(arbitraryDalvikExecutableMethodWithAccess, { maxLength: 3 }),
384
+ virtualMethods: fc.array(arbitraryDalvikExecutableMethodWithAccess, { maxLength: 3 }),
385
+ }).filter(classData =>
386
+ // Filter out empty classData (all arrays empty) as it's semantically equivalent to undefined
387
+ classData.staticFields.length > 0 ||
388
+ classData.instanceFields.length > 0 ||
389
+ classData.directMethods.length > 0 ||
390
+ classData.virtualMethods.length > 0
391
+ );
392
+
393
+ // Class definition
394
+ const arbitraryDalvikExecutableClassDefinition: fc.Arbitrary<DalvikExecutableClassDefinition<Instructions>> = fc.record({
395
+ class: arbitraryDalvikClassName,
396
+ accessFlags: arbitraryDalvikExecutableAccessFlags,
397
+ superclass: arbitraryDalvikClassName,
398
+ interfaces: fc.array(arbitraryDalvikClassName, { maxLength: 3 }),
399
+ sourceFile: fc.option(fc.stringMatching(/^[a-zA-Z0-9_]+\.java$/), { nil: undefined }),
400
+ annotations: fc.option(arbitraryDalvikExecutableClassAnnotations, { nil: undefined }),
401
+ staticValues: fc.array(arbitraryDalvikExecutableEncodedValue, { maxLength: 3 }),
402
+ classData: fc.option(arbitraryDalvikExecutableClassData, { nil: undefined }),
403
+ }).map(classDef => {
404
+ // Match parser logic: if all members are synthetic, set class synthetic to true
405
+ const allMembers = [
406
+ ...classDef.classData?.staticFields ?? [],
407
+ ...classDef.classData?.instanceFields ?? [],
408
+ ...classDef.classData?.directMethods ?? [],
409
+ // Note: virtualMethods are not included to match parser behavior
410
+ ];
411
+ const allMembersAreSynthetic = (
412
+ allMembers.every(member => member.accessFlags.synthetic)
413
+ && allMembers.length > 0
414
+ );
415
+
416
+ if (allMembersAreSynthetic) {
417
+ return {
418
+ ...classDef,
419
+ accessFlags: {
420
+ ...classDef.accessFlags,
421
+ synthetic: true,
422
+ },
423
+ };
424
+ }
425
+
426
+ return classDef;
427
+ });
428
+
429
+ // Root DalvikExecutable
430
+ return fc.record({
431
+ classDefinitions: fc.array(arbitraryDalvikExecutableClassDefinition, { minLength: 1, maxLength: 3 }),
432
+ link: fc.option(fc.uint8Array({ minLength: 1, maxLength: 10 }), { nil: undefined }),
433
+ });
434
+ };
@@ -3,6 +3,7 @@ import * as fc from 'fast-check';
3
3
  export const arbitraryDosDateTime = fc.date({
4
4
  min: new Date(Date.UTC(1980, 0, 1)),
5
5
  max: new Date(Date.UTC(2099, 11, 31)),
6
+ noInvalidDate: true,
6
7
  }).map(date => {
7
8
  date.setSeconds(0);
8
9
  date.setMilliseconds(0);
@@ -6,7 +6,7 @@ function addZipEntryToZip(zip: JSZip, zipEntry: ZipEntry) {
6
6
  const options = {
7
7
  comment: zipEntry.comment,
8
8
  date: zipEntry.date,
9
- // unixPermissions: zipEntry.permissions.type === 'unix' ? zipEntry.permissions.unixPermissions : undefined,
9
+ // UnixPermissions: zipEntry.permissions.type === 'unix' ? zipEntry.permissions.unixPermissions : undefined,
10
10
  // dosPermissions: zipEntry.permissions.type === 'dos' ? zipEntry.permissions.dosPermissions : undefined,
11
11
  };
12
12
 
@@ -1,5 +1,5 @@
1
1
  import { getParserName, type Parser, setParserName } from './parser.js';
2
- import { ParserParsingFailedError } from './parserError.js';
2
+ import { isParserParsingFailedError } from './parserError.js';
3
3
 
4
4
  export const createArrayParser = <ElementOutput, Sequence>(
5
5
  elementParser: Parser<ElementOutput, Sequence>,
@@ -19,7 +19,7 @@ export const createArrayParser = <ElementOutput, Sequence>(
19
19
  elements.push(element);
20
20
  elementParserContext.unlookahead();
21
21
  } catch (error) {
22
- if (error instanceof ParserParsingFailedError) {
22
+ if (isParserParsingFailedError(error)) {
23
23
  return elements;
24
24
  }
25
25
 
@@ -1,4 +1,4 @@
1
- import { Unparser } from "./unparser.js";
1
+ import { type Unparser } from './unparser.js';
2
2
 
3
3
  export const createArrayUnparser = <ElementInput, Sequence>(
4
4
  elementUnparser: Unparser<ElementInput, Sequence>,
@@ -10,4 +10,4 @@ export const createArrayUnparser = <ElementInput, Sequence>(
10
10
  };
11
11
 
12
12
  return arrayUnparser;
13
- }
13
+ };
package/src/backsmali.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import fs from 'node:fs/promises';
2
+ import path from 'node:path';
2
3
  import { execa } from 'execa';
3
4
  import { temporaryDirectory, temporaryFile } from 'tempy';
4
- import path from 'node:path';
5
+ import { smaliClass } from './smali.js';
5
6
 
6
- export async function baksmaliClass(dexStream: AsyncIterable<Uint8Array>, smaliFilePath: string) {
7
+ export async function baksmaliClass(
8
+ dexStream: Uint8Array | AsyncIterable<Uint8Array>,
9
+ smaliFilePath: string,
10
+ ): Promise<string> {
7
11
  const inputFilePath = temporaryFile();
8
12
  const outputDirectoryPath = temporaryDirectory();
9
13
 
@@ -11,8 +15,10 @@ export async function baksmaliClass(dexStream: AsyncIterable<Uint8Array>, smaliF
11
15
 
12
16
  await execa('baksmali', [
13
17
  'disassemble',
14
- '--classes', 'L' + smaliFilePath + ';',
15
- '--output', outputDirectoryPath,
18
+ '--classes',
19
+ 'L' + smaliFilePath + ';',
20
+ '--output',
21
+ outputDirectoryPath,
16
22
  inputFilePath,
17
23
  ]);
18
24
 
@@ -28,3 +34,41 @@ export async function baksmaliClass(dexStream: AsyncIterable<Uint8Array>, smaliF
28
34
 
29
35
  return smali;
30
36
  }
37
+
38
+ export async function backsmaliSmaliIsolateClass(
39
+ dexStream: Uint8Array | AsyncIterable<Uint8Array>,
40
+ smaliFilePath: string,
41
+ ): Promise<Uint8Array> {
42
+ const smali = await baksmaliClass(dexStream, smaliFilePath);
43
+ return smaliClass(smali);
44
+ }
45
+
46
+ export async function baksmaliListClasses(dexStream: Uint8Array | AsyncIterable<Uint8Array>): Promise<string[]> {
47
+ const inputFilePath = temporaryFile();
48
+
49
+ await fs.writeFile(inputFilePath, dexStream);
50
+
51
+ const result = await execa('baksmali', [
52
+ 'list',
53
+ 'classes',
54
+ inputFilePath,
55
+ ]);
56
+
57
+ await fs.unlink(inputFilePath);
58
+
59
+ if (result.stderr) {
60
+ throw new Error(`baksmali error: ${result.stderr}`);
61
+ }
62
+
63
+ const classes = result.stdout
64
+ .split('\n')
65
+ .filter(line => line.trim())
66
+ .map(line => line.trim())
67
+ .map(class_ => (
68
+ class_
69
+ .replace(/^L/, '')
70
+ .replace(/;$/, '')
71
+ ));
72
+
73
+ return classes;
74
+ }
package/src/bash.ts ADDED
@@ -0,0 +1,120 @@
1
+ // Word: a single argument/token (may contain expansions)
2
+ export type BashWord = {
3
+ parts: BashWordPart[];
4
+ };
5
+
6
+ export type BashWordPart =
7
+ | BashWordPartLiteral
8
+ | BashWordPartSingleQuoted
9
+ | BashWordPartDoubleQuoted
10
+ | BashWordPartVariable
11
+ | BashWordPartVariableBraced
12
+ | BashWordPartCommandSubstitution
13
+ | BashWordPartBacktickSubstitution
14
+ | BashWordPartArithmeticExpansion;
15
+
16
+ export type BashWordPartLiteral = {
17
+ type: 'literal';
18
+ value: string;
19
+ };
20
+
21
+ export type BashWordPartSingleQuoted = {
22
+ type: 'singleQuoted';
23
+ value: string;
24
+ };
25
+
26
+ export type BashWordPartDoubleQuoted = {
27
+ type: 'doubleQuoted';
28
+ parts: BashWordPart[];
29
+ };
30
+
31
+ export type BashWordPartVariable = {
32
+ type: 'variable';
33
+ name: string;
34
+ };
35
+
36
+ export type BashWordPartVariableBraced = {
37
+ type: 'variableBraced';
38
+ name: string;
39
+ operator?: string;
40
+ operand?: BashWord;
41
+ };
42
+
43
+ export type BashWordPartCommandSubstitution = {
44
+ type: 'commandSubstitution';
45
+ command: BashCommand;
46
+ };
47
+
48
+ export type BashWordPartBacktickSubstitution = {
49
+ type: 'backtickSubstitution';
50
+ command: BashCommand;
51
+ };
52
+
53
+ export type BashWordPartArithmeticExpansion = {
54
+ type: 'arithmeticExpansion';
55
+ expression: string;
56
+ };
57
+
58
+ // Redirect: file descriptor operations
59
+ export type BashRedirect = {
60
+ fd?: number;
61
+ operator: '>' | '>>' | '<' | '<<' | '<<<' | '>&' | '<&' | '>|';
62
+ target: BashWord | BashHereDoc;
63
+ };
64
+
65
+ export type BashHereDoc = {
66
+ type: 'hereDoc';
67
+ delimiter: string;
68
+ content: string;
69
+ quoted: boolean;
70
+ };
71
+
72
+ // Assignment
73
+ export type BashAssignment = {
74
+ name: string;
75
+ value?: BashWord;
76
+ };
77
+
78
+ // Simple command: name + args + redirects
79
+ export type BashSimpleCommand = {
80
+ type: 'simple';
81
+ name?: BashWord;
82
+ args: BashWord[];
83
+ redirects: BashRedirect[];
84
+ assignments: BashAssignment[];
85
+ };
86
+
87
+ // Compound commands (structural syntax only)
88
+ export type BashSubshell = {
89
+ type: 'subshell';
90
+ body: BashCommand;
91
+ };
92
+
93
+ export type BashBraceGroup = {
94
+ type: 'braceGroup';
95
+ body: BashCommand;
96
+ };
97
+
98
+ export type BashCommandUnit = BashSimpleCommand | BashSubshell | BashBraceGroup;
99
+
100
+ // Pipeline: cmd1 | cmd2 | cmd3
101
+ export type BashPipeline = {
102
+ type: 'pipeline';
103
+ negated: boolean;
104
+ commands: BashCommandUnit[];
105
+ };
106
+
107
+ export type BashCommandListSeparator = '&&' | '||' | ';' | '&' | '\n';
108
+
109
+ // Command list: pipelines connected by && || ; &
110
+ export type BashCommandList = {
111
+ type: 'list';
112
+ entries: {
113
+ pipeline: BashPipeline;
114
+ separator?: BashCommandListSeparator;
115
+ }[];
116
+ };
117
+
118
+ // Top-level
119
+ export type BashCommand = BashCommandList;
120
+ export type BashScript = BashCommand;