@halospv3/hce.shared-config 2.6.4 → 3.0.0-develop.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/CHANGELOG.md +356 -101
  2. package/README.md +100 -87
  3. package/dotnet/.github/workflows/_unit_test.yml +6 -3
  4. package/dotnet/.github/workflows/ci.yml +2 -2
  5. package/dotnet/.github/workflows/dotnet-release.yml +31 -28
  6. package/dotnet/.github/workflows/sample-dotnet-build.yml +16 -11
  7. package/dotnet/ExecNupkgDeterministicator.README.md +20 -0
  8. package/dotnet/ExecNupkgDeterministicator.targets +173 -0
  9. package/dotnet/GitVersion.yml +3 -1
  10. package/dotnet/GitVersion6.0.yml +3 -1
  11. package/dotnet/HCE.Shared.sln +34 -0
  12. package/dotnet/HCE.Shared.targets +1 -0
  13. package/dotnet/PublishAll.targets +2 -0
  14. package/dotnet/SignAfterPack.targets +104 -0
  15. package/dotnet/samples/HCE.Shared.DeterministicNupkg/Dummy.cs +6 -0
  16. package/dotnet/samples/HCE.Shared.DeterministicNupkg/HCE.Shared.DeterministicNupkg.csproj +21 -0
  17. package/dotnet/samples/HCE.Shared.SignAfterPack/Class1.cs +6 -0
  18. package/dotnet/samples/HCE.Shared.SignAfterPack/HCE.Shared.SignAfterPack.csproj +21 -0
  19. package/dotnet/samples/HCE.Shared.SignAfterPack/sampleCert.samplepfx +0 -0
  20. package/dotnet/samples/README.md +7 -0
  21. package/package.json +72 -73
  22. package/src/CaseInsensitiveMap.ts +34 -0
  23. package/src/commitlintConfig.ts +17 -9
  24. package/src/debug.ts +3 -3
  25. package/src/dotnet/GithubNugetRegistryInfo.ts +60 -0
  26. package/src/dotnet/GitlabNugetRegistryInfo.ts +112 -0
  27. package/src/dotnet/IsNextVersionAlreadyPublished.cli.ts +44 -0
  28. package/src/dotnet/MSBuildProject.ts +557 -76
  29. package/src/dotnet/MSBuildProjectProperties.ts +280 -15
  30. package/src/dotnet/NugetProjectProperties.ts +608 -0
  31. package/src/dotnet/NugetRegistryInfo.ts +939 -0
  32. package/src/dotnet/helpers.ts +448 -0
  33. package/src/eslintConfig.ts +174 -71
  34. package/src/index.ts +1 -3
  35. package/src/semantic-release__commit-analyzer.d.ts +44 -38
  36. package/src/semantic-release__exec.d.ts +15 -0
  37. package/src/semantic-release__git.d.ts +85 -88
  38. package/src/semantic-release__github.d.ts +139 -139
  39. package/src/semanticReleaseConfig.ts +106 -47
  40. package/src/semanticReleaseConfigDotnet.ts +394 -104
  41. package/src/setupGitPluginSpec.ts +149 -57
  42. package/src/tsconfig.json +8 -8
  43. package/src/utils/Exact.ts +49 -0
  44. package/src/utils/GracefulRecursion.d.ts +12 -0
  45. package/src/utils/env.ts +44 -0
  46. package/src/utils/execAsync.ts +77 -0
  47. package/src/utils/miscTypes.ts +17 -0
  48. package/src/utils/reflection/FunctionLike.d.ts +17 -0
  49. package/src/utils/reflection/GetterDescriptor.d.ts +8 -0
  50. package/src/utils/reflection/InstancePropertyDescriptorMap.d.ts +32 -0
  51. package/src/utils/reflection/InstanceTypeOrSelfPropertyDescriptorMap.d.ts +20 -0
  52. package/src/utils/reflection/OwnGetterDescriptorMap.d.ts +17 -0
  53. package/src/utils/reflection/OwnKeyOf.d.ts +20 -0
  54. package/src/utils/reflection/OwnPropertyDescriptorMap.d.ts +82 -0
  55. package/src/utils/reflection/PropertyDescriptorMap.d.ts +15 -0
  56. package/src/utils/reflection/filterForGetters.ts +59 -0
  57. package/src/utils/reflection/getOwnPropertyDescriptors.ts +52 -0
  58. package/src/utils/reflection/getOwnPropertyDescriptorsRecursively.ts +127 -0
  59. package/src/utils/reflection/getPrototypeChainOf.ts +85 -0
  60. package/src/utils/reflection/getPrototypeOf.ts +12 -0
  61. package/src/utils/reflection/inheritance.ts +262 -0
  62. package/src/utils/reflection/isConstructor.ts +74 -0
  63. package/src/utils/reflection/isGetterDescriptor.ts +11 -0
  64. package/src/utils/reflection/listOwnGetters.ts +80 -0
  65. package/src/utils/reflection.ts +18 -0
  66. package/cjs/commitlintConfig-wrapper.mjs +0 -6
  67. package/cjs/commitlintConfig.cjs +0 -14
  68. package/cjs/commitlintConfig.cjs.map +0 -1
  69. package/cjs/commitlintConfig.d.ts +0 -4
  70. package/cjs/commitlintConfig.d.ts.map +0 -1
  71. package/cjs/debug.cjs +0 -13
  72. package/cjs/debug.cjs.map +0 -1
  73. package/cjs/debug.d.ts +0 -4
  74. package/cjs/debug.d.ts.map +0 -1
  75. package/cjs/dotnet/MSBuildProject.cjs +0 -84
  76. package/cjs/dotnet/MSBuildProject.cjs.map +0 -1
  77. package/cjs/dotnet/MSBuildProject.d.ts +0 -42
  78. package/cjs/dotnet/MSBuildProject.d.ts.map +0 -1
  79. package/cjs/dotnet/MSBuildProjectProperties.cjs +0 -22
  80. package/cjs/dotnet/MSBuildProjectProperties.cjs.map +0 -1
  81. package/cjs/dotnet/MSBuildProjectProperties.d.ts +0 -13
  82. package/cjs/dotnet/MSBuildProjectProperties.d.ts.map +0 -1
  83. package/cjs/dotnet/createDummyNupkg.cjs +0 -26
  84. package/cjs/dotnet/createDummyNupkg.cjs.map +0 -1
  85. package/cjs/dotnet/createDummyNupkg.d.ts +0 -2
  86. package/cjs/dotnet/createDummyNupkg.d.ts.map +0 -1
  87. package/cjs/dotnet/dotnetGHPR.cjs +0 -173
  88. package/cjs/dotnet/dotnetGHPR.cjs.map +0 -1
  89. package/cjs/dotnet/dotnetGHPR.d.ts +0 -37
  90. package/cjs/dotnet/dotnetGHPR.d.ts.map +0 -1
  91. package/cjs/dotnet/dotnetGLPR.cjs +0 -41
  92. package/cjs/dotnet/dotnetGLPR.cjs.map +0 -1
  93. package/cjs/dotnet/dotnetGLPR.d.ts +0 -13
  94. package/cjs/dotnet/dotnetGLPR.d.ts.map +0 -1
  95. package/cjs/dotnet/dotnetHelpers.cjs +0 -141
  96. package/cjs/dotnet/dotnetHelpers.cjs.map +0 -1
  97. package/cjs/dotnet/dotnetHelpers.d.ts +0 -26
  98. package/cjs/dotnet/dotnetHelpers.d.ts.map +0 -1
  99. package/cjs/dotnet-wrapper.mjs +0 -6
  100. package/cjs/dotnet.cjs +0 -15
  101. package/cjs/dotnet.cjs.map +0 -1
  102. package/cjs/dotnet.d.ts +0 -7
  103. package/cjs/dotnet.d.ts.map +0 -1
  104. package/cjs/envUtils-wrapper.mjs +0 -6
  105. package/cjs/envUtils.cjs +0 -37
  106. package/cjs/envUtils.cjs.map +0 -1
  107. package/cjs/envUtils.d.ts +0 -15
  108. package/cjs/envUtils.d.ts.map +0 -1
  109. package/cjs/eslintConfig-wrapper.mjs +0 -6
  110. package/cjs/eslintConfig.cjs +0 -52
  111. package/cjs/eslintConfig.cjs.map +0 -1
  112. package/cjs/eslintConfig.d.ts +0 -3
  113. package/cjs/eslintConfig.d.ts.map +0 -1
  114. package/cjs/findStaticConfig-wrapper.mjs +0 -6
  115. package/cjs/findStaticConfig.cjs +0 -34
  116. package/cjs/findStaticConfig.cjs.map +0 -1
  117. package/cjs/findStaticConfig.d.ts +0 -2
  118. package/cjs/findStaticConfig.d.ts.map +0 -1
  119. package/cjs/index-wrapper.mjs +0 -6
  120. package/cjs/index.cjs +0 -10
  121. package/cjs/index.cjs.map +0 -1
  122. package/cjs/index.d.ts +0 -5
  123. package/cjs/index.d.ts.map +0 -1
  124. package/cjs/semantic-release__commit-analyzer.d.cjs +0 -2
  125. package/cjs/semantic-release__commit-analyzer.d.cjs.map +0 -1
  126. package/cjs/semantic-release__git.d.cjs +0 -2
  127. package/cjs/semantic-release__git.d.cjs.map +0 -1
  128. package/cjs/semantic-release__github.d.cjs +0 -2
  129. package/cjs/semantic-release__github.d.cjs.map +0 -1
  130. package/cjs/semanticReleaseConfig-wrapper.mjs +0 -6
  131. package/cjs/semanticReleaseConfig.cjs +0 -33
  132. package/cjs/semanticReleaseConfig.cjs.map +0 -1
  133. package/cjs/semanticReleaseConfig.d.ts +0 -4
  134. package/cjs/semanticReleaseConfig.d.ts.map +0 -1
  135. package/cjs/semanticReleaseConfigDotnet-wrapper.mjs +0 -7
  136. package/cjs/semanticReleaseConfigDotnet.cjs +0 -112
  137. package/cjs/semanticReleaseConfigDotnet.cjs.map +0 -1
  138. package/cjs/semanticReleaseConfigDotnet.d.ts +0 -51
  139. package/cjs/semanticReleaseConfigDotnet.d.ts.map +0 -1
  140. package/cjs/setupGitPluginSpec-wrapper.mjs +0 -6
  141. package/cjs/setupGitPluginSpec.cjs +0 -67
  142. package/cjs/setupGitPluginSpec.cjs.map +0 -1
  143. package/cjs/setupGitPluginSpec.d.ts +0 -19
  144. package/cjs/setupGitPluginSpec.d.ts.map +0 -1
  145. package/src/dotnet/createDummyNupkg.ts +0 -30
  146. package/src/dotnet/dotnetGHPR.ts +0 -232
  147. package/src/dotnet/dotnetGLPR.ts +0 -46
  148. package/src/dotnet/dotnetHelpers.ts +0 -184
  149. package/src/dotnet.ts +0 -6
  150. package/src/envUtils.ts +0 -36
  151. package/src/findStaticConfig.ts +0 -31
  152. package/static/.releaserc.yml +0 -35
