@clipboard-health/oxlint-plugin 1.2.0 → 1.3.0

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/README.md CHANGED
@@ -12,6 +12,7 @@ ESLint.
12
12
  - `enforce-ts-rest-in-controllers`
13
13
  - `no-empty-boolean-call`
14
14
  - `no-cross-contract-imports`
15
+ - `no-parallel-mongo-session-ops`
15
16
  - `require-contract-fixture-construction`
16
17
  - `require-contract-response-parse`
17
18
  - `require-http-module-factory`
@@ -27,3 +28,17 @@ apply the shared controller, module, contract, and cross-contract-import scopes.
27
28
  `no-empty-boolean-call`, `require-contract-response-parse`, and
28
29
  `require-run-validators-with-upsert` are opt-in because their safe adoption scope is
29
30
  repository-specific.
31
+
32
+ `no-parallel-mongo-session-ops` is also opt-in. Out of the box it covers `Promise.all`,
33
+ `Promise.allSettled`, `Promise.race`, and `Promise.any`. Repositories with their own
34
+ bounded-concurrency helpers must name them, or those call sites go unchecked:
35
+
36
+ ```ts
37
+ "@clipboard-health/no-parallel-mongo-session-ops": [
38
+ "warn",
39
+ { additionalPrimitives: ["batchPromiseAllWithLimit", "batchPromiseAllSettledWithLimit"] },
40
+ ],
41
+ ```
42
+
43
+ Exclude tests: app-level session objects such as `session.sessionUserId` share the name but are
44
+ unrelated to MongoDB sessions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/oxlint-plugin",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Clipboard's Oxlint plugin.",
5
5
  "keywords": [
6
6
  "oxlint",
@@ -20,11 +20,11 @@
20
20
  "access": "public"
21
21
  },
22
22
  "dependencies": {
23
- "@oxlint/plugins": "1.78.0",
23
+ "@oxlint/plugins": "1.79.0",
24
24
  "tslib": "2.8.1"
25
25
  },
26
26
  "devDependencies": {
27
- "oxlint": "1.78.0"
27
+ "oxlint": "1.79.0"
28
28
  },
29
29
  "types": "./src/index.d.ts"
30
30
  }
