@venizia/dev-configs 0.4.7-0 → 0.4.7-1

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 (182) hide show
  1. package/LICENSE.md +1 -3
  2. package/README.md +1 -1
  3. package/dist/checks/catalog.d.ts +4 -2
  4. package/dist/checks/catalog.d.ts.map +1 -1
  5. package/dist/checks/catalog.js +72 -70
  6. package/dist/checks/catalog.js.map +1 -1
  7. package/dist/checks/module-cycles.d.ts +11 -9
  8. package/dist/checks/module-cycles.d.ts.map +1 -1
  9. package/dist/checks/module-cycles.js +32 -30
  10. package/dist/checks/module-cycles.js.map +1 -1
  11. package/dist/checks/purity/manifest.d.ts +44 -38
  12. package/dist/checks/purity/manifest.d.ts.map +1 -1
  13. package/dist/checks/purity/manifest.js +121 -115
  14. package/dist/checks/purity/manifest.js.map +1 -1
  15. package/dist/checks/purity/probe.d.ts +22 -18
  16. package/dist/checks/purity/probe.d.ts.map +1 -1
  17. package/dist/checks/purity/probe.js +99 -98
  18. package/dist/checks/purity/probe.js.map +1 -1
  19. package/dist/checks/purity/run.d.ts +18 -16
  20. package/dist/checks/purity/run.d.ts.map +1 -1
  21. package/dist/checks/purity/run.js +90 -86
  22. package/dist/checks/purity/run.js.map +1 -1
  23. package/dist/checks/test-controls.d.ts +17 -15
  24. package/dist/checks/test-controls.d.ts.map +1 -1
  25. package/dist/checks/test-controls.js +71 -70
  26. package/dist/checks/test-controls.js.map +1 -1
  27. package/dist/checks/test-dead-code.d.ts +5 -3
  28. package/dist/checks/test-dead-code.d.ts.map +1 -1
  29. package/dist/checks/test-dead-code.js +23 -21
  30. package/dist/checks/test-dead-code.js.map +1 -1
  31. package/dist/checks/test-mutate.d.ts +12 -7
  32. package/dist/checks/test-mutate.d.ts.map +1 -1
  33. package/dist/checks/test-mutate.js +59 -56
  34. package/dist/checks/test-mutate.js.map +1 -1
  35. package/dist/checks/test-weight.d.ts +11 -8
  36. package/dist/checks/test-weight.d.ts.map +1 -1
  37. package/dist/checks/test-weight.js +98 -94
  38. package/dist/checks/test-weight.js.map +1 -1
  39. package/dist/cli/check.d.ts.map +1 -1
  40. package/dist/cli/check.js +11 -8
  41. package/dist/cli/check.js.map +1 -1
  42. package/dist/cli/codemod/async-style.d.ts +29 -8
  43. package/dist/cli/codemod/async-style.d.ts.map +1 -1
  44. package/dist/cli/codemod/async-style.js +102 -20
  45. package/dist/cli/codemod/async-style.js.map +1 -1
  46. package/dist/cli/codemod/index.d.ts.map +1 -1
  47. package/dist/cli/codemod/index.js +6 -2
  48. package/dist/cli/codemod/index.js.map +1 -1
  49. package/dist/cli/codemod/model-settings.d.ts +7 -5
  50. package/dist/cli/codemod/model-settings.d.ts.map +1 -1
  51. package/dist/cli/codemod/model-settings.js +293 -291
  52. package/dist/cli/codemod/model-settings.js.map +1 -1
  53. package/dist/cli/codemod/namespace-functions/index.d.ts +20 -0
  54. package/dist/cli/codemod/namespace-functions/index.d.ts.map +1 -0
  55. package/dist/cli/codemod/namespace-functions/index.js +182 -0
  56. package/dist/cli/codemod/namespace-functions/index.js.map +1 -0
  57. package/dist/cli/codemod/namespace-functions/logger-scope.d.ts +32 -0
  58. package/dist/cli/codemod/namespace-functions/logger-scope.d.ts.map +1 -0
  59. package/dist/cli/codemod/namespace-functions/logger-scope.js +128 -0
  60. package/dist/cli/codemod/namespace-functions/logger-scope.js.map +1 -0
  61. package/dist/cli/codemod/namespace-functions/mapping.d.ts +66 -0
  62. package/dist/cli/codemod/namespace-functions/mapping.d.ts.map +1 -0
  63. package/dist/cli/codemod/namespace-functions/mapping.js +99 -0
  64. package/dist/cli/codemod/namespace-functions/mapping.js.map +1 -0
  65. package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts +7 -0
  66. package/dist/cli/codemod/namespace-functions/mappings/boot.d.ts.map +1 -0
  67. package/dist/cli/codemod/namespace-functions/mappings/boot.js +28 -0
  68. package/dist/cli/codemod/namespace-functions/mappings/boot.js.map +1 -0
  69. package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts +4 -0
  70. package/dist/cli/codemod/namespace-functions/mappings/connectors.d.ts.map +1 -0
  71. package/dist/cli/codemod/namespace-functions/mappings/connectors.js +218 -0
  72. package/dist/cli/codemod/namespace-functions/mappings/connectors.js.map +1 -0
  73. package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts +7 -0
  74. package/dist/cli/codemod/namespace-functions/mappings/core-server.d.ts.map +1 -0
  75. package/dist/cli/codemod/namespace-functions/mappings/core-server.js +162 -0
  76. package/dist/cli/codemod/namespace-functions/mappings/core-server.js.map +1 -0
  77. package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts +7 -0
  78. package/dist/cli/codemod/namespace-functions/mappings/core-worker.d.ts.map +1 -0
  79. package/dist/cli/codemod/namespace-functions/mappings/core-worker.js +18 -0
  80. package/dist/cli/codemod/namespace-functions/mappings/core-worker.js.map +1 -0
  81. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts +7 -0
  82. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.d.ts.map +1 -0
  83. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js +192 -0
  84. package/dist/cli/codemod/namespace-functions/mappings/dev-configs.js.map +1 -0
  85. package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts +7 -0
  86. package/dist/cli/codemod/namespace-functions/mappings/filter.d.ts.map +1 -0
  87. package/dist/cli/codemod/namespace-functions/mappings/filter.js +30 -0
  88. package/dist/cli/codemod/namespace-functions/mappings/filter.js.map +1 -0
  89. package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts +16 -0
  90. package/dist/cli/codemod/namespace-functions/mappings/helpers.d.ts.map +1 -0
  91. package/dist/cli/codemod/namespace-functions/mappings/helpers.js +535 -0
  92. package/dist/cli/codemod/namespace-functions/mappings/helpers.js.map +1 -0
  93. package/dist/cli/codemod/namespace-functions/mappings/index.d.ts +4 -0
  94. package/dist/cli/codemod/namespace-functions/mappings/index.d.ts.map +1 -0
  95. package/dist/cli/codemod/namespace-functions/mappings/index.js +25 -0
  96. package/dist/cli/codemod/namespace-functions/mappings/index.js.map +1 -0
  97. package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts +10 -0
  98. package/dist/cli/codemod/namespace-functions/mappings/inversion.d.ts.map +1 -0
  99. package/dist/cli/codemod/namespace-functions/mappings/inversion.js +81 -0
  100. package/dist/cli/codemod/namespace-functions/mappings/inversion.js.map +1 -0
  101. package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts +17 -0
  102. package/dist/cli/codemod/namespace-functions/mappings/kernel.d.ts.map +1 -0
  103. package/dist/cli/codemod/namespace-functions/mappings/kernel.js +229 -0
  104. package/dist/cli/codemod/namespace-functions/mappings/kernel.js.map +1 -0
  105. package/dist/cli/codemod/namespace-functions/rewriter.d.ts +61 -0
  106. package/dist/cli/codemod/namespace-functions/rewriter.d.ts.map +1 -0
  107. package/dist/cli/codemod/namespace-functions/rewriter.js +564 -0
  108. package/dist/cli/codemod/namespace-functions/rewriter.js.map +1 -0
  109. package/dist/cli/index.js +2 -1
  110. package/dist/cli/index.js.map +1 -1
  111. package/dist/compilers/bun.d.ts +1 -1
  112. package/dist/compilers/bun.js +1 -1
  113. package/dist/compilers/bun.js.map +1 -1
  114. package/dist/linters/house/await-support.d.ts +17 -3
  115. package/dist/linters/house/await-support.d.ts.map +1 -1
  116. package/dist/linters/house/await-support.js +52 -23
  117. package/dist/linters/house/await-support.js.map +1 -1
  118. package/dist/linters/house/common.d.ts +13 -9
  119. package/dist/linters/house/common.d.ts.map +1 -1
  120. package/dist/linters/house/common.js +18 -15
  121. package/dist/linters/house/common.js.map +1 -1
  122. package/dist/linters/house/configs.d.ts +5 -2
  123. package/dist/linters/house/configs.d.ts.map +1 -1
  124. package/dist/linters/house/configs.js +11 -2
  125. package/dist/linters/house/configs.js.map +1 -1
  126. package/dist/linters/house/const-class-values.rule.d.ts.map +1 -1
  127. package/dist/linters/house/const-class-values.rule.js +27 -19
  128. package/dist/linters/house/const-class-values.rule.js.map +1 -1
  129. package/dist/linters/house/effect-cleanup-consistency.rule.js +3 -3
  130. package/dist/linters/house/effect-cleanup-consistency.rule.js.map +1 -1
  131. package/dist/linters/house/hook-support.d.ts.map +1 -1
  132. package/dist/linters/house/hook-support.js +17 -14
  133. package/dist/linters/house/hook-support.js.map +1 -1
  134. package/dist/linters/house/index.d.ts +3 -0
  135. package/dist/linters/house/index.d.ts.map +1 -1
  136. package/dist/linters/house/index.js +3 -0
  137. package/dist/linters/house/index.js.map +1 -1
  138. package/dist/linters/house/max-params-own.rule.d.ts.map +1 -1
  139. package/dist/linters/house/max-params-own.rule.js +33 -24
  140. package/dist/linters/house/max-params-own.rule.js.map +1 -1
  141. package/dist/linters/house/no-async-hook-callback.rule.js +1 -1
  142. package/dist/linters/house/no-async-hook-callback.rule.js.map +1 -1
  143. package/dist/linters/house/no-await-promise-chain.rule.d.ts +24 -0
  144. package/dist/linters/house/no-await-promise-chain.rule.d.ts.map +1 -0
  145. package/dist/linters/house/no-await-promise-chain.rule.js +298 -0
  146. package/dist/linters/house/no-await-promise-chain.rule.js.map +1 -0
  147. package/dist/linters/house/no-inline-await.rule.d.ts.map +1 -1
  148. package/dist/linters/house/no-inline-await.rule.js +6 -12
  149. package/dist/linters/house/no-inline-await.rule.js.map +1 -1
  150. package/dist/linters/house/no-new-error.rule.js +4 -4
  151. package/dist/linters/house/no-new-error.rule.js.map +1 -1
  152. package/dist/linters/house/no-return-await.rule.d.ts.map +1 -1
  153. package/dist/linters/house/no-return-await.rule.js +11 -11
  154. package/dist/linters/house/no-return-await.rule.js.map +1 -1
  155. package/dist/linters/house/no-silent-catch.rule.d.ts +22 -0
  156. package/dist/linters/house/no-silent-catch.rule.d.ts.map +1 -0
  157. package/dist/linters/house/no-silent-catch.rule.js +88 -0
  158. package/dist/linters/house/no-silent-catch.rule.js.map +1 -0
  159. package/dist/linters/house/plugin.d.ts.map +1 -1
  160. package/dist/linters/house/plugin.js +4 -0
  161. package/dist/linters/house/plugin.js.map +1 -1
  162. package/dist/linters/house/promise-chain-support.d.ts +49 -0
  163. package/dist/linters/house/promise-chain-support.d.ts.map +1 -0
  164. package/dist/linters/house/promise-chain-support.js +188 -0
  165. package/dist/linters/house/promise-chain-support.js.map +1 -0
  166. package/dist/linters/rules/no-restricted-properties.rule.js +1 -1
  167. package/dist/linters/rules/no-restricted-properties.rule.js.map +1 -1
  168. package/dist/testing/rejection.helper.d.ts +25 -20
  169. package/dist/testing/rejection.helper.d.ts.map +1 -1
  170. package/dist/testing/rejection.helper.js +33 -31
  171. package/dist/testing/rejection.helper.js.map +1 -1
  172. package/dist/testing/repo-tmp.d.ts +19 -9
  173. package/dist/testing/repo-tmp.d.ts.map +1 -1
  174. package/dist/testing/repo-tmp.js +50 -48
  175. package/dist/testing/repo-tmp.js.map +1 -1
  176. package/dist/testing/spawn.helper.d.ts +12 -10
  177. package/dist/testing/spawn.helper.d.ts.map +1 -1
  178. package/dist/testing/spawn.helper.js +22 -16
  179. package/dist/testing/spawn.helper.js.map +1 -1
  180. package/dist/testing/tmpdir-preload.js +1 -1
  181. package/dist/testing/tmpdir-preload.js.map +1 -1
  182. package/package.json +2 -3
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PromiseChainSupport = void 0;
4
+ const common_1 = require("./common");
5
+ const CHAIN_METHODS = new Set(['then', 'catch', 'finally']);
6
+ /** Wrappers that leave the value of the expression they hold unchanged: `x!`, `x as T`, `x satisfies T`, `<T>x`, `a?.b`. */
7
+ const VALUE_WRAPPERS = new Set([
8
+ 'TSAsExpression',
9
+ 'TSSatisfiesExpression',
10
+ 'TSNonNullExpression',
11
+ 'TSTypeAssertion',
12
+ 'ChainExpression',
13
+ ]);
14
+ const CONSTANT_UNARY_OPERATORS = new Set(['-', '+', '!', 'void']);
15
+ /** The shared parts of the promise chain rules: what a chain call is, what a constant is, what a silent handler body is. */
16
+ class PromiseChainSupport {
17
+ /** `node` without the wrappers that do not change its value (`!`, `as`, `satisfies`, `<T>`, an optional chain). */
18
+ static unwrap(opts) {
19
+ let current = opts.node;
20
+ while (VALUE_WRAPPERS.has(common_1.AstNode.getType({ node: current }) ?? '')) {
21
+ current = common_1.AstNode.readField({ node: current, key: 'expression' });
22
+ }
23
+ return current;
24
+ }
25
+ /** The name a member access reads: `a.name`, `a['name']`, ``a[`name`]``; `undefined` for a computed key. */
26
+ static memberName(opts) {
27
+ const { member } = opts;
28
+ const property = common_1.AstNode.readField({ node: member, key: 'property' });
29
+ if (common_1.AstNode.readField({ node: member, key: 'computed' }) !== true) {
30
+ const name = common_1.AstNode.readField({ node: property, key: 'name' });
31
+ return typeof name === 'string' ? name : undefined;
32
+ }
33
+ if (common_1.AstNode.getType({ node: property }) === 'Literal') {
34
+ const value = common_1.AstNode.readField({ node: property, key: 'value' });
35
+ return typeof value === 'string' ? value : undefined;
36
+ }
37
+ const expressions = common_1.AstNode.readField({ node: property, key: 'expressions' });
38
+ const quasis = common_1.AstNode.readField({ node: property, key: 'quasis' });
39
+ if (common_1.AstNode.getType({ node: property }) === 'TemplateLiteral' &&
40
+ Array.isArray(expressions) &&
41
+ expressions.length === 0 &&
42
+ Array.isArray(quasis)) {
43
+ const cooked = common_1.AstNode.readField({
44
+ node: common_1.AstNode.readField({ node: quasis[0], key: 'value' }),
45
+ key: 'cooked',
46
+ });
47
+ return typeof cooked === 'string' ? cooked : undefined;
48
+ }
49
+ return undefined;
50
+ }
51
+ /**
52
+ * `then`, `catch` or `finally` when `node` (after `unwrap`) is a call of that method - `p.then(f)`, `p?.catch(f)`,
53
+ * `p['finally'](f)` - and `undefined` for anything else. It reads the syntax only, so a thenable counts too.
54
+ */
55
+ static chainMethodOf(opts) {
56
+ const call = PromiseChainSupport.unwrap({ node: opts.node });
57
+ if (common_1.AstNode.getType({ node: call }) !== 'CallExpression') {
58
+ return undefined;
59
+ }
60
+ const callee = PromiseChainSupport.unwrap({
61
+ node: common_1.AstNode.readField({ node: call, key: 'callee' }),
62
+ });
63
+ if (common_1.AstNode.getType({ node: callee }) !== 'MemberExpression') {
64
+ return undefined;
65
+ }
66
+ const name = PromiseChainSupport.memberName({ member: callee });
67
+ return name !== undefined && CHAIN_METHODS.has(name) ? name : undefined;
68
+ }
69
+ /**
70
+ * Whether a chain call (`.then`, `.catch`, `.finally`) sits anywhere on the spine of `node`: the receivers and
71
+ * callees a call chain is built from (`a.then(f).limit(1)` has one, `a.where(x).limit(1)` has none).
72
+ */
73
+ static hasChainOnSpine(opts) {
74
+ let current = PromiseChainSupport.unwrap({ node: opts.node });
75
+ for (;;) {
76
+ const type = common_1.AstNode.getType({ node: current });
77
+ if (type === 'CallExpression') {
78
+ if (PromiseChainSupport.chainMethodOf({ node: current }) !== undefined) {
79
+ return true;
80
+ }
81
+ current = PromiseChainSupport.unwrap({
82
+ node: common_1.AstNode.readField({ node: current, key: 'callee' }),
83
+ });
84
+ continue;
85
+ }
86
+ if (type === 'MemberExpression') {
87
+ current = PromiseChainSupport.unwrap({
88
+ node: common_1.AstNode.readField({ node: current, key: 'object' }),
89
+ });
90
+ continue;
91
+ }
92
+ return false;
93
+ }
94
+ }
95
+ /**
96
+ * A value that is the same every time and reads nothing: a literal, `undefined`, a template with no `${}`, `-1`,
97
+ * `void 0`, and an array or object literal built only from those (`[]`, `{}`, `{ items: [] }`).
98
+ */
99
+ static isConstant(opts) {
100
+ const node = PromiseChainSupport.unwrapTypes({ node: opts.node });
101
+ const type = common_1.AstNode.getType({ node }) ?? '';
102
+ const constant = (child) => PromiseChainSupport.isConstant({ node: child });
103
+ switch (type) {
104
+ case 'Literal': {
105
+ return true;
106
+ }
107
+ case 'Identifier': {
108
+ return common_1.AstNode.readField({ node, key: 'name' }) === 'undefined';
109
+ }
110
+ case 'TemplateLiteral': {
111
+ const expressions = common_1.AstNode.readField({ node, key: 'expressions' });
112
+ return Array.isArray(expressions) && expressions.length === 0;
113
+ }
114
+ case 'UnaryExpression': {
115
+ return (CONSTANT_UNARY_OPERATORS.has(String(common_1.AstNode.readField({ node, key: 'operator' }))) &&
116
+ constant(common_1.AstNode.readField({ node, key: 'argument' })));
117
+ }
118
+ case 'ArrayExpression': {
119
+ const elements = common_1.AstNode.readField({ node, key: 'elements' });
120
+ return (Array.isArray(elements) &&
121
+ elements.every((element) => element === null || constant(element)));
122
+ }
123
+ case 'ObjectExpression': {
124
+ const properties = common_1.AstNode.readField({ node, key: 'properties' });
125
+ return (Array.isArray(properties) &&
126
+ properties.every((property) => common_1.AstNode.getType({ node: property }) === 'Property' &&
127
+ common_1.AstNode.readField({ node: property, key: 'computed' }) !== true &&
128
+ common_1.AstNode.readField({ node: property, key: 'method' }) !== true &&
129
+ common_1.AstNode.readField({ node: property, key: 'kind' }) === 'init' &&
130
+ constant(common_1.AstNode.readField({ node: property, key: 'value' }))));
131
+ }
132
+ default: {
133
+ return false;
134
+ }
135
+ }
136
+ }
137
+ /**
138
+ * Whether the statements of a handler or a `catch` block leave no trace of the error: none at all (a comment does
139
+ * not count), or only `return;`, `return <constant>;` and `<name or member> = <constant>;`. Anything else - a
140
+ * call, a `throw`, a read of a variable - is a decision someone made, and counts as handled.
141
+ */
142
+ static isSilentBlock(opts) {
143
+ const { statements } = opts;
144
+ return (Array.isArray(statements) &&
145
+ statements.every((statement) => {
146
+ const type = common_1.AstNode.getType({ node: statement });
147
+ if (type === 'EmptyStatement') {
148
+ return true;
149
+ }
150
+ if (type === 'ReturnStatement') {
151
+ const argument = common_1.AstNode.readField({ node: statement, key: 'argument' });
152
+ return argument === null || PromiseChainSupport.isConstant({ node: argument });
153
+ }
154
+ const expression = common_1.AstNode.readField({ node: statement, key: 'expression' });
155
+ return (type === 'ExpressionStatement' &&
156
+ common_1.AstNode.getType({ node: expression }) === 'AssignmentExpression' &&
157
+ common_1.AstNode.readField({ node: expression, key: 'operator' }) === '=' &&
158
+ PromiseChainSupport.isPlainTarget({
159
+ node: common_1.AstNode.readField({ node: expression, key: 'left' }),
160
+ }) &&
161
+ PromiseChainSupport.isConstant({
162
+ node: common_1.AstNode.readField({ node: expression, key: 'right' }),
163
+ }));
164
+ }));
165
+ }
166
+ /** `name`, `this.name`, `a.b.c`: an assignment target that reads no computed key and calls nothing. */
167
+ static isPlainTarget(opts) {
168
+ const { node } = opts;
169
+ const type = common_1.AstNode.getType({ node });
170
+ if (type === 'Identifier' || type === 'ThisExpression') {
171
+ return true;
172
+ }
173
+ return (type === 'MemberExpression' &&
174
+ common_1.AstNode.readField({ node, key: 'computed' }) !== true &&
175
+ PromiseChainSupport.isPlainTarget({ node: common_1.AstNode.readField({ node, key: 'object' }) }));
176
+ }
177
+ /** `node` without the type-only wrappers (`as`, `satisfies`, `!`, `<T>`), keeping an optional chain. */
178
+ static unwrapTypes(opts) {
179
+ let current = opts.node;
180
+ while (VALUE_WRAPPERS.has(common_1.AstNode.getType({ node: current }) ?? '') &&
181
+ common_1.AstNode.getType({ node: current }) !== 'ChainExpression') {
182
+ current = common_1.AstNode.readField({ node: current, key: 'expression' });
183
+ }
184
+ return current;
185
+ }
186
+ }
187
+ exports.PromiseChainSupport = PromiseChainSupport;
188
+ //# sourceMappingURL=promise-chain-support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promise-chain-support.js","sourceRoot":"","sources":["../../../src/linters/house/promise-chain-support.ts"],"names":[],"mappings":"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","sourcesContent":["import { AstNode } from './common';\n\nconst CHAIN_METHODS = new Set(['then', 'catch', 'finally']);\n/** Wrappers that leave the value of the expression they hold unchanged: `x!`, `x as T`, `x satisfies T`, `<T>x`, `a?.b`. */\nconst VALUE_WRAPPERS = new Set([\n 'TSAsExpression',\n 'TSSatisfiesExpression',\n 'TSNonNullExpression',\n 'TSTypeAssertion',\n 'ChainExpression',\n]);\nconst CONSTANT_UNARY_OPERATORS = new Set(['-', '+', '!', 'void']);\n\n/** The shared parts of the promise chain rules: what a chain call is, what a constant is, what a silent handler body is. */\nexport class PromiseChainSupport {\n /** `node` without the wrappers that do not change its value (`!`, `as`, `satisfies`, `<T>`, an optional chain). */\n static unwrap(opts: { node: unknown }): unknown {\n let current = opts.node;\n\n while (VALUE_WRAPPERS.has(AstNode.getType({ node: current }) ?? '')) {\n current = AstNode.readField({ node: current, key: 'expression' });\n }\n\n return current;\n }\n\n /** The name a member access reads: `a.name`, `a['name']`, ``a[`name`]``; `undefined` for a computed key. */\n static memberName(opts: { member: unknown }): string | undefined {\n const { member } = opts;\n const property = AstNode.readField({ node: member, key: 'property' });\n\n if (AstNode.readField({ node: member, key: 'computed' }) !== true) {\n const name = AstNode.readField({ node: property, key: 'name' });\n return typeof name === 'string' ? name : undefined;\n }\n\n if (AstNode.getType({ node: property }) === 'Literal') {\n const value = AstNode.readField({ node: property, key: 'value' });\n return typeof value === 'string' ? value : undefined;\n }\n\n const expressions = AstNode.readField({ node: property, key: 'expressions' });\n const quasis = AstNode.readField({ node: property, key: 'quasis' });\n if (\n AstNode.getType({ node: property }) === 'TemplateLiteral' &&\n Array.isArray(expressions) &&\n expressions.length === 0 &&\n Array.isArray(quasis)\n ) {\n const cooked = AstNode.readField({\n node: AstNode.readField({ node: quasis[0], key: 'value' }),\n key: 'cooked',\n });\n return typeof cooked === 'string' ? cooked : undefined;\n }\n\n return undefined;\n }\n\n /**\n * `then`, `catch` or `finally` when `node` (after `unwrap`) is a call of that method - `p.then(f)`, `p?.catch(f)`,\n * `p['finally'](f)` - and `undefined` for anything else. It reads the syntax only, so a thenable counts too.\n */\n static chainMethodOf(opts: { node: unknown }): string | undefined {\n const call = PromiseChainSupport.unwrap({ node: opts.node });\n if (AstNode.getType({ node: call }) !== 'CallExpression') {\n return undefined;\n }\n\n const callee = PromiseChainSupport.unwrap({\n node: AstNode.readField({ node: call, key: 'callee' }),\n });\n if (AstNode.getType({ node: callee }) !== 'MemberExpression') {\n return undefined;\n }\n\n const name = PromiseChainSupport.memberName({ member: callee });\n return name !== undefined && CHAIN_METHODS.has(name) ? name : undefined;\n }\n\n /**\n * Whether a chain call (`.then`, `.catch`, `.finally`) sits anywhere on the spine of `node`: the receivers and\n * callees a call chain is built from (`a.then(f).limit(1)` has one, `a.where(x).limit(1)` has none).\n */\n static hasChainOnSpine(opts: { node: unknown }): boolean {\n let current = PromiseChainSupport.unwrap({ node: opts.node });\n\n for (;;) {\n const type = AstNode.getType({ node: current });\n\n if (type === 'CallExpression') {\n if (PromiseChainSupport.chainMethodOf({ node: current }) !== undefined) {\n return true;\n }\n current = PromiseChainSupport.unwrap({\n node: AstNode.readField({ node: current, key: 'callee' }),\n });\n continue;\n }\n\n if (type === 'MemberExpression') {\n current = PromiseChainSupport.unwrap({\n node: AstNode.readField({ node: current, key: 'object' }),\n });\n continue;\n }\n\n return false;\n }\n }\n\n /**\n * A value that is the same every time and reads nothing: a literal, `undefined`, a template with no `${}`, `-1`,\n * `void 0`, and an array or object literal built only from those (`[]`, `{}`, `{ items: [] }`).\n */\n static isConstant(opts: { node: unknown }): boolean {\n const node = PromiseChainSupport.unwrapTypes({ node: opts.node });\n const type = AstNode.getType({ node }) ?? '';\n const constant = (child: unknown) => PromiseChainSupport.isConstant({ node: child });\n\n switch (type) {\n case 'Literal': {\n return true;\n }\n case 'Identifier': {\n return AstNode.readField({ node, key: 'name' }) === 'undefined';\n }\n case 'TemplateLiteral': {\n const expressions = AstNode.readField({ node, key: 'expressions' });\n return Array.isArray(expressions) && expressions.length === 0;\n }\n case 'UnaryExpression': {\n return (\n CONSTANT_UNARY_OPERATORS.has(String(AstNode.readField({ node, key: 'operator' }))) &&\n constant(AstNode.readField({ node, key: 'argument' }))\n );\n }\n case 'ArrayExpression': {\n const elements = AstNode.readField({ node, key: 'elements' });\n return (\n Array.isArray(elements) &&\n elements.every((element) => element === null || constant(element))\n );\n }\n case 'ObjectExpression': {\n const properties = AstNode.readField({ node, key: 'properties' });\n return (\n Array.isArray(properties) &&\n properties.every(\n (property) =>\n AstNode.getType({ node: property }) === 'Property' &&\n AstNode.readField({ node: property, key: 'computed' }) !== true &&\n AstNode.readField({ node: property, key: 'method' }) !== true &&\n AstNode.readField({ node: property, key: 'kind' }) === 'init' &&\n constant(AstNode.readField({ node: property, key: 'value' })),\n )\n );\n }\n default: {\n return false;\n }\n }\n }\n\n /**\n * Whether the statements of a handler or a `catch` block leave no trace of the error: none at all (a comment does\n * not count), or only `return;`, `return <constant>;` and `<name or member> = <constant>;`. Anything else - a\n * call, a `throw`, a read of a variable - is a decision someone made, and counts as handled.\n */\n static isSilentBlock(opts: { statements: unknown }): boolean {\n const { statements } = opts;\n\n return (\n Array.isArray(statements) &&\n statements.every((statement) => {\n const type = AstNode.getType({ node: statement });\n\n if (type === 'EmptyStatement') {\n return true;\n }\n\n if (type === 'ReturnStatement') {\n const argument = AstNode.readField({ node: statement, key: 'argument' });\n return argument === null || PromiseChainSupport.isConstant({ node: argument });\n }\n\n const expression = AstNode.readField({ node: statement, key: 'expression' });\n return (\n type === 'ExpressionStatement' &&\n AstNode.getType({ node: expression }) === 'AssignmentExpression' &&\n AstNode.readField({ node: expression, key: 'operator' }) === '=' &&\n PromiseChainSupport.isPlainTarget({\n node: AstNode.readField({ node: expression, key: 'left' }),\n }) &&\n PromiseChainSupport.isConstant({\n node: AstNode.readField({ node: expression, key: 'right' }),\n })\n );\n })\n );\n }\n\n /** `name`, `this.name`, `a.b.c`: an assignment target that reads no computed key and calls nothing. */\n static isPlainTarget(opts: { node: unknown }): boolean {\n const { node } = opts;\n const type = AstNode.getType({ node });\n\n if (type === 'Identifier' || type === 'ThisExpression') {\n return true;\n }\n\n return (\n type === 'MemberExpression' &&\n AstNode.readField({ node, key: 'computed' }) !== true &&\n PromiseChainSupport.isPlainTarget({ node: AstNode.readField({ node, key: 'object' }) })\n );\n }\n\n /** `node` without the type-only wrappers (`as`, `satisfies`, `!`, `<T>`), keeping an optional chain. */\n static unwrapTypes(opts: { node: unknown }): unknown {\n let current = opts.node;\n\n while (\n VALUE_WRAPPERS.has(AstNode.getType({ node: current }) ?? '') &&\n AstNode.getType({ node: current }) !== 'ChainExpression'\n ) {\n current = AstNode.readField({ node: current, key: 'expression' });\n }\n\n return current;\n }\n}\n"]}
@@ -12,7 +12,7 @@ class NoRestrictedPropertiesRule {
12
12
  static { this.SECURE_CONTEXT = [
13
13
  {
14
14
  property: 'randomUUID',
15
- message: 'crypto.randomUUID exists only in a secure context (https, localhost) - on a plain-http origin it is undefined. Use uuidV4 from @venizia/ignis-helpers/uuid.',
15
+ message: 'crypto.randomUUID exists only in a secure context (https, localhost) - on a plain-http origin it is undefined. Use UuidV4Generator.generate() from @venizia/ignis-helpers/uuid.',
16
16
  },
17
17
  ]; }
18
18
  static build(opts = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"no-restricted-properties.rule.js","sourceRoot":"","sources":["../../../src/linters/rules/no-restricted-properties.rule.ts"],"names":[],"mappings":";;;AASA;;;;;GAKG;AACH,MAAa,0BAA0B;aACrB,SAAI,GAAG,0BAA0B,CAAC;aAElC,mBAAc,GAAmC;QAC/D;YACE,QAAQ,EAAE,YAAY;YACtB,OAAO,EACL,6JAA6J;SAChK;KACF,CAAC;IAEF,MAAM,CAAC,KAAK,CACV,OAA8E,EAAE;QAEhF,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,eAAe,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;QAC1D,OAAO,CAAC,QAAQ,EAAE,GAAG,0BAA0B,CAAC,cAAc,EAAE,GAAG,eAAe,CAAC,CAAC;IACtF,CAAC;;AAhBH,gEAiBC","sourcesContent":["import type { TOxlintRuleEntry, TRuleSeverity } from './common';\n\n/** One forbidden property access, with the message the author sees. */\nexport interface IRestrictedProperty {\n property: string;\n object?: string;\n message: string;\n}\n\n/**\n * `no-restricted-properties`. The default bans what exists only in a secure context (https,\n * localhost): on a plain-http LAN origin or in a WebView, `crypto.randomUUID` is `undefined` and the\n * first call throws. Catches the call, `globalThis.crypto.randomUUID`, the bracket form and\n * destructuring; leaves `getRandomValues` alone.\n */\nexport class NoRestrictedPropertiesRule {\n static readonly NAME = 'no-restricted-properties';\n\n static readonly SECURE_CONTEXT: readonly IRestrictedProperty[] = [\n {\n property: 'randomUUID',\n message:\n 'crypto.randomUUID exists only in a secure context (https, localhost) - on a plain-http origin it is undefined. Use uuidV4 from @venizia/ignis-helpers/uuid.',\n },\n ];\n\n static build(\n opts: { severity?: TRuleSeverity; extraProperties?: IRestrictedProperty[] } = {},\n ): TOxlintRuleEntry<'no-restricted-properties'> {\n const { severity = 'error', extraProperties = [] } = opts;\n return [severity, ...NoRestrictedPropertiesRule.SECURE_CONTEXT, ...extraProperties];\n }\n}\n"]}
1
+ {"version":3,"file":"no-restricted-properties.rule.js","sourceRoot":"","sources":["../../../src/linters/rules/no-restricted-properties.rule.ts"],"names":[],"mappings":";;;AASA;;;;;GAKG;AACH,MAAa,0BAA0B;aACrB,SAAI,GAAG,0BAA0B,CAAC;aAElC,mBAAc,GAAmC;QAC/D;YACE,QAAQ,EAAE,YAAY;YACtB,OAAO,EACL,iLAAiL;SACpL;KACF,CAAC;IAEF,MAAM,CAAC,KAAK,CACV,OAA8E,EAAE;QAEhF,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,eAAe,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;QAC1D,OAAO,CAAC,QAAQ,EAAE,GAAG,0BAA0B,CAAC,cAAc,EAAE,GAAG,eAAe,CAAC,CAAC;IACtF,CAAC;;AAhBH,gEAiBC","sourcesContent":["import type { TOxlintRuleEntry, TRuleSeverity } from './common';\n\n/** One forbidden property access, with the message the author sees. */\nexport interface IRestrictedProperty {\n property: string;\n object?: string;\n message: string;\n}\n\n/**\n * `no-restricted-properties`. The default bans what exists only in a secure context (https,\n * localhost): on a plain-http LAN origin or in a WebView, `crypto.randomUUID` is `undefined` and the\n * first call throws. Catches the call, `globalThis.crypto.randomUUID`, the bracket form and\n * destructuring; leaves `getRandomValues` alone.\n */\nexport class NoRestrictedPropertiesRule {\n static readonly NAME = 'no-restricted-properties';\n\n static readonly SECURE_CONTEXT: readonly IRestrictedProperty[] = [\n {\n property: 'randomUUID',\n message:\n 'crypto.randomUUID exists only in a secure context (https, localhost) - on a plain-http origin it is undefined. Use UuidV4Generator.generate() from @venizia/ignis-helpers/uuid.',\n },\n ];\n\n static build(\n opts: { severity?: TRuleSeverity; extraProperties?: IRestrictedProperty[] } = {},\n ): TOxlintRuleEntry<'no-restricted-properties'> {\n const { severity = 'error', extraProperties = [] } = opts;\n return [severity, ...NoRestrictedPropertiesRule.SECURE_CONTEXT, ...extraProperties];\n }\n}\n"]}
@@ -1,21 +1,26 @@
1
- /**
2
- * Awaits a task that must reject and returns what it rejected with; throws when the task resolves.
3
- *
4
- * bun types the async matchers (`expect(task).rejects` / `.resolves`) as `void`, so
5
- * `await expect(task).rejects.toThrow()` is refused by `await-thenable` - and left un-awaited the
6
- * assertion never runs. Awaiting the task itself needs no matcher and no cast:
7
- * `expectRejection({ task, message })` or `expect(await rejectionOf(task)).toBeInstanceOf(Error)`,
8
- * and for a resolution `expect(await task).toMatchObject(...)`.
9
- */
10
- export declare const rejectionOf: (task: Promise<unknown>) => Promise<unknown>;
11
- /**
12
- * The task rejects with an Error whose message contains `message` (a string) or matches it (a
13
- * RegExp). The message is compared as a string on purpose: bun 1.4.2 `toMatchObject` with an
14
- * asymmetric matcher overwrites the matched property on the object it checks, so a later read of
15
- * `error.message` gets the matcher back.
16
- */
17
- export declare const expectRejection: (opts: {
18
- task: Promise<unknown>;
19
- message: string | RegExp;
20
- }) => Promise<void>;
1
+ export declare class RejectionHelper {
2
+ /**
3
+ * Awaits a task that must reject and returns what it rejected with; throws when the task resolves.
4
+ *
5
+ * bun types the async matchers (`expect(task).rejects` / `.resolves`) as `void`, so
6
+ * `await expect(task).rejects.toThrow()` is refused by `await-thenable` - and left un-awaited the
7
+ * assertion never runs. Awaiting the task itself needs no matcher and no cast:
8
+ * `RejectionHelper.expectRejection({ task, message })` or
9
+ * `expect(await RejectionHelper.rejectionOf({ task })).toBeInstanceOf(Error)`,
10
+ * and for a resolution `expect(await task).toMatchObject(...)`.
11
+ */
12
+ static rejectionOf(opts: {
13
+ task: Promise<unknown>;
14
+ }): Promise<unknown>;
15
+ /**
16
+ * The task rejects with an Error whose message contains `message` (a string) or matches it (a
17
+ * RegExp). The message is compared as a string on purpose: bun 1.4.2 `toMatchObject` with an
18
+ * asymmetric matcher overwrites the matched property on the object it checks, so a later read of
19
+ * `error.message` gets the matcher back.
20
+ */
21
+ static expectRejection(opts: {
22
+ task: Promise<unknown>;
23
+ message: string | RegExp;
24
+ }): Promise<void>;
25
+ }
21
26
  //# sourceMappingURL=rejection.helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rejection.helper.d.ts","sourceRoot":"","sources":["../../src/testing/rejection.helper.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAU,MAAM,OAAO,CAAC,OAAO,CAAC,KAAG,OAAO,CAAC,OAAO,CAQzE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,MAAM;IAC1C,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,KAAG,OAAO,CAAC,IAAI,CAMf,CAAC"}
1
+ {"version":3,"file":"rejection.helper.d.ts","sourceRoot":"","sources":["../../src/testing/rejection.helper.ts"],"names":[],"mappings":"AAEA,qBAAa,eAAe;IAC1B;;;;;;;;;OASG;WACU,WAAW,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAU5E;;;;;OAKG;WACU,eAAe,CAAC,IAAI,EAAE;QACjC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACvB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,IAAI,CAAC;CAOlB"}
@@ -1,37 +1,39 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expectRejection = exports.rejectionOf = void 0;
3
+ exports.RejectionHelper = void 0;
4
4
  const bun_test_1 = require("bun:test");
5
- /**
6
- * Awaits a task that must reject and returns what it rejected with; throws when the task resolves.
7
- *
8
- * bun types the async matchers (`expect(task).rejects` / `.resolves`) as `void`, so
9
- * `await expect(task).rejects.toThrow()` is refused by `await-thenable` - and left un-awaited the
10
- * assertion never runs. Awaiting the task itself needs no matcher and no cast:
11
- * `expectRejection({ task, message })` or `expect(await rejectionOf(task)).toBeInstanceOf(Error)`,
12
- * and for a resolution `expect(await task).toMatchObject(...)`.
13
- */
14
- const rejectionOf = async (task) => {
15
- try {
16
- await task;
5
+ class RejectionHelper {
6
+ /**
7
+ * Awaits a task that must reject and returns what it rejected with; throws when the task resolves.
8
+ *
9
+ * bun types the async matchers (`expect(task).rejects` / `.resolves`) as `void`, so
10
+ * `await expect(task).rejects.toThrow()` is refused by `await-thenable` - and left un-awaited the
11
+ * assertion never runs. Awaiting the task itself needs no matcher and no cast:
12
+ * `RejectionHelper.expectRejection({ task, message })` or
13
+ * `expect(await RejectionHelper.rejectionOf({ task })).toBeInstanceOf(Error)`,
14
+ * and for a resolution `expect(await task).toMatchObject(...)`.
15
+ */
16
+ static async rejectionOf(opts) {
17
+ try {
18
+ await opts.task;
19
+ }
20
+ catch (error) {
21
+ return error;
22
+ }
23
+ return bun_test_1.expect.unreachable('Expected the task to reject, but it resolved');
17
24
  }
18
- catch (error) {
19
- return error;
25
+ /**
26
+ * The task rejects with an Error whose message contains `message` (a string) or matches it (a
27
+ * RegExp). The message is compared as a string on purpose: bun 1.4.2 `toMatchObject` with an
28
+ * asymmetric matcher overwrites the matched property on the object it checks, so a later read of
29
+ * `error.message` gets the matcher back.
30
+ */
31
+ static async expectRejection(opts) {
32
+ const { task, message } = opts;
33
+ const reason = await RejectionHelper.rejectionOf({ task });
34
+ (0, bun_test_1.expect)(reason).toBeInstanceOf(Error);
35
+ (0, bun_test_1.expect)(reason instanceof Error ? reason.message : String(reason)).toMatch(message);
20
36
  }
21
- return bun_test_1.expect.unreachable('Expected the task to reject, but it resolved');
22
- };
23
- exports.rejectionOf = rejectionOf;
24
- /**
25
- * The task rejects with an Error whose message contains `message` (a string) or matches it (a
26
- * RegExp). The message is compared as a string on purpose: bun 1.4.2 `toMatchObject` with an
27
- * asymmetric matcher overwrites the matched property on the object it checks, so a later read of
28
- * `error.message` gets the matcher back.
29
- */
30
- const expectRejection = async (opts) => {
31
- const { task, message } = opts;
32
- const reason = await (0, exports.rejectionOf)(task);
33
- (0, bun_test_1.expect)(reason).toBeInstanceOf(Error);
34
- (0, bun_test_1.expect)(reason instanceof Error ? reason.message : String(reason)).toMatch(message);
35
- };
36
- exports.expectRejection = expectRejection;
37
+ }
38
+ exports.RejectionHelper = RejectionHelper;
37
39
  //# sourceMappingURL=rejection.helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rejection.helper.js","sourceRoot":"","sources":["../../src/testing/rejection.helper.ts"],"names":[],"mappings":";;;AAAA,uCAAkC;AAElC;;;;;;;;GAQG;AACI,MAAM,WAAW,GAAG,KAAK,EAAE,IAAsB,EAAoB,EAAE;IAC5E,IAAI,CAAC;QACH,MAAM,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,iBAAM,CAAC,WAAW,CAAC,8CAA8C,CAAC,CAAC;AAC5E,CAAC,CAAC;AARW,QAAA,WAAW,eAQtB;AAEF;;;;;GAKG;AACI,MAAM,eAAe,GAAG,KAAK,EAAE,IAGrC,EAAiB,EAAE;IAClB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,IAAA,mBAAW,EAAC,IAAI,CAAC,CAAC;IAEvC,IAAA,iBAAM,EAAC,MAAM,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,IAAA,iBAAM,EAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACrF,CAAC,CAAC;AATW,QAAA,eAAe,mBAS1B","sourcesContent":["import { expect } from 'bun:test';\n\n/**\n * Awaits a task that must reject and returns what it rejected with; throws when the task resolves.\n *\n * bun types the async matchers (`expect(task).rejects` / `.resolves`) as `void`, so\n * `await expect(task).rejects.toThrow()` is refused by `await-thenable` - and left un-awaited the\n * assertion never runs. Awaiting the task itself needs no matcher and no cast:\n * `expectRejection({ task, message })` or `expect(await rejectionOf(task)).toBeInstanceOf(Error)`,\n * and for a resolution `expect(await task).toMatchObject(...)`.\n */\nexport const rejectionOf = async (task: Promise<unknown>): Promise<unknown> => {\n try {\n await task;\n } catch (error) {\n return error;\n }\n\n return expect.unreachable('Expected the task to reject, but it resolved');\n};\n\n/**\n * The task rejects with an Error whose message contains `message` (a string) or matches it (a\n * RegExp). The message is compared as a string on purpose: bun 1.4.2 `toMatchObject` with an\n * asymmetric matcher overwrites the matched property on the object it checks, so a later read of\n * `error.message` gets the matcher back.\n */\nexport const expectRejection = async (opts: {\n task: Promise<unknown>;\n message: string | RegExp;\n}): Promise<void> => {\n const { task, message } = opts;\n const reason = await rejectionOf(task);\n\n expect(reason).toBeInstanceOf(Error);\n expect(reason instanceof Error ? reason.message : String(reason)).toMatch(message);\n};\n"]}
1
+ {"version":3,"file":"rejection.helper.js","sourceRoot":"","sources":["../../src/testing/rejection.helper.ts"],"names":[],"mappings":";;;AAAA,uCAAkC;AAElC,MAAa,eAAe;IAC1B;;;;;;;;;OASG;IACH,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,IAAgC;QACvD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,IAAI,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,iBAAM,CAAC,WAAW,CAAC,8CAA8C,CAAC,CAAC;IAC5E,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,IAG5B;QACC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3D,IAAA,iBAAM,EAAC,MAAM,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACrC,IAAA,iBAAM,EAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACrF,CAAC;CACF;AArCD,0CAqCC","sourcesContent":["import { expect } from 'bun:test';\n\nexport class RejectionHelper {\n /**\n * Awaits a task that must reject and returns what it rejected with; throws when the task resolves.\n *\n * bun types the async matchers (`expect(task).rejects` / `.resolves`) as `void`, so\n * `await expect(task).rejects.toThrow()` is refused by `await-thenable` - and left un-awaited the\n * assertion never runs. Awaiting the task itself needs no matcher and no cast:\n * `RejectionHelper.expectRejection({ task, message })` or\n * `expect(await RejectionHelper.rejectionOf({ task })).toBeInstanceOf(Error)`,\n * and for a resolution `expect(await task).toMatchObject(...)`.\n */\n static async rejectionOf(opts: { task: Promise<unknown> }): Promise<unknown> {\n try {\n await opts.task;\n } catch (error) {\n return error;\n }\n\n return expect.unreachable('Expected the task to reject, but it resolved');\n }\n\n /**\n * The task rejects with an Error whose message contains `message` (a string) or matches it (a\n * RegExp). The message is compared as a string on purpose: bun 1.4.2 `toMatchObject` with an\n * asymmetric matcher overwrites the matched property on the object it checks, so a later read of\n * `error.message` gets the matcher back.\n */\n static async expectRejection(opts: {\n task: Promise<unknown>;\n message: string | RegExp;\n }): Promise<void> {\n const { task, message } = opts;\n const reason = await RejectionHelper.rejectionOf({ task });\n\n expect(reason).toBeInstanceOf(Error);\n expect(reason instanceof Error ? reason.message : String(reason)).toMatch(message);\n }\n}\n"]}
@@ -1,12 +1,22 @@
1
1
  /** The folder under the repository root that holds a test run's temp folders. */
2
2
  export declare const REPO_TMP_DIRECTORY: string;
3
- /** The nearest ancestor of `start` that holds a `.git` (folder or file); `start` itself when there is none. */
4
- export declare const findRepositoryRoot: (start?: string) => string;
5
- /**
6
- * Points TMPDIR at the repository's test folder unless it is already inside it (make's value, or a
7
- * private folder a caller made there), and returns the folder now in force. Idempotent.
8
- */
9
- export declare const useRepoTmp: (opts?: {
10
- root?: string;
11
- }) => string;
3
+ export declare class RepoTmp {
4
+ /** The nearest ancestor of `start` that holds a `.git` (folder or file); `start` itself when there is none. */
5
+ static findRoot(opts?: {
6
+ start?: string;
7
+ }): string;
8
+ /**
9
+ * `Bun.spawn` and `Bun.spawnSync` hand a child the environment the process started with, not the one
10
+ * it has now, so a TMPDIR set here would never reach it. Wrapped to default to the current one; an
11
+ * `env` a caller passes wins. `spawn(cmd, options)` and `spawn({ cmd, ...options })` are both handled.
12
+ */
13
+ private static withCurrentEnv;
14
+ /**
15
+ * Points TMPDIR at the repository's test folder unless it is already inside it (make's value, or a
16
+ * private folder a caller made there), and returns the folder now in force. Idempotent.
17
+ */
18
+ static apply(opts?: {
19
+ root?: string;
20
+ }): string;
21
+ }
12
22
  //# sourceMappingURL=repo-tmp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo-tmp.d.ts","sourceRoot":"","sources":["../../src/testing/repo-tmp.ts"],"names":[],"mappings":"AAWA,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAE3E,+GAA+G;AAC/G,eAAO,MAAM,kBAAkB,GAAI,QAAO,MAAsB,KAAG,MAelE,CAAC;AAwBF;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,OAAM;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,KAAG,MAkBzD,CAAC"}
1
+ {"version":3,"file":"repo-tmp.d.ts","sourceRoot":"","sources":["../../src/testing/repo-tmp.ts"],"names":[],"mappings":"AAWA,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAI3E,qBAAa,OAAO;IAClB,+GAA+G;IAC/G,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,MAAM;IAkBtD;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAe7B;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,MAAM;CAqBnD"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useRepoTmp = exports.findRepositoryRoot = exports.REPO_TMP_DIRECTORY = void 0;
3
+ exports.RepoTmp = exports.REPO_TMP_DIRECTORY = void 0;
4
4
  /**
5
5
  * Where a test makes its temp folders: `.agents/local/tmp/tests` of the repository, never the system
6
6
  * temp. `make test` sets TMPDIR to a folder like it; this does the same for a run without make - a
@@ -13,55 +13,57 @@ const node_fs_1 = require("node:fs");
13
13
  const node_path_1 = require("node:path");
14
14
  /** The folder under the repository root that holds a test run's temp folders. */
15
15
  exports.REPO_TMP_DIRECTORY = (0, node_path_1.join)('.agents', 'local', 'tmp', 'tests');
16
- /** The nearest ancestor of `start` that holds a `.git` (folder or file); `start` itself when there is none. */
17
- const findRepositoryRoot = (start = process.cwd()) => {
18
- let current = (0, node_path_1.resolve)(start);
19
- for (;;) {
20
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(current, '.git'))) {
21
- return current;
22
- }
23
- const parent = (0, node_path_1.dirname)(current);
24
- if (parent === current) {
25
- return (0, node_path_1.resolve)(start);
16
+ class RepoTmp {
17
+ /** The nearest ancestor of `start` that holds a `.git` (folder or file); `start` itself when there is none. */
18
+ static findRoot(opts = {}) {
19
+ const { start = process.cwd() } = opts;
20
+ let current = (0, node_path_1.resolve)(start);
21
+ for (;;) {
22
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(current, '.git'))) {
23
+ return current;
24
+ }
25
+ const parent = (0, node_path_1.dirname)(current);
26
+ if (parent === current) {
27
+ return (0, node_path_1.resolve)(start);
28
+ }
29
+ current = parent;
26
30
  }
27
- current = parent;
28
31
  }
29
- };
30
- exports.findRepositoryRoot = findRepositoryRoot;
31
- /**
32
- * `Bun.spawn` and `Bun.spawnSync` hand a child the environment the process started with, not the one
33
- * it has now, so a TMPDIR set here would never reach it. Wrapped to default to the current one; an
34
- * `env` a caller passes wins. `spawn(cmd, options)` and `spawn({ cmd, ...options })` are both handled.
35
- */
36
- const withCurrentEnv = (spawn) => {
37
- return (...args) => {
38
- const [first, second] = args;
39
- const options = Array.isArray(first) ? second : first;
40
- if (options?.env !== undefined) {
41
- return spawn(...args);
32
+ /**
33
+ * `Bun.spawn` and `Bun.spawnSync` hand a child the environment the process started with, not the one
34
+ * it has now, so a TMPDIR set here would never reach it. Wrapped to default to the current one; an
35
+ * `env` a caller passes wins. `spawn(cmd, options)` and `spawn({ cmd, ...options })` are both handled.
36
+ */
37
+ static withCurrentEnv(spawn) {
38
+ return (...args) => {
39
+ const [first, second] = args;
40
+ const options = Array.isArray(first) ? second : first;
41
+ if (options?.env !== undefined) {
42
+ return spawn(...args);
43
+ }
44
+ return Array.isArray(first)
45
+ ? spawn(first, { ...second, env: process.env })
46
+ : spawn({ ...first, env: process.env });
47
+ };
48
+ }
49
+ /**
50
+ * Points TMPDIR at the repository's test folder unless it is already inside it (make's value, or a
51
+ * private folder a caller made there), and returns the folder now in force. Idempotent.
52
+ */
53
+ static apply(opts = {}) {
54
+ const root = opts.root ?? RepoTmp.findRoot();
55
+ const testTmp = (0, node_path_1.join)(root, exports.REPO_TMP_DIRECTORY);
56
+ const current = process.env.TMPDIR;
57
+ if (current !== undefined && `${(0, node_path_1.resolve)(current)}${node_path_1.sep}`.startsWith(`${testTmp}${node_path_1.sep}`)) {
58
+ return current;
42
59
  }
43
- return Array.isArray(first)
44
- ? spawn(first, { ...second, env: process.env })
45
- : spawn({ ...first, env: process.env });
46
- };
47
- };
48
- /**
49
- * Points TMPDIR at the repository's test folder unless it is already inside it (make's value, or a
50
- * private folder a caller made there), and returns the folder now in force. Idempotent.
51
- */
52
- const useRepoTmp = (opts = {}) => {
53
- const root = opts.root ?? (0, exports.findRepositoryRoot)();
54
- const testTmp = (0, node_path_1.join)(root, exports.REPO_TMP_DIRECTORY);
55
- const current = process.env.TMPDIR;
56
- if (current !== undefined && `${(0, node_path_1.resolve)(current)}${node_path_1.sep}`.startsWith(`${testTmp}${node_path_1.sep}`)) {
57
- return current;
60
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(testTmp, '.git'), { recursive: true });
61
+ process.env.TMPDIR = testTmp;
62
+ process.env.GIT_CEILING_DIRECTORIES = testTmp;
63
+ Bun.spawn = RepoTmp.withCurrentEnv(Bun.spawn);
64
+ Bun.spawnSync = RepoTmp.withCurrentEnv(Bun.spawnSync);
65
+ return testTmp;
58
66
  }
59
- (0, node_fs_1.mkdirSync)((0, node_path_1.join)(testTmp, '.git'), { recursive: true });
60
- process.env.TMPDIR = testTmp;
61
- process.env.GIT_CEILING_DIRECTORIES = testTmp;
62
- Bun.spawn = withCurrentEnv(Bun.spawn);
63
- Bun.spawnSync = withCurrentEnv(Bun.spawnSync);
64
- return testTmp;
65
- };
66
- exports.useRepoTmp = useRepoTmp;
67
+ }
68
+ exports.RepoTmp = RepoTmp;
67
69
  //# sourceMappingURL=repo-tmp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo-tmp.js","sourceRoot":"","sources":["../../src/testing/repo-tmp.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,qCAAgD;AAChD,yCAAwD;AAExD,iFAAiF;AACpE,QAAA,kBAAkB,GAAG,IAAA,gBAAI,EAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAE3E,+GAA+G;AACxG,MAAM,kBAAkB,GAAG,CAAC,QAAgB,OAAO,CAAC,GAAG,EAAE,EAAU,EAAE;IAC1E,IAAI,OAAO,GAAG,IAAA,mBAAO,EAAC,KAAK,CAAC,CAAC;IAE7B,SAAS,CAAC;QACR,IAAI,IAAA,oBAAU,EAAC,IAAA,gBAAI,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YACtC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,CAAC;QAChC,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YACvB,OAAO,IAAA,mBAAO,EAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;AACH,CAAC,CAAC;AAfW,QAAA,kBAAkB,sBAe7B;AAIF;;;;GAIG;AACH,MAAM,cAAc,GAAG,CAAC,KAAa,EAAU,EAAE;IAC/C,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE;QACjB,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;QAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;QAEtD,IAAK,OAAyC,EAAE,GAAG,KAAK,SAAS,EAAE,CAAC;YAClE,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACzB,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAI,MAAiB,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;YAC3D,CAAC,CAAC,KAAK,CAAC,EAAE,GAAI,KAAgB,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF;;;GAGG;AACI,MAAM,UAAU,GAAG,CAAC,OAA0B,EAAE,EAAU,EAAE;IACjE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAA,0BAAkB,GAAE,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAA,gBAAI,EAAC,IAAI,EAAE,0BAAkB,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;IAEnC,IAAI,OAAO,KAAK,SAAS,IAAI,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,GAAG,eAAG,EAAE,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,eAAG,EAAE,CAAC,EAAE,CAAC;QACxF,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAA,mBAAS,EAAC,IAAA,gBAAI,EAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,uBAAuB,GAAG,OAAO,CAAC;IAC9C,GAAG,CAAC,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,KAA0B,CAAgC,CAAC;IAC1F,GAAG,CAAC,SAAS,GAAG,cAAc,CAC5B,GAAG,CAAC,SAA8B,CACA,CAAC;IAErC,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAlBW,QAAA,UAAU,cAkBrB","sourcesContent":["/**\n * Where a test makes its temp folders: `.agents/local/tmp/tests` of the repository, never the system\n * temp. `make test` sets TMPDIR to a folder like it; this does the same for a run without make - a\n * bare `bun test` on one file - and is the one place that knows the path.\n *\n * The empty `.git` marker in it stops git and bun's ignore lookup from reading the enclosing\n * repository, so GIT_CEILING_DIRECTORIES is set to the same folder.\n */\nimport { existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join, resolve, sep } from 'node:path';\n\n/** The folder under the repository root that holds a test run's temp folders. */\nexport const REPO_TMP_DIRECTORY = join('.agents', 'local', 'tmp', 'tests');\n\n/** The nearest ancestor of `start` that holds a `.git` (folder or file); `start` itself when there is none. */\nexport const findRepositoryRoot = (start: string = process.cwd()): string => {\n let current = resolve(start);\n\n for (;;) {\n if (existsSync(join(current, '.git'))) {\n return current;\n }\n\n const parent = dirname(current);\n if (parent === current) {\n return resolve(start);\n }\n\n current = parent;\n }\n};\n\ntype TSpawn = (...args: unknown[]) => unknown;\n\n/**\n * `Bun.spawn` and `Bun.spawnSync` hand a child the environment the process started with, not the one\n * it has now, so a TMPDIR set here would never reach it. Wrapped to default to the current one; an\n * `env` a caller passes wins. `spawn(cmd, options)` and `spawn({ cmd, ...options })` are both handled.\n */\nconst withCurrentEnv = (spawn: TSpawn): TSpawn => {\n return (...args) => {\n const [first, second] = args;\n const options = Array.isArray(first) ? second : first;\n\n if ((options as { env?: unknown } | undefined)?.env !== undefined) {\n return spawn(...args);\n }\n\n return Array.isArray(first)\n ? spawn(first, { ...(second as object), env: process.env })\n : spawn({ ...(first as object), env: process.env });\n };\n};\n\n/**\n * Points TMPDIR at the repository's test folder unless it is already inside it (make's value, or a\n * private folder a caller made there), and returns the folder now in force. Idempotent.\n */\nexport const useRepoTmp = (opts: { root?: string } = {}): string => {\n const root = opts.root ?? findRepositoryRoot();\n const testTmp = join(root, REPO_TMP_DIRECTORY);\n const current = process.env.TMPDIR;\n\n if (current !== undefined && `${resolve(current)}${sep}`.startsWith(`${testTmp}${sep}`)) {\n return current;\n }\n\n mkdirSync(join(testTmp, '.git'), { recursive: true });\n process.env.TMPDIR = testTmp;\n process.env.GIT_CEILING_DIRECTORIES = testTmp;\n Bun.spawn = withCurrentEnv(Bun.spawn as unknown as TSpawn) as unknown as typeof Bun.spawn;\n Bun.spawnSync = withCurrentEnv(\n Bun.spawnSync as unknown as TSpawn,\n ) as unknown as typeof Bun.spawnSync;\n\n return testTmp;\n};\n"]}
1
+ {"version":3,"file":"repo-tmp.js","sourceRoot":"","sources":["../../src/testing/repo-tmp.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,qCAAgD;AAChD,yCAAwD;AAExD,iFAAiF;AACpE,QAAA,kBAAkB,GAAG,IAAA,gBAAI,EAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAI3E,MAAa,OAAO;IAClB,+GAA+G;IAC/G,MAAM,CAAC,QAAQ,CAAC,OAA2B,EAAE;QAC3C,MAAM,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;QACvC,IAAI,OAAO,GAAG,IAAA,mBAAO,EAAC,KAAK,CAAC,CAAC;QAE7B,SAAS,CAAC;YACR,IAAI,IAAA,oBAAU,EAAC,IAAA,gBAAI,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;gBACtC,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,MAAM,MAAM,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,CAAC;YAChC,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;gBACvB,OAAO,IAAA,mBAAO,EAAC,KAAK,CAAC,CAAC;YACxB,CAAC;YAED,OAAO,GAAG,MAAM,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,MAAM,CAAC,cAAc,CAAC,KAAa;QACzC,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE;YACjB,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;YAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YAEtD,IAAK,OAAyC,EAAE,GAAG,KAAK,SAAS,EAAE,CAAC;gBAClE,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YACxB,CAAC;YAED,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACzB,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,GAAI,MAAiB,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;gBAC3D,CAAC,CAAC,KAAK,CAAC,EAAE,GAAI,KAAgB,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACxD,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,OAA0B,EAAE;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAA,gBAAI,EAAC,IAAI,EAAE,0BAAkB,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;QAEnC,IAAI,OAAO,KAAK,SAAS,IAAI,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,GAAG,eAAG,EAAE,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,eAAG,EAAE,CAAC,EAAE,CAAC;YACxF,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,IAAA,mBAAS,EAAC,IAAA,gBAAI,EAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,uBAAuB,GAAG,OAAO,CAAC;QAC9C,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,cAAc,CAChC,GAAG,CAAC,KAA0B,CACA,CAAC;QACjC,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,cAAc,CACpC,GAAG,CAAC,SAA8B,CACA,CAAC;QAErC,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAjED,0BAiEC","sourcesContent":["/**\n * Where a test makes its temp folders: `.agents/local/tmp/tests` of the repository, never the system\n * temp. `make test` sets TMPDIR to a folder like it; this does the same for a run without make - a\n * bare `bun test` on one file - and is the one place that knows the path.\n *\n * The empty `.git` marker in it stops git and bun's ignore lookup from reading the enclosing\n * repository, so GIT_CEILING_DIRECTORIES is set to the same folder.\n */\nimport { existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join, resolve, sep } from 'node:path';\n\n/** The folder under the repository root that holds a test run's temp folders. */\nexport const REPO_TMP_DIRECTORY = join('.agents', 'local', 'tmp', 'tests');\n\ntype TSpawn = (...args: unknown[]) => unknown;\n\nexport class RepoTmp {\n /** The nearest ancestor of `start` that holds a `.git` (folder or file); `start` itself when there is none. */\n static findRoot(opts: { start?: string } = {}): string {\n const { start = process.cwd() } = opts;\n let current = resolve(start);\n\n for (;;) {\n if (existsSync(join(current, '.git'))) {\n return current;\n }\n\n const parent = dirname(current);\n if (parent === current) {\n return resolve(start);\n }\n\n current = parent;\n }\n }\n\n /**\n * `Bun.spawn` and `Bun.spawnSync` hand a child the environment the process started with, not the one\n * it has now, so a TMPDIR set here would never reach it. Wrapped to default to the current one; an\n * `env` a caller passes wins. `spawn(cmd, options)` and `spawn({ cmd, ...options })` are both handled.\n */\n private static withCurrentEnv(spawn: TSpawn): TSpawn {\n return (...args) => {\n const [first, second] = args;\n const options = Array.isArray(first) ? second : first;\n\n if ((options as { env?: unknown } | undefined)?.env !== undefined) {\n return spawn(...args);\n }\n\n return Array.isArray(first)\n ? spawn(first, { ...(second as object), env: process.env })\n : spawn({ ...(first as object), env: process.env });\n };\n }\n\n /**\n * Points TMPDIR at the repository's test folder unless it is already inside it (make's value, or a\n * private folder a caller made there), and returns the folder now in force. Idempotent.\n */\n static apply(opts: { root?: string } = {}): string {\n const root = opts.root ?? RepoTmp.findRoot();\n const testTmp = join(root, REPO_TMP_DIRECTORY);\n const current = process.env.TMPDIR;\n\n if (current !== undefined && `${resolve(current)}${sep}`.startsWith(`${testTmp}${sep}`)) {\n return current;\n }\n\n mkdirSync(join(testTmp, '.git'), { recursive: true });\n process.env.TMPDIR = testTmp;\n process.env.GIT_CEILING_DIRECTORIES = testTmp;\n Bun.spawn = RepoTmp.withCurrentEnv(\n Bun.spawn as unknown as TSpawn,\n ) as unknown as typeof Bun.spawn;\n Bun.spawnSync = RepoTmp.withCurrentEnv(\n Bun.spawnSync as unknown as TSpawn,\n ) as unknown as typeof Bun.spawnSync;\n\n return testTmp;\n }\n}\n"]}
@@ -1,13 +1,15 @@
1
1
  /** Long enough for a cold `bun build` under a loaded parallel run; it bounds a stall, not a slow run. */
2
2
  export declare const SPAWN_TIMEOUT_MS = 120000;
3
- /**
4
- * `Bun.spawnSync`, bounded. A sync spawn blocks the worker thread, so bun's own test timeout cannot
5
- * fire while it waits: the `timeout` option is the only bound. Its default signal, SIGTERM, can be
6
- * outlived by a stuck child - one such child once stalled a whole suite for 770s - so this kills with
7
- * SIGKILL, and throws naming the command instead of returning a half-finished result.
8
- */
9
- export declare const spawnSyncBounded: <const In extends Bun.SpawnOptions.Writable = "ignore", const Out extends Bun.SpawnOptions.Readable = "pipe", const Err extends Bun.SpawnOptions.Readable = "pipe">(opts: Bun.SpawnOptions.SpawnSyncOptions<In, Out, Err> & {
10
- cmd: string[];
11
- onExit?: undefined;
12
- }) => Bun.SyncSubprocess<Out, Err>;
3
+ export declare class SpawnHelper {
4
+ /**
5
+ * `Bun.spawnSync`, bounded. A sync spawn blocks the worker thread, so bun's own test timeout cannot
6
+ * fire while it waits: the `timeout` option is the only bound. Its default signal, SIGTERM, can be
7
+ * outlived by a stuck child - one such child once stalled a whole suite for 770s - so this kills with
8
+ * SIGKILL, and throws naming the command instead of returning a half-finished result.
9
+ */
10
+ static spawnSyncBounded<const In extends Bun.SpawnOptions.Writable = 'ignore', const Out extends Bun.SpawnOptions.Readable = 'pipe', const Err extends Bun.SpawnOptions.Readable = 'pipe'>(opts: Bun.SpawnOptions.SpawnSyncOptions<In, Out, Err> & {
11
+ cmd: string[];
12
+ onExit?: undefined;
13
+ }): Bun.SyncSubprocess<Out, Err>;
14
+ }
13
15
  //# sourceMappingURL=spawn.helper.d.ts.map