@@ -1,75 +1,167 @@
1
1
  import type { AssetEntry, Options as GitOptions } from '@semantic-release/git';
2
- import type { PluginSpec } from 'semantic-release';
2
+ import type { PluginSpecSRGit, PluginSpecTuple } from './semanticReleaseConfig.js';
3
3
 
4
4
  export const GitPluginId = '@semantic-release/git';
5
- /** As specified at https://github.com/semantic-release/git#options */
5
+
6
+ /**
7
+ * As specified at https://github.com/semantic-release/git#options
8
+ * To use, assign or create an object with the same (but mutable) properties and deeply-copy to the object
9
+ * @satisfies { GitOptions }
10
+ */
6
11
  export const DefaultOptions = {
7
- assets: ['README.md', 'CHANGELOG.md', 'package.json', 'package-lock.json', 'npm-shrinkwrap.json'],
8
- message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
9
- } satisfies GitOptions;
12
+ assets: [
13
+ 'README.md',
14
+ 'CHANGELOG.md',
15
+ 'package.json',
16
+ 'package-lock.json',
17
+ 'npm-shrinkwrap.json',
18
+ ],
19
+ message:
20
+ 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
21
+ } as const satisfies GitOptions;
22
+
23
+ /**
24
+ * Check if {@link unk} is an {@link AssetEntry}.
25
+ * @param unk Anything.
26
+ * @returns `true` if {@link unk} is an {@link AssetEntry}. Else, `false`.
27
+ */
28
+ function isGitAsset(unk: unknown): unk is AssetEntry {
29
+ if (typeof unk === 'string')
30
+ return true;
31
+ // Avoid ending condition with `typeof unk.path === 'string'`.
32
+ // TS narrowing is bugged; requires the check to be performed TWICE!!
33
+ if (typeof unk === 'object' && unk != undefined && 'path' in unk) {
34
+ return typeof unk.path === 'string';
35
+ }
36
+ return false;
37
+ }
38
+
39
+ /**
40
+ * Convert one or more {@link AssetEntry AssetEntries} to a `string[]`.
41
+ * @param assets The `assets` property of a {@link GitOptions} object. This may not be `false`.
42
+ * @returns A `string[]` of the given {@link AssetEntry} objects or strings.
43
+ */
44
+ function gitAssetsToStringArray(
45
+ assets: Exclude<GitOptions['assets'], false>,
46
+ ): string[] {
47
+ if (assets === undefined)
48
+ return [];
49
+ if (Array.isArray(assets)) {
50
+ return assets.filter(asset => isGitAsset(asset))
51
+ .map(v => typeof v === 'string' ? v : v.path);
52
+ }
53
+ if (typeof assets === 'string')
54
+ return [assets] as string[];
55
+ if (typeof assets.path === 'string')
56
+ return [assets.path];
57
+ else
58
+ throw new TypeError('assets is not typeof GitOptions[\'assets\'!');
59
+ }
60
+
61
+ /**
62
+ * Sanitize a {@link GitOptions} object so its {@link GitOptions#assets} property is either `false` or a `string[]`.
63
+ * @param opts A {@link GitOptions} object.
64
+ * @returns A {@link GitOptions} object whose {@link GitOptions#assets} is `string[] | false`.
65
+ */
66
+ function sanitizeGitOptions(opts: GitOptions): Omit<GitOptions, 'assets'> & { assets: string[] | false } {
67
+ return { ...opts, assets: opts.assets === false ? opts.assets : gitAssetsToStringArray(opts.assets) };
68
+ }
69
+
70
+ /**
71
+ *Determine if {@link opts} is a {@link GitOptions} object.
72
+ * @param opts Anything.
73
+ * @returns `true` if {@link opts} is a {@link GitOptions} object. Else, `false`.
74
+ */
75
+ function isGitOptions(opts: unknown): opts is GitOptions {
76
+ let isOptions = false;
77
+
78
+ if (typeof opts !== 'object' || opts == undefined)
79
+ return isOptions;
80
+ if ('assets' in opts) {
81
+ isOptions = Array.isArray(opts.assets)
82
+ ? opts.assets.every(unk => isGitAsset(unk))
83
+ : isGitAsset(opts.assets);
84
+ }
85
+ if ('message' in opts)
86
+ isOptions = typeof opts.message === 'string';
87
+ return isOptions;
88
+ }
89
+
90
+ /**
91
+ * Determine if {@link pluginSpec} includes a {@link GitOptions} object.
92
+ * @param pluginSpec a {@link PluginSpecTuple}.
93
+ * @returns `true` if {@link pluginSpec[1]} is a {@link GitOptions} object. Else, `false`.
94
+ */
95
+ function hasGitOptions<P extends string>(pluginSpec: PluginSpecTuple<P>): pluginSpec is PluginSpecTuple<P, GitOptions> {
96
+ return isGitOptions(pluginSpec[1]);
97
+ };
10
98
 
