@blumintinc/eslint-plugin-blumint 1.18.9 → 1.18.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -218,7 +218,7 @@ function noFrontendImportsFromFunctionsPatterns(pattern) {
218
218
  module.exports = {
219
219
  meta: {
220
220
  name: '@blumintinc/eslint-plugin-blumint',
221
- version: '1.18.9',
221
+ version: '1.18.11',
222
222
  },
223
223
  parseOptions: {
224
224
  ecmaVersion: 2020,
@@ -169,7 +169,43 @@ exports.parallelizeAsyncOperations = (0, createRule_1.createRule)({
169
169
  * "transactionCollector", etc. This may produce false positives for
170
170
  * unrelated managers, but errs on the side of safety.
171
171
  */
172
- const COORDINATOR_PATTERN = /batch|manager|collector|transaction|tx|unitofwork|accumulator/i;
172
+ const COORDINATOR_PATTERN = /batch|manager|collector|transaction|tx|coordinator|unitofwork|accumulator|aggregator/i;
173
+ /**
174
+ * Matches guard/assertion callees by their leading verb. Anchored at the
175
+ * start so it fires on the callee's own verb (assertStartable,
176
+ * validateInput, ensureExists, requireAuth, checkAccess, verifyOwnership,
177
+ * guardAgainstX) rather than on an arbitrary substring elsewhere in the
178
+ * name.
179
+ */
180
+ const GUARD_PATTERN = /^(assert|ensure|require|validate|verify|guard|check)/i;
181
+ /**
182
+ * Extracts the callee's method name (the identifier bearing the leading
183
+ * verb) from an await expression argument. Handles both direct
184
+ * CallExpressions and optional-call ChainExpressions, and both bare
185
+ * identifier callees and member-expression callees.
186
+ */
187
+ function getCalleeMethodName(awaitExpr) {
188
+ let callExpr = null;
189
+ if (awaitExpr.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
190
+ callExpr = awaitExpr.argument;
191
+ }
192
+ else if (awaitExpr.argument.type === utils_1.AST_NODE_TYPES.ChainExpression &&
193
+ awaitExpr.argument.expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
194
+ callExpr = awaitExpr.argument.expression;
195
+ }
196
+ if (!callExpr) {
197
+ return null;
198
+ }
199
+ const callee = callExpr.callee;
200
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
201
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
202
+ return callee.property.name;
203
+ }
204
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
205
+ return callee.name;
206
+ }
207
+ return null;
208
+ }
173
209
  /**
174
210
  * Checks if there are dependencies between await expressions
175
211
  */
@@ -214,28 +250,34 @@ exports.parallelizeAsyncOperations = (0, createRule_1.createRule)({
214
250
  }
215
251
  // 3. Check for operations that might have side effects
216
252
  // If any node has a side effect, we should not parallelize the sequence
217
- let callExpr = null;
218
- if (awaitExpr.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
219
- callExpr = awaitExpr.argument;
253
+ const methodName = getCalleeMethodName(awaitExpr);
254
+ if (methodName &&
255
+ sideEffectPatterns.some((pattern) => pattern.test(methodName))) {
256
+ return true;
220
257
  }
221
- else if (awaitExpr.argument.type === utils_1.AST_NODE_TYPES.ChainExpression &&
222
- awaitExpr.argument.expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
223
- callExpr = awaitExpr.argument.expression;
258
+ }
259
+ // 4. Guard-then-side-effect ordering barrier. A discarded-result await
260
+ // whose callee reads as a guard/assertion (assert*, ensure*, validate*,
261
+ // ...) is a control-flow gate: it throws to abort the run when its
262
+ // precondition fails, so any await after it must run ONLY if the guard
263
+ // resolves. Promise.all invokes every operand eagerly, so it would fire
264
+ // the gated side effect even when the guard rejects. Treat the guard's
265
+ // presence (when something follows it) as a sequencing dependency that
266
+ // blocks parallelizing the whole run. Only discarded-result awaits
267
+ // (ExpressionStatements) qualify; `const ok = await validate(x)` has a
268
+ // variable and is handled by the data-dependency path above.
269
+ for (let i = 0; i < awaitNodes.length - 1; i++) {
270
+ const node = awaitNodes[i];
271
+ if (node.type !== utils_1.AST_NODE_TYPES.ExpressionStatement) {
272
+ continue;
224
273
  }
225
- if (callExpr) {
226
- const callee = callExpr.callee;
227
- let methodName = null;
228
- if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
229
- callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
230
- methodName = callee.property.name;
231
- }
232
- else if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
233
- methodName = callee.name;
234
- }
235
- if (methodName &&
236
- sideEffectPatterns.some((pattern) => pattern.test(methodName))) {
237
- return true;
238
- }
274
+ const awaitExpr = getAwaitExpression(node);
275
+ if (!awaitExpr) {
276
+ continue;
277
+ }
278
+ const methodName = getCalleeMethodName(awaitExpr);
279
+ if (methodName && GUARD_PATTERN.test(methodName)) {
280
+ return true;
239
281
  }
240
282
  }
241
283
  // If any node is a variable declaration with destructuring, consider it as having dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.18.9",
3
+ "version": "1.18.11",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -1,4 +1,32 @@
1
1
  [
2
+ {
3
+ "version": "1.18.11",
4
+ "date": "2026-07-11T16:25:05.418Z",
5
+ "rules": [
6
+ {
7
+ "name": "parallelize-async-operations",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 1284
11
+ ],
12
+ "summary": "treat throw-gated guard awaits as a sequencing barrier (closes #1284)"
13
+ }
14
+ ]
15
+ },
16
+ {
17
+ "version": "1.18.10",
18
+ "date": "2026-07-11T15:24:03.394Z",
19
+ "rules": [
20
+ {
21
+ "name": "parallelize-async-operations",
22
+ "changeType": "fix",
23
+ "issues": [
24
+ 1283
25
+ ],
26
+ "summary": "restore coordinator to COORDINATOR_PATTERN (closes #1283)"
27
+ }
28
+ ]
29
+ },
2
30
  {
3
31
  "version": "1.18.9",
4
32
  "date": "2026-07-11T01:51:22.294Z",