tstyche 1.0.0-beta.4 → 1.0.0-beta.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.0-beta.6] - 2023-12-03
4
+
5
+ ### Added
6
+
7
+ - **New!** Add `.toHaveProperty()` matcher ([#36](https://github.com/tstyche/tstyche/pull/36))
8
+
9
+ ### Fixed
10
+
11
+ - Accept template literals as arguments of the `.toRaiseError()` matcher ([#38](https://github.com/tstyche/tstyche/pull/38))
12
+
13
+ ## [1.0.0-beta.5] - 2023-11-27
14
+
15
+ ### Changed
16
+
17
+ - **Breaking!** Move retry logic to the `Lock` class ([#31](https://github.com/tstyche/tstyche/pull/31))
18
+ - Bring back support for Node.js 16 ([#28](https://github.com/tstyche/tstyche/pull/28), [#27](https://github.com/tstyche/tstyche/pull/27))
19
+
20
+ ### Added
21
+
22
+ - **New!** Add support for the `current` target tag ([#33](https://github.com/tstyche/tstyche/pull/33))
23
+
24
+ ### Fixed
25
+
26
+ - Allow `.raiseError()` to take template literals as arguments ([#35](https://github.com/tstyche/tstyche/pull/35))
27
+
3
28
  ## [1.0.0-beta.4] - 2023-11-24
4
29
 
5
30
  ### Added
@@ -8,9 +33,9 @@
8
33
 
9
34
  ### Removed
10
35
 
11
- - **Breaking:** Remove the `context()` helper ([#24](https://github.com/tstyche/tstyche/pull/24))
12
- - **Breaking:** Drop support for Node.js 16 ([#22](https://github.com/tstyche/tstyche/pull/22))
13
- - **Breaking:** Rename methods of the `StoreService` class ([`5d74201`](https://github.com/tstyche/tstyche/commit/5d74201))
36
+ - **Breaking!** Remove the `context()` helper ([#24](https://github.com/tstyche/tstyche/pull/24))
37
+ - **Breaking!** Drop support for Node.js 16 ([#22](https://github.com/tstyche/tstyche/pull/22))
38
+ - **Breaking!** Rename methods of the `StoreService` class ([`5d74201`](https://github.com/tstyche/tstyche/commit/5d74201))
14
39
 
15
40
  ### Fixed
16
41
 
@@ -40,6 +65,8 @@
40
65
 
41
66
  _First pre-release._
42
67
 
68
+ [1.0.0-beta.6]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.6
69
+ [1.0.0-beta.5]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.5
43
70
  [1.0.0-beta.4]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.4
44
71
  [1.0.0-beta.3]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.3
45
72
  [1.0.0-beta.2]: https://github.com/tstyche/tstyche/releases/tag/v1.0.0-beta.2
package/README.md CHANGED
@@ -56,9 +56,10 @@ test("handles numbers", () => {
56
56
 
57
57
  Here is the list of all matchers:
58
58
 
59
- - `.toBeAssignable()`, `.toEqual()`, `.toMatch()` compares types or types of expression;
60
- - `.toRaiseError()` captures the error message or code;
61
- - `.toBeString()`, `.toBeNumber()`, `.toBeVoid()` and 9 more checks for primitive types.
59
+ - `.toBeAssignable()`, `.toEqual()`, `.toMatch()` compares types or types of expression,
60
+ - `.toHaveProperty()` looks up keys on an object type,
61
+ - `.toRaiseError()` captures the type error message or code,
62
+ - `.toBeString()`, `.toBeNumber()`, `.toBeVoid()` and 9 more shorthand checks for primitive types.
62
63
 
63
64
  ## Runner
64
65
 
package/build/index.d.cts CHANGED
@@ -133,6 +133,10 @@ interface Matchers {
133
133
  */
134
134
  (target: unknown): void;
135
135
  };
136
+ /**
137
+ * Checks if a property key exists on the source type.
138
+ */
139
+ toHaveProperty: (key: string | number | symbol) => void;
136
140
  /**
137
141
  * Checks if the target type is a subtype the source type.
138
142
  */
package/build/index.d.ts CHANGED
@@ -133,6 +133,10 @@ interface Matchers {
133
133
  */
134
134
  (target: unknown): void;
135
135
  };
136
+ /**
137
+ * Checks if a property key exists on the source type.
138
+ */
139
+ toHaveProperty: (key: string | number | symbol) => void;
136
140
  /**
137
141
  * Checks if the target type is a subtype the source type.
138
142
  */
package/build/tstyche.js CHANGED
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
  import fs from 'node:fs/promises';
6
6
  import { createRequire } from 'node:module';
7
7
  import { spawn } from 'node:child_process';
8
- import { setInterval } from 'node:timers/promises';
8
+ import https from 'node:https';
9
9
 
10
10
  class EventEmitter {
11
11
  static #handlers = new Set();
@@ -1396,7 +1396,10 @@ class IdentifierLookup {
1396
1396
  };
1397
1397
  }
1398
1398
  clone() {
1399
- return structuredClone(this.#identifiers);
1399
+ return {
1400
+ namedImports: { ...this.#identifiers.namedImports },
1401
+ namespace: this.#identifiers.namespace,
1402
+ };
1400
1403
  }
1401
1404
  handleImportDeclaration(node) {
1402
1405
  if (this.#moduleSpecifiers.includes(node.moduleSpecifier.getText()) &&
@@ -1508,6 +1511,7 @@ class CollectService {
1508
1511
  "toBeUnknown",
1509
1512
  "toBeVoid",
1510
1513
  "toEqual",
1514
+ "toHaveProperty",
1511
1515
  "toMatch",
1512
1516
  "toRaiseError",
1513
1517
  ];
@@ -1679,8 +1683,12 @@ class Checker {
1679
1683
  #assertNonNullishTypeChecker(assertion) {
1680
1684
  this.#assertNonNullish(assertion.typeChecker, "The 'typeChecker' was not provided.");
1681
1685
  }
1686
+ #assertStringsOrNumber(expression) {
1687
+ return (expression != null &&
1688
+ (this.compiler.isStringLiteralLike(expression) || this.compiler.isNumericLiteral(expression)));
1689
+ }
1682
1690
  #assertStringsOrNumbers(nodes) {
1683
- return nodes.every((expression) => this.compiler.isStringLiteral(expression) || this.compiler.isNumericLiteral(expression));
1691
+ return nodes.every((expression) => this.#assertStringsOrNumber(expression));
1684
1692
  }
1685
1693
  explain(assertion) {
1686
1694
  this.#assertNonNullishTypeChecker(assertion);
@@ -1738,6 +1746,26 @@ class Checker {
1738
1746
  : `Type '${targetTypeText}' is not identical to type '${sourceTypeText}'.`, origin),
1739
1747
  ];
1740
1748
  }
1749
+ case "toHaveProperty": {
1750
+ this.#assertNonNullishSourceType(assertion);
1751
+ this.#assertNonNullishTargetType(assertion);
1752
+ const sourceText = assertion.typeChecker.typeToString(assertion.sourceType.type);
1753
+ let targetArgumentText;
1754
+ if (assertion.targetType.type.flags & this.compiler.TypeFlags.StringOrNumberLiteral) {
1755
+ targetArgumentText = String(assertion.targetType.type.value);
1756
+ }
1757
+ else if (assertion.targetType.type.flags & this.compiler.TypeFlags.UniqueESSymbol) {
1758
+ targetArgumentText = `[${this.compiler.unescapeLeadingUnderscores(assertion.targetType.type.symbol.escapedName)}]`;
1759
+ }
1760
+ else {
1761
+ throw new Error("An argument for 'key' must be of type 'string | number | symbol'.");
1762
+ }
1763
+ return [
1764
+ Diagnostic.error(assertion.isNot
1765
+ ? `Property '${targetArgumentText}' exists on type '${sourceText}'.`
1766
+ : `Property '${targetArgumentText}' does not exist on type '${sourceText}'.`, origin),
1767
+ ];
1768
+ }
1741
1769
  case "toMatch": {
1742
1770
  this.#assertNonNullishSourceType(assertion);
1743
1771
  this.#assertNonNullishTargetType(assertion);
@@ -1791,7 +1819,7 @@ class Checker {
1791
1819
  }
1792
1820
  const isMatch = this.#matchExpectedError(diagnostic, argument);
1793
1821
  if (!assertion.isNot && !isMatch) {
1794
- const expectedText = this.compiler.isStringLiteral(argument)
1822
+ const expectedText = this.compiler.isStringLiteralLike(argument)
1795
1823
  ? `matching substring '${argument.text}'`
1796
1824
  : `with code ${argument.text}`;
1797
1825
  const related = [
@@ -1801,7 +1829,7 @@ class Checker {
1801
1829
  diagnostics.push(Diagnostic.error(`${sourceText} did not raise a type error ${expectedText}.`, origin).add({ related }));
1802
1830
  }
1803
1831
  if (assertion.isNot && isMatch) {
1804
- const expectedText = this.compiler.isStringLiteral(argument)
1832
+ const expectedText = this.compiler.isStringLiteralLike(argument)
1805
1833
  ? `matching substring '${argument.text}'`
1806
1834
  : `with code ${argument.text}`;
1807
1835
  const related = [
@@ -1887,6 +1915,27 @@ class Checker {
1887
1915
  this.#assertNonNullish(assertion.typeChecker?.isTypeIdenticalTo, "The 'isTypeIdenticalTo' method is missing in the provided type checker.");
1888
1916
  return assertion.typeChecker.isTypeIdenticalTo(assertion.sourceType.type, assertion.targetType.type);
1889
1917
  }
1918
+ case "toHaveProperty": {
1919
+ this.#assertNonNullishSourceType(assertion);
1920
+ this.#assertNonNullishTargetType(assertion);
1921
+ if (!(assertion.sourceType.type.flags & this.compiler.TypeFlags.StructuredType)) {
1922
+ const receivedText = assertion.typeChecker?.typeToString(assertion.sourceType.type);
1923
+ throw new Error(`An argument for 'source' must be of object type, received: '${receivedText}'.`);
1924
+ }
1925
+ let targetArgumentText;
1926
+ if (assertion.targetType.type.flags & this.compiler.TypeFlags.StringOrNumberLiteral) {
1927
+ targetArgumentText = String(assertion.targetType.type.value);
1928
+ }
1929
+ else if (assertion.targetType.type.flags & this.compiler.TypeFlags.UniqueESSymbol) {
1930
+ targetArgumentText = this.compiler.unescapeLeadingUnderscores(assertion.targetType.type.escapedName);
1931
+ }
1932
+ else {
1933
+ throw new Error("An argument for 'key' must be of type 'string | number | symbol'.");
1934
+ }
1935
+ return assertion.sourceType.type.getProperties().some((property) => {
1936
+ return this.compiler.unescapeLeadingUnderscores(property.escapedName) === targetArgumentText;
1937
+ });
1938
+ }
1890
1939
  case "toMatch": {
1891
1940
  this.#assertNonNullishSourceType(assertion);
1892
1941
  this.#assertNonNullishTargetType(assertion);
@@ -1904,7 +1953,7 @@ class Checker {
1904
1953
  return false;
1905
1954
  }
1906
1955
  return assertion.targetArguments.every((expectedArgument, index) => {
1907
- if (this.compiler.isStringLiteral(expectedArgument)) {
1956
+ if (this.compiler.isStringLiteralLike(expectedArgument)) {
1908
1957
  return this.compiler
1909
1958
  .flattenDiagnosticMessageText(assertion.diagnostics[index]?.messageText, " ", 0)
1910
1959
  .includes(expectedArgument.text);
@@ -1920,7 +1969,7 @@ class Checker {
1920
1969
  }
1921
1970
  }
1922
1971
  #matchExpectedError(diagnostic, argument) {
1923
- if (this.compiler.isStringLiteral(argument)) {
1972
+ if (this.compiler.isStringLiteralLike(argument)) {
1924
1973
  return this.compiler.flattenDiagnosticMessageText(diagnostic.messageText, " ", 0).includes(argument.text);
1925
1974
  }
1926
1975
  if (this.compiler.isNumericLiteral(argument)) {
@@ -2330,7 +2379,7 @@ class OptionDefinitionsMap {
2330
2379
  items: {
2331
2380
  brand: "string",
2332
2381
  name: "target",
2333
- pattern: "^([45]\\.[0-9](\\.[0-9])?)|beta|latest|next|rc$",
2382
+ pattern: "^([45]\\.[0-9](\\.[0-9])?)|beta|current|latest|next|rc$",
2334
2383
  },
2335
2384
  name: "target",
2336
2385
  },
@@ -2426,7 +2475,7 @@ class OptionUsageText {
2426
2475
  const supportedTagsText = `Supported tags: ${["'", supportedTags.join("', '"), "'"].join("")}.`;
2427
2476
  switch (this.#optionGroup) {
2428
2477
  case 2:
2429
- usageText.push("Argument for the '--target' option must be a single tag or a comma separated list of versions.", "Usage examples: '--target 4.9', '--target 5.0.4', '--target 4.7,4.8,latest'.", supportedTagsText);
2478
+ usageText.push("Argument for the '--target' option must be a single tag or a comma separated list.", "Usage examples: '--target 4.9', '--target 5.0.4', '--target 4.7,4.8,latest'.", supportedTagsText);
2430
2479
  break;
2431
2480
  case 4:
2432
2481
  usageText.push("Item of the 'target' list must be a supported version tag.", supportedTagsText);
@@ -2916,58 +2965,85 @@ class Lock {
2916
2965
  #lockFilePath;
2917
2966
  static #lockSuffix = "__lock__";
2918
2967
  constructor(targetPath) {
2919
- this.#lockFilePath = Lock.getLockFilePath(targetPath);
2968
+ this.#lockFilePath = Lock.#getLockFilePath(targetPath);
2920
2969
  writeFileSync(this.#lockFilePath, "");
2921
2970
  process.on("exit", () => {
2922
2971
  this.release();
2923
2972
  });
2924
2973
  }
2925
- static getLockFilePath(targetPath) {
2974
+ static #getLockFilePath(targetPath) {
2926
2975
  return `${targetPath}${Lock.#lockSuffix}`;
2927
2976
  }
2928
- static isLocked(targetPath) {
2929
- return existsSync(Lock.getLockFilePath(targetPath));
2977
+ static async isLocked(targetPath, options) {
2978
+ let isLocked = existsSync(Lock.#getLockFilePath(targetPath));
2979
+ if (!isLocked) {
2980
+ return isLocked;
2981
+ }
2982
+ if (options?.timeout == null) {
2983
+ return isLocked;
2984
+ }
2985
+ const waitStartTime = Date.now();
2986
+ while (isLocked) {
2987
+ if (options.signal?.aborted === true) {
2988
+ break;
2989
+ }
2990
+ if (Date.now() - waitStartTime > options.timeout) {
2991
+ options.onDiagnostic?.(`Lock wait timeout of ${options.timeout / 1000}s was exceeded.`);
2992
+ break;
2993
+ }
2994
+ await Lock.#sleep(1000);
2995
+ isLocked = existsSync(Lock.#getLockFilePath(targetPath));
2996
+ }
2997
+ return isLocked;
2930
2998
  }
2931
2999
  release() {
2932
3000
  rmSync(this.#lockFilePath, { force: true });
2933
3001
  }
3002
+ static async #sleep(time) {
3003
+ return new Promise((resolve) => setTimeout(resolve, time));
3004
+ }
2934
3005
  }
2935
3006
 
2936
3007
  class CompilerModuleWorker {
2937
3008
  #cachePath;
3009
+ #onDiagnostic;
2938
3010
  #readyFileName = "__ready__";
2939
3011
  #timeout = Environment.timeout * 1000;
2940
- constructor(cachePath) {
3012
+ constructor(cachePath, onDiagnostic) {
2941
3013
  this.#cachePath = cachePath;
3014
+ this.#onDiagnostic = onDiagnostic;
2942
3015
  }
2943
3016
  async ensure(compilerVersion, signal) {
2944
3017
  const installationPath = path.join(this.#cachePath, compilerVersion);
2945
3018
  const readyFilePath = path.join(installationPath, this.#readyFileName);
2946
3019
  const tsserverFilePath = path.join(installationPath, "node_modules", "typescript", "lib", "tsserverlibrary.js");
2947
3020
  const typescriptFilePath = path.join(installationPath, "node_modules", "typescript", "lib", "typescript.js");
2948
- if (Lock.isLocked(installationPath)) {
2949
- for await (const now of setInterval(1000, Date.now(), { signal })) {
2950
- const startTime = Date.now();
2951
- if (!Lock.isLocked(installationPath)) {
2952
- break;
2953
- }
2954
- if (startTime - now > this.#timeout) {
2955
- throw new Error(`Lock wait timeout of ${this.#timeout / 1000}s was exceeded.`);
2956
- }
2957
- }
3021
+ if (await Lock.isLocked(installationPath, {
3022
+ onDiagnostic: (text) => {
3023
+ this.#onDiagnostic(Diagnostic.error([`Failed to install 'typescript@${compilerVersion}'.`, text]));
3024
+ },
3025
+ signal,
3026
+ timeout: this.#timeout,
3027
+ })) {
3028
+ return;
2958
3029
  }
2959
3030
  if (existsSync(readyFilePath)) {
2960
3031
  return tsserverFilePath;
2961
3032
  }
2962
3033
  EventEmitter.dispatch(["store:info", { compilerVersion, installationPath: this.#normalizePath(installationPath) }]);
2963
- await fs.mkdir(installationPath, { recursive: true });
2964
- const lock = new Lock(installationPath);
2965
- await fs.writeFile(path.join(installationPath, "package.json"), this.#getPackageJson(compilerVersion));
2966
- await this.#installPackage(installationPath, signal);
2967
- await fs.writeFile(tsserverFilePath, await this.#getPatched(compilerVersion, tsserverFilePath));
2968
- await fs.writeFile(typescriptFilePath, await this.#getPatched(compilerVersion, typescriptFilePath));
2969
- await fs.writeFile(readyFilePath, "");
2970
- lock.release();
3034
+ try {
3035
+ await fs.mkdir(installationPath, { recursive: true });
3036
+ const lock = new Lock(installationPath);
3037
+ await fs.writeFile(path.join(installationPath, "package.json"), this.#getPackageJson(compilerVersion));
3038
+ await this.#installPackage(installationPath, signal);
3039
+ await fs.writeFile(tsserverFilePath, await this.#getPatched(compilerVersion, tsserverFilePath));
3040
+ await fs.writeFile(typescriptFilePath, await this.#getPatched(compilerVersion, typescriptFilePath));
3041
+ await fs.writeFile(readyFilePath, "");
3042
+ lock.release();
3043
+ }
3044
+ catch (error) {
3045
+ this.#onDiagnostic(Diagnostic.fromError(`Failed to install 'typescript@${compilerVersion}'.`, error));
3046
+ }
2971
3047
  return tsserverFilePath;
2972
3048
  }
2973
3049
  #getPackageJson(version) {
@@ -3043,14 +3119,34 @@ class ManifestWorker {
3043
3119
  this.#prune = prune;
3044
3120
  }
3045
3121
  async #fetch(signal) {
3046
- const result = await fetch(new URL("typescript", this.#registryUrl), {
3047
- headers: { accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*" },
3048
- signal,
3122
+ return new Promise((resolve, reject) => {
3123
+ const request = https.get(new URL("typescript", this.#registryUrl), {
3124
+ headers: { accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*" },
3125
+ signal,
3126
+ }, (result) => {
3127
+ if (result.statusCode !== 200) {
3128
+ reject(new Error(`Request failed with status code ${String(result.statusCode)}.`));
3129
+ return;
3130
+ }
3131
+ result.setEncoding("utf8");
3132
+ let rawData = "";
3133
+ result.on("data", (chunk) => {
3134
+ rawData += chunk;
3135
+ });
3136
+ result.on("end", () => {
3137
+ try {
3138
+ const packageMetadata = JSON.parse(rawData);
3139
+ resolve(packageMetadata);
3140
+ }
3141
+ catch (error) {
3142
+ reject(error);
3143
+ }
3144
+ });
3145
+ });
3146
+ request.on("error", (error) => {
3147
+ reject(error);
3148
+ });
3049
3149
  });
3050
- if (!result.ok) {
3051
- throw new Error(`Request failed with status code ${String(result.status)}.`);
3052
- }
3053
- return result.json();
3054
3150
  }
3055
3151
  isOutdated(manifest, ageTolerance = 0) {
3056
3152
  if (Date.now() - manifest.lastUpdated > 2 * 60 * 60 * 1000 + ageTolerance * 1000) {
@@ -3093,15 +3189,15 @@ class ManifestWorker {
3093
3189
  .sort();
3094
3190
  const minorVersions = [...new Set(manifest.versions.map((version) => version.slice(0, -2)))];
3095
3191
  for (const tag of minorVersions) {
3096
- const resolvedVersion = manifest.versions.findLast((version) => version.startsWith(tag));
3192
+ const resolvedVersion = manifest.versions.filter((version) => version.startsWith(tag)).pop();
3097
3193
  if (resolvedVersion != null) {
3098
3194
  manifest.resolutions[tag] = resolvedVersion;
3099
3195
  }
3100
3196
  }
3101
- for (const distributionTagKey of ["beta", "latest", "next", "rc"]) {
3102
- const distributionTagValue = packageMetadata["dist-tags"][distributionTagKey];
3197
+ for (const tagKey of ["beta", "latest", "next", "rc"]) {
3198
+ const distributionTagValue = packageMetadata["dist-tags"][tagKey];
3103
3199
  if (distributionTagValue != null) {
3104
- manifest.resolutions[distributionTagKey] = distributionTagValue;
3200
+ manifest.resolutions[tagKey] = distributionTagValue;
3105
3201
  }
3106
3202
  }
3107
3203
  return manifest;
@@ -3171,7 +3267,7 @@ class StoreService {
3171
3267
  #nodeRequire = createRequire(import.meta.url);
3172
3268
  constructor() {
3173
3269
  this.#cachePath = Environment.storePath;
3174
- this.#compilerModuleWorker = new CompilerModuleWorker(this.#cachePath);
3270
+ this.#compilerModuleWorker = new CompilerModuleWorker(this.#cachePath, this.#onDiagnostic);
3175
3271
  this.#manifestWorker = new ManifestWorker(this.#cachePath, async () => this.prune());
3176
3272
  }
3177
3273
  get supportedTags() {
@@ -3179,7 +3275,7 @@ class StoreService {
3179
3275
  this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3180
3276
  return [];
3181
3277
  }
3182
- return [...Object.keys(this.#manifest.resolutions), ...this.#manifest.versions].sort();
3278
+ return [...Object.keys(this.#manifest.resolutions), ...this.#manifest.versions, "current"].sort();
3183
3279
  }
3184
3280
  async install(tag, signal) {
3185
3281
  if (!this.#manifest) {
@@ -3191,14 +3287,7 @@ class StoreService {
3191
3287
  this.#onDiagnostic(Diagnostic.error(`Cannot add the 'typescript' package for the '${tag}' tag.`));
3192
3288
  return;
3193
3289
  }
3194
- let modulePath;
3195
- try {
3196
- modulePath = await this.#compilerModuleWorker.ensure(version, signal);
3197
- }
3198
- catch (error) {
3199
- this.#onDiagnostic(Diagnostic.fromError(`Failed to install 'typescript@${version}'.`, error));
3200
- }
3201
- return modulePath;
3290
+ return this.#compilerModuleWorker.ensure(version, signal);
3202
3291
  }
3203
3292
  async load(tag, signal) {
3204
3293
  let modulePath;
@@ -3218,9 +3307,9 @@ class StoreService {
3218
3307
  }
3219
3308
  return;
3220
3309
  }
3221
- #onDiagnostic(diagnostic) {
3310
+ #onDiagnostic = (diagnostic) => {
3222
3311
  EventEmitter.dispatch(["store:error", { diagnostics: [diagnostic] }]);
3223
- }
3312
+ };
3224
3313
  async open(signal) {
3225
3314
  if (this.#manifest) {
3226
3315
  return;
@@ -3235,6 +3324,14 @@ class StoreService {
3235
3324
  this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3236
3325
  return;
3237
3326
  }
3327
+ if (tag === "current") {
3328
+ try {
3329
+ tag = this.#nodeRequire("typescript").version;
3330
+ }
3331
+ catch (error) {
3332
+ this.#onDiagnostic(Diagnostic.fromError("Failed to resolve tag 'current'. The 'typescript' package might be not installed.", error));
3333
+ }
3334
+ }
3238
3335
  if (this.#manifest.versions.includes(tag)) {
3239
3336
  return tag;
3240
3337
  }
@@ -3260,7 +3357,7 @@ class StoreService {
3260
3357
  this.#onDiagnostic(Diagnostic.error("Store manifest is not open. Call 'StoreService.open()' first."));
3261
3358
  return false;
3262
3359
  }
3263
- if (this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions) {
3360
+ if (this.#manifest.versions.includes(tag) || tag in this.#manifest.resolutions || tag === "current") {
3264
3361
  return true;
3265
3362
  }
3266
3363
  if (this.#manifest.resolutions["latest"] != null &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tstyche",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.6",
4
4
  "description": "The Essential Type Testing Tool.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -57,14 +57,14 @@
57
57
  "devDependencies": {
58
58
  "@jest/globals": "29.7.0",
59
59
  "@rollup/plugin-typescript": "11.1.5",
60
- "@types/node": "20.9.5",
61
- "@typescript-eslint/eslint-plugin": "6.12.0",
62
- "@typescript-eslint/parser": "6.12.0",
60
+ "@types/node": "20.10.2",
61
+ "@typescript-eslint/eslint-plugin": "6.13.1",
62
+ "@typescript-eslint/parser": "6.13.1",
63
63
  "ajv": "8.12.0",
64
64
  "c8": "8.0.1",
65
- "cspell": "8.0.0",
66
- "eslint": "8.54.0",
67
- "eslint-config-prettier": "9.0.0",
65
+ "cspell": "8.1.0",
66
+ "eslint": "8.55.0",
67
+ "eslint-config-prettier": "9.1.0",
68
68
  "eslint-import-resolver-typescript": "3.6.1",
69
69
  "eslint-plugin-import": "2.29.0",
70
70
  "eslint-plugin-jest": "27.6.0",
@@ -76,7 +76,7 @@
76
76
  "jest-serializer-ansi-escapes": "2.0.1",
77
77
  "magic-string": "0.30.5",
78
78
  "prettier": "3.1.0",
79
- "rollup": "4.5.2",
79
+ "rollup": "4.6.1",
80
80
  "rollup-plugin-dts": "6.1.0",
81
81
  "rollup-plugin-tsconfig-paths": "1.5.2",
82
82
  "ts-node": "10.9.1",
@@ -91,8 +91,8 @@
91
91
  "optional": true
92
92
  }
93
93
  },
94
- "packageManager": "yarn@4.0.2",
94
+ "packageManager": "yarn@3.7.0",
95
95
  "engines": {
96
- "node": "^18.12 || >=20.x"
96
+ "node": "^16.14 || 18.x || >=20.x"
97
97
  }
98
98
  }