11
- function gitAssetsToArray(assets: AssetEntry | AssetEntry[] | false | undefined) {
12
- if (assets === undefined || assets === false) return [];
13
- if (Array.isArray(assets)) return assets;
14
- if (typeof assets === 'string') return [assets];
15
- return assets.path === 'string' ? [assets.path] : [];
99
+ /**
100
+ * Determined if the plugin ID in {@link pluginSpec} is {@link GitPluginId}.
101
+ * @param pluginSpec A {@link PluginSpecTuple}
102
+ * @returns `true` if {@link pluginSpec[0]} is {@link GitPluginId}
103
+ */
104
+ function isGitPluginSpecTuple<T>(pluginSpec: [string, T]): pluginSpec is [typeof GitPluginId, T] {
105
+ return pluginSpec[0] === GitPluginId;
16
106
  }
17
107
 
18
108
  /**
19
109
  * https://github.com/semantic-release/git#options
20
110
  *
21
111
  * This plugin may be deprecated at a later date.
22
- * Why do you need to commit during release?
23
- * If you don't need to update a changelog or version in a file, then you don't need this.
24
- *
25
- * @returns A {@link PluginSpec} array with {@link PluginSpec<GitOptions>}. If Git plugin not in original array, returns the original array.
26
- * todo: deprecate for generic or parameterized function
112
+ * Q: Why would I need to commit during release?
113
+ * A: This is for committing your changelog, README, and/or other files updated during the release procedure.
114
+ * @param plugins An ordered array of {@link PluginSpecTuple PluginSpecTuples}.
115
+ * @returns A {@link PluginSpecTuple}[]. Duplicate `@semantic-release/git` plugin entries are merged or overridden. The last entry takes priority e.g. if the last entry is `{assets: false}`, previous entries' assets are ignored.
27
116
  */
28
- export function setupGitPluginSpec(plugins: PluginSpec[]): PluginSpec[] {
29
- let newPlugins = plugins;
30
- let gitPluginIndex = -1;
31
-
32
- function pluginSpecIsGit(pluginSpec: PluginSpec, pluginSpecIndex: number) {
33
- if (pluginSpec === GitPluginId || pluginSpec[0] === GitPluginId) {
34
- gitPluginIndex = pluginSpecIndex;
35
- return true;
36
- }
37
- return false;
38
- }
39
-
40
- // if Git plugin not in load order, return as-is.
41
- if (!newPlugins.some(pluginSpecIsGit)) return plugins;
117
+ export function setupGitPluginSpec(plugins: PluginSpecTuple[]): PluginSpecTuple[] {
118
+ /** if Git plugin not in load order, return as-is. */
119
+ const firstGitPluginIndex = plugins.findIndex(plugin => isGitPluginSpecTuple(plugin));
120
+ if (firstGitPluginIndex === -1)
121
+ return plugins;
42
122
 
43
- // if string, replace with tuple with default options.
44
- newPlugins = newPlugins.map((plugin) =>
45
- plugin === GitPluginId ? [GitPluginId, DefaultOptions] : plugin,
46
- );
123
+ /**
124
+ * the following two const variables are references--not clones.
125
+ * Modifying them will affect the plugins array.
126
+ */
127
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
128
+ const firstGitPlugin = plugins[firstGitPluginIndex]!;
129
+ const firstGitOpts: ReturnType<typeof sanitizeGitOptions> = isGitOptions(firstGitPlugin[1])
130
+ ? sanitizeGitOptions(firstGitPlugin[1])
131
+ : DefaultOptions;
47
132
 
48
- // ensure assets is an array
49
- (newPlugins[gitPluginIndex][1] as GitOptions).assets = gitAssetsToArray(
50
- (newPlugins[gitPluginIndex][1] as GitOptions).assets,
51
- );
133
+ /**
134
+ * remove duplicate Git plugin entries;
135
+ * merge extra options into firstGitPlugin's options
136
+ * if `firstGitOpts.assets === false`, do not change it.
137
+ * All duplicate PluginSpecSRGit entries are then reassigned `undefined` and all
138
+ * `undefined` items are filtered from the plugins array.
139
+ */
140
+ return plugins.map((current: PluginSpecTuple, index): PluginSpecTuple | PluginSpecSRGit | undefined => {
141
+ // skip everything up to and including the first Git PluginSpec
142
+ if (index <= firstGitPluginIndex || !isGitPluginSpecTuple(current))
143
+ return current;
52
144
 
53
- const discardPile: number[] = [];
145
+ /** if another Git PluginSpec is discovered, copy its options to the first Git PluginSpec and return undefined. */
146
+ if (hasGitOptions(current)) {
147
+ const currentGitOpts = sanitizeGitOptions(current[1]);
54
148
 
55
- // de-duplicate Git plugin entries; assign single entry to newPlugins[gitPluginIndex]
56
- newPlugins.forEach((current, i) => {
57
- if (i > gitPluginIndex && pluginSpecIsGit(current, gitPluginIndex)) {
58
- // convert its assets to an array
59
- const { assets } = newPlugins[i][1] as GitOptions;
60
- const { message } = newPlugins[i][1] as GitOptions;
61
- // push unique assets to first entry's assets array
62
- if (assets !== undefined)
63
- ((newPlugins[gitPluginIndex][1] as GitOptions).assets as AssetEntry[]).push(
64
- ...gitAssetsToArray(assets),
65
- );
66
- if (message !== undefined) (newPlugins[gitPluginIndex][1] as GitOptions).message = message;
67
- discardPile.push(i);
68
- }
69
- });
70
- for (const i of discardPile.sort().reverse()) {
71
- newPlugins.splice(i, 1);
72
- }
149
+ if (currentGitOpts.assets === false) {
150
+ firstGitOpts.assets = false;
151
+ }
152
+ else {
153
+ const assets: string[] = gitAssetsToStringArray(currentGitOpts.assets);
154
+ if (Array.isArray(firstGitOpts.assets)) {
155
+ firstGitOpts.assets.push(...assets);
156
+ }
157
+ else {
158
+ firstGitOpts.assets = assets;
159
+ }
160
+ }
73
161
 
74
- return newPlugins;
162
+ if (typeof currentGitOpts.message === 'string')
163
+ firstGitOpts.message = currentGitOpts.message;
164
+ }
165
+ return undefined;
166
+ }).filter(pluginSpec => pluginSpec !== undefined);
75
167
  }
package/src/tsconfig.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
- "extends": "../baseTsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../_tsout/src",
5
- "target": "ES5"
6
- },
7
- "include": [
8
- "./**/*"
9
- ]
2
+ "extends": "../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "../_tsout/src",
5
+ "isolatedDeclarations": false
6
+ },
7
+ "include": [
8
+ "./**/*"
9
+ ]
10
10
  }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * ### `Exact<T, S>`