package/src/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export declare const rules: {
2
2
  "enforce-ts-rest-in-controllers": import("@oxlint/plugins").Rule;
3
3
  "no-empty-boolean-call": import("@oxlint/plugins").Rule;
4
4
  "no-cross-contract-imports": import("@oxlint/plugins").Rule;
5
+ "no-parallel-mongo-session-ops": import("@oxlint/plugins").Rule;
5
6
  "require-contract-fixture-construction": import("@oxlint/plugins").Rule;
6
7
  "require-contract-response-parse": import("@oxlint/plugins").Rule;
7
8
  "require-http-module-factory": import("@oxlint/plugins").Rule;
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const enforce_ts_rest_in_controllers_1 = tslib_1.__importDefault(require("./lib/rules/enforce-ts-rest-in-controllers"));
6
6
  const no_cross_contract_imports_1 = tslib_1.__importDefault(require("./lib/rules/no-cross-contract-imports"));
7
7
  const no_empty_boolean_call_1 = tslib_1.__importDefault(require("./lib/rules/no-empty-boolean-call"));
8
+ const no_parallel_mongo_session_ops_1 = tslib_1.__importDefault(require("./lib/rules/no-parallel-mongo-session-ops"));
8
9
  const require_contract_fixture_construction_1 = tslib_1.__importDefault(require("./lib/rules/require-contract-fixture-construction"));
9
10
  const require_contract_response_parse_1 = tslib_1.__importDefault(require("./lib/rules/require-contract-response-parse"));
10
11
  const require_http_module_factory_1 = tslib_1.__importDefault(require("./lib/rules/require-http-module-factory"));
@@ -14,6 +15,7 @@ exports.rules = {
14
15
  "enforce-ts-rest-in-controllers": enforce_ts_rest_in_controllers_1.default,
15
16
  "no-empty-boolean-call": no_empty_boolean_call_1.default,
16
17
  "no-cross-contract-imports": no_cross_contract_imports_1.default,
18
+ "no-parallel-mongo-session-ops": no_parallel_mongo_session_ops_1.default,
17
19
  "require-contract-fixture-construction": require_contract_fixture_construction_1.default,
18
20
  "require-contract-response-parse": require_contract_response_parse_1.default,
19
21
  "require-http-module-factory": require_http_module_factory_1.default,
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/oxlint-plugin/src/index.ts"],"names":[],"mappings":";;;;AAAA,wHAAoF;AACpF,8GAA2E;AAC3E,sGAAmE;AACnE,sIAAmG;AACnG,0HAAuF;AACvF,kHAA+E;AAC/E,gIAA4F;AAC5F,0HAAsF;AAEzE,QAAA,KAAK,GAAG;IACnB,gCAAgC,EAAE,wCAA0B;IAC5D,uBAAuB,EAAE,+BAAkB;IAC3C,2BAA2B,EAAE,mCAAsB;IACnD,uCAAuC,EAAE,+CAAkC;IAC3E,iCAAiC,EAAE,yCAA4B;IAC/D,6BAA6B,EAAE,qCAAwB;IACvD,oCAAoC,EAAE,4CAA8B;IACpE,iCAAiC,EAAE,yCAA2B;CAC/D,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/oxlint-plugin/src/index.ts"],"names":[],"mappings":";;;;AAAA,wHAAoF;AACpF,8GAA2E;AAC3E,sGAAmE;AACnE,sHAAkF;AAClF,sIAAmG;AACnG,0HAAuF;AACvF,kHAA+E;AAC/E,gIAA4F;AAC5F,0HAAsF;AAEzE,QAAA,KAAK,GAAG;IACnB,gCAAgC,EAAE,wCAA0B;IAC5D,uBAAuB,EAAE,+BAAkB;IAC3C,2BAA2B,EAAE,mCAAsB;IACnD,+BAA+B,EAAE,uCAAyB;IAC1D,uCAAuC,EAAE,+CAAkC;IAC3E,iCAAiC,EAAE,yCAA4B;IAC/D,6BAA6B,EAAE,qCAAwB;IACvD,oCAAoC,EAAE,4CAA8B;IACpE,iCAAiC,EAAE,yCAA2B;CAC/D,CAAC"}
@@ -0,0 +1,2 @@
1
+ declare const rule: import("@oxlint/plugins").Rule;
2
+ export default rule;
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * @fileoverview Rule to disallow parallel operations sharing one MongoDB transaction session.
5
+ *
6
+ * The MongoDB driver documents parallel operations inside a transaction as undefined behaviour.
7
+ * From `startTransaction` and `withTransaction` in the driver's `lib/sessions.js`:
8
+ *
9
+ * > Running operations in parallel is not supported during a transaction. The use of
10
+ * > `Promise.all`, `Promise.allSettled`, `Promise.race`, etc to parallelize operations inside a
11
+ * > transaction is undefined behaviour.
12
+ *
13
+ * In practice one branch aborts the transaction and its siblings, still in flight, fail with
14
+ * `Transaction with { txnNumber: N } has been aborted.`
15
+ *
16
+ * The rule reports a concurrency primitive that reaches a session declared *outside* it, meaning
17
+ * every branch shares one session. A session created *inside* the callback gives each branch its
18
+ * own and is not reported, which is the safe way to fan out.
19
+ *
20
+ * Oxlint JS plugins receive no type information, so a session is recognised by binding name or by
21
+ * `.session` member access rather than by the `ClientSession` type. Bindings are resolved through
22
+ * scope analysis, so same-named sessions in different scopes do not collide.
23
+ *
24
+ * Repositories with their own bounded-concurrency helpers should list them in
25
+ * `additionalPrimitives`; the defaults cover only the `Promise` combinators.
26
+ *
27
+ * The invariant is one sentence: a session is per-branch when a `map` callback creates it, and
28
+ * shared otherwise. Everything else follows from that — a callback parameter receives whatever was
29
+ * mapped over, an alias is the same session, a `flatMap` callback runs once per group rather than
30
+ * per task, and a session created in a scope that encloses the fan-out is shared.
31
+ *
32
+ * Known limitations. The rule is a net for the shapes people write, not a soundness proof, and
33
+ * cannot become one without type and dataflow information:
34
+ *
35
+ * - a session forwarded as an opaque options object through a call chain, or held on `this`, is
36
+ * invisible
37
+ * - a session reaching the fan-out through a helper the rule does not model is invisible
38
+ * - recognition is by binding name, so a session under an unlisted name is missed
39
+ *
40
+ * Prefer accepting a miss over reporting safe code: a false positive gets the rule switched off,
41
+ * which costs more than any single missed call site.
42
+ */
43
+ const plugins_1 = require("@oxlint/plugins");
44
+ const findVariable_1 = require("../../internal/findVariable");
45
+ const SESSION_NAMES = new Set([
46
+ "session",
47
+ "mongoSession",
48
+ "transactionSession",
49
+ "clientSession",
50
+ "dbSession",
51
+ ]);
52
+ const PROMISE_COMBINATORS = new Set(["all", "allSettled", "race", "any"]);
53
+ const TASK_LIST_METHODS = new Set(["map", "flatMap"]);
54
+ /**
55
+ * Only a `map` callback runs once per task. A `flatMap` callback runs once per group and can
56
+ * return many operations, so a session created there is shared by all of them.
57
+ */
58
+ const PER_BRANCH_METHODS = new Set(["map"]);
59
+ function isWithin(inner, outer) {
60
+ return Number(inner[0]) >= Number(outer[0]) && Number(inner[1]) <= Number(outer[1]);
61
+ }
62
+ /**
63
+ * A call plausibly produces a fresh session; a bare identifier or member expression is an alias of
64
+ * something that already exists.
65
+ */
66
+ function createsSession(init) {
67
+ const expression = init?.type === "AwaitExpression" ? init.argument : init;
68
+ return expression?.type === "CallExpression" || expression?.type === "NewExpression";
69
+ }
70
+ /** A task list is the only initialiser worth following; anything else is unrelated data. */
71
+ function isTaskList(init) {
72
+ if (!init) {
73
+ return false;
74
+ }
75
+ if (init.type === "ArrayExpression") {
76
+ return true;
77
+ }
78
+ return (init.type === "CallExpression" &&
79
+ init.callee.type === "MemberExpression" &&
80
+ !init.callee.computed &&
81
+ init.callee.property.type === "Identifier" &&
82
+ TASK_LIST_METHODS.has(init.callee.property.name));
83
+ }
84
+ const rule = (0, plugins_1.defineRule)({
85
+ meta: {
86
+ type: "problem",
87
+ docs: {
88
+ description: "Disallow parallel operations sharing one MongoDB transaction session",
89
+ url: "https://github.com/ClipboardHealth/core-utils/tree/main/packages/oxlint-plugin/src/lib/rules/no-parallel-mongo-session-ops",
90
+ },
91
+ schema: [
92
+ {
93
+ type: "object",
94
+ properties: {
95
+ additionalPrimitives: {
96
+ type: "array",
97
+ items: { type: "string" },
98
+ description: "Names of project-local helpers that run their tasks concurrently, for example a bounded-concurrency wrapper around Promise.all.",
99
+ },
100
+ },
101
+ additionalProperties: false,
102
+ },
103
+ ],
104
+ messages: {
105
+ parallelSessionOps: "`{{ primitive }}` runs its operations in parallel on the `{{ name }}` transaction session, which the MongoDB driver documents as undefined behaviour. Await them sequentially, or give each branch its own session.",
106
+ },
107
+ },
108
+ create(context) {
109
+ const [options] = context.options;
110
+ const additionalPrimitives = new Set(options?.additionalPrimitives ?? []);
111
+ const parallelCalls = [];
112
+ const sessionReferences = [];
113
+ const identifierReferences = [];
114
+ const nonReferenceRanges = new Set();
115
+ /** Keyed by resolved binding, so same-named lists in sibling scopes stay distinct. */
116
+ const taskLists = new Map();
117
+ /**
118
+ * Bodies of `map`/`flatMap` callbacks: the only regions that run once per branch. A session
119
+ * created anywhere else inside the primitive, such as in an enclosing IIFE, is created once
120
+ * and shared.
121
+ */
122
+ const taskCallbackRanges = [];
123
+ function resolve(identifier) {
124
+ return (0, findVariable_1.findVariable)(context.sourceCode, identifier);
125
+ }
126
+ function primitiveName(node) {
127
+ const { callee } = node;
128
+ if (callee.type === "Identifier" && additionalPrimitives.has(callee.name)) {
129
+ return callee.name;
130
+ }
131
+ if (callee.type === "MemberExpression" &&
132
+ !callee.computed &&
133
+ callee.object.type === "Identifier" &&
134
+ callee.object.name === "Promise" &&
135
+ callee.property.type === "Identifier" &&
136
+ PROMISE_COMBINATORS.has(callee.property.name)) {
137
+ return `Promise.${callee.property.name}`;
138
+ }
139
+ return undefined;
140
+ }
141
+ /**
142
+ * A configured helper taking a concurrency of 1 runs one task at a time, so it is already
143
+ * sequential and safe.
144
+ */
145
+ function isSequential(node) {
146
+ const { callee } = node;
147
+ if (!(callee.type === "Identifier" && additionalPrimitives.has(callee.name))) {
148
+ return false;
149
+ }
150
+ return node.arguments.some((argument) => "value" in argument && argument.value === 1);
151
+ }
152
+ /**
153
+ * A session is per-branch only when a task callback *creates* one. Neither declaration scope
154
+ * nor creation alone is proof: a callback parameter receives whatever the caller mapped over,
155
+ * an alias of an outer binding is the very same session, and a session created in a scope
156
+ * that encloses the fan-out is shared by every branch. All three stay reportable.
157
+ */
158
+ function createsOwnSession(variable, range) {
159
+ return (variable?.defs.some((definition) => {
160
+ if (definition.type !== "Variable" || !isWithin(definition.name.range, range)) {
161
+ return false;
162
+ }
163
+ return (definition.node.type === "VariableDeclarator" && createsSession(definition.node.init));
164
+ }) === true);
165
+ }
166
+ return {
167
+ CallExpression(node) {
168
+ const primitive = primitiveName(node);
169
+ if (primitive !== undefined && !isSequential(node)) {
170
+ parallelCalls.push({ node, primitive });
171
+ }
172
+ const { callee } = node;
173
+ if (callee.type === "MemberExpression" &&
174
+ !callee.computed &&
175
+ callee.property.type === "Identifier" &&
176
+ PER_BRANCH_METHODS.has(callee.property.name)) {
177
+ for (const argument of node.arguments) {
178
+ if (argument.type === "ArrowFunctionExpression" ||
179
+ argument.type === "FunctionExpression") {
180
+ taskCallbackRanges.push(argument.range);
181
+ }
182
+ }
183
+ }
184
+ },
185
+ // A non-shorthand key, as in `{ session: workplaceId }`, is a label rather than a reference.
186
+ Property(node) {
187
+ if (!("key" in node) || !("shorthand" in node)) {
188
+ return;
189
+ }
190
+ if (!node.shorthand && !node.computed && node.key.type === "Identifier") {
191
+ nonReferenceRanges.add(node.key.range.join(":"));
192
+ }
193
+ },
194
+ // `options.session` and `options?.session` carry the session in on an object.
195
+ MemberExpression(node) {
196
+ if (node.type !== "MemberExpression" || node.computed) {
197
+ return;
198
+ }
199
+ if (node.property.type !== "Identifier") {
200
+ return;
201
+ }
202
+ if (node.property.name === "session") {
203
+ const object = node.object;
204
+ sessionReferences.push({
205
+ range: node.range,
206
+ variable: object.type === "Identifier" ? resolve(object) : undefined,
207
+ label: object.type === "Identifier" ? object.name : "session",
208
+ });
209
+ }
210
+ nonReferenceRanges.add(node.property.range.join(":"));
211
+ },
212
+ Identifier(node) {
213
+ const variable = resolve(node);
214
+ identifierReferences.push({ range: node.range, variable });
215
+ if (SESSION_NAMES.has(node.name)) {
216
+ sessionReferences.push({ range: node.range, variable, label: node.name });
217
+ }
218
+ },
219
+ VariableDeclarator(node) {
220
+ const { id, init } = node;
221
+ if (id.type !== "Identifier" || !isTaskList(init)) {
222
+ return;
223
+ }
224
+ const variable = resolve(id);
225
+ if (variable && init) {
226
+ taskLists.set(variable, init);
227
+ }
228
+ },
229
+ "Program:exit"() {
230
+ for (const { node, primitive } of parallelCalls) {
231
+ const argumentRanges = node.arguments.map((argument) => argument.range);
232
+ const searchRanges = [node.range];
233
+ // `const tasks = items.map(...)` before `Promise.all(tasks)` keeps the session out of
234
+ // the call site, so search where the list was built too.
235
+ for (const reference of identifierReferences) {
236
+ if (!argumentRanges.some((range) => isWithin(reference.range, range))) {
237
+ continue;
238
+ }
239
+ const taskList = reference.variable && taskLists.get(reference.variable);
240
+ if (taskList && !isWithin(taskList.range, node.range)) {
241
+ searchRanges.push(taskList.range);
242
+ }
243
+ }
244
+ // Only callbacks reached by this call can create a per-branch session.
245
+ const perBranchRanges = taskCallbackRanges.filter((callback) => searchRanges.some((range) => isWithin(callback, range)));
246
+ const shared = sessionReferences.find((reference) => searchRanges.some((range) => isWithin(reference.range, range)) &&
247
+ !nonReferenceRanges.has(reference.range.join(":")) &&
248
+ !perBranchRanges.some((range) => createsOwnSession(reference.variable, range)));
249
+ if (shared) {
250
+ context.report({
251
+ node,
252
+ messageId: "parallelSessionOps",
253
+ data: { primitive, name: shared.label },
254
+ });
255
+ }
256
+ }
257
+ },
258
+ };
259
+ },
260
+ });
261
+ exports.default = rule;
262
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/oxlint-plugin/src/lib/rules/no-parallel-mongo-session-ops/index.ts"],"names":[],"mappings":"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"}