3
+ *
4
+ * [issue](https://github.com/microsoft/TypeScript/issues/12936#issuecomment-2816928183)\
5
+ * [author](https://github.com/ahrjarrett)\
6
+ * license: unlicensed[!]
7
+ * @see
8
+ * - {@link https://tsplay.dev/NnGG6m}
9
+ */
10
+ export type Exact<T, S> = [keyof T] extends [keyof S]
11
+ ? [T] extends [S] ? { [K in keyof T]: T[K] } : S
12
+ : { [K in keyof T as K extends keyof S ? never : K]: TypeError<`Excess: '${Coerce<K>}'`> };
13
+
14
+ // eslint-disable-next-line jsdoc/require-jsdoc, @typescript-eslint/no-unused-vars
15
+ function exact<S, T extends Exact<T, S>>(x: S, _y: T): T {
16
+ return x as unknown as T;
17
+ };
18
+
19
+ // should succeed
20
+ exact({ a: 1 }, { a: 1 });
21
+ exact({ a: 1, b: 'two' }, { a: 1, b: 'two' });
22
+
23
+ // should raise a TypeError
24
+ try {
25
+ // @ts-expect-error Type 'boolean' is not assignable to type 'TypeError<"Excess: 'c'">'.ts(2322)
26
+ exact({ a: 1 }, { a: 1, b: 'two', c: false });
27
+ }
28
+ catch { /* empty */ }
29
+ try {
30
+ // @ts-expect-error Type 'number' is not assignable to type 'TypeError<"Excess: 'c'">'.ts(2322)
31
+ exact({ a: 1, b: 2 }, { a: 1, b: 2, c: 3 });
32
+ }
33
+ catch { /* empty */ }
34
+ try {
35
+ // @ts-expect-error Argument of type '{ a: number; }' is not assignable to parameter of type '{ a: number; b: number; }'. Property 'b' is missing in type '{ a: number; }' but required in type '{ a: number; b: number; }'.ts(2345)
36
+ exact({ a: 1, b: 2 }, { a: 1 });
37
+ }
38
+ catch { /* empty */ }
39
+ interface TypeError<Message> { [' TypeError']: Message }
40
+ // prior art: use arktype's ^^^ leading whitespace trick to avoid collisions
41
+
42
+ type Coerce<T> = `${T & (string | number)}`;
43
+
44
+ try {
45
+ // @ts-expect-error Should error
46
+ exact({ a: 1 }, { a: 1, b: 2 });
47
+ // ^ 🚫 raises a TypeError here
48
+ }
49
+ catch { /* empty */ }
@@ -0,0 +1,12 @@
1
+ // #region https://dev.to/adrien2p/mastering-recursive-types-in-typescript-handling-depth-limitations-gracefully-5f4o#a-more-robust-solution-tuplebased-increment-and-decrement-types
2
+ export type Length<T extends unknown[]> = (T extends { length: number } ? T['length'] : never) & number;
3
+ export type TupleOf<N extends number, T extends unknown[] = []> = Length<T> extends N
4
+ ? T
5
+ : TupleOf<N, [...T, unknown]>;
6
+ export type Pop<T extends unknown[]> = T extends [...infer U, unknown] ? U : never;
7
+ // Increment adds an element to a tuple, effectively creating N + 1
8
+ export type Increment<N extends number> = Length<[1, ...TupleOf<N>]>;
9
+ // Decrement removes an element from a tuple, effectively creating N - 1
10
+ export type Decrement<N extends number> = Length<Pop<TupleOf<N>>>;
11
+
12
+ // #endregion https://dev.to/adrien2p/mastering-recursive-types-in-typescript-handling-depth-limitations-gracefully-5f4o#a-more-robust-solution-tuplebased-increment-and-decrement-types
@@ -0,0 +1,44 @@
1
+ import { get,
2
+ config as loadDotenv,
3
+ type DotenvConfigOptions,
4
+ type GetOptions,
5
+ } from '@dotenvx/dotenvx';
6
+ import { env } from 'node:process';
7
+
8
+ /**
9
+ * A thin wrapper for {@link loadDotenv}. Loads a .env file from {@link process.cwd()} with the given options (or defaults), returns the new value of {@link process.env} with optional overrides.
10
+ * @param [dotenvOptions] An optional {@link DotenvConfigOptions} object to pass to {@link loadDotenv}.
11
+ * @param [overrides] If provided, this {@link NodeJS.ProcessEnv} object is merged into the return value, overriding existing properties where overlap occurs.
12
+ * @returns A {@link NodeJS.ProcessEnv} object whose properties are variables loaded from the
13
+ * process environment, the nearest .env file, and {@link overrides} (if provided). Where
14
+ * overlap occurs, the later source takes priority.
15
+ */
16
+ export function getEnv(dotenvOptions?: DotenvConfigOptions, overrides?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
17
+ loadDotenv(dotenvOptions);
18
+
19
+ if (overrides)
20
+ Object.assign(env, overrides);
21
+
22
+ return env;
23
+ }
24
+
25
+ /**
26
+ * Get the value from the given env var in the current process or nearby .env file.
27
+ * If found in process environment, its value is returned.
28
+ * Else, try to get it from the nearest .env file.
29
+ * If NOT found, return `undefined`
30
+ * @param envVar The environment variable to lookup.
31
+ * @param [options] Options to pass to {@link get}
32
+ * @returns The string value of the environment variable or `undefined`.
33
+ * `undefined` may be returned when the variable is undefined or its string is
34
+ * empty, whitespace, or appears to have been converted from `null` or
35
+ * `undefined`.
36
+ */
37
+ export function getEnvVarValue(envVar: string, options?: GetOptions): string | undefined {
38
+ options ??= { ignore: ['MISSING_KEY', 'MISSING_ENV_FILE'] };
39
+ const value = String(env[envVar] ?? get(envVar, options)).trim();
40
+ // I hate this. Why is undefined converted to a string?
41
+ return value === '' || value === 'undefined'
42
+ ? undefined
43
+ : value;
44
+ }
@@ -0,0 +1,77 @@
1
+ /* eslint-disable jsdoc/no-defaults */
2
+ import { type } from 'arktype';
3
+ import { exec } from 'node:child_process';
4
+ import { constants } from 'node:os';
5
+ import { promisify } from 'node:util';
6
+ import { isNativeError } from 'node:util/types';
7
+
8
+ /**
9
+ * A `promisify(exec)` wrapper to optionally assign the child process's STDERR as the {@link Error.prototype.cause}.
10
+ * @see {@link promisify}, {@link exec}
11
+ * @param command The command to run, with space-separated arguments.
12
+ * @param [setStderrAsCause=false] If true and the child process's stderr is available, the thrown Error's {@link Error.prototype.cause} is assigned the stderr string.
13
+ * @returns A promise of the child process's STDOUT and STDERR streams as strings
14
+ * @throws {Error | ChildProcessSpawnException}
15
+ */
16
+ export async function execAsync(command: string, setStderrAsCause = false): Promise<{
17
+ stdout: string;
18
+ stderr: string;
19
+ }> {
20
+ return await promisify(exec)(command).catch((error: unknown): never => {
21
+ if (!isNativeError(error))
22
+ throw new Error(JSON.stringify(error));
23
+
24
+ if (setStderrAsCause && 'stderr' in error && typeof error.stderr === 'string' && error.stderr !== '')
25
+ error.cause ??= error.stderr;
26
+
27
+ if ('stdout' in error && typeof error.stdout === 'string') {
28
+ error.message
29
+ += '\nSTDOUT:\n'
30
+ + ` ${error.stdout.replaceAll('\n', '\n ')}`;
31
+ }
32
+ if ('stderr' in error && typeof error.stderr === 'string') {
33
+ error.message
34
+ += '\nSTDERR:\n'
35
+ + ` ${error.stderr.replaceAll('\n', '\n ')}`;
36
+ }
37
+
38
+ throw new ChildProcessSpawnException(error.message, error);
39
+ });
40
+ }
41
+
42
+ const T_ExecException = type('Error').and({
43
+ 'cmd?': 'string | null',
44
+ 'killed?': 'boolean | null',
45
+ 'code?': 'number | null',
46
+ 'signal?': type.null.or((Object.keys(constants.signals) as NodeJS.Signals[])
47
+ .map(v => type(`'${v}'`))
48
+ // eslint-disable-next-line unicorn/no-array-reduce
49
+ .reduce((previous, current) => previous.or(current))),
50
+ 'stdout?': 'string',
51
+ 'stderr?': 'string',
52
+ });
53
+
54
+ type _ExecException = typeof T_ExecException.inferOut;
55
+
56
+ export class ChildProcessSpawnException extends Error implements _ExecException {
57
+ constructor(
58
+ message: Parameters<typeof Error>[0],
59
+ options: typeof T_ExecException.inferIn,
60
+ ) {
61
+ options = T_ExecException.from(options);
62
+ super(message, options);
63
+ this.cmd = options.cmd;
64
+ this.code = options.code;
65
+ this.killed = options.killed;
66
+ this.signal = options.signal;
67
+ this.stderr = options.stderr;
68
+ this.stdout = options.stdout;
69
+ }
70
+
71
+ cmd: typeof T_ExecException.inferOut.cmd;
72
+ code: typeof T_ExecException.inferOut.code;
73
+ killed: typeof T_ExecException.inferOut.killed;
74
+ signal: typeof T_ExecException.inferOut.signal;
75
+ stderr: typeof T_ExecException.inferOut.stderr;
76
+ stdout: typeof T_ExecException.inferOut.stdout;
77
+ }
@@ -0,0 +1,17 @@
1
+ import { type } from 'arktype';
2
+
3
+ export const tBooleanString = type('"true" | "false"');
4
+ export type BooleanString = typeof tBooleanString.infer;
5
+
6
+ export const tEmptyOrBooleanString = type(tBooleanString.or('""'));
7
+ export type EmptyOrBooleanString = typeof tEmptyOrBooleanString.infer;
8
+
9
+ export type Integer<N extends number> = `${N}` extends `${number}.${number}` ? never : N;
10
+
11
+ /**
12
+ * @see https://stackoverflow.com/a/73920140/14894786
13
+ */
14
+ export type TupleIndices<T extends readonly unknown[]>
15
+ = Extract<keyof T, `${number}`> extends `${infer N extends number}` ? N : never;
16
+
17
+ export type InstanceOrStatic = 'Instance' | 'Static';
@@ -0,0 +1,17 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /**
3
+ * A generic alternative to the boxing {@link Function} type. Based on the
4
+ * expressions of {@link Parameters} and {@link ReturnType}.
5
+ * @template [T=((...args: any[] | readonly any[]) => any)] Any function-like type.
6
+ * @template [P=Parameters<T>]
7
+ * [INTERNAL] The parameters of {@link T}.
8
+ * @template [R=ReturnType<T>]
9
+ * [INTERNAL] The return type of {@link T}.
10
+ * @default ((...args: any[] | readonly any[]) => any)
11
+ * @since 3.0.0
12
+ */
13
+ export type FunctionLike<
14
+ T extends ((...args: any[] | readonly any[]) => any) = ((...args: any[] | readonly any[]) => any),
15
+ P extends Parameters<T> = Parameters<T>,
16
+ R extends ReturnType<T> = ReturnType<T>,
17
+ > = T & ((...args: P) => R);
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A type for casting a property descriptor to a getter descriptor.\
3
+ * Cast to this only after checking `typeof propertyDescriptor.get === 'function`
4
+ * @template [T=unknown] The type of the property.
5
+ * @since 4.0.0
6
+ */
7
+ export type GetterDescriptor<T = unknown> = Omit<TypedPropertyDescriptor<T>, 'get'>
8
+ & Required<Pick<TypedPropertyDescriptor<T>, 'get'>>;
@@ -0,0 +1,32 @@
1
+ import type { PropertyDescriptorMap } from './PropertyDescriptorMap.ts';
2
+ import type {
3
+ BaseClassProto,
4
+ ConstructorConstraint,
5
+ InstanceTypeOrSelf,
6
+ SuperClassLike,
7
+ WithProto,
8
+ } from './inheritance.ts';
9
+
10
+ /**
11
+ * Generically-typed return type of `Object.GetOwnPropertyDescriptors` for class
12
+ * instances.
13
+ *
14
+ * Allows for statically-inferred property keys and values.
15
+ *
16
+ * Classes' `public` members are returned, including instance Getters and
17
+ * Setters.
18
+ * @template Class
19
+ * Any class type with its `[[Prototype]]` attached via a type-only `__proto__` property.
20
+ * The type of `__proto__` must extend {@link SuperClassLike} or {@link BaseClassProto}.
21
+ * @example
22
+ * ```ts
23
+ * const { NugetProjectProperties: NPP } = await import('../dotnet/NugetProjectProperties.js');
24
+ * const _instanceMembers: InstancePropertyDescriptorMap<typeof NPP> = Object.getOwnPropertyDescriptors(new NugetProjectProperties('',new CaseInsensitiveMap([['','']])));
25
+ * ```
26
+ * @since 3.0.0
27
+ */
28
+ export type InstancePropertyDescriptorMap<
29
+ Class extends ConstructorConstraint<Class> & WithProto<SuperClassLike | BaseClassProto>,
30
+ > = Class['__proto__'] extends BaseClassProto
31
+ ? PropertyDescriptorMap<InstanceType<Class>>
32
+ : PropertyDescriptorMap<InstanceType<Class>, InstanceTypeOrSelf<Class['__proto__']>>;
@@ -0,0 +1,20 @@
1
+ import type { BaseClassProto, ConstructorLike, InstanceTypeOrSelf, ProtoOrSuperClass, SuperClassLike, WithProto } from './inheritance.ts';
2
+ import type { InstancePropertyDescriptorMap } from './InstancePropertyDescriptorMap.ts';
3
+ import type { PropertyDescriptorMap } from './PropertyDescriptorMap.ts';
4
+
5
+ /**
6
+ * Variant of {@link InstancePropertyDescriptorMap} with a lesser constraint on {@link T}.
7
+ * If {@link T} and/or {@link __proto__} are instantiable, the resulting
8
+ * property descriptor maps will be of the instances' types.
9
+ * @template T `null` or an `object`-like type.
10
+ * @template __proto__ The `[[Prototype]]` of {@link T}.
11
+ * @since 3.0.0
12
+ */
13
+ export type InstanceTypeOrSelfPropertyDescriptorMap<
14
+ T extends object | null,
15
+ __proto__ extends ProtoOrSuperClass,
16
+ > = T extends ConstructorLike<T>
17
+ ? __proto__ extends SuperClassLike | BaseClassProto
18
+ ? InstancePropertyDescriptorMap<T & WithProto<__proto__>>
19
+ : PropertyDescriptorMap<InstanceType<T>, InstanceTypeOrSelf<__proto__>>
20
+ : PropertyDescriptorMap<InstanceTypeOrSelf<T>, InstanceTypeOrSelf<__proto__>>;
@@ -0,0 +1,17 @@
1
+ import type { GetterDescriptor } from './GetterDescriptor.ts';
2
+ import type { OwnKeyOf } from './OwnKeyOf.js';
3
+
4
+ /**
5
+ * # !WARNING!
6
+ * > This does _not_ filter out non-getter properties! ALL properties are treated as getters. Because `get` is optional and present on all property descriptors, this type should only be used to cast properties for which `typeof p.get === 'function'`.
7
+ *
8
+ * A {@link GetterDescriptorMap} variant for omitting keys inherited from {@link __proto__}.
9
+ * Note: If `T` is `InstanceOf<class>`, then `__proto__` must be `InstanceTypeOrSelf<__proto__>`
10
+ * @template T The type the descriptor map describes.
11
+ * @template __proto__ The `[[Prototype]]` of {@link T}. Keys of {@link __proto__} are omitted from the descriptor map type.
12
+ * @since 3.0.0
13
+ */
14
+ export type OwnGetterDescriptorMap<
15
+ T,
16
+ __proto__ extends object | null,
17
+ > = { [P in OwnKeyOf<T, __proto__>]: GetterDescriptor<T[P]> };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The `keyof` type {@link T} excluding any keys of type {@link __proto__}
3
+ * @template T
4
+ * @template __proto__ `null` or any `object`-like type.
5
+ * @example
6
+ * OwnKeyOf<NPP, MSBPP> === "IsPackable" | "SuppressDependenciesWhenPacking" | "PackageVersion" | "PackageId" | "PackageDescription" | "Authors" | "Copyright" | "PackageRequireLicenseAcceptance" | "DevelopmentDependency" | "PackageLicenseExpression" | "PackageLicenseFile" | "PackageProjectUrl" | "PackageIcon" | "PackageReleaseNotes" | "PackageReadmeFile" | "PackageTags" | "PackageOutputPath" | "IncludeSymbols" | "IncludeSource" | "PackageType" | "IsTool" | "RepositoryUrl" | "RepositoryType" | "RepositoryCommit" | "SymbolPackageFormat" | "NoPackageAnalysis" | "MinClientVersion" | "IncludeBuildOutput" | "IncludeContentInPack" | "BuildOutputTargetFolder" | "ContentTargetFolders" | "NuspecFile" | "NuspecBasePath" | "NuspecProperties" | "Title" | "Company" | "Product"
7
+ * OwnKeyOf<typeof NPP, typeof MSBPP> === never ; // class NPP does not have non-inherited static members
8
+ * OwnKeyOf<typeof NPP, null> === "prototype" | "GetFullPath"
9
+ * @since 3.0.0
10
+ */
11
+ export type OwnKeyOf<T, __proto__ extends object | null>
12
+ = Exclude<
13
+ __proto__ extends null
14
+ ? keyof T
15
+ : Exclude<
16
+ keyof T,
17
+ keyof __proto__
18
+ >,
19
+ '__proto__'
20
+ >;
@@ -0,0 +1,82 @@
1
+ import type {
2
+ BaseClass,
3
+ ClassLike,
4
+ ClassLike_Unknown,
5
+ ProtoOrSuperClass,
6
+ WithProto,
7
+ } from './inheritance.ts';
8
+
9
+ // todo: Omit [P in keyof T] where T['__proto__'][P] === T[P].
10
+ // todo: NEVER omit overridden class members! Does TypeScript expose that override in accessible metadata?
11
+
12
+ /**
13
+ * A typed {@link PropertyDescriptorMap} without any keys found in its `[[Prototype]]` chain.
14
+ * This may mean overridden class properties are wrongly omitted.
15
+ * @template T Any type with its `[[Prototype]]` attached via a type-only `__proto__` property. The type of `__proto__` must extends {@link ProtoOrSuperClass}.
16
+ * @since 3.0.0
17
+ */
18
+ export type OwnPropertyDescriptorMap<T extends WithProto<ProtoOrSuperClass>>
19
+ = T['__proto__'] extends null
20
+ ? { [P0 in keyof T]: TypedPropertyDescriptor<T[P0]>; }
21
+ : Omit<
22
+ { [P0 in keyof T]: TypedPropertyDescriptor<T[P0]>; },
23
+ Exclude<
24
+ keyof { [P1 in keyof T['__proto__']]: TypedPropertyDescriptor<T['__proto__'][P1]> },
25
+ keyof ClassLike<BaseClass<ClassLike_Unknown>>
26
+ >
27
+ >;
28
+
29
+ // // todo: use SharedKeys to only keys where SharedKeys<T> in keyof T
30
+ // // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ // type SharedKeys<T extends WithProto<ProtoOrSuperClass>> = Extract<keyof T, keyof T['__proto__']>;
32
+
33
+ // // todo: make this useful. The ReturnType is incorrect
34
+ // // eslint-disable-next-line @typescript-eslint/no-unused-vars
35
+ // declare function removeMatchingPropertyDescriptors<T0, T1>(
36
+ // a: { [P0 in keyof T0]: TypedPropertyDescriptor<T0[P0]> },
37
+ // b: { [P1 in keyof T1]: TypedPropertyDescriptor<T1[P1]> }
38
+ // ): Omit<typeof a, keyof (T0 & T1)>;
39
+
40
+ // /**
41
+ // * @see https://stackoverflow.com/a/56874389/1489478
42
+ // * @example
43
+ // * ```ts
44
+ // * interface MyInterface {
45
+ // * a: number;
46
+ // * b: string;
47
+ // * c: number;
48
+ // * }
49
+ // * type MyType = KeysMatching<MyInterface, number>;
50
+ // * type MyType = "a" | "c";
51
+ // * @todo KeysMatching
52
+ // */
53
+ // // eslint-disable-next-line @typescript-eslint/no-unused-vars
54
+ // type KeysMatching<T extends object, V> = {
55
+ // [K in keyof T]-?: T[K] extends V ? K : never
56
+ // }[keyof T];
57
+
58
+ // /**
59
+ // * @todo KeysNotMatching
60
+ // */
61
+ // // eslint-disable-next-line @typescript-eslint/no-unused-vars
62
+ // type KeysNotMatching<T extends object, V> = {
63
+ // [K in keyof T]-?: T[K] extends V ? never : K
64
+ // }[keyof T];
65
+
66
+ // // type _ = KeysMatching<Class_NPP,Class_MSBPP[keyof Class_MSBPP]>
67
+ // /**
68
+ // * @see https://stackoverflow.com/a/56874389/14894786
69
+ // * @example
70
+ // * interface AnotherInterface {
71
+ // * narrower: 1;
72
+ // * exact: number;
73
+ // * wider: string | number;
74
+ // * }
75
+ // * type AnotherTypeWrite = KeysMatchingWrite<AnotherInterface, number>;
76
+ // * type AnotherTypeWrite = 'exact' | 'wider';
77
+ // * @todo KeysMatchingWrite
78
+ // */
79
+ // // eslint-disable-next-line @typescript-eslint/no-unused-vars
80
+ // type KeysMatchingWrite<T extends object, V> = {
81
+ // [K in keyof T]-?: [V] extends [T[K]] ? K : never
82
+ // }[keyof T];