@geonosis/oxlint-plugin-biological-architecture 1.3.0 → 2.0.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 +1 -1
- package/corpus/.oxlintrc.json +97 -5
- package/corpus/domains/document/workflows/bad-per-kind-workflow.ts +5 -0
- package/corpus/domains/document/workflows/good-minted-workflow.ts +8 -0
- package/corpus/engine/bad-durable-free-read.ts +20 -0
- package/corpus/engine/bad-no-retry.ts +8 -0
- package/corpus/engine/good-durable-other-spelling.ts +10 -0
- package/corpus/engine/good-durable-read-is-a-step.ts +18 -0
- package/corpus/engine/good-durable-read-through-helper.ts +20 -0
- package/corpus/engine/good-inline-free-read.ts +7 -0
- package/corpus/engine/good-retry-budget.ts +9 -0
- package/corpus/floors/domains/alpha/src/good-downhill.ts +3 -0
- package/corpus/floors/domains/alpha/src/index.ts +1 -0
- package/corpus/floors/domains/alpha/src/reaches-own-ui.ts +3 -0
- package/corpus/floors/domains/alpha/src/reaches-sideways-type.ts +3 -0
- package/corpus/floors/domains/alpha/src/reaches-sideways.ts +3 -0
- package/corpus/floors/domains/alpha/src/ui/panel.ts +3 -0
- package/corpus/floors/domains/beta/src/index.ts +3 -0
- package/corpus/floors/foundations/above/src/good-downhill.ts +3 -0
- package/corpus/floors/foundations/base/src/good-downhill.ts +3 -0
- package/corpus/floors/foundations/base/src/index.ts +1 -0
- package/corpus/floors/foundations/base/src/reaches-sideways.ts +3 -0
- package/corpus/floors/foundations/base/src/reaches-up.ts +3 -0
- package/corpus/floors/packages/method/src/own.ts +1 -0
- package/corpus/floors/packages/method/src/reaches-up.ts +3 -0
- package/corpus/floors.oxlintrc.json +86 -0
- package/corpus/manifest.json +4 -0
- package/corpus/subpaths/bad-root-barrel.ts +5 -0
- package/corpus/subpaths/good-spans-two-subpaths.ts +5 -0
- package/corpus/subpaths/good-subpath.ts +4 -0
- package/corpus/subpaths/good-type-only.ts +4 -0
- package/corpus/time/bad-boundary-type.ts +3 -0
- package/corpus/time/bad-second-door.ts +6 -0
- package/corpus/time/door.ts +15 -0
- package/corpus/time/generated/from-openapi.ts +1 -0
- package/corpus/time/good-boundary-primitive.ts +3 -0
- package/corpus/todos/bad-unplanned.ts +2 -0
- package/corpus/todos/good-planned.ts +2 -0
- package/corpus/todos/good-string-not-a-comment.ts +2 -0
- package/corpus/workers/good-with-cells.ts +7 -0
- package/corpus/workers/good-with-connection.ts +7 -0
- package/corpus/workflows/bad-unscoped-host.ts +7 -0
- package/corpus/workflows/good-host-config-elsewhere.ts +6 -0
- package/corpus/workflows/good-scoped-entrypoint.ts +17 -0
- package/corpus/workflows/good-scoped-host.ts +11 -0
- package/dist/{chunk-UVENNNFM.js → chunk-LTDP2733.js} +36 -3
- package/dist/index.d.ts +17 -2
- package/dist/index.js +1115 -370
- package/dist/presets.d.ts +31 -2
- package/dist/presets.js +5 -1
- package/package.json +2 -2
- /package/corpus/{packages/workflows/src → domains/document/workflows}/bad-per-kind-saga.ts +0 -0
- /package/corpus/{apps/api/src → workers}/bad-unscoped-worker.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS,
|
|
3
|
+
PRESUMED_ENGINES,
|
|
4
|
+
RENAMED_TO,
|
|
3
5
|
rulesOfPreset
|
|
4
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LTDP2733.js";
|
|
5
7
|
|
|
6
8
|
// src/index.ts
|
|
7
9
|
import { createRequire } from "module";
|
|
@@ -19,6 +21,15 @@ var literalNamedBy = (node, callee) => {
|
|
|
19
21
|
// src/rules/lib/paths.ts
|
|
20
22
|
var normalise = (filename) => filename.replaceAll("\\", "/");
|
|
21
23
|
var fileOf = (path2) => path2.split("/").pop() || path2;
|
|
24
|
+
var isDoor = (filename, door) => {
|
|
25
|
+
const path2 = normalise(filename);
|
|
26
|
+
if (path2 === door || path2.endsWith(`/${door}`)) return true;
|
|
27
|
+
try {
|
|
28
|
+
return new RegExp(door).test(path2);
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
22
33
|
|
|
23
34
|
// src/rules/lib/imports.ts
|
|
24
35
|
var REQUIRE = "require";
|
|
@@ -160,6 +171,252 @@ tier or into a plain \`lib/\` module, never the other way round.`,
|
|
|
160
171
|
});
|
|
161
172
|
var atom_no_deps_default = atomNoDeps;
|
|
162
173
|
|
|
174
|
+
// src/rules/lib/nodes.ts
|
|
175
|
+
var walkNode = (node, visit) => {
|
|
176
|
+
const seen = /* @__PURE__ */ new Set();
|
|
177
|
+
const stack = [node];
|
|
178
|
+
while (stack.length > 0) {
|
|
179
|
+
const at = stack.pop();
|
|
180
|
+
if (at === null || typeof at !== "object" || seen.has(at)) continue;
|
|
181
|
+
seen.add(at);
|
|
182
|
+
if (Array.isArray(at)) {
|
|
183
|
+
stack.push(...at);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const record2 = at;
|
|
187
|
+
if (typeof record2.type === "string") visit(record2);
|
|
188
|
+
for (const [key, value] of Object.entries(record2)) {
|
|
189
|
+
if (key === "parent") continue;
|
|
190
|
+
if (value !== null && typeof value === "object") stack.push(value);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
var propertyNameOf = (node) => {
|
|
195
|
+
if (node.type !== "MemberExpression" || node.computed === true) return null;
|
|
196
|
+
const property = node.property;
|
|
197
|
+
return property?.type === "Identifier" && typeof property.name === "string" ? property.name : null;
|
|
198
|
+
};
|
|
199
|
+
var calleeNameOf = (node) => {
|
|
200
|
+
const callee = node.callee;
|
|
201
|
+
return callee?.type === "Identifier" && typeof callee.name === "string" ? callee.name : null;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/rules/lib/options.ts
|
|
205
|
+
var NOTHING = "nothing";
|
|
206
|
+
var isMissing = (value) => value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
207
|
+
var refusalHeader = (rule, option) => `biological-architecture/${rule} requires option \`${option}\`.`;
|
|
208
|
+
var OptionMissing = class extends Error {
|
|
209
|
+
option;
|
|
210
|
+
rule;
|
|
211
|
+
constructor(rule, option, message) {
|
|
212
|
+
super(message);
|
|
213
|
+
this.name = "OptionMissing";
|
|
214
|
+
this.option = option;
|
|
215
|
+
this.rule = rule;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var requireOption = (rule, value, option, accepts) => {
|
|
219
|
+
if (isMissing(value)) {
|
|
220
|
+
throw new OptionMissing(
|
|
221
|
+
rule,
|
|
222
|
+
option,
|
|
223
|
+
`${refusalHeader(rule, option)} It is enabled but its \`${option}\` option is ${NOTHING}, so it can never fire. Configure \`${option}\`, or turn the rule off \u2014 an enabled rule that cannot fire reads exactly like a rule that found nothing.${accepts === void 0 ? "" : ` It accepts ${accepts}.`}`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
return value;
|
|
227
|
+
};
|
|
228
|
+
var requireOptionHere = (rule, value, option, file, accepts) => {
|
|
229
|
+
if (isMissing(value)) {
|
|
230
|
+
throw new OptionMissing(
|
|
231
|
+
rule,
|
|
232
|
+
option,
|
|
233
|
+
`${refusalHeader(rule, option)} It is enabled over "${file}", which it would judge, and its \`${option}\` option is ${NOTHING}. Configure \`${option}\`, or turn the rule off for this tree.${accepts === void 0 ? "" : ` It accepts ${accepts}.`}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return value;
|
|
237
|
+
};
|
|
238
|
+
var raisedOver = /* @__PURE__ */ new Map();
|
|
239
|
+
var fileOf2 = (context) => {
|
|
240
|
+
const named = context?.filename;
|
|
241
|
+
return typeof named === "string" ? named : "";
|
|
242
|
+
};
|
|
243
|
+
var refusingOncePerRun = (create) => (context) => {
|
|
244
|
+
try {
|
|
245
|
+
return create(context);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (!(error instanceof OptionMissing)) throw error;
|
|
248
|
+
const file = fileOf2(context);
|
|
249
|
+
if (file === "") throw error;
|
|
250
|
+
const key = `${error.rule}/${error.option}`;
|
|
251
|
+
const first = raisedOver.get(key);
|
|
252
|
+
if (first !== void 0 && first !== file) return {};
|
|
253
|
+
raisedOver.set(key, file);
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/rules/lib/probe.ts
|
|
259
|
+
var bagOf = (options) => typeof options[0] === "object" && options[0] !== null && !Array.isArray(options[0]) ? options[0] : {};
|
|
260
|
+
var firstOf = (configured, fallback) => {
|
|
261
|
+
const [first] = configured !== void 0 && configured.length > 0 ? configured : fallback;
|
|
262
|
+
if (first === void 0) throw new Error("no value and no default to build a probe from");
|
|
263
|
+
return first;
|
|
264
|
+
};
|
|
265
|
+
var LOOKAHEAD = /\(\?[!=][^)]*\)/g;
|
|
266
|
+
var OPTIONAL_GROUP = /\(\?:[^)]*\)\?/g;
|
|
267
|
+
var FIRST_ALTERNATIVE = /\(\?:([^)|]*)\|[^)]*\)/g;
|
|
268
|
+
var GROUP = /\(\?:([^)]*)\)/g;
|
|
269
|
+
var ANY_SEGMENT = /\[\^\/\]\+|\.\+|\.\*/g;
|
|
270
|
+
var OPTIONAL_CLASS = /\[[^\]]*\]\?/g;
|
|
271
|
+
var CLASS = /\[([^\]])[^\]]*\]/g;
|
|
272
|
+
var OPTIONAL_CHAR = /[^/]\?/g;
|
|
273
|
+
var QUANTIFIER = /[*+]/g;
|
|
274
|
+
var LINTABLE = /\.[cm]?[jt]sx?$/;
|
|
275
|
+
var EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
276
|
+
var pathMatching = (pattern, leaf) => {
|
|
277
|
+
const literal = pattern.replaceAll(LOOKAHEAD, "").replaceAll(OPTIONAL_GROUP, "").replaceAll(FIRST_ALTERNATIVE, "$1").replaceAll(GROUP, "$1").replaceAll(ANY_SEGMENT, "probe").replaceAll(OPTIONAL_CLASS, "").replaceAll(CLASS, "$1").replaceAll(String.raw`\.`, ".").replaceAll(String.raw`\/`, "/").replaceAll(OPTIONAL_CHAR, "").replaceAll("^", "").replaceAll("$", "").replaceAll(QUANTIFIER, "");
|
|
278
|
+
const here = new RegExp(pattern);
|
|
279
|
+
const candidates = [
|
|
280
|
+
literal.endsWith("/") ? `${literal}${leaf}` : literal,
|
|
281
|
+
...EXTENSIONS.map((extension) => `${literal}${extension}`)
|
|
282
|
+
];
|
|
283
|
+
const found = candidates.find((one) => LINTABLE.test(one) && here.test(one));
|
|
284
|
+
if (found === void 0) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`no probe path can be built from the pattern ${pattern}: nothing of the shape "${candidates[0] ?? ""}" both matches it and is a file oxlint would parse. Write the file by hand, or narrow the option to a path a probe can be placed at.`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
return found;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// src/rules/boundary-time-is-primitive.ts
|
|
293
|
+
var RULE = "boundary-time-is-primitive";
|
|
294
|
+
var ACCEPTS = "{ types: string[] (type or namespace names), doors: string[] (a path, a specifier, or a regex over the filename; default none) }";
|
|
295
|
+
var asNode = (value) => typeof value === "object" && value !== null ? value : void 0;
|
|
296
|
+
var nameOf = (value) => {
|
|
297
|
+
const name = asNode(value)?.["name"];
|
|
298
|
+
return typeof name === "string" ? name : void 0;
|
|
299
|
+
};
|
|
300
|
+
var referencedType = (reference) => {
|
|
301
|
+
const typeName = asNode(reference["typeName"]);
|
|
302
|
+
if (typeName === void 0) return void 0;
|
|
303
|
+
if (typeName.type === "Identifier") return nameOf(typeName);
|
|
304
|
+
if (typeName.type !== "TSQualifiedName") return void 0;
|
|
305
|
+
const namespace = nameOf(typeName["left"]);
|
|
306
|
+
const member = nameOf(typeName["right"]);
|
|
307
|
+
return namespace === void 0 || member === void 0 ? void 0 : `${namespace}.${member}`;
|
|
308
|
+
};
|
|
309
|
+
var forbids = (types, referenced) => types.includes(referenced) || types.includes(referenced.split(".")[0] ?? referenced);
|
|
310
|
+
var functionSignature = (value) => {
|
|
311
|
+
const fn = asNode(value);
|
|
312
|
+
if (fn?.type !== "ArrowFunctionExpression" && fn?.type !== "FunctionExpression") return [];
|
|
313
|
+
return [fn["params"], fn["returnType"]];
|
|
314
|
+
};
|
|
315
|
+
var signatureOf = (declaration) => {
|
|
316
|
+
if (declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") {
|
|
317
|
+
return [declaration["params"], declaration["returnType"]];
|
|
318
|
+
}
|
|
319
|
+
if (declaration.type === "VariableDeclaration") {
|
|
320
|
+
const declarators = Array.isArray(declaration["declarations"]) ? declaration["declarations"] : [];
|
|
321
|
+
return declarators.flatMap((one) => [
|
|
322
|
+
asNode(one)?.["id"] === void 0 ? void 0 : asNode(asNode(one)?.["id"])?.["typeAnnotation"],
|
|
323
|
+
...functionSignature(asNode(one)?.["init"])
|
|
324
|
+
]);
|
|
325
|
+
}
|
|
326
|
+
if (declaration.type === "TSTypeAliasDeclaration") return [declaration["typeAnnotation"]];
|
|
327
|
+
if (declaration.type === "TSInterfaceDeclaration") return [declaration["body"]];
|
|
328
|
+
if (declaration.type === "ClassDeclaration") {
|
|
329
|
+
const members = asNode(declaration["body"])?.["body"];
|
|
330
|
+
return (Array.isArray(members) ? members : []).flatMap((member) => [
|
|
331
|
+
asNode(member)?.["typeAnnotation"],
|
|
332
|
+
...functionSignature(asNode(member)?.["value"])
|
|
333
|
+
]);
|
|
334
|
+
}
|
|
335
|
+
return [];
|
|
336
|
+
};
|
|
337
|
+
var declaredName = (declaration) => {
|
|
338
|
+
const own = nameOf(declaration["id"]);
|
|
339
|
+
if (own !== void 0) return own;
|
|
340
|
+
const declarators = Array.isArray(declaration["declarations"]) ? declaration["declarations"] : [];
|
|
341
|
+
return nameOf(asNode(declarators[0])?.["id"]) ?? "the export";
|
|
342
|
+
};
|
|
343
|
+
var behindADoor = (filename, doors) => doors.some((door) => isDoor(filename, door));
|
|
344
|
+
var boundaryTimeIsPrimitive = {
|
|
345
|
+
create(context) {
|
|
346
|
+
const types = requireOption(RULE, context.options?.[0]?.types, "types", ACCEPTS);
|
|
347
|
+
const doors = context.options?.[0]?.doors ?? [];
|
|
348
|
+
if (behindADoor(context.filename, doors)) return {};
|
|
349
|
+
const behind = doors.length === 0 ? "the module that owns the library" : doors.join(", ");
|
|
350
|
+
const report = (node) => {
|
|
351
|
+
const declaration = asNode(node.declaration);
|
|
352
|
+
if (declaration === void 0) return;
|
|
353
|
+
for (const root of signatureOf(declaration)) {
|
|
354
|
+
walkNode(root, (found) => {
|
|
355
|
+
if (found.type !== "TSTypeReference") return;
|
|
356
|
+
const referenced = referencedType(found);
|
|
357
|
+
if (referenced === void 0 || !forbids(types, referenced)) return;
|
|
358
|
+
context.report({
|
|
359
|
+
data: { doors: behind, name: declaredName(declaration), type: referenced },
|
|
360
|
+
messageId: "libraryTypeAtBoundary",
|
|
361
|
+
node: found
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
return {
|
|
367
|
+
ExportNamedDeclaration: report,
|
|
368
|
+
ExportDefaultDeclaration: report
|
|
369
|
+
};
|
|
370
|
+
},
|
|
371
|
+
fixShape: `An exported signature carries time as a PRIMITIVE: epoch milliseconds for an instant, an ISO date
|
|
372
|
+
string (\`YYYY-MM-DD\`) for a business date. A \`Date\`, a \`Dayjs\` or a \`Temporal\` type in a parameter,
|
|
373
|
+
a return or an exported shape hands every caller the library and its configuration, and no database
|
|
374
|
+
column, wire message or contract can hold one at all \u2014 the boundary is where the conversion belongs.
|
|
375
|
+
Convert at the door the \`doors\` option names; that module is the one place allowed to say the
|
|
376
|
+
library's type out loud, and a display string is produced at the edge from the primitive rather than
|
|
377
|
+
carried across as a formatted value. Only the signature is judged: a \`Date\` a function builds and
|
|
378
|
+
keeps inside its own body is its own business.`,
|
|
379
|
+
meta: {
|
|
380
|
+
docs: {
|
|
381
|
+
description: "A date-library type in an exported signature couples every caller to the library and its configuration. Time crosses a boundary as a primitive \u2014 epoch milliseconds or an ISO date string \u2014 and the library type stays inside the module the `doors` option names. Enabled without `types` it refuses the run."
|
|
382
|
+
},
|
|
383
|
+
messages: {
|
|
384
|
+
libraryTypeAtBoundary: '"{{name}}" puts `{{type}}` in an exported signature. Time crosses a boundary as a primitive \u2014 epoch milliseconds for an instant, an ISO date string for a business date \u2014 and `{{type}}` stays behind {{doors}}.'
|
|
385
|
+
},
|
|
386
|
+
schema: [
|
|
387
|
+
{
|
|
388
|
+
additionalProperties: false,
|
|
389
|
+
properties: {
|
|
390
|
+
doors: { items: { type: "string" }, type: "array" },
|
|
391
|
+
types: { items: { type: "string" }, type: "array" }
|
|
392
|
+
},
|
|
393
|
+
type: "object"
|
|
394
|
+
}
|
|
395
|
+
],
|
|
396
|
+
type: "problem"
|
|
397
|
+
},
|
|
398
|
+
/**
|
|
399
|
+
* A file that is not one of the declared doors, exporting the repo's own first forbidden type.
|
|
400
|
+
* `String(at)` rather than a second annotation: one finding is the claim, and a probe that fired
|
|
401
|
+
* twice would pass a count nobody asked for.
|
|
402
|
+
*/
|
|
403
|
+
probe(options) {
|
|
404
|
+
const { doors = [], types = [] } = bagOf(options);
|
|
405
|
+
const type = firstOf(types, []);
|
|
406
|
+
const path2 = ["geonosis-probe-boundary.ts", "src/geonosis-probe-boundary.ts"].find(
|
|
407
|
+
(candidate) => !behindADoor(candidate, doors)
|
|
408
|
+
);
|
|
409
|
+
if (path2 === void 0) {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`biological-architecture/${RULE}: every path a probe could be written at is itself one of the declared \`doors\` (${doors.join(", ")}), so there is nowhere left for this rule to speak.`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
return { path: path2, source: `export const boundary = (at: ${type}): string => String(at)
|
|
415
|
+
` };
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
var boundary_time_is_primitive_default = boundaryTimeIsPrimitive;
|
|
419
|
+
|
|
163
420
|
// src/rules/cell-must-be-stateful.ts
|
|
164
421
|
import * as fs from "fs";
|
|
165
422
|
var STATE_SIGNATURE = /\buse[A-Z]\w*\s*\(|from\s+['"][^'"]*\/organelles(?:\/|['"])|from\s+['"][^'"]*\/stores?(?:\/|['"])|createContext\s*\(/;
|
|
@@ -590,66 +847,44 @@ configs. Everywhere else, use the dialect-free aliases the seam exports (\`Datab
|
|
|
590
847
|
};
|
|
591
848
|
var dialect_through_the_seam_default = dialectThroughTheSeam;
|
|
592
849
|
|
|
593
|
-
// src/rules/lib/options.ts
|
|
594
|
-
var NOTHING = "nothing";
|
|
595
|
-
var isMissing = (value) => value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
596
|
-
var requireOption = (rule, value, option, accepts) => {
|
|
597
|
-
if (isMissing(value)) {
|
|
598
|
-
throw new Error(
|
|
599
|
-
`biological-architecture/${rule} is enabled but its \`${option}\` option is ${NOTHING}, so it can never fire. Configure \`${option}\`, or turn the rule off \u2014 an enabled rule that cannot fire reads exactly like a rule that found nothing.${accepts === void 0 ? "" : ` It accepts ${accepts}.`}`
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
return value;
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
// src/rules/lib/probe.ts
|
|
606
|
-
var bagOf = (options) => typeof options[0] === "object" && options[0] !== null && !Array.isArray(options[0]) ? options[0] : {};
|
|
607
|
-
var firstOf = (configured, fallback) => {
|
|
608
|
-
const [first] = configured !== void 0 && configured.length > 0 ? configured : fallback;
|
|
609
|
-
if (first === void 0) throw new Error("no value and no default to build a probe from");
|
|
610
|
-
return first;
|
|
611
|
-
};
|
|
612
|
-
var LOOKAHEAD = /\(\?[!=][^)]*\)/g;
|
|
613
|
-
var OPTIONAL_GROUP = /\(\?:[^)]*\)\?/g;
|
|
614
|
-
var FIRST_ALTERNATIVE = /\(\?:([^)|]*)\|[^)]*\)/g;
|
|
615
|
-
var GROUP = /\(\?:([^)]*)\)/g;
|
|
616
|
-
var ANY_SEGMENT = /\[\^\/\]\+|\.\+|\.\*/g;
|
|
617
|
-
var OPTIONAL_CLASS = /\[[^\]]*\]\?/g;
|
|
618
|
-
var CLASS = /\[([^\]])[^\]]*\]/g;
|
|
619
|
-
var OPTIONAL_CHAR = /[^/]\?/g;
|
|
620
|
-
var QUANTIFIER = /[*+]/g;
|
|
621
|
-
var LINTABLE = /\.[cm]?[jt]sx?$/;
|
|
622
|
-
var EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
623
|
-
var pathMatching = (pattern, leaf) => {
|
|
624
|
-
const literal = pattern.replaceAll(LOOKAHEAD, "").replaceAll(OPTIONAL_GROUP, "").replaceAll(FIRST_ALTERNATIVE, "$1").replaceAll(GROUP, "$1").replaceAll(ANY_SEGMENT, "probe").replaceAll(OPTIONAL_CLASS, "").replaceAll(CLASS, "$1").replaceAll(String.raw`\.`, ".").replaceAll(String.raw`\/`, "/").replaceAll(OPTIONAL_CHAR, "").replaceAll("^", "").replaceAll("$", "").replaceAll(QUANTIFIER, "");
|
|
625
|
-
const here = new RegExp(pattern);
|
|
626
|
-
const candidates = [
|
|
627
|
-
literal.endsWith("/") ? `${literal}${leaf}` : literal,
|
|
628
|
-
...EXTENSIONS.map((extension) => `${literal}${extension}`)
|
|
629
|
-
];
|
|
630
|
-
const found = candidates.find((one) => LINTABLE.test(one) && here.test(one));
|
|
631
|
-
if (found === void 0) {
|
|
632
|
-
throw new Error(
|
|
633
|
-
`no probe path can be built from the pattern ${pattern}: nothing of the shape "${candidates[0] ?? ""}" both matches it and is a file oxlint would parse. Write the file by hand, or narrow the option to a path a probe can be placed at.`
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
return found;
|
|
637
|
-
};
|
|
638
|
-
|
|
639
850
|
// src/rules/document-sagas-are-generic.ts
|
|
640
851
|
var escapeForRegex = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
852
|
+
var RULE2 = "document-sagas-are-generic";
|
|
853
|
+
var DEFAULT_FACTORIES = ["saga", "createWorkflow"];
|
|
854
|
+
var perEntityNameOf = (node, factories) => {
|
|
855
|
+
for (const factory of factories) {
|
|
856
|
+
const name = literalNamedBy(node, factory);
|
|
857
|
+
if (name !== null) return name;
|
|
858
|
+
}
|
|
859
|
+
return null;
|
|
860
|
+
};
|
|
861
|
+
var declaresAFlow = (text, factories) => factories.some((factory) => new RegExp(`\\b${escapeForRegex(factory)}\\s*\\(`).test(text));
|
|
641
862
|
var documentSagasAreGeneric = {
|
|
642
863
|
create(context) {
|
|
643
|
-
const
|
|
644
|
-
|
|
864
|
+
const factories = requireOption(
|
|
865
|
+
RULE2,
|
|
866
|
+
context.options?.[0]?.factories ?? DEFAULT_FACTORIES,
|
|
867
|
+
"factories"
|
|
868
|
+
);
|
|
869
|
+
const filename = normalise(context.filename);
|
|
870
|
+
const named = context.options?.[0]?.within;
|
|
871
|
+
const insideTree = named === void 0 || named === "" ? void 0 : new RegExp(named).test(filename);
|
|
872
|
+
if (insideTree === false) return {};
|
|
873
|
+
if (insideTree === void 0 && !declaresAFlow(context.sourceCode?.text ?? "", factories)) {
|
|
874
|
+
return {};
|
|
875
|
+
}
|
|
876
|
+
const prefixes = requireOptionHere(
|
|
877
|
+
RULE2,
|
|
645
878
|
context.options?.[0]?.perKindPrefixes,
|
|
646
|
-
"perKindPrefixes"
|
|
879
|
+
"perKindPrefixes",
|
|
880
|
+
filename
|
|
647
881
|
);
|
|
648
|
-
|
|
882
|
+
const within = requireOptionHere(RULE2, named, "within", filename);
|
|
883
|
+
if (!new RegExp(within).test(filename)) return {};
|
|
649
884
|
const perKindPrefix = new RegExp(`^(?:${prefixes.map(escapeForRegex).join("|")})\\.`);
|
|
650
885
|
return {
|
|
651
886
|
CallExpression(node) {
|
|
652
|
-
const name =
|
|
887
|
+
const name = perEntityNameOf(node, factories);
|
|
653
888
|
if (name === null || !perKindPrefix.test(name)) return;
|
|
654
889
|
context.report({ data: { name }, messageId: "perKindSaga", node });
|
|
655
890
|
}
|
|
@@ -657,34 +892,41 @@ var documentSagasAreGeneric = {
|
|
|
657
892
|
},
|
|
658
893
|
fixShape: `A saga named for one document kind (\`invoice.issue\`, \`quote.accept\`) is the hand-written fork the
|
|
659
894
|
generic base entity replaced. Mint it from the generic factory \u2014 \`documentSagas(entity, effects)\` \u2014
|
|
660
|
-
and pass the kind as data.
|
|
661
|
-
|
|
895
|
+
and pass the kind as data. The same holds for a workflow: \`createWorkflow('invoice.create', \u2026)\` is
|
|
896
|
+
the fork, \`documentWorkflows(entity)\` is the mint. A name assembled from the entity \u2014 a template
|
|
897
|
+
literal \u2014 is what minting looks like and is left alone by construction. Configure the kind vocabulary
|
|
898
|
+
through \`perKindPrefixes\`, the tree that holds the flows through \`within\` (a regex over the
|
|
899
|
+
filename), and \`factories\` if the repo declares them under other names; enabled without
|
|
900
|
+
\`perKindPrefixes\` or \`within\` the rule refuses the run.`,
|
|
662
901
|
meta: {
|
|
663
902
|
docs: {
|
|
664
|
-
description: "Document sagas are minted, not written: `documentSagas(entity, effects)` gives every entity that extends Document its create/save/setStatus/remove/send/convert/duplicate/share from one body. A saga
|
|
903
|
+
description: "Document sagas are minted, not written: `documentSagas(entity, effects)` gives every entity that extends Document its create/save/setStatus/remove/send/convert/duplicate/share from one body. A saga or workflow named with a LITERAL for one entity is a fork; a name assembled from the entity is the mint and is unreadable here on purpose. `factories`, `perKindPrefixes` and `within` are options."
|
|
665
904
|
},
|
|
666
905
|
messages: {
|
|
667
|
-
perKindSaga: '"{{name}}" is a hand-written
|
|
906
|
+
perKindSaga: '"{{name}}" is a hand-written flow for one entity. Every entity that extends Document gets its sagas from `documentSagas(entity, effects)` and its workflows from the same mint; put what this entity decides in its `Document.extend` definition or in its `DocumentEffects`.'
|
|
668
907
|
},
|
|
669
908
|
schema: [
|
|
670
909
|
{
|
|
671
910
|
additionalProperties: false,
|
|
672
911
|
properties: {
|
|
673
|
-
|
|
912
|
+
factories: { items: { type: "string" }, type: "array" },
|
|
913
|
+
perKindPrefixes: { items: { type: "string" }, type: "array" },
|
|
914
|
+
within: { type: "string" }
|
|
674
915
|
},
|
|
675
916
|
type: "object"
|
|
676
917
|
}
|
|
677
918
|
],
|
|
678
919
|
type: "problem"
|
|
679
920
|
},
|
|
680
|
-
/**
|
|
921
|
+
/** Every half comes from the options now: the vocabulary, the tree, and the factory it is minted by. */
|
|
681
922
|
probe(options) {
|
|
682
|
-
const { perKindPrefixes = [] } = bagOf(options);
|
|
923
|
+
const { perKindPrefixes = [], within = "" } = bagOf(options);
|
|
924
|
+
const factory = firstOf(bagOf(options).factories, DEFAULT_FACTORIES);
|
|
683
925
|
return {
|
|
684
|
-
path: "
|
|
685
|
-
source: `import {
|
|
926
|
+
path: pathMatching(within, "geonosis-probe-saga.ts"),
|
|
927
|
+
source: `import { ${factory} } from 'sagaflow-js'
|
|
686
928
|
|
|
687
|
-
export const geonosisProbe =
|
|
929
|
+
export const geonosisProbe = ${factory}('${perKindPrefixes[0] ?? ""}.geonosis-probe', {})
|
|
688
930
|
`
|
|
689
931
|
};
|
|
690
932
|
}
|
|
@@ -752,6 +994,170 @@ export const geonosisProbe = pgTable('${perKindTables[0] ?? ""}', { id: text('id
|
|
|
752
994
|
};
|
|
753
995
|
var documents_share_one_table_default = documentsShareOneTable;
|
|
754
996
|
|
|
997
|
+
// src/rules/lib/objects.ts
|
|
998
|
+
var propertiesOf = (node) => {
|
|
999
|
+
const object = node;
|
|
1000
|
+
if (object?.type !== "ObjectExpression") return null;
|
|
1001
|
+
const properties = object.properties ?? [];
|
|
1002
|
+
return properties.every((one) => one.type === "Property") ? properties : null;
|
|
1003
|
+
};
|
|
1004
|
+
var valueOf = (properties, name) => {
|
|
1005
|
+
const found = properties.find((one) => {
|
|
1006
|
+
const key = one.key;
|
|
1007
|
+
return key?.type === "Identifier" && key.name === name || key?.type === "Literal" && key.value === name;
|
|
1008
|
+
});
|
|
1009
|
+
return found?.value;
|
|
1010
|
+
};
|
|
1011
|
+
var hasProperty = (properties, name) => valueOf(properties, name) !== void 0;
|
|
1012
|
+
var stringOf = (node) => {
|
|
1013
|
+
const literal = node;
|
|
1014
|
+
return literal?.type === "Literal" && typeof literal.value === "string" ? literal.value : null;
|
|
1015
|
+
};
|
|
1016
|
+
|
|
1017
|
+
// src/rules/lib/steps.ts
|
|
1018
|
+
var DEFAULT_STEP_FACTORIES = ["createStep"];
|
|
1019
|
+
var stepFactoriesOf = (rule, configured) => new Set(requireOption(rule, configured ?? DEFAULT_STEP_FACTORIES, "stepFactories"));
|
|
1020
|
+
var isStepCall = (node, factories) => {
|
|
1021
|
+
const callee = calleeNameOf(node);
|
|
1022
|
+
return callee !== null && factories.has(callee);
|
|
1023
|
+
};
|
|
1024
|
+
var argumentsOf = (node) => node.arguments ?? [];
|
|
1025
|
+
var configOf = (node) => {
|
|
1026
|
+
const [first] = argumentsOf(node);
|
|
1027
|
+
if (first === void 0) return { kind: "unreadable" };
|
|
1028
|
+
if (stringOf(first) !== null) return { kind: "bare" };
|
|
1029
|
+
const properties = propertiesOf(first);
|
|
1030
|
+
return properties === null ? { kind: "unreadable" } : { kind: "object", properties };
|
|
1031
|
+
};
|
|
1032
|
+
var stepNameOf = (node, config) => {
|
|
1033
|
+
const [first] = argumentsOf(node);
|
|
1034
|
+
const bare = stringOf(first);
|
|
1035
|
+
if (bare !== null) return bare;
|
|
1036
|
+
if (config.kind === "object") {
|
|
1037
|
+
const named = config.properties.find((one) => {
|
|
1038
|
+
const key = one.key;
|
|
1039
|
+
return key?.type === "Identifier" && key.name === "name";
|
|
1040
|
+
});
|
|
1041
|
+
const literal = stringOf(named?.value);
|
|
1042
|
+
if (literal !== null) return literal;
|
|
1043
|
+
}
|
|
1044
|
+
return calleeNameOf(node) ?? "a step";
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
// src/rules/durable-body-reads-through-steps.ts
|
|
1048
|
+
var RULE3 = "durable-body-reads-through-steps";
|
|
1049
|
+
var ACCEPTS2 = "{ ports: string[] (required), durableKey: string, durableValue: boolean | string, workflowFactories: string[], stepFactories: string[] }";
|
|
1050
|
+
var DEFAULT_WORKFLOW_FACTORIES = ["createWorkflow"];
|
|
1051
|
+
var DEFAULT_DURABLE_KEY = "execution";
|
|
1052
|
+
var DEFAULT_DURABLE_VALUE = "durable";
|
|
1053
|
+
var declaresDurable = (node, key, value) => argumentsOf(node).some((argument) => {
|
|
1054
|
+
const properties = propertiesOf(argument);
|
|
1055
|
+
if (properties === null) return false;
|
|
1056
|
+
const declared = valueOf(properties, key);
|
|
1057
|
+
return declared?.type === "Literal" && declared.value === value;
|
|
1058
|
+
});
|
|
1059
|
+
var portReachedBy = (node, ports) => {
|
|
1060
|
+
const direct = calleeNameOf(node);
|
|
1061
|
+
if (direct !== null) return ports.has(direct) ? direct : null;
|
|
1062
|
+
let at = node.callee ?? {};
|
|
1063
|
+
while (at.type === "MemberExpression") {
|
|
1064
|
+
const named = propertyNameOf(at);
|
|
1065
|
+
if (named !== null && ports.has(named)) return named;
|
|
1066
|
+
at = at.object;
|
|
1067
|
+
}
|
|
1068
|
+
return at.type === "Identifier" && ports.has(at.name) ? at.name : null;
|
|
1069
|
+
};
|
|
1070
|
+
var insideAStep = (node, factories) => {
|
|
1071
|
+
const memoised = /* @__PURE__ */ new Set();
|
|
1072
|
+
walkNode(node, (inner) => {
|
|
1073
|
+
if (isStepCall(inner, factories)) walkNode(inner, (one) => memoised.add(one));
|
|
1074
|
+
});
|
|
1075
|
+
return memoised;
|
|
1076
|
+
};
|
|
1077
|
+
var durableBodyReadsThroughSteps = {
|
|
1078
|
+
create(context) {
|
|
1079
|
+
const bag = context.options?.[0] ?? {};
|
|
1080
|
+
const ports = new Set(requireOption(RULE3, bag.ports, "ports", ACCEPTS2));
|
|
1081
|
+
const steps = stepFactoriesOf(RULE3, bag.stepFactories);
|
|
1082
|
+
const workflows = new Set(
|
|
1083
|
+
requireOption(RULE3, bag.workflowFactories ?? DEFAULT_WORKFLOW_FACTORIES, "workflowFactories")
|
|
1084
|
+
);
|
|
1085
|
+
const durableKey = requireOption(RULE3, bag.durableKey ?? DEFAULT_DURABLE_KEY, "durableKey");
|
|
1086
|
+
const durableValue = bag.durableValue ?? DEFAULT_DURABLE_VALUE;
|
|
1087
|
+
return {
|
|
1088
|
+
CallExpression(node) {
|
|
1089
|
+
const factory = calleeNameOf(node);
|
|
1090
|
+
if (factory === null || !workflows.has(factory)) return;
|
|
1091
|
+
if (!declaresDurable(node, durableKey, durableValue)) return;
|
|
1092
|
+
const [name] = argumentsOf(node);
|
|
1093
|
+
const workflow = stringOf(name) ?? factory;
|
|
1094
|
+
const memoised = insideAStep(node, steps);
|
|
1095
|
+
walkNode(node, (inner) => {
|
|
1096
|
+
if (inner.type !== "CallExpression" || memoised.has(inner)) return;
|
|
1097
|
+
const port = portReachedBy(inner, ports);
|
|
1098
|
+
if (port === null) return;
|
|
1099
|
+
context.report({ data: { port, workflow }, messageId: "freeRead", node: inner });
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
},
|
|
1104
|
+
fixShape: `In a DURABLE workflow body every value that gates a branch or feeds a step comes from the workflow
|
|
1105
|
+
input or from a step result. A port read written straight into the body \u2014 \`store.read(id)\` \u2014 is
|
|
1106
|
+
inline-only: the platform memoises steps by name and does not journal reads, so on a replay the read
|
|
1107
|
+
sees what the first invocation wrote, the branch flips, and the run unwinds work it had completed.
|
|
1108
|
+
Move the decision into a step (\`const doc = await ensureDraft(id)\`) and it is memoised with the run.
|
|
1109
|
+
Name this repo's ports in \`ports\`; enabled without them the rule refuses the run. What it can SEE is a
|
|
1110
|
+
call on a name \`ports\` lists: a read moved one function out \u2014 \`readStatus(id)\`, with the port inside
|
|
1111
|
+
it \u2014 is invisible here, and no lint rule that reads one file can follow it. Name the helper in
|
|
1112
|
+
\`ports\` too when a body reaches the outside world through one; a helper that is really a step is
|
|
1113
|
+
better made one. \`durableKey\` /
|
|
1114
|
+
\`durableValue\` say how a workflow declares itself durable, and \`workflowFactories\` /
|
|
1115
|
+
\`stepFactories\` how it declares its parts.`,
|
|
1116
|
+
meta: {
|
|
1117
|
+
docs: {
|
|
1118
|
+
description: "A durable workflow body reads through its steps. A platform memoises steps by name and journals no reads, so a free port read that gates a branch takes the other branch on a replay and reverses half a completed run. The same read inside a step is memoised with it; an inline body is invoked once and is exempt. `ports` is required."
|
|
1119
|
+
},
|
|
1120
|
+
messages: {
|
|
1121
|
+
freeRead: 'This durable body ("{{workflow}}") reads through `{{port}}` outside a step. A replay is answered from the run record for steps and re-runs everything else, so this read returns what the first invocation wrote and the branch it gates flips \u2014 reversing work the run had already completed. Make the decision a step, or take the value from the workflow input.'
|
|
1122
|
+
},
|
|
1123
|
+
schema: [
|
|
1124
|
+
{
|
|
1125
|
+
additionalProperties: false,
|
|
1126
|
+
properties: {
|
|
1127
|
+
durableKey: { type: "string" },
|
|
1128
|
+
durableValue: { type: ["boolean", "string"] },
|
|
1129
|
+
ports: { items: { type: "string" }, type: "array" },
|
|
1130
|
+
stepFactories: { items: { type: "string" }, type: "array" },
|
|
1131
|
+
workflowFactories: { items: { type: "string" }, type: "array" }
|
|
1132
|
+
},
|
|
1133
|
+
type: "object"
|
|
1134
|
+
}
|
|
1135
|
+
],
|
|
1136
|
+
type: "problem"
|
|
1137
|
+
},
|
|
1138
|
+
/**
|
|
1139
|
+
* A durable body with one free read and no step at all: a step would be the remedy, and a probe
|
|
1140
|
+
* that carried one would be claiming a finding from the shape the rule accepts.
|
|
1141
|
+
*/
|
|
1142
|
+
probe(options) {
|
|
1143
|
+
const bag = bagOf(options);
|
|
1144
|
+
const factory = firstOf(bag.workflowFactories, DEFAULT_WORKFLOW_FACTORIES);
|
|
1145
|
+
const port = firstOf(bag.ports, []);
|
|
1146
|
+
const key = bag.durableKey ?? DEFAULT_DURABLE_KEY;
|
|
1147
|
+
const value = bag.durableValue ?? DEFAULT_DURABLE_VALUE;
|
|
1148
|
+
return {
|
|
1149
|
+
path: "geonosis-probe-durable-body.ts",
|
|
1150
|
+
source: `export const geonosisProbe = ${factory}(
|
|
1151
|
+
'geonosis-probe',
|
|
1152
|
+
async (input) => ${port}.read(input),
|
|
1153
|
+
{ ${key}: ${typeof value === "string" ? `'${value}'` : value} },
|
|
1154
|
+
)
|
|
1155
|
+
`
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
var durable_body_reads_through_steps_default = durableBodyReadsThroughSteps;
|
|
1160
|
+
|
|
755
1161
|
// src/rules/effect-hook-naming.ts
|
|
756
1162
|
var HOOK_FILE_PATTERN = /(?:features\/[^/]+\/)?organelles\/.*\.ts$/;
|
|
757
1163
|
var effectHookNaming = {
|
|
@@ -823,22 +1229,22 @@ file and the export together \u2014 half the pair still fires.`,
|
|
|
823
1229
|
var effect_hook_naming_default = effectHookNaming;
|
|
824
1230
|
|
|
825
1231
|
// src/rules/emit-declares-attempts.ts
|
|
826
|
-
var
|
|
1232
|
+
var RULE4 = "emit-declares-attempts";
|
|
827
1233
|
var DEFAULT_EMITTERS = ["emitEventStep"];
|
|
828
1234
|
var OPTIONS = "options";
|
|
829
1235
|
var ATTEMPTS = "attempts";
|
|
830
1236
|
var EVENT_NAME = "eventName";
|
|
831
1237
|
var NO_RETRY = 1;
|
|
832
1238
|
var objectOf = (node) => node?.type === "ObjectExpression" ? node : null;
|
|
833
|
-
var
|
|
1239
|
+
var propertiesOf2 = (object) => {
|
|
834
1240
|
const properties = object.properties ?? [];
|
|
835
1241
|
return properties.every((one) => one.type === "Property") ? properties : null;
|
|
836
1242
|
};
|
|
837
|
-
var
|
|
1243
|
+
var valueOf2 = (properties, name) => properties.find((one) => one.key?.type === "Identifier" && one.key.name === name)?.value;
|
|
838
1244
|
var emitDeclaresAttempts = {
|
|
839
1245
|
create(context) {
|
|
840
1246
|
const emitters = new Set(
|
|
841
|
-
requireOption(
|
|
1247
|
+
requireOption(RULE4, context.options?.[0]?.emitters ?? DEFAULT_EMITTERS, "emitters")
|
|
842
1248
|
);
|
|
843
1249
|
const budgetHelpers = new Set(context.options?.[0]?.budgetHelpers ?? []);
|
|
844
1250
|
return {
|
|
@@ -848,11 +1254,11 @@ var emitDeclaresAttempts = {
|
|
|
848
1254
|
if (!emitters.has(emitter)) return;
|
|
849
1255
|
const payload = objectOf(node.arguments?.[0]);
|
|
850
1256
|
if (payload === null) return;
|
|
851
|
-
const properties =
|
|
1257
|
+
const properties = propertiesOf2(payload);
|
|
852
1258
|
if (properties === null) return;
|
|
853
|
-
const named =
|
|
1259
|
+
const named = valueOf2(properties, EVENT_NAME);
|
|
854
1260
|
const site = named?.type === "Literal" && typeof named.value === "string" ? named.value : emitter;
|
|
855
|
-
const options =
|
|
1261
|
+
const options = valueOf2(properties, OPTIONS);
|
|
856
1262
|
if (options !== void 0 && objectOf(options) === null) {
|
|
857
1263
|
if (budgetHelpers.size === 0) {
|
|
858
1264
|
context.report({ data: { emitter, site }, messageId: "undeclaredHelpers", node });
|
|
@@ -875,9 +1281,9 @@ var emitDeclaresAttempts = {
|
|
|
875
1281
|
});
|
|
876
1282
|
return;
|
|
877
1283
|
}
|
|
878
|
-
const bag = options === void 0 ? [] :
|
|
1284
|
+
const bag = options === void 0 ? [] : propertiesOf2(objectOf(options));
|
|
879
1285
|
if (bag === null) return;
|
|
880
|
-
const attempts =
|
|
1286
|
+
const attempts = valueOf2(bag, ATTEMPTS);
|
|
881
1287
|
if (attempts === void 0) {
|
|
882
1288
|
context.report({ data: { emitter, site }, messageId: "noBudget", node });
|
|
883
1289
|
return;
|
|
@@ -1095,14 +1501,39 @@ var font_roles_only_default = fontRolesOnly;
|
|
|
1095
1501
|
|
|
1096
1502
|
// src/rules/layer-walls.ts
|
|
1097
1503
|
var isExternal = (source) => !source.startsWith("./") && !source.startsWith("../") && !source.startsWith("/");
|
|
1504
|
+
var PROBE_TARGET = "geonosis-probe-target.ts";
|
|
1505
|
+
var PROBE_WALL = "geonosis-probe-wall.ts";
|
|
1506
|
+
var inverts = (pattern, leaf) => {
|
|
1507
|
+
try {
|
|
1508
|
+
pathMatching(pattern, leaf);
|
|
1509
|
+
return true;
|
|
1510
|
+
} catch {
|
|
1511
|
+
return false;
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
var placeIn = (paths, tried) => {
|
|
1515
|
+
for (const pattern of paths) {
|
|
1516
|
+
let candidate;
|
|
1517
|
+
try {
|
|
1518
|
+
candidate = pathMatching(pattern, PROBE_WALL);
|
|
1519
|
+
} catch {
|
|
1520
|
+
tried.push(pattern);
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
if (new RegExp(pattern).test(`/repo-root/${candidate}`)) return candidate;
|
|
1524
|
+
tried.push(pattern);
|
|
1525
|
+
}
|
|
1526
|
+
return void 0;
|
|
1527
|
+
};
|
|
1098
1528
|
var layerWalls = {
|
|
1099
1529
|
create(context) {
|
|
1100
|
-
const { externals = "allow", layers = [] } = context.options?.[0] ?? {};
|
|
1530
|
+
const { externals = "allow", layers = [], typeOnly = "skip" } = context.options?.[0] ?? {};
|
|
1101
1531
|
requireOption("layer-walls", layers, "layers");
|
|
1102
1532
|
const compiled = layers.map((layer) => ({
|
|
1103
1533
|
mayImport: new Set(layer.mayImport ?? []),
|
|
1104
1534
|
name: layer.name,
|
|
1105
|
-
paths: layer.paths.map((source) => new RegExp(source))
|
|
1535
|
+
paths: layer.paths.map((source) => new RegExp(source)),
|
|
1536
|
+
typeOnly: layer.typeOnly ?? typeOnly
|
|
1106
1537
|
}));
|
|
1107
1538
|
const layerOf = (pathish) => compiled.find((layer) => layer.paths.some((pattern) => pattern.test(pathish)));
|
|
1108
1539
|
const filename = normalise(context.filename);
|
|
@@ -1125,7 +1556,7 @@ var layerWalls = {
|
|
|
1125
1556
|
node
|
|
1126
1557
|
});
|
|
1127
1558
|
};
|
|
1128
|
-
return onImportSource(check);
|
|
1559
|
+
return onImportSource(check, { typeOnly: here.typeOnly === "count" ? "visit" : "skip" });
|
|
1129
1560
|
},
|
|
1130
1561
|
fixShape: `Layers are declared in the rule's \`layers\` option \u2014 \`{ name, paths, mayImport }\` \u2014 and a file may
|
|
1131
1562
|
import its own layer plus the layers it names, nothing else. When this fires, MOVE something: the
|
|
@@ -1134,10 +1565,13 @@ belongs in the layer it keeps reaching for. Widening \`mayImport\` to make the d
|
|
|
1134
1565
|
deleting the wall, and the wall is the only reason the layers are worth naming.
|
|
1135
1566
|
|
|
1136
1567
|
A relative source is resolved against the importing file before it is judged, so \`../../shell/boot\`
|
|
1137
|
-
is the same breach as naming the layer outright. Type-only imports and re-exports are exempt
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1568
|
+
is the same breach as naming the layer outright. Type-only imports and re-exports are exempt unless
|
|
1569
|
+
\`typeOnly\` is \`"count"\` \u2014 rule-wide, or on the one layer whose wall is about which side may KNOW
|
|
1570
|
+
about which, since \`import type\` reaches that far exactly as a value does, while a layer borrowing a
|
|
1571
|
+
type to keep a package out of its runtime graph is the reason the default is \`"skip"\`. A package or a \`node:\` builtin
|
|
1572
|
+
is not a wall unless \`externals\` is \`"deny"\`. A file in no declared layer is unconstrained, so a
|
|
1573
|
+
repo can wall one corner of its tree and leave the rest alone. Enabled with no \`layers\` at all the
|
|
1574
|
+
rule refuses the run: walls nobody declared are not walls held.`,
|
|
1141
1575
|
meta: {
|
|
1142
1576
|
docs: {
|
|
1143
1577
|
description: "Import direction between a repo's own declared layers. Each layer names the files it owns (regex sources) and the layers it may import; a file may always import its own layer. Relative sources are resolved against the importing file first. Enabled with no layers configured it refuses the run."
|
|
@@ -1157,13 +1591,15 @@ with no \`layers\` at all the rule refuses the run: walls nobody declared are no
|
|
|
1157
1591
|
properties: {
|
|
1158
1592
|
mayImport: { items: { type: "string" }, type: "array" },
|
|
1159
1593
|
name: { type: "string" },
|
|
1160
|
-
paths: { items: { type: "string" }, type: "array" }
|
|
1594
|
+
paths: { items: { type: "string" }, type: "array" },
|
|
1595
|
+
typeOnly: { enum: ["count", "skip"], type: "string" }
|
|
1161
1596
|
},
|
|
1162
1597
|
required: ["name", "paths"],
|
|
1163
1598
|
type: "object"
|
|
1164
1599
|
},
|
|
1165
1600
|
type: "array"
|
|
1166
|
-
}
|
|
1601
|
+
},
|
|
1602
|
+
typeOnly: { enum: ["count", "skip"], type: "string" }
|
|
1167
1603
|
},
|
|
1168
1604
|
type: "object"
|
|
1169
1605
|
}
|
|
@@ -1181,21 +1617,27 @@ with no \`layers\` at all the rule refuses the run: walls nobody declared are no
|
|
|
1181
1617
|
*/
|
|
1182
1618
|
probe(options) {
|
|
1183
1619
|
const { layers = [] } = bagOf(options);
|
|
1620
|
+
const tried = [];
|
|
1184
1621
|
for (const here of layers) {
|
|
1185
1622
|
const mayImport = new Set(here.mayImport ?? []);
|
|
1186
1623
|
const target = layers.find((other) => other.name !== here.name && !mayImport.has(other.name));
|
|
1187
|
-
const
|
|
1188
|
-
const
|
|
1189
|
-
if (target === void 0 ||
|
|
1190
|
-
const source = pathMatching(targetPath,
|
|
1624
|
+
const path2 = placeIn(here.paths, tried);
|
|
1625
|
+
const targetPath = target?.paths.find((one) => inverts(one, PROBE_TARGET));
|
|
1626
|
+
if (target === void 0 || path2 === void 0 || targetPath === void 0) continue;
|
|
1627
|
+
const source = pathMatching(targetPath, PROBE_TARGET);
|
|
1191
1628
|
return {
|
|
1192
|
-
path:
|
|
1629
|
+
path: path2,
|
|
1193
1630
|
source: `import { geonosisProbe } from '${source}'
|
|
1194
1631
|
|
|
1195
1632
|
export const geonosisProbeWall = geonosisProbe
|
|
1196
1633
|
`
|
|
1197
1634
|
};
|
|
1198
1635
|
}
|
|
1636
|
+
if (tried.length > 0) {
|
|
1637
|
+
throw new Error(
|
|
1638
|
+
`biological-architecture/layer-walls: no layer here names a place a probe file could be put \u2014 tried ${tried.join(", ")}. Each of those matches an import specifier and can never match a filename, so a probe built from one lands outside the layer it is meant to be inside. Give the layer a directory pattern as well (#119).`
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1199
1641
|
throw new Error(
|
|
1200
1642
|
"biological-architecture/layer-walls: every declared layer may import every other, so no single file can cross a wall \u2014 there is no wall here to probe."
|
|
1201
1643
|
);
|
|
@@ -1377,36 +1819,6 @@ primitives count as composition; a pure \`export \u2026 from\` shim is not a mol
|
|
|
1377
1819
|
};
|
|
1378
1820
|
var molecule_must_compose_default = moleculeMustCompose;
|
|
1379
1821
|
|
|
1380
|
-
// src/rules/lib/nodes.ts
|
|
1381
|
-
var walkNode = (node, visit) => {
|
|
1382
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1383
|
-
const stack = [node];
|
|
1384
|
-
while (stack.length > 0) {
|
|
1385
|
-
const at = stack.pop();
|
|
1386
|
-
if (at === null || typeof at !== "object" || seen.has(at)) continue;
|
|
1387
|
-
seen.add(at);
|
|
1388
|
-
if (Array.isArray(at)) {
|
|
1389
|
-
stack.push(...at);
|
|
1390
|
-
continue;
|
|
1391
|
-
}
|
|
1392
|
-
const record2 = at;
|
|
1393
|
-
if (typeof record2.type === "string") visit(record2);
|
|
1394
|
-
for (const [key, value] of Object.entries(record2)) {
|
|
1395
|
-
if (key === "parent") continue;
|
|
1396
|
-
if (value !== null && typeof value === "object") stack.push(value);
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
};
|
|
1400
|
-
var propertyNameOf = (node) => {
|
|
1401
|
-
if (node.type !== "MemberExpression" || node.computed === true) return null;
|
|
1402
|
-
const property = node.property;
|
|
1403
|
-
return property?.type === "Identifier" && typeof property.name === "string" ? property.name : null;
|
|
1404
|
-
};
|
|
1405
|
-
var calleeNameOf = (node) => {
|
|
1406
|
-
const callee = node.callee;
|
|
1407
|
-
return callee?.type === "Identifier" && typeof callee.name === "string" ? callee.name : null;
|
|
1408
|
-
};
|
|
1409
|
-
|
|
1410
1822
|
// src/rules/lib/mutations.ts
|
|
1411
1823
|
var DEFAULT_MUTATION_PREFIXES = [
|
|
1412
1824
|
"create",
|
|
@@ -1427,64 +1839,19 @@ var writesWith = (prefixes) => (node) => {
|
|
|
1427
1839
|
};
|
|
1428
1840
|
var writeNameOf = (node) => propertyNameOf(node.callee ?? {}) ?? "a service write";
|
|
1429
1841
|
|
|
1430
|
-
// src/rules/lib/objects.ts
|
|
1431
|
-
var propertiesOf2 = (node) => {
|
|
1432
|
-
const object = node;
|
|
1433
|
-
if (object?.type !== "ObjectExpression") return null;
|
|
1434
|
-
const properties = object.properties ?? [];
|
|
1435
|
-
return properties.every((one) => one.type === "Property") ? properties : null;
|
|
1436
|
-
};
|
|
1437
|
-
var valueOf2 = (properties, name) => {
|
|
1438
|
-
const found = properties.find((one) => {
|
|
1439
|
-
const key = one.key;
|
|
1440
|
-
return key?.type === "Identifier" && key.name === name || key?.type === "Literal" && key.value === name;
|
|
1441
|
-
});
|
|
1442
|
-
return found?.value;
|
|
1443
|
-
};
|
|
1444
|
-
var hasProperty = (properties, name) => valueOf2(properties, name) !== void 0;
|
|
1445
|
-
var stringOf = (node) => {
|
|
1446
|
-
const literal = node;
|
|
1447
|
-
return literal?.type === "Literal" && typeof literal.value === "string" ? literal.value : null;
|
|
1448
|
-
};
|
|
1449
|
-
|
|
1450
|
-
// src/rules/lib/steps.ts
|
|
1451
|
-
var DEFAULT_STEP_FACTORIES = ["createStep"];
|
|
1452
|
-
var stepFactoriesOf = (rule, configured) => new Set(requireOption(rule, configured ?? DEFAULT_STEP_FACTORIES, "stepFactories"));
|
|
1453
|
-
var isStepCall = (node, factories) => {
|
|
1454
|
-
const callee = calleeNameOf(node);
|
|
1455
|
-
return callee !== null && factories.has(callee);
|
|
1456
|
-
};
|
|
1457
|
-
var argumentsOf = (node) => node.arguments ?? [];
|
|
1458
|
-
var configOf = (node) => {
|
|
1459
|
-
const [first] = argumentsOf(node);
|
|
1460
|
-
if (first === void 0) return { kind: "unreadable" };
|
|
1461
|
-
if (stringOf(first) !== null) return { kind: "bare" };
|
|
1462
|
-
const properties = propertiesOf2(first);
|
|
1463
|
-
return properties === null ? { kind: "unreadable" } : { kind: "object", properties };
|
|
1464
|
-
};
|
|
1465
|
-
var stepNameOf = (node, config) => {
|
|
1466
|
-
const [first] = argumentsOf(node);
|
|
1467
|
-
const bare = stringOf(first);
|
|
1468
|
-
if (bare !== null) return bare;
|
|
1469
|
-
if (config.kind === "object") {
|
|
1470
|
-
const named = config.properties.find((one) => {
|
|
1471
|
-
const key = one.key;
|
|
1472
|
-
return key?.type === "Identifier" && key.name === "name";
|
|
1473
|
-
});
|
|
1474
|
-
const literal = stringOf(named?.value);
|
|
1475
|
-
if (literal !== null) return literal;
|
|
1476
|
-
}
|
|
1477
|
-
return calleeNameOf(node) ?? "a step";
|
|
1478
|
-
};
|
|
1479
|
-
|
|
1480
1842
|
// src/rules/mutating-step-declares-retry.ts
|
|
1481
|
-
var
|
|
1482
|
-
var
|
|
1843
|
+
var RULE5 = "mutating-step-declares-retry";
|
|
1844
|
+
var DEFAULT_RETRY_KEY = "maxRetries";
|
|
1483
1845
|
var NO_RETRY2 = 0;
|
|
1484
1846
|
var mutatingStepDeclaresRetry = {
|
|
1485
1847
|
create(context) {
|
|
1486
|
-
const factories = stepFactoriesOf(
|
|
1487
|
-
const isWrite = writesWith(mutationPrefixesOf(
|
|
1848
|
+
const factories = stepFactoriesOf(RULE5, context.options?.[0]?.stepFactories);
|
|
1849
|
+
const isWrite = writesWith(mutationPrefixesOf(RULE5, context.options?.[0]?.mutationPrefixes));
|
|
1850
|
+
const retryKey = requireOption(
|
|
1851
|
+
RULE5,
|
|
1852
|
+
context.options?.[0]?.retryKey ?? DEFAULT_RETRY_KEY,
|
|
1853
|
+
"retryKey"
|
|
1854
|
+
);
|
|
1488
1855
|
return {
|
|
1489
1856
|
CallExpression(node) {
|
|
1490
1857
|
if (!isStepCall(node, factories)) return;
|
|
@@ -1499,18 +1866,18 @@ var mutatingStepDeclaresRetry = {
|
|
|
1499
1866
|
const step = stepNameOf(node, config);
|
|
1500
1867
|
if (config.kind === "unreadable") return;
|
|
1501
1868
|
if (config.kind === "bare") {
|
|
1502
|
-
context.report({ data: { step }, messageId: "noConfig", node });
|
|
1869
|
+
context.report({ data: { key: retryKey, step }, messageId: "noConfig", node });
|
|
1503
1870
|
return;
|
|
1504
1871
|
}
|
|
1505
|
-
const budget =
|
|
1872
|
+
const budget = valueOf(config.properties, retryKey);
|
|
1506
1873
|
if (budget === void 0) {
|
|
1507
|
-
context.report({ data: { step }, messageId: "noRetry", node });
|
|
1874
|
+
context.report({ data: { key: retryKey, step }, messageId: "noRetry", node });
|
|
1508
1875
|
return;
|
|
1509
1876
|
}
|
|
1510
1877
|
if (budget.type !== "Literal" || typeof budget.value !== "number") return;
|
|
1511
1878
|
if (budget.value > NO_RETRY2) return;
|
|
1512
1879
|
context.report({
|
|
1513
|
-
data: { budget: String(budget.value), step },
|
|
1880
|
+
data: { budget: String(budget.value), key: retryKey, step },
|
|
1514
1881
|
messageId: "zeroRetries",
|
|
1515
1882
|
node
|
|
1516
1883
|
});
|
|
@@ -1518,27 +1885,29 @@ var mutatingStepDeclaresRetry = {
|
|
|
1518
1885
|
};
|
|
1519
1886
|
},
|
|
1520
1887
|
fixShape: `A step whose invoke body WRITES declares a retry class in its config object:
|
|
1521
|
-
\`createStep({ name: 'place-order', ...RETRY_DB }, invoke, compensate)\`.
|
|
1522
|
-
so a step named with a bare string cannot have one \u2014 take the object form and put the budget in
|
|
1523
|
-
from the repo's shared retry constants rather than as a number at the call site. A budget the
|
|
1524
|
-
cannot evaluate (a constant, a spread) passes: it says nothing
|
|
1525
|
-
inline. Read-only and compute steps are exempt \u2014 there is nothing
|
|
1526
|
-
\`
|
|
1527
|
-
|
|
1888
|
+
\`createStep({ name: 'place-order', ...RETRY_DB }, invoke, compensate)\`. A retry budget defaults to
|
|
1889
|
+
none, so a step named with a bare string cannot have one \u2014 take the object form and put the budget in
|
|
1890
|
+
it, from the repo's shared retry constants rather than as a number at the call site. A budget the
|
|
1891
|
+
rule cannot evaluate (a constant, a spread, a nested \`{ limit, backoff }\`) passes: it says nothing
|
|
1892
|
+
rather than pushing the number back inline. Read-only and compute steps are exempt \u2014 there is nothing
|
|
1893
|
+
to revert. Configure \`retryKey\` to the key this engine writes the budget under (Medusa's
|
|
1894
|
+
\`maxRetries\`, the org engine's \`retry\`), \`stepFactories\` if the repo wraps \`createStep\`, and
|
|
1895
|
+
\`mutationPrefixes\` if its writes are named otherwise.`,
|
|
1528
1896
|
meta: {
|
|
1529
1897
|
docs: {
|
|
1530
|
-
description: "A mutating step declares a retry class.
|
|
1898
|
+
description: "A mutating step declares a retry class. A budget left unsaid defaults to zero retries, so a transient failure reverts real state a retry would have absorbed. Read-only steps are exempt; a non-literal budget passes. `retryKey`, `stepFactories` and `mutationPrefixes` are options."
|
|
1531
1899
|
},
|
|
1532
1900
|
messages: {
|
|
1533
|
-
noConfig: 'Step "{{step}}" writes but is named with a bare string, so it carries no retry class \u2014 and `
|
|
1534
|
-
noRetry: 'Mutating step "{{step}}" declares no
|
|
1535
|
-
zeroRetries: 'Mutating step "{{step}}" declares `
|
|
1901
|
+
noConfig: 'Step "{{step}}" writes but is named with a bare string, so it carries no retry class \u2014 and `{{key}}` defaults to 0, which means a transient failure reverts real state. Take the object form and give it a retry class from the shared constants.',
|
|
1902
|
+
noRetry: 'Mutating step "{{step}}" declares no `{{key}}`. Without one the budget is zero, so the first transient failure reverts every write this step made. Add the repo\'s retry constant to its config.',
|
|
1903
|
+
zeroRetries: 'Mutating step "{{step}}" declares `{{key}}: {{budget}}`, which is the default spelled out \u2014 no retries at all. Take the budget from the shared retry constants instead.'
|
|
1536
1904
|
},
|
|
1537
1905
|
schema: [
|
|
1538
1906
|
{
|
|
1539
1907
|
additionalProperties: false,
|
|
1540
1908
|
properties: {
|
|
1541
1909
|
mutationPrefixes: { items: { type: "string" }, type: "array" },
|
|
1910
|
+
retryKey: { type: "string" },
|
|
1542
1911
|
stepFactories: { items: { type: "string" }, type: "array" }
|
|
1543
1912
|
},
|
|
1544
1913
|
type: "object"
|
|
@@ -1550,13 +1919,13 @@ otherwise.`,
|
|
|
1550
1919
|
var mutating_step_declares_retry_default = mutatingStepDeclaresRetry;
|
|
1551
1920
|
|
|
1552
1921
|
// src/rules/named-when-block.ts
|
|
1553
|
-
var
|
|
1922
|
+
var RULE6 = "named-when-block";
|
|
1554
1923
|
var DEFAULT_BLOCKS = ["when"];
|
|
1555
1924
|
var NAMED = 3;
|
|
1556
1925
|
var namedWhenBlock = {
|
|
1557
1926
|
create(context) {
|
|
1558
1927
|
const blocks = new Set(
|
|
1559
|
-
requireOption(
|
|
1928
|
+
requireOption(RULE6, context.options?.[0]?.blocks ?? DEFAULT_BLOCKS, "blocks")
|
|
1560
1929
|
);
|
|
1561
1930
|
return {
|
|
1562
1931
|
CallExpression(node) {
|
|
@@ -2322,26 +2691,26 @@ A component returns UI; a hook returns state.`,
|
|
|
2322
2691
|
var no_hook_in_component_disguise_default = noHookInComponentDisguise;
|
|
2323
2692
|
|
|
2324
2693
|
// src/rules/no-index-count-as-exact.ts
|
|
2325
|
-
var
|
|
2694
|
+
var RULE7 = "no-index-count-as-exact";
|
|
2326
2695
|
var DEFAULT_ESTIMATE_FIELDS = ["estimate_count", "count_estimate"];
|
|
2327
2696
|
var DEFAULT_LABEL_KEY = "count_is_estimate";
|
|
2328
2697
|
var noIndexCountAsExact = {
|
|
2329
2698
|
create(context) {
|
|
2330
2699
|
const fields = new Set(
|
|
2331
2700
|
requireOption(
|
|
2332
|
-
|
|
2701
|
+
RULE7,
|
|
2333
2702
|
context.options?.[0]?.estimateFields ?? DEFAULT_ESTIMATE_FIELDS,
|
|
2334
2703
|
"estimateFields"
|
|
2335
2704
|
)
|
|
2336
2705
|
);
|
|
2337
2706
|
const label = requireOption(
|
|
2338
|
-
|
|
2707
|
+
RULE7,
|
|
2339
2708
|
context.options?.[0]?.labelKey ?? DEFAULT_LABEL_KEY,
|
|
2340
2709
|
"labelKey"
|
|
2341
2710
|
);
|
|
2342
2711
|
return {
|
|
2343
2712
|
ObjectExpression(node) {
|
|
2344
|
-
const properties =
|
|
2713
|
+
const properties = propertiesOf(node);
|
|
2345
2714
|
if (properties === null || hasProperty(properties, label)) return;
|
|
2346
2715
|
let field = null;
|
|
2347
2716
|
for (const property of properties) {
|
|
@@ -2591,20 +2960,20 @@ logic in \`lib/\`.`,
|
|
|
2591
2960
|
var no_invalid_feature_folders_default = noInvalidFeatureFolders;
|
|
2592
2961
|
|
|
2593
2962
|
// src/rules/no-loader-side-effect-in-index-file.ts
|
|
2594
|
-
var
|
|
2963
|
+
var RULE8 = "no-loader-side-effect-in-index-file";
|
|
2595
2964
|
var DEFAULT_DIRECTORIES = ["(?:^|/)subscribers/", "(?:^|/)jobs/"];
|
|
2596
2965
|
var DEFAULT_RESOURCE_FACTORIES = ["createWorkflow", "createStep", "createHook"];
|
|
2597
2966
|
var INDEX = /(?:^|\/)index\.[cm]?[jt]sx?$/;
|
|
2598
2967
|
var noLoaderSideEffectInIndexFile = {
|
|
2599
2968
|
create(context) {
|
|
2600
2969
|
const directories = requireOption(
|
|
2601
|
-
|
|
2970
|
+
RULE8,
|
|
2602
2971
|
context.options?.[0]?.directories ?? DEFAULT_DIRECTORIES,
|
|
2603
2972
|
"directories"
|
|
2604
2973
|
).map((source) => new RegExp(source));
|
|
2605
2974
|
const factories = new Set(
|
|
2606
2975
|
requireOption(
|
|
2607
|
-
|
|
2976
|
+
RULE8,
|
|
2608
2977
|
context.options?.[0]?.resourceFactories ?? DEFAULT_RESOURCE_FACTORIES,
|
|
2609
2978
|
"resourceFactories"
|
|
2610
2979
|
)
|
|
@@ -2767,7 +3136,7 @@ the same component. Thin preset wrappers over one imported component do not coun
|
|
|
2767
3136
|
var no_logic_in_component_files_default = noLogicInComponentFiles;
|
|
2768
3137
|
|
|
2769
3138
|
// src/rules/no-mock-db-in-integration.ts
|
|
2770
|
-
var
|
|
3139
|
+
var RULE9 = "no-mock-db-in-integration";
|
|
2771
3140
|
var BUN_MOCK = "mock.module";
|
|
2772
3141
|
var MOCK_CALLS = /* @__PURE__ */ new Set(["jest.mock", BUN_MOCK, "vi.mock"]);
|
|
2773
3142
|
var calleeOf = (node) => {
|
|
@@ -2785,8 +3154,8 @@ var inDirectory = (filename, directory) => {
|
|
|
2785
3154
|
var noMockDbInIntegration = {
|
|
2786
3155
|
create(context) {
|
|
2787
3156
|
const options = context.options?.[0] ?? {};
|
|
2788
|
-
const dirs = requireOption(
|
|
2789
|
-
const modules = requireOption(
|
|
3157
|
+
const dirs = requireOption(RULE9, options.integrationDirs, "integrationDirs");
|
|
3158
|
+
const modules = requireOption(RULE9, options.dbModules, "dbModules");
|
|
2790
3159
|
const mockNames = new Set(options.mockNames ?? []);
|
|
2791
3160
|
const filename = normalise(context.filename);
|
|
2792
3161
|
const spans = /* @__PURE__ */ new Set();
|
|
@@ -2887,14 +3256,14 @@ below the seam, and a caller that names it has crossed it.`,
|
|
|
2887
3256
|
var no_orm_outside_db_default = noOrmOutsideDb;
|
|
2888
3257
|
|
|
2889
3258
|
// src/rules/no-raw-html-atoms.ts
|
|
2890
|
-
var
|
|
3259
|
+
var RULE10 = "no-raw-html-atoms";
|
|
2891
3260
|
var WHERE_RAW_HTML_IS_THE_POINT = "atoms";
|
|
2892
3261
|
var noRawHtmlAtoms = {
|
|
2893
3262
|
create(context) {
|
|
2894
|
-
const restrictions = requireOption(
|
|
3263
|
+
const restrictions = requireOption(RULE10, context.options[0], "element\u2192atom map");
|
|
2895
3264
|
const scope = context.options[1]?.scope;
|
|
2896
3265
|
const filename = context.filename;
|
|
2897
|
-
const inScope = scope === void 0 ? !inTier(filename, WHERE_RAW_HTML_IS_THE_POINT) : requireTiers(
|
|
3266
|
+
const inScope = scope === void 0 ? !inTier(filename, WHERE_RAW_HTML_IS_THE_POINT) : requireTiers(RULE10, scope).some((tier) => inTier(filename, tier));
|
|
2898
3267
|
if (!inScope) return {};
|
|
2899
3268
|
const elementMap = /* @__PURE__ */ new Map();
|
|
2900
3269
|
for (const r of restrictions) {
|
|
@@ -3420,8 +3789,8 @@ var no_type_definitions_in_components_default = noTypeDefinitionsInComponents;
|
|
|
3420
3789
|
|
|
3421
3790
|
// src/rules/no-unregistered-plugin-dir.ts
|
|
3422
3791
|
import { readFileSync as readFileSync3 } from "fs";
|
|
3423
|
-
var
|
|
3424
|
-
var
|
|
3792
|
+
var RULE11 = "no-unregistered-plugin-dir";
|
|
3793
|
+
var ACCEPTS3 = "{ roots: string[], registry: string | string[], manifests: string[] }";
|
|
3425
3794
|
var trimSlashes = (path2) => path2.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
3426
3795
|
var withoutComments = (source) => source.replaceAll(/\/\*[\s\S]*?\*\//g, " ").replaceAll(/\/\/[^\n]*/g, " ");
|
|
3427
3796
|
var literalsOf = (source) => [...withoutComments(source).matchAll(/['"`]([^'"`\n]+)['"`]/g)].map((match) => match[1] ?? "");
|
|
@@ -3438,10 +3807,10 @@ var rootedAt = (filename, root) => {
|
|
|
3438
3807
|
var noUnregisteredPluginDir = {
|
|
3439
3808
|
create(context) {
|
|
3440
3809
|
const options = context.options?.[0] ?? {};
|
|
3441
|
-
const roots = requireOption(
|
|
3442
|
-
const declared = requireOption(
|
|
3810
|
+
const roots = requireOption(RULE11, options.roots, "roots", ACCEPTS3).map(trimSlashes);
|
|
3811
|
+
const declared = requireOption(RULE11, options.registry, "registry", ACCEPTS3);
|
|
3443
3812
|
const registries = (typeof declared === "string" ? [declared] : declared).map(trimSlashes);
|
|
3444
|
-
const manifests = new Set(requireOption(
|
|
3813
|
+
const manifests = new Set(requireOption(RULE11, options.manifests, "manifests", ACCEPTS3));
|
|
3445
3814
|
const filename = normalise(context.filename);
|
|
3446
3815
|
return {
|
|
3447
3816
|
Program(node) {
|
|
@@ -3695,16 +4064,16 @@ source. The CELL is the only level where several sources meet; it derives and pa
|
|
|
3695
4064
|
var organelle_single_source_default = organelleSingleSource;
|
|
3696
4065
|
|
|
3697
4066
|
// src/rules/plugin-route-namespaced.ts
|
|
3698
|
-
var
|
|
4067
|
+
var RULE12 = "plugin-route-namespaced";
|
|
3699
4068
|
var DEFAULT_SCOPES = ["store", "admin"];
|
|
3700
4069
|
var ROUTE_FILE = /(?:^|\/)api\/(.+\/)?route\.[cm]?[jt]sx?$/;
|
|
3701
4070
|
var NAMESPACED_FROM = 2;
|
|
3702
4071
|
var readPackages = (configured) => {
|
|
3703
|
-
const rules2 = requireOption(
|
|
4072
|
+
const rules2 = requireOption(RULE12, configured, "packages");
|
|
3704
4073
|
for (const rule of rules2) {
|
|
3705
4074
|
if (typeof rule.under !== "string" || rule.under === "") {
|
|
3706
4075
|
throw new Error(
|
|
3707
|
-
`biological-architecture/${
|
|
4076
|
+
`biological-architecture/${RULE12}: every \`packages\` entry needs \`under\` \u2014 the directory whose children are packages. Without it the rule cannot tell which files belong to a plugin.`
|
|
3708
4077
|
);
|
|
3709
4078
|
}
|
|
3710
4079
|
}
|
|
@@ -3731,7 +4100,7 @@ var pluginRouteNamespaced = {
|
|
|
3731
4100
|
create(context) {
|
|
3732
4101
|
const rules2 = readPackages(context.options?.[0]?.packages);
|
|
3733
4102
|
const scopes = new Set(
|
|
3734
|
-
requireOption(
|
|
4103
|
+
requireOption(RULE12, context.options?.[0]?.scopes ?? DEFAULT_SCOPES, "scopes")
|
|
3735
4104
|
);
|
|
3736
4105
|
const path2 = normalise(context.filename);
|
|
3737
4106
|
const route = routePathOf(path2);
|
|
@@ -3918,7 +4287,7 @@ decides what an event MEANS and throws when it cannot.`,
|
|
|
3918
4287
|
var queue_loop_is_the_library_default = queueLoopIsTheLibrary;
|
|
3919
4288
|
|
|
3920
4289
|
// src/rules/route-no-inline-mutation.ts
|
|
3921
|
-
var
|
|
4290
|
+
var RULE13 = "route-no-inline-mutation";
|
|
3922
4291
|
var DEFAULT_PATHS = [String.raw`(?:^|/)api/.*/route\.[cm]?[jt]sx?$`];
|
|
3923
4292
|
var DEFAULT_INLINE_MUTATION_OK = "arch:inline-mutation-ok";
|
|
3924
4293
|
var commentsOf2 = (sourceCode) => sourceCode?.getAllComments?.() ?? sourceCode?.ast?.comments ?? [];
|
|
@@ -3962,15 +4331,15 @@ var annotated = (found, comments, marker) => {
|
|
|
3962
4331
|
};
|
|
3963
4332
|
var routeNoInlineMutation = {
|
|
3964
4333
|
create(context) {
|
|
3965
|
-
const paths = requireOption(
|
|
4334
|
+
const paths = requireOption(RULE13, context.options?.[0]?.paths ?? DEFAULT_PATHS, "paths").map(
|
|
3966
4335
|
(source) => new RegExp(source)
|
|
3967
4336
|
);
|
|
3968
4337
|
const marker = requireOption(
|
|
3969
|
-
|
|
4338
|
+
RULE13,
|
|
3970
4339
|
context.options?.[0]?.inlineMutationOk ?? DEFAULT_INLINE_MUTATION_OK,
|
|
3971
4340
|
"inlineMutationOk"
|
|
3972
4341
|
);
|
|
3973
|
-
const isWrite = writesWith(mutationPrefixesOf(
|
|
4342
|
+
const isWrite = writesWith(mutationPrefixesOf(RULE13, context.options?.[0]?.mutationPrefixes));
|
|
3974
4343
|
const filename = normalise(context.filename);
|
|
3975
4344
|
if (!paths.some((pattern) => pattern.test(filename))) return {};
|
|
3976
4345
|
return {
|
|
@@ -4040,7 +4409,7 @@ var route_no_inline_mutation_default = routeNoInlineMutation;
|
|
|
4040
4409
|
|
|
4041
4410
|
// src/rules/router-schema-parity.ts
|
|
4042
4411
|
import { existsSync as existsSync2 } from "fs";
|
|
4043
|
-
var
|
|
4412
|
+
var RULE14 = "router-schema-parity";
|
|
4044
4413
|
var EXTENSIONS2 = [".ts", ".tsx"];
|
|
4045
4414
|
var trimSlashes2 = (path2) => path2.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
4046
4415
|
var baseOf = (file) => file.replace(/\.[cm]?[jt]sx?$/, "");
|
|
@@ -4058,8 +4427,8 @@ var directlyUnder = (filename, directory) => {
|
|
|
4058
4427
|
var routerSchemaParity = {
|
|
4059
4428
|
create(context) {
|
|
4060
4429
|
const options = context.options?.[0] ?? {};
|
|
4061
|
-
const routers = trimSlashes2(requireOption(
|
|
4062
|
-
const schemas = trimSlashes2(requireOption(
|
|
4430
|
+
const routers = trimSlashes2(requireOption(RULE14, options.routers, "routers"));
|
|
4431
|
+
const schemas = trimSlashes2(requireOption(RULE14, options.schemas, "schemas"));
|
|
4063
4432
|
const suffix = options.suffix ?? "";
|
|
4064
4433
|
const ignored = new Set(options.ignore ?? []);
|
|
4065
4434
|
const mounts = options.mounts ?? {};
|
|
@@ -4131,7 +4500,7 @@ repo-root-relative, because in one consumer they live in different packages.`,
|
|
|
4131
4500
|
var router_schema_parity_default = routerSchemaParity;
|
|
4132
4501
|
|
|
4133
4502
|
// src/rules/service-name-not-reserved.ts
|
|
4134
|
-
var
|
|
4503
|
+
var RULE15 = "service-name-not-reserved";
|
|
4135
4504
|
var DEFAULT_MODULE_FACTORIES = ["Module"];
|
|
4136
4505
|
var MODULES_2_19 = [
|
|
4137
4506
|
"analytics",
|
|
@@ -4171,13 +4540,13 @@ var serviceNameNotReserved = {
|
|
|
4171
4540
|
create(context) {
|
|
4172
4541
|
const factories = new Set(
|
|
4173
4542
|
requireOption(
|
|
4174
|
-
|
|
4543
|
+
RULE15,
|
|
4175
4544
|
context.options?.[0]?.moduleFactories ?? DEFAULT_MODULE_FACTORIES,
|
|
4176
4545
|
"moduleFactories"
|
|
4177
4546
|
)
|
|
4178
4547
|
);
|
|
4179
4548
|
const reserved = new Set(
|
|
4180
|
-
requireOption(
|
|
4549
|
+
requireOption(RULE15, context.options?.[0]?.reserved ?? MODULES_2_19, "reserved")
|
|
4181
4550
|
);
|
|
4182
4551
|
const constants = /* @__PURE__ */ new Map();
|
|
4183
4552
|
const pending = [];
|
|
@@ -4369,7 +4738,7 @@ var isFactShaped = (literal) => {
|
|
|
4369
4738
|
if (/^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$/.test(literal)) return true;
|
|
4370
4739
|
return literal.length >= 16 && !/^[a-z0-9:\-[\]/.%#!]+$/.test(literal);
|
|
4371
4740
|
};
|
|
4372
|
-
var
|
|
4741
|
+
var nameOf2 = (node) => {
|
|
4373
4742
|
if (node === void 0) return void 0;
|
|
4374
4743
|
if (typeof node === "string") return node;
|
|
4375
4744
|
if (node.type === "Identifier" || node.type === "JSXIdentifier") return node.name;
|
|
@@ -4380,12 +4749,12 @@ var isSpelledAsStyleOrModule = (node) => {
|
|
|
4380
4749
|
let current = node.parent;
|
|
4381
4750
|
let child = node;
|
|
4382
4751
|
while (current && typeof current.type === "string" && current.type !== "Program") {
|
|
4383
|
-
if (current.type === "JSXAttribute") return styleAttributes.has(
|
|
4752
|
+
if (current.type === "JSXAttribute") return styleAttributes.has(nameOf2(current.name) ?? "");
|
|
4384
4753
|
if (current.type === "Property" && current.key === child) return true;
|
|
4385
|
-
if (current.type === "Property" && styleAttributes.has(
|
|
4754
|
+
if (current.type === "Property" && styleAttributes.has(nameOf2(current.key) ?? "")) return true;
|
|
4386
4755
|
if (current.type === "CallExpression") {
|
|
4387
4756
|
const callee = current.callee;
|
|
4388
|
-
const calleeName = callee?.type === "MemberExpression" ?
|
|
4757
|
+
const calleeName = callee?.type === "MemberExpression" ? nameOf2(callee.property) : nameOf2(callee);
|
|
4389
4758
|
if (styleCallees.has(calleeName ?? "") || formFieldCallees.has(calleeName ?? "")) return true;
|
|
4390
4759
|
const firstArgument = current.arguments?.[0];
|
|
4391
4760
|
if (declarationCallees.has(calleeName ?? "") && firstArgument === child) return true;
|
|
@@ -4473,12 +4842,12 @@ can be misspelled and a second default nobody agreed to.`,
|
|
|
4473
4842
|
var ssot_no_process_env_default = ssotNoProcessEnv;
|
|
4474
4843
|
|
|
4475
4844
|
// src/rules/step-declares-compensation-or-none.ts
|
|
4476
|
-
var
|
|
4845
|
+
var RULE16 = "step-declares-compensation-or-none";
|
|
4477
4846
|
var NO_COMPENSATION = "noCompensation";
|
|
4478
4847
|
var WITH_COMPENSATION = 3;
|
|
4479
4848
|
var stepDeclaresCompensationOrNone = {
|
|
4480
4849
|
create(context) {
|
|
4481
|
-
const factories = stepFactoriesOf(
|
|
4850
|
+
const factories = stepFactoriesOf(RULE16, context.options?.[0]?.stepFactories);
|
|
4482
4851
|
return {
|
|
4483
4852
|
CallExpression(node) {
|
|
4484
4853
|
if (!isStepCall(node, factories)) return;
|
|
@@ -4486,7 +4855,7 @@ var stepDeclaresCompensationOrNone = {
|
|
|
4486
4855
|
const config = configOf(node);
|
|
4487
4856
|
if (config.kind === "unreadable") return;
|
|
4488
4857
|
if (config.kind === "object") {
|
|
4489
|
-
const flag =
|
|
4858
|
+
const flag = valueOf(config.properties, NO_COMPENSATION);
|
|
4490
4859
|
if (flag?.type === "Literal" && flag.value === true) return;
|
|
4491
4860
|
}
|
|
4492
4861
|
context.report({ data: { step: stepNameOf(node, config) }, messageId: "undeclared", node });
|
|
@@ -4522,20 +4891,52 @@ variable \u2014 passes, since the flag may be in the base. Configure \`stepFacto
|
|
|
4522
4891
|
var step_declares_compensation_or_none_default = stepDeclaresCompensationOrNone;
|
|
4523
4892
|
|
|
4524
4893
|
// src/rules/step-opens-its-own-cell.ts
|
|
4525
|
-
var
|
|
4894
|
+
var RULE17 = "step-opens-its-own-cell";
|
|
4526
4895
|
var STEP = "step";
|
|
4896
|
+
var ACCEPTS4 = "{ within: string (required), hosts: string[], scopeKey: string, engine: string, wrapper: string }";
|
|
4897
|
+
var DEFAULT_HOSTS = ["createRuntime", "entrypointFor"];
|
|
4898
|
+
var DEFAULT_SCOPE_KEY = "perStep";
|
|
4527
4899
|
var readable = (pattern) => pattern.replaceAll("\\", "").replace(/\$$/, "");
|
|
4900
|
+
var isHostCall = (node, hosts) => {
|
|
4901
|
+
const callee = calleeNameOf(node);
|
|
4902
|
+
return callee !== null && hosts.has(callee);
|
|
4903
|
+
};
|
|
4904
|
+
var configOf2 = (node) => propertiesOf((node.arguments ?? [])[0]);
|
|
4905
|
+
var opaque = (call, hosts) => {
|
|
4906
|
+
let unreadable = false;
|
|
4907
|
+
walkNode(call, (inner) => {
|
|
4908
|
+
if (inner.type === "CallExpression" && isHostCall(inner, hosts) && configOf2(inner) === null) {
|
|
4909
|
+
unreadable = true;
|
|
4910
|
+
}
|
|
4911
|
+
});
|
|
4912
|
+
return unreadable;
|
|
4913
|
+
};
|
|
4914
|
+
var namesKey = (node, key) => {
|
|
4915
|
+
if (node.type !== "Property") return false;
|
|
4916
|
+
const named = node.key;
|
|
4917
|
+
return named?.type === "Identifier" && named.name === key || named?.type === "Literal" && named.value === key;
|
|
4918
|
+
};
|
|
4919
|
+
var declaresScope = (call, scopeKey) => {
|
|
4920
|
+
let declared = false;
|
|
4921
|
+
walkNode(call, (inner) => {
|
|
4922
|
+
if (namesKey(inner, scopeKey)) declared = true;
|
|
4923
|
+
});
|
|
4924
|
+
return declared;
|
|
4925
|
+
};
|
|
4528
4926
|
var stepOpensItsOwnCell = {
|
|
4529
4927
|
create(context) {
|
|
4530
|
-
const
|
|
4531
|
-
const
|
|
4532
|
-
const
|
|
4928
|
+
const bag = context.options?.[0] ?? {};
|
|
4929
|
+
const within = requireOption(RULE17, bag.within, "within", ACCEPTS4);
|
|
4930
|
+
const hosts = new Set(requireOption(RULE17, bag.hosts ?? DEFAULT_HOSTS, "hosts", ACCEPTS4));
|
|
4931
|
+
const scopeKey = requireOption(RULE17, bag.scopeKey ?? DEFAULT_SCOPE_KEY, "scopeKey", ACCEPTS4);
|
|
4932
|
+
const { engine, wrapper } = bag;
|
|
4533
4933
|
const filename = normalise(context.filename);
|
|
4534
4934
|
if (!new RegExp(within).test(filename)) return {};
|
|
4535
|
-
if (wrapper !== void 0 && new RegExp(wrapper).test(filename)) return {};
|
|
4536
4935
|
const file = filename.split("/").pop() ?? filename;
|
|
4936
|
+
const isWrapper = wrapper !== void 0 && new RegExp(wrapper).test(filename);
|
|
4537
4937
|
return {
|
|
4538
4938
|
ImportDeclaration(node) {
|
|
4939
|
+
if (engine === void 0 || isWrapper) return;
|
|
4539
4940
|
if (node.importKind === "type") return;
|
|
4540
4941
|
if (node.source.value !== engine) return;
|
|
4541
4942
|
for (const specifier of node.specifiers ?? []) {
|
|
@@ -4552,26 +4953,58 @@ var stepOpensItsOwnCell = {
|
|
|
4552
4953
|
node
|
|
4553
4954
|
});
|
|
4554
4955
|
}
|
|
4956
|
+
},
|
|
4957
|
+
Program(node) {
|
|
4958
|
+
const found = [];
|
|
4959
|
+
walkNode(node, (one) => {
|
|
4960
|
+
if (one.type === "CallExpression" && isHostCall(one, hosts)) found.push(one);
|
|
4961
|
+
});
|
|
4962
|
+
const nested = /* @__PURE__ */ new Set();
|
|
4963
|
+
for (const call of found) {
|
|
4964
|
+
walkNode(call, (inner) => {
|
|
4965
|
+
if (inner !== call && inner.type === "CallExpression" && isHostCall(inner, hosts)) {
|
|
4966
|
+
nested.add(inner);
|
|
4967
|
+
}
|
|
4968
|
+
});
|
|
4969
|
+
}
|
|
4970
|
+
for (const call of found) {
|
|
4971
|
+
if (nested.has(call)) continue;
|
|
4972
|
+
if (opaque(call, hosts)) continue;
|
|
4973
|
+
if (declaresScope(call, scopeKey)) continue;
|
|
4974
|
+
context.report({
|
|
4975
|
+
data: { file, host: calleeNameOf(call) ?? "a durable host", scopeKey },
|
|
4976
|
+
messageId: "unscopedHost",
|
|
4977
|
+
node: call
|
|
4978
|
+
});
|
|
4979
|
+
}
|
|
4555
4980
|
}
|
|
4556
4981
|
};
|
|
4557
4982
|
},
|
|
4558
|
-
fixShape: `Inside the guarded workflow tree,
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4983
|
+
fixShape: `Inside the guarded workflow tree, a durable host declares the scope its steps run in:
|
|
4984
|
+
\`createRuntime({ journal, scope: { perStep: () => withConnection(env) } })\`, and the adapter's entry
|
|
4985
|
+
(\`entrypointFor\`) declares it in the runtime it builds per run. A durable instance hibernates and
|
|
4986
|
+
retries between steps, so the connection a step uses has to be opened inside that step rather than
|
|
4987
|
+
shared across the run \u2014 \`perStep\` is asked for a scope per unit of work, which is what makes that
|
|
4988
|
+
possible. A host whose config cannot be read whole (a spread, a variable) is not judged.
|
|
4989
|
+
On \`sagaflow-js\` there is no such seam and the older shape stays reachable: set \`engine\` and
|
|
4990
|
+
\`wrapper\`, and import \`step\` from the repo's own wrapper rather than from the engine package.
|
|
4991
|
+
Configure \`within\`; \`hosts\`, \`scopeKey\`, \`engine\` and \`wrapper\` have defaults or are off. Enabled
|
|
4992
|
+
without \`within\` the rule refuses the run.`,
|
|
4563
4993
|
meta: {
|
|
4564
4994
|
docs: {
|
|
4565
|
-
description: "A durable
|
|
4995
|
+
description: "A durable host declares the scope its steps run in \u2014 `scope.perStep` on the runtime it builds \u2014 because a durable run hibernates and retries, so a handle opened once per run is dead by the time a later step uses it. A repo on an engine with no such seam names its `engine` and `wrapper` instead, and a saga importing the engine's `step` around that wrapper is the finding. The guarded tree is required; enabled without it the rule refuses the run."
|
|
4566
4996
|
},
|
|
4567
4997
|
messages: {
|
|
4568
|
-
engineStep: '"{{file}}" imports `step` from `{{engine}}`. Import it from {{wrapper}} instead: a durable run spans hibernation and retries, so the connection a step uses has to be opened inside that step rather than shared across steps. The wrapper opens it; the engine\'s `step` does not.'
|
|
4998
|
+
engineStep: '"{{file}}" imports `step` from `{{engine}}`. Import it from {{wrapper}} instead: a durable run spans hibernation and retries, so the connection a step uses has to be opened inside that step rather than shared across steps. The wrapper opens it; the engine\'s `step` does not.',
|
|
4999
|
+
unscopedHost: '"{{file}}" builds a durable host with `{{host}}` and declares no `{{scopeKey}}`. A durable instance hibernates between steps and replays them on retry, so a handle opened once per run is closed by the time the second step reaches for it \u2014 and every step here would share it. Declare `scope: {{ {{scopeKey}}: () => \u2026 }}` on the runtime this host builds, so each step body and each undo opens the one it uses.'
|
|
4569
5000
|
},
|
|
4570
5001
|
schema: [
|
|
4571
5002
|
{
|
|
4572
5003
|
additionalProperties: false,
|
|
4573
5004
|
properties: {
|
|
4574
5005
|
engine: { type: "string" },
|
|
5006
|
+
hosts: { items: { type: "string" }, type: "array" },
|
|
5007
|
+
scopeKey: { type: "string" },
|
|
4575
5008
|
within: { type: "string" },
|
|
4576
5009
|
wrapper: { type: "string" }
|
|
4577
5010
|
},
|
|
@@ -4581,18 +5014,23 @@ connection it uses rather than share one opened once per run \u2014 the wrapper
|
|
|
4581
5014
|
type: "problem"
|
|
4582
5015
|
},
|
|
4583
5016
|
/**
|
|
4584
|
-
*
|
|
4585
|
-
*
|
|
4586
|
-
*
|
|
4587
|
-
* moved to where the option points — and out of the `wrapper`, which is the one file allowed
|
|
4588
|
-
* to reach the engine.
|
|
5017
|
+
* The probe is built for the spelling the OPTIONS select. A repo that named an `engine` is on the
|
|
5018
|
+
* older one and is exercised by a saga reaching past its wrapper — which is also why the path has
|
|
5019
|
+
* to land inside `within` and outside `wrapper`, the one file allowed to make that import.
|
|
4589
5020
|
*/
|
|
4590
5021
|
probe(options) {
|
|
4591
|
-
const { engine
|
|
5022
|
+
const { engine, hosts, within = "", wrapper } = bagOf(options);
|
|
5023
|
+
if (engine === void 0) {
|
|
5024
|
+
return {
|
|
5025
|
+
path: pathMatching(within, "geonosis-probe-durable-host.ts"),
|
|
5026
|
+
source: `export const geonosisProbe = ${firstOf(hosts, DEFAULT_HOSTS)}({ journal: {} })
|
|
5027
|
+
`
|
|
5028
|
+
};
|
|
5029
|
+
}
|
|
4592
5030
|
const path2 = pathMatching(within, "geonosis-probe-engine-step.ts");
|
|
4593
5031
|
if (wrapper !== void 0 && new RegExp(wrapper).test(path2)) {
|
|
4594
5032
|
throw new Error(
|
|
4595
|
-
`biological-architecture/${
|
|
5033
|
+
`biological-architecture/${RULE17}: the probe path "${path2}" is also the \`wrapper\`, which is the one file allowed to reach \`${engine}\` \u2014 no single file inside \`within\` can both land where the rule looks and be somewhere it speaks.`
|
|
4596
5034
|
);
|
|
4597
5035
|
}
|
|
4598
5036
|
return {
|
|
@@ -4661,13 +5099,126 @@ belongs on the admin surface.`,
|
|
|
4661
5099
|
};
|
|
4662
5100
|
var store_route_scopes_tenant_data_default = storeRouteScopesTenantData;
|
|
4663
5101
|
|
|
5102
|
+
// src/rules/subpath-over-barrel.ts
|
|
5103
|
+
var RULE18 = "subpath-over-barrel";
|
|
5104
|
+
var ACCEPTS5 = "{ packages: { name: string, subpaths: { [subpath: string]: string[] } }[] (required) }";
|
|
5105
|
+
var homesOf = (declared) => {
|
|
5106
|
+
const subpaths = requireOption(RULE18, Object.keys(declared.subpaths ?? {}), "subpaths", ACCEPTS5);
|
|
5107
|
+
return new Map(
|
|
5108
|
+
subpaths.flatMap(
|
|
5109
|
+
(subpath) => (declared.subpaths?.[subpath] ?? []).map((name) => [name, subpath])
|
|
5110
|
+
)
|
|
5111
|
+
);
|
|
5112
|
+
};
|
|
5113
|
+
var importedNamesOf = (node) => {
|
|
5114
|
+
if (node.importKind === "type") return [];
|
|
5115
|
+
const specifiers = node.specifiers ?? [];
|
|
5116
|
+
if (specifiers.length === 0) return null;
|
|
5117
|
+
const names2 = [];
|
|
5118
|
+
for (const specifier of specifiers) {
|
|
5119
|
+
if (specifier.type !== "ImportSpecifier") return null;
|
|
5120
|
+
if (specifier.importKind === "type") continue;
|
|
5121
|
+
const imported = specifier.imported;
|
|
5122
|
+
if (imported?.type !== "Identifier" || typeof imported.name !== "string") return null;
|
|
5123
|
+
names2.push(imported.name);
|
|
5124
|
+
}
|
|
5125
|
+
return names2;
|
|
5126
|
+
};
|
|
5127
|
+
var subpathOverBarrel = {
|
|
5128
|
+
create(context) {
|
|
5129
|
+
const declared = requireOption(RULE18, context.options?.[0]?.packages, "packages", ACCEPTS5);
|
|
5130
|
+
const homes = new Map(
|
|
5131
|
+
declared.map((one) => [requireOption(RULE18, one.name, "name", ACCEPTS5), homesOf(one)])
|
|
5132
|
+
);
|
|
5133
|
+
return {
|
|
5134
|
+
ImportDeclaration(node) {
|
|
5135
|
+
const source = node.source?.value;
|
|
5136
|
+
if (typeof source !== "string") return;
|
|
5137
|
+
const home = homes.get(source);
|
|
5138
|
+
if (home === void 0) return;
|
|
5139
|
+
const names2 = importedNamesOf(node);
|
|
5140
|
+
if (names2 === null || names2.length === 0) return;
|
|
5141
|
+
const reached = new Set(names2.map((name) => home.get(name)));
|
|
5142
|
+
if (reached.size !== 1 || reached.has(void 0)) return;
|
|
5143
|
+
const [only] = [...reached];
|
|
5144
|
+
context.report({
|
|
5145
|
+
data: { package: source, subpath: `${source}/${only ?? ""}` },
|
|
5146
|
+
messageId: "rootBarrel",
|
|
5147
|
+
node
|
|
5148
|
+
});
|
|
5149
|
+
}
|
|
5150
|
+
};
|
|
5151
|
+
},
|
|
5152
|
+
fixShape: `Import from the SUBPATH that owns the names, not from the package root:
|
|
5153
|
+
\`import { Document } from '@repo/domains/document'\`. A subpath exists because its dependency
|
|
5154
|
+
footprint differs from the root's, so the root entry is the union of them all and a file that names
|
|
5155
|
+
it to reach one pays for every other \u2014 at install, at bundle and at worker start, with nothing to
|
|
5156
|
+
show for it. An import whose names span two subpaths is what the root is for and is left alone, as
|
|
5157
|
+
is a type-only import, which erases before any of that happens. Declare the map in \`packages\`
|
|
5158
|
+
(\`{ name, subpaths: { document: ['Document', \u2026] } }\`); enabled without it the rule refuses the run.`,
|
|
5159
|
+
meta: {
|
|
5160
|
+
docs: {
|
|
5161
|
+
description: "A root import whose names all live under one of the package's declared subpaths names the subpath instead. The root entry is the union of every subpath's module graph, so reaching one through it pays for all of them. Names spanning two subpaths, and type-only imports, are exempt. The package\u2192subpath map is a required option."
|
|
5162
|
+
},
|
|
5163
|
+
messages: {
|
|
5164
|
+
rootBarrel: "Every name imported here from `{{package}}` lives under `{{subpath}}`. Import from the subpath: the root entry re-exports all of them, so this file takes the union of every subpath\u2019s module graph to reach one of them."
|
|
5165
|
+
},
|
|
5166
|
+
schema: [
|
|
5167
|
+
{
|
|
5168
|
+
additionalProperties: false,
|
|
5169
|
+
properties: {
|
|
5170
|
+
packages: {
|
|
5171
|
+
items: {
|
|
5172
|
+
additionalProperties: false,
|
|
5173
|
+
properties: {
|
|
5174
|
+
name: { type: "string" },
|
|
5175
|
+
subpaths: {
|
|
5176
|
+
additionalProperties: {
|
|
5177
|
+
items: { type: "string" },
|
|
5178
|
+
type: "array"
|
|
5179
|
+
},
|
|
5180
|
+
type: "object"
|
|
5181
|
+
}
|
|
5182
|
+
},
|
|
5183
|
+
type: "object"
|
|
5184
|
+
},
|
|
5185
|
+
type: "array"
|
|
5186
|
+
}
|
|
5187
|
+
},
|
|
5188
|
+
type: "object"
|
|
5189
|
+
}
|
|
5190
|
+
],
|
|
5191
|
+
type: "problem"
|
|
5192
|
+
},
|
|
5193
|
+
/** One import of one declared name, at the root — the smallest shape that must fire. */
|
|
5194
|
+
probe(options) {
|
|
5195
|
+
const [first] = bagOf(options).packages ?? [];
|
|
5196
|
+
const name = first?.name ?? "";
|
|
5197
|
+
const [subpath] = Object.keys(first?.subpaths ?? {});
|
|
5198
|
+
const [exported] = first?.subpaths?.[subpath ?? ""] ?? [];
|
|
5199
|
+
if (exported === void 0) {
|
|
5200
|
+
throw new Error(
|
|
5201
|
+
`biological-architecture/${RULE18}: the configured packages declare no subpath with a name in it, so there is nothing a probe could import from the root.`
|
|
5202
|
+
);
|
|
5203
|
+
}
|
|
5204
|
+
return {
|
|
5205
|
+
path: "geonosis-probe-subpath.ts",
|
|
5206
|
+
source: `import { ${exported} } from '${name}'
|
|
5207
|
+
|
|
5208
|
+
export const geonosisProbe = ${exported}
|
|
5209
|
+
`
|
|
5210
|
+
};
|
|
5211
|
+
}
|
|
5212
|
+
};
|
|
5213
|
+
var subpath_over_barrel_default = subpathOverBarrel;
|
|
5214
|
+
|
|
4664
5215
|
// src/rules/subscriber-declares-id.ts
|
|
4665
|
-
var
|
|
5216
|
+
var RULE19 = "subscriber-declares-id";
|
|
4666
5217
|
var DEFAULT_PATHS2 = ["(?:^|/)subscribers/"];
|
|
4667
5218
|
var CONFIG = "config";
|
|
4668
5219
|
var CONTEXT = "context";
|
|
4669
5220
|
var SUBSCRIBER_ID = "subscriberId";
|
|
4670
|
-
var
|
|
5221
|
+
var declaredName2 = (node) => {
|
|
4671
5222
|
const declaration = node.declaration;
|
|
4672
5223
|
if (declaration?.type !== "VariableDeclaration") return null;
|
|
4673
5224
|
const [first] = declaration.declarations ?? [];
|
|
@@ -4676,7 +5227,7 @@ var declaredName = (node) => {
|
|
|
4676
5227
|
};
|
|
4677
5228
|
var subscriberDeclaresId = {
|
|
4678
5229
|
create(context) {
|
|
4679
|
-
const paths = requireOption(
|
|
5230
|
+
const paths = requireOption(RULE19, context.options?.[0]?.paths ?? DEFAULT_PATHS2, "paths").map(
|
|
4680
5231
|
(source) => new RegExp(source)
|
|
4681
5232
|
);
|
|
4682
5233
|
const filename = normalise(context.filename);
|
|
@@ -4684,13 +5235,13 @@ var subscriberDeclaresId = {
|
|
|
4684
5235
|
const file = filename.split("/").slice(-2).join("/") || fileOf(filename);
|
|
4685
5236
|
return {
|
|
4686
5237
|
ExportNamedDeclaration(node) {
|
|
4687
|
-
if (
|
|
5238
|
+
if (declaredName2(node) !== CONFIG) return;
|
|
4688
5239
|
const [first] = node.declaration.declarations ?? [];
|
|
4689
|
-
const properties =
|
|
5240
|
+
const properties = propertiesOf(first?.init);
|
|
4690
5241
|
if (properties === null) return;
|
|
4691
|
-
const inner =
|
|
4692
|
-
if (inner !== null &&
|
|
4693
|
-
if (
|
|
5242
|
+
const inner = propertiesOf(valueOf(properties, CONTEXT));
|
|
5243
|
+
if (inner !== null && valueOf(inner, SUBSCRIBER_ID) !== void 0) return;
|
|
5244
|
+
if (valueOf(properties, CONTEXT) !== void 0 && inner === null) return;
|
|
4694
5245
|
context.report({ data: { file }, messageId: "inferred", node });
|
|
4695
5246
|
}
|
|
4696
5247
|
};
|
|
@@ -4799,8 +5350,10 @@ table to \`schema/control/\`.`,
|
|
|
4799
5350
|
var tenant_tables_carry_org_id_default = tenantTablesCarryOrgId;
|
|
4800
5351
|
|
|
4801
5352
|
// src/rules/time-through-the-door.ts
|
|
4802
|
-
var
|
|
4803
|
-
var
|
|
5353
|
+
var RULE20 = "time-through-the-door";
|
|
5354
|
+
var ACCEPTS6 = "{ door: string (a regex over the filename), exempt: string[] (regex sources, default the test paths: __tests__/, .test., .spec.), libraries: string[] (default ['dayjs']) }";
|
|
5355
|
+
var DEFAULT_LIBRARIES = ["dayjs"];
|
|
5356
|
+
var DEFAULT_EXEMPT = ["__tests__/", String.raw`\.test\.`, String.raw`\.spec\.`];
|
|
4804
5357
|
var dateStatics = /* @__PURE__ */ new Set(["parse", "UTC"]);
|
|
4805
5358
|
var dateInstanceMethods = /* @__PURE__ */ new Set([
|
|
4806
5359
|
"toISOString",
|
|
@@ -4822,19 +5375,37 @@ var memberName = (callee) => ({
|
|
|
4822
5375
|
object: callee.object?.type === "Identifier" ? callee.object.name : void 0,
|
|
4823
5376
|
property: callee.property?.type === "Identifier" ? callee.property.name : void 0
|
|
4824
5377
|
});
|
|
5378
|
+
var readable2 = (pattern) => pattern.replaceAll("\\", "").replace(/\$$/, "");
|
|
4825
5379
|
var timeThroughTheDoor = {
|
|
4826
5380
|
create(context) {
|
|
4827
|
-
const
|
|
4828
|
-
|
|
5381
|
+
const door = requireOption(RULE20, context.options?.[0]?.door, "door", ACCEPTS6);
|
|
5382
|
+
const exempt = context.options?.[0]?.exempt ?? DEFAULT_EXEMPT;
|
|
5383
|
+
const libraries = requireOption(
|
|
5384
|
+
RULE20,
|
|
5385
|
+
context.options?.[0]?.libraries ?? DEFAULT_LIBRARIES,
|
|
5386
|
+
"libraries",
|
|
5387
|
+
ACCEPTS6
|
|
5388
|
+
);
|
|
5389
|
+
const filename = normalise(context.filename);
|
|
5390
|
+
if (isDoor(filename, door)) return {};
|
|
5391
|
+
if (exempt.some((pattern) => new RegExp(pattern).test(filename))) return {};
|
|
5392
|
+
const where = readable2(door);
|
|
4829
5393
|
return {
|
|
4830
5394
|
ImportDeclaration(node) {
|
|
4831
|
-
|
|
4832
|
-
|
|
5395
|
+
const library = libraries.find(
|
|
5396
|
+
(one) => node.source.value === one || node.source.value.startsWith(`${one}/`)
|
|
5397
|
+
);
|
|
5398
|
+
if (library !== void 0) {
|
|
5399
|
+
context.report({ data: { door: where, library }, messageId: "dayjsOutsideDoor", node });
|
|
4833
5400
|
}
|
|
4834
5401
|
},
|
|
4835
5402
|
NewExpression(node) {
|
|
4836
5403
|
if (node.callee.type === "Identifier" && node.callee.name === "Date") {
|
|
4837
|
-
context.report({
|
|
5404
|
+
context.report({
|
|
5405
|
+
data: { door: where, what: "new Date" },
|
|
5406
|
+
messageId: "outsideTimeDoor",
|
|
5407
|
+
node
|
|
5408
|
+
});
|
|
4838
5409
|
}
|
|
4839
5410
|
},
|
|
4840
5411
|
CallExpression(node) {
|
|
@@ -4844,27 +5415,76 @@ var timeThroughTheDoor = {
|
|
|
4844
5415
|
if (object === "Date" && property === "now") return;
|
|
4845
5416
|
if (object === "Date" && dateStatics.has(property) || dateInstanceMethods.has(property)) {
|
|
4846
5417
|
context.report({
|
|
5418
|
+
data: { door: where, what: `${object ?? "\u2026"}.${property}` },
|
|
4847
5419
|
messageId: "outsideTimeDoor",
|
|
4848
|
-
node
|
|
4849
|
-
data: { what: `${object ?? "\u2026"}.${property}` }
|
|
5420
|
+
node
|
|
4850
5421
|
});
|
|
4851
5422
|
}
|
|
4852
5423
|
}
|
|
4853
5424
|
};
|
|
4854
5425
|
},
|
|
4855
|
-
fixShape: `\`new Date(...)\`, \`Date.parse\`, \`toISOString\`, \`toLocaleDateString
|
|
4856
|
-
goes through the one time module
|
|
4857
|
-
\`isoDate\` / \`today\` / \`addDays\` from it
|
|
5426
|
+
fixShape: `\`new Date(...)\`, \`Date.parse\`, \`toISOString\`, \`toLocaleDateString\` and the repo's date
|
|
5427
|
+
library \u2014 every time operation goes through the one time module the \`door\` option names, which owns
|
|
5428
|
+
the plugins, the zone and the formats. Import \`isoDate\` / \`today\` / \`addDays\` from it, or add the
|
|
5429
|
+
operation there. A second module that imports the library is a second configured library: plugin
|
|
5430
|
+
state is global, so the two disagree the moment one of them extends something the other did not.
|
|
5431
|
+
Name the library in \`libraries\` (default \`['dayjs']\`) \u2014 a repo on date-fns or luxon says so and
|
|
5432
|
+
gets the whole rule rather than its raw-\`Date\` half.
|
|
5433
|
+
|
|
5434
|
+
Three kinds of time, never mixed. An INSTANT is epoch milliseconds. A BUSINESS DATE is an ISO date
|
|
5435
|
+
string, \`YYYY-MM-DD\`, parsed strictly in UTC \u2014 a calendar day is not an instant and rendering one as
|
|
5436
|
+
one moves it by a timezone. DISPLAY is a rendering: a formatter belongs at the edge, in the
|
|
5437
|
+
component or the response, never in a query, a saga or a stored value.
|
|
5438
|
+
|
|
5439
|
+
The clock is injected, never ambient. Take \`nowMs: number\` as a parameter and let the composition
|
|
5440
|
+
root pass \`Date.now()\` in \u2014 \`Date.now()\` is the one call allowed anywhere, and a function that reads
|
|
5441
|
+
it internally cannot be tested at a chosen instant.`,
|
|
4858
5442
|
meta: {
|
|
4859
5443
|
docs: {
|
|
4860
|
-
description: "Time is handled
|
|
5444
|
+
description: "Time is handled behind one module \u2014 the `door` option names it \u2014 and nothing else constructs, parses or formats a Date or imports dayjs. Date.now() is the one call allowed everywhere. Enabled without a `door` it refuses the run."
|
|
4861
5445
|
},
|
|
4862
5446
|
messages: {
|
|
4863
|
-
outsideTimeDoor: "{{what}} outside the time door. Import the operation from
|
|
4864
|
-
dayjsOutsideDoor: "
|
|
5447
|
+
outsideTimeDoor: "{{what}} outside the time door. Import the operation from `{{door}}` (isoDate, today, addDays, addMonths, \u2026) or add it there.",
|
|
5448
|
+
dayjsOutsideDoor: "`{{library}}` is imported outside the time door. `{{door}}` owns the plugins, the zone and the formats; import from it \u2014 a second configured instance of the library diverges from the first with no symptom."
|
|
4865
5449
|
},
|
|
4866
|
-
schema: [
|
|
5450
|
+
schema: [
|
|
5451
|
+
{
|
|
5452
|
+
additionalProperties: false,
|
|
5453
|
+
properties: {
|
|
5454
|
+
door: { type: "string" },
|
|
5455
|
+
exempt: { items: { type: "string" }, type: "array" },
|
|
5456
|
+
libraries: { items: { type: "string" }, type: "array" }
|
|
5457
|
+
},
|
|
5458
|
+
type: "object"
|
|
5459
|
+
}
|
|
5460
|
+
],
|
|
4867
5461
|
type: "problem"
|
|
5462
|
+
},
|
|
5463
|
+
/**
|
|
5464
|
+
* A file OUTSIDE the door, which is where this rule speaks — the inverse of every other probe
|
|
5465
|
+
* here, whose options say where to look rather than where not to.
|
|
5466
|
+
*
|
|
5467
|
+
* The candidates are tried against the repo's own `door` and `exempt` rather than assumed clear:
|
|
5468
|
+
* a repo whose door pattern is broad enough to swallow all of them has configured a rule that
|
|
5469
|
+
* can never fire, and a probe that reported a plant it had not checked would call that green.
|
|
5470
|
+
*/
|
|
5471
|
+
probe(options) {
|
|
5472
|
+
const { door = "", exempt = [], libraries = DEFAULT_LIBRARIES } = bagOf(options);
|
|
5473
|
+
const outside = ["geonosis-probe-clock.ts", "src/geonosis-probe-clock.ts"].find(
|
|
5474
|
+
(path2) => !new RegExp(door).test(path2) && !exempt.some((pattern) => new RegExp(pattern).test(path2))
|
|
5475
|
+
);
|
|
5476
|
+
if (outside === void 0) {
|
|
5477
|
+
throw new Error(
|
|
5478
|
+
`biological-architecture/${RULE20}: every path a probe could be written at is inside the \`door\` (${door}) or one of its \`exempt\` patterns, so there is nowhere left for this rule to speak.`
|
|
5479
|
+
);
|
|
5480
|
+
}
|
|
5481
|
+
return {
|
|
5482
|
+
path: outside,
|
|
5483
|
+
source: `import theClock from '${libraries[0] ?? DEFAULT_LIBRARIES[0]}'
|
|
5484
|
+
|
|
5485
|
+
export const clock = (): unknown => theClock
|
|
5486
|
+
`
|
|
5487
|
+
};
|
|
4868
5488
|
}
|
|
4869
5489
|
};
|
|
4870
5490
|
var time_through_the_door_default = timeThroughTheDoor;
|
|
@@ -5263,6 +5883,83 @@ the tissue arrange that cell.`,
|
|
|
5263
5883
|
});
|
|
5264
5884
|
var tissue_no_stores_default = tissueNoStores;
|
|
5265
5885
|
|
|
5886
|
+
// src/rules/todo-names-its-plan.ts
|
|
5887
|
+
var ID_DEFAULT = String.raw`\d{3,}`;
|
|
5888
|
+
var commentsOf3 = (sourceCode) => sourceCode?.getAllComments?.() ?? sourceCode?.ast?.comments ?? [];
|
|
5889
|
+
var escapeForRegex3 = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5890
|
+
var todoNamesItsPlan = {
|
|
5891
|
+
create(context) {
|
|
5892
|
+
const { id = ID_DEFAULT, markers = [] } = context.options?.[0] ?? {};
|
|
5893
|
+
requireOption("todo-names-its-plan", markers, "markers", "{ markers: string[], id?: string }");
|
|
5894
|
+
const marker = new RegExp(
|
|
5895
|
+
`\\b(${markers.map(escapeForRegex3).join("|")})\\b(?:\\(([^)]*)\\))?`,
|
|
5896
|
+
"g"
|
|
5897
|
+
);
|
|
5898
|
+
const names2 = new RegExp(`^${id}$`);
|
|
5899
|
+
return {
|
|
5900
|
+
Program(node) {
|
|
5901
|
+
for (const comment of commentsOf3(context.sourceCode)) {
|
|
5902
|
+
for (const found of comment.value.matchAll(marker)) {
|
|
5903
|
+
const cited = found[2];
|
|
5904
|
+
if (cited !== void 0 && names2.test(cited)) continue;
|
|
5905
|
+
context.report({
|
|
5906
|
+
data: {
|
|
5907
|
+
line: String(comment.loc.start.line),
|
|
5908
|
+
marker: found[1] ?? ""
|
|
5909
|
+
},
|
|
5910
|
+
loc: comment.loc,
|
|
5911
|
+
messageId: "unplannedTodo",
|
|
5912
|
+
node
|
|
5913
|
+
});
|
|
5914
|
+
}
|
|
5915
|
+
}
|
|
5916
|
+
}
|
|
5917
|
+
};
|
|
5918
|
+
},
|
|
5919
|
+
fixShape: `A debt marker names the plan that owns it: \`// TODO(025): the cache lands with the batteries floor\`.
|
|
5920
|
+
Write the plan number of the plan whose acceptance criterion this work belongs to \u2014 \`geonosis-ledger
|
|
5921
|
+
plan new <slug>\` makes one, and \`plan ready\` is what reasons over them. A marker with no plan is a
|
|
5922
|
+
promise with no owner and no date, and nothing downstream can schedule it. If the work is not worth a
|
|
5923
|
+
plan, delete the marker and the code it apologises for. Configure \`markers\` with the words this repo
|
|
5924
|
+
treats as debt (TODO, FIXME, HACK\u2026) and \`id\` with the shape of a plan id if it is not \`NNN\`;
|
|
5925
|
+
enabled with no \`markers\` the rule refuses the run, because a marker rule that knows no markers
|
|
5926
|
+
reads exactly like a clean tree.`,
|
|
5927
|
+
meta: {
|
|
5928
|
+
docs: {
|
|
5929
|
+
description: "A debt marker (TODO, FIXME, \u2026) must name the plan that owns it \u2014 `TODO(025):`. The marker words are an option and the plan-id shape is an option; enabled with no markers the rule refuses the run. Comments come from the parser, so the word in a string literal is not a finding."
|
|
5930
|
+
},
|
|
5931
|
+
messages: {
|
|
5932
|
+
unplannedTodo: "{{marker}} at line {{line}} names no plan. Write `{{marker}}(NNN):` with the plan that owns this work, or delete it \u2014 a marker outside the plan graph is work nobody scheduled."
|
|
5933
|
+
},
|
|
5934
|
+
schema: [
|
|
5935
|
+
{
|
|
5936
|
+
additionalProperties: false,
|
|
5937
|
+
properties: {
|
|
5938
|
+
id: { type: "string" },
|
|
5939
|
+
markers: { items: { type: "string" }, type: "array" }
|
|
5940
|
+
},
|
|
5941
|
+
type: "object"
|
|
5942
|
+
}
|
|
5943
|
+
],
|
|
5944
|
+
type: "problem"
|
|
5945
|
+
},
|
|
5946
|
+
/**
|
|
5947
|
+
* The repo's OWN first marker, in a comment, citing nothing. It has to be the configured word:
|
|
5948
|
+
* a probe hardcoding one spelling would report the rule as unreachable in every repo that spells
|
|
5949
|
+
* its debt with another, which is the whole class of finding probes exist to end.
|
|
5950
|
+
*/
|
|
5951
|
+
probe(options) {
|
|
5952
|
+
const { markers } = bagOf(options);
|
|
5953
|
+
return {
|
|
5954
|
+
path: "geonosis-probe-todo.ts",
|
|
5955
|
+
source: `// ${firstOf(markers, ["TODO"])}: a probe that names no plan
|
|
5956
|
+
export const geonosisProbeTodo = 1
|
|
5957
|
+
`
|
|
5958
|
+
};
|
|
5959
|
+
}
|
|
5960
|
+
};
|
|
5961
|
+
var todo_names_its_plan_default = todoNamesItsPlan;
|
|
5962
|
+
|
|
5266
5963
|
// src/rules/lib/colors.ts
|
|
5267
5964
|
var FUNCTIONS = [
|
|
5268
5965
|
"color",
|
|
@@ -5602,9 +6299,9 @@ var isNamedOnly = (value) => /^[a-z]+$/i.test(value.trim());
|
|
|
5602
6299
|
var tokens_only_colors_default = tokensOnlyColors;
|
|
5603
6300
|
|
|
5604
6301
|
// src/rules/unique-query-step-name.ts
|
|
5605
|
-
var
|
|
5606
|
-
var
|
|
5607
|
-
var
|
|
6302
|
+
var RULE21 = "unique-query-step-name";
|
|
6303
|
+
var ACCEPTS7 = "{ queryStepCalls: string[] (required), workflowFactories: string[] }";
|
|
6304
|
+
var DEFAULT_WORKFLOW_FACTORIES2 = ["createWorkflow"];
|
|
5608
6305
|
var CONFIG2 = "config";
|
|
5609
6306
|
var NAME = "name";
|
|
5610
6307
|
var COLLIDES_FROM = 2;
|
|
@@ -5623,9 +6320,9 @@ var chainedName = (node, steps) => {
|
|
|
5623
6320
|
const inner = callee.object;
|
|
5624
6321
|
if (inner === void 0 || !isCallOf(inner, steps)) return null;
|
|
5625
6322
|
const [first] = node.arguments ?? [];
|
|
5626
|
-
const properties =
|
|
6323
|
+
const properties = propertiesOf(first);
|
|
5627
6324
|
if (properties === null) return [inner, UNREADABLE];
|
|
5628
|
-
const value =
|
|
6325
|
+
const value = valueOf(properties, NAME);
|
|
5629
6326
|
if (value === void 0) return [inner, ABSENT];
|
|
5630
6327
|
const literal = stringOf(value);
|
|
5631
6328
|
return [inner, literal === null ? UNREADABLE : { kind: "named", value: literal }];
|
|
@@ -5646,17 +6343,13 @@ var uniqueQueryStepName = {
|
|
|
5646
6343
|
create(context) {
|
|
5647
6344
|
const factories = new Set(
|
|
5648
6345
|
requireOption(
|
|
5649
|
-
|
|
5650
|
-
context.options?.[0]?.workflowFactories ??
|
|
6346
|
+
RULE21,
|
|
6347
|
+
context.options?.[0]?.workflowFactories ?? DEFAULT_WORKFLOW_FACTORIES2,
|
|
5651
6348
|
"workflowFactories"
|
|
5652
6349
|
)
|
|
5653
6350
|
);
|
|
5654
6351
|
const calls = new Set(
|
|
5655
|
-
requireOption(
|
|
5656
|
-
RULE16,
|
|
5657
|
-
context.options?.[0]?.queryStepCalls ?? DEFAULT_QUERY_STEP_CALLS,
|
|
5658
|
-
"queryStepCalls"
|
|
5659
|
-
)
|
|
6352
|
+
requireOption(RULE21, context.options?.[0]?.queryStepCalls, "queryStepCalls", ACCEPTS7)
|
|
5660
6353
|
);
|
|
5661
6354
|
return {
|
|
5662
6355
|
CallExpression(node) {
|
|
@@ -5690,11 +6383,12 @@ var uniqueQueryStepName = {
|
|
|
5690
6383
|
\`useQueryGraphStep({ entity: 'order' }).config({ name: 'get-orders' })\`. Without it the step-handler
|
|
5691
6384
|
map last-wins and one of the two reads silently never happens. A single un-named step is fine \u2014 it
|
|
5692
6385
|
collides with nothing. A name held in a constant passes, since the rule cannot evaluate it.
|
|
5693
|
-
|
|
5694
|
-
|
|
6386
|
+
Name the repo's query steps in \`queryStepCalls\` \u2014 enabled without it the rule refuses the run,
|
|
6387
|
+
because the collision it reports is one framework's step-handler map and not every engine's \u2014 and
|
|
6388
|
+
\`workflowFactories\` if the repo wraps \`createWorkflow\`.`,
|
|
5695
6389
|
meta: {
|
|
5696
6390
|
docs: {
|
|
5697
|
-
description: "Every query step in a workflow body that holds more than one carries a unique `.config({ name })`. Medusa's step-handler map last-wins, so two un-named or same-named steps silently collide. A single un-named step is exempt. `workflowFactories`
|
|
6391
|
+
description: "Every query step in a workflow body that holds more than one carries a unique `.config({ name })`. Medusa's step-handler map last-wins, so two un-named or same-named steps silently collide. A single un-named step is exempt. `workflowFactories` is an option; enabled without `queryStepCalls` it refuses the run."
|
|
5698
6392
|
},
|
|
5699
6393
|
messages: {
|
|
5700
6394
|
duplicate: "Two query steps in this workflow both answer to `.config({ name: '{{name}}' })`. The step-handler map last-wins, so one of the two reads silently never happens \u2014 give each its own name.",
|
|
@@ -5719,8 +6413,8 @@ wraps the query steps.`,
|
|
|
5719
6413
|
*/
|
|
5720
6414
|
probe(options) {
|
|
5721
6415
|
const bag = bagOf(options);
|
|
5722
|
-
const factory = firstOf(bag.workflowFactories,
|
|
5723
|
-
const step = firstOf(bag.queryStepCalls,
|
|
6416
|
+
const factory = firstOf(bag.workflowFactories, DEFAULT_WORKFLOW_FACTORIES2);
|
|
6417
|
+
const step = firstOf(bag.queryStepCalls, []);
|
|
5724
6418
|
return {
|
|
5725
6419
|
path: "geonosis-probe-query-steps.ts",
|
|
5726
6420
|
source: `export const geonosisProbe = ${factory}('geonosis-probe', () => {
|
|
@@ -5736,7 +6430,8 @@ wraps the query steps.`,
|
|
|
5736
6430
|
var unique_query_step_name_default = uniqueQueryStepName;
|
|
5737
6431
|
|
|
5738
6432
|
// src/rules/worker-handles-are-scoped.ts
|
|
5739
|
-
var
|
|
6433
|
+
var RULE22 = "worker-handles-are-scoped";
|
|
6434
|
+
var ACCEPTS8 = "{ within: string (a regex over the filename), wrapper: string[] }";
|
|
5740
6435
|
var handlerKeys = /* @__PURE__ */ new Set(["fetch", "queue", "scheduled", "email"]);
|
|
5741
6436
|
var entrypointMakers = /* @__PURE__ */ new Set([
|
|
5742
6437
|
"WorkflowEntrypoint",
|
|
@@ -5745,8 +6440,10 @@ var entrypointMakers = /* @__PURE__ */ new Set([
|
|
|
5745
6440
|
]);
|
|
5746
6441
|
var cellHandlesAreScoped = {
|
|
5747
6442
|
create(context) {
|
|
5748
|
-
const
|
|
5749
|
-
|
|
6443
|
+
const within = requireOption(RULE22, context.options?.[0]?.within, "within", ACCEPTS8);
|
|
6444
|
+
const wrappers = requireOption(RULE22, context.options?.[0]?.wrapper, "wrapper", ACCEPTS8);
|
|
6445
|
+
if (!new RegExp(within).test(normalise(context.filename))) return {};
|
|
6446
|
+
const scopedBy = new Set(wrappers);
|
|
5750
6447
|
let handler = null;
|
|
5751
6448
|
let handlerNode = null;
|
|
5752
6449
|
let scoped = false;
|
|
@@ -5760,7 +6457,7 @@ var cellHandlesAreScoped = {
|
|
|
5760
6457
|
},
|
|
5761
6458
|
CallExpression(node) {
|
|
5762
6459
|
if (node.callee?.type !== "Identifier") return;
|
|
5763
|
-
if (node.callee.name
|
|
6460
|
+
if (node.callee.name !== void 0 && scopedBy.has(node.callee.name)) scoped = true;
|
|
5764
6461
|
if (node.callee.name === "createWorkflowEntrypoint") {
|
|
5765
6462
|
handler = "the workflow entrypoint";
|
|
5766
6463
|
handlerNode = node;
|
|
@@ -5777,26 +6474,48 @@ var cellHandlesAreScoped = {
|
|
|
5777
6474
|
"Program:exit"(node) {
|
|
5778
6475
|
if (handler === null || scoped) return;
|
|
5779
6476
|
context.report({
|
|
5780
|
-
data: { handler },
|
|
6477
|
+
data: { handler, wrapper: wrappers[0] ?? "" },
|
|
5781
6478
|
messageId: "handlerOutsideWithCells",
|
|
5782
6479
|
node: handlerNode ?? node
|
|
5783
6480
|
});
|
|
5784
6481
|
}
|
|
5785
6482
|
};
|
|
5786
6483
|
},
|
|
5787
|
-
fixShape: `A worker that opens a database-cell handle wraps every invocation in
|
|
5788
|
-
handlers (\`fetch\`, \`queue\`, \`scheduled\`, \`email\`) and the workflow entrypoint alike \u2014 so the
|
|
5789
|
-
is closed when the invocation ends: \`withCells(() => \u2026, (closing) => ctx.waitUntil(closing))\`.
|
|
5790
|
-
handler that declares itself and never calls it opens a pool per call and never closes one
|
|
6484
|
+
fixShape: `A worker that opens a database-cell handle wraps every invocation in the repo's scope wrapper \u2014 the
|
|
6485
|
+
module handlers (\`fetch\`, \`queue\`, \`scheduled\`, \`email\`) and the workflow entrypoint alike \u2014 so the
|
|
6486
|
+
handle is closed when the invocation ends: \`withCells(() => \u2026, (closing) => ctx.waitUntil(closing))\`.
|
|
6487
|
+
A handler that declares itself and never calls it opens a pool per call and never closes one. Name
|
|
6488
|
+
the wrapper in \`wrapper\` and the tree the cell workers live in in \`within\`; enabled without either
|
|
6489
|
+
the rule refuses the run.`,
|
|
5791
6490
|
meta: {
|
|
5792
6491
|
docs: {
|
|
5793
|
-
description: "Every invocation of a cell worker \u2014 the module handlers and the Workflow entrypoint \u2014 runs inside `
|
|
6492
|
+
description: "Every invocation of a cell worker \u2014 the module handlers and the Workflow entrypoint \u2014 runs inside the scope wrapper the `wrapper` option names, which shares one cell handle per invocation and closes it when the invocation ends. A file that declares such an invocation and never calls it opens a pool it never closes. The wrapper and the guarded tree are options; enabled without them it refuses the run."
|
|
5794
6493
|
},
|
|
5795
6494
|
messages: {
|
|
5796
|
-
handlerOutsideWithCells: "This file declares {{handler}} and never calls `
|
|
6495
|
+
handlerOutsideWithCells: "This file declares {{handler}} and never calls `{{wrapper}}`. Wrap the invocation \u2014 `{{wrapper}}(() => \u2026, (closing) => ctx.waitUntil(closing))` \u2014 so the cell handle it opens is closed when it ends."
|
|
5797
6496
|
},
|
|
5798
|
-
schema: [
|
|
6497
|
+
schema: [
|
|
6498
|
+
{
|
|
6499
|
+
additionalProperties: false,
|
|
6500
|
+
properties: {
|
|
6501
|
+
within: { type: "string" },
|
|
6502
|
+
wrapper: { items: { type: "string" }, type: "array" }
|
|
6503
|
+
},
|
|
6504
|
+
type: "object"
|
|
6505
|
+
}
|
|
6506
|
+
],
|
|
5799
6507
|
type: "problem"
|
|
6508
|
+
},
|
|
6509
|
+
/** A module handler inside the configured tree that never opens a scope — the smallest breach. */
|
|
6510
|
+
probe(options) {
|
|
6511
|
+
const { within = "" } = bagOf(options);
|
|
6512
|
+
return {
|
|
6513
|
+
path: pathMatching(within, "geonosis-probe-worker.ts"),
|
|
6514
|
+
source: `export default {
|
|
6515
|
+
queue: async (): Promise<void> => undefined,
|
|
6516
|
+
}
|
|
6517
|
+
`
|
|
6518
|
+
};
|
|
5800
6519
|
}
|
|
5801
6520
|
};
|
|
5802
6521
|
var worker_handles_are_scoped_default = cellHandlesAreScoped;
|
|
@@ -5893,80 +6612,105 @@ change. One field: \`useStore((s) => s.field)\`. Several fields: ONE call with
|
|
|
5893
6612
|
var zustand_v5_best_practices_default = zustandV5BestPractices;
|
|
5894
6613
|
|
|
5895
6614
|
// src/index.ts
|
|
5896
|
-
var
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
"molecule-atoms-only": molecule_atoms_only_default,
|
|
5915
|
-
"molecule-must-compose": molecule_must_compose_default,
|
|
5916
|
-
"mutating-step-declares-retry": mutating_step_declares_retry_default,
|
|
5917
|
-
"plugin-route-namespaced": plugin_route_namespaced_default,
|
|
5918
|
-
"route-no-inline-mutation": route_no_inline_mutation_default,
|
|
5919
|
-
"unique-query-step-name": unique_query_step_name_default,
|
|
5920
|
-
"named-when-block": named_when_block_default,
|
|
5921
|
-
"next-route-segment-is-thin-delegate": next_route_segment_is_thin_delegate_default,
|
|
5922
|
-
"no-brand-names": no_brand_names_default,
|
|
5923
|
-
"no-card-shaped-div": no_card_shaped_div_default,
|
|
5924
|
-
"no-cross-feature-stores": no_cross_feature_stores_default,
|
|
5925
|
-
"no-d1-transaction": no_d1_transaction_default,
|
|
5926
|
-
"no-duplicate-jsx-patterns": no_duplicate_jsx_patterns_default,
|
|
5927
|
-
"no-hook-in-component-disguise": no_hook_in_component_disguise_default,
|
|
5928
|
-
"no-index-count-as-exact": no_index_count_as_exact_default,
|
|
5929
|
-
"no-inert-hidden-jsx": no_inert_hidden_jsx_default,
|
|
5930
|
-
"no-inline-data-in-jsx": no_inline_data_in_jsx_default,
|
|
5931
|
-
"no-invalid-feature-folders": no_invalid_feature_folders_default,
|
|
5932
|
-
"no-loader-side-effect-in-index-file": no_loader_side_effect_in_index_file_default,
|
|
5933
|
-
"no-logic-in-component-files": no_logic_in_component_files_default,
|
|
5934
|
-
"no-mock-db-in-integration": no_mock_db_in_integration_default,
|
|
5935
|
-
"no-orm-outside-db": no_orm_outside_db_default,
|
|
5936
|
-
"no-raw-html-atoms": no_raw_html_atoms_default,
|
|
5937
|
-
"no-raw-sql-outside-allowed": no_raw_sql_outside_allowed_default,
|
|
5938
|
-
"no-react-namespace": no_react_namespace_default,
|
|
5939
|
-
"no-renamed-html-props": no_renamed_html_props_default,
|
|
5940
|
-
"no-render-prop-reader": no_render_prop_reader_default,
|
|
5941
|
-
"no-trivial-wrapper-component": no_trivial_wrapper_component_default,
|
|
5942
|
-
"no-ts-in-bio-folders": no_ts_in_bio_folders_default,
|
|
5943
|
-
"no-type-definitions-in-components": no_type_definitions_in_components_default,
|
|
5944
|
-
"no-unregistered-plugin-dir": no_unregistered_plugin_dir_default,
|
|
5945
|
-
"no-void-port": no_void_port_default,
|
|
5946
|
-
"organelle-dependency": organelle_dependency_default,
|
|
5947
|
-
"organelle-single-source": organelle_single_source_default,
|
|
5948
|
-
"queries-require-org-scope": queries_require_org_scope_default,
|
|
5949
|
-
"queue-loop-is-the-library": queue_loop_is_the_library_default,
|
|
5950
|
-
"router-schema-parity": router_schema_parity_default,
|
|
5951
|
-
"service-name-not-reserved": service_name_not_reserved_default,
|
|
5952
|
-
"ssot-no-inline-facts": ssot_no_inline_facts_default,
|
|
5953
|
-
"ssot-no-process-env": ssot_no_process_env_default,
|
|
5954
|
-
"step-declares-compensation-or-none": step_declares_compensation_or_none_default,
|
|
5955
|
-
"step-opens-its-own-cell": step_opens_its_own_cell_default,
|
|
5956
|
-
"store-route-scopes-tenant-data": store_route_scopes_tenant_data_default,
|
|
5957
|
-
"subscriber-declares-id": subscriber_declares_id_default,
|
|
5958
|
-
"tables-declare-their-plane": tables_declare_their_plane_default,
|
|
5959
|
-
"tenant-tables-carry-org-id": tenant_tables_carry_org_id_default,
|
|
5960
|
-
"time-through-the-door": time_through_the_door_default,
|
|
5961
|
-
"tokens-only-colors": tokens_only_colors_default,
|
|
5962
|
-
"tissue-must-compose": tissue_must_compose_default,
|
|
5963
|
-
"tissue-no-data-props": tissue_no_data_props_default,
|
|
5964
|
-
"tissue-no-hooks": tissue_no_hooks_default,
|
|
5965
|
-
"tissue-no-organelles": tissue_no_organelles_default,
|
|
5966
|
-
"tissue-no-stores": tissue_no_stores_default,
|
|
5967
|
-
"worker-handles-are-scoped": worker_handles_are_scoped_default,
|
|
5968
|
-
"zustand-v5-best-practices": zustand_v5_best_practices_default
|
|
6615
|
+
var refusingOnce = (declared) => Object.fromEntries(
|
|
6616
|
+
Object.entries(declared).map(([name, rule]) => [
|
|
6617
|
+
name,
|
|
6618
|
+
{ ...rule, create: refusingOncePerRun(rule.create) }
|
|
6619
|
+
])
|
|
6620
|
+
);
|
|
6621
|
+
var withEngines = (declared) => {
|
|
6622
|
+
const presumed = new Map(
|
|
6623
|
+
PRESUMED_ENGINES.flatMap(
|
|
6624
|
+
(one) => one.rules.map((name) => [name, { engine: one.engine, packages: one.packages }])
|
|
6625
|
+
)
|
|
6626
|
+
);
|
|
6627
|
+
return Object.fromEntries(
|
|
6628
|
+
Object.entries(declared).map(([name, rule]) => {
|
|
6629
|
+
const engine = presumed.get(name);
|
|
6630
|
+
return [name, engine === void 0 ? rule : { ...rule, presumes: engine }];
|
|
6631
|
+
})
|
|
6632
|
+
);
|
|
5969
6633
|
};
|
|
6634
|
+
var rules = refusingOnce(
|
|
6635
|
+
withEngines({
|
|
6636
|
+
"atom-no-deps": atom_no_deps_default,
|
|
6637
|
+
"boundary-time-is-primitive": boundary_time_is_primitive_default,
|
|
6638
|
+
"cell-must-be-stateful": cell_must_be_stateful_default,
|
|
6639
|
+
"cell-must-not-compose-cell": cell_must_not_compose_cell_default,
|
|
6640
|
+
"cell-no-cross-feature-organelles": cell_no_cross_feature_organelles_default,
|
|
6641
|
+
"cell-no-tissues": cell_no_tissues_default,
|
|
6642
|
+
"cells-folder-index-is-barrel": cells_folder_index_is_barrel_default,
|
|
6643
|
+
"compound-must-be-stateless": compound_must_be_stateless_default,
|
|
6644
|
+
"compound-no-stores": compound_no_stores_default,
|
|
6645
|
+
"constants-in-constants-file": constants_in_constants_file_default,
|
|
6646
|
+
"dialect-through-the-seam": dialect_through_the_seam_default,
|
|
6647
|
+
"document-sagas-are-generic": document_sagas_are_generic_default,
|
|
6648
|
+
"documents-share-one-table": documents_share_one_table_default,
|
|
6649
|
+
"durable-body-reads-through-steps": durable_body_reads_through_steps_default,
|
|
6650
|
+
"effect-hook-naming": effect_hook_naming_default,
|
|
6651
|
+
"emit-declares-attempts": emit_declares_attempts_default,
|
|
6652
|
+
"font-roles-only": font_roles_only_default,
|
|
6653
|
+
"layer-walls": layer_walls_default,
|
|
6654
|
+
"max-comment-density": max_comment_density_default,
|
|
6655
|
+
"molecule-atoms-only": molecule_atoms_only_default,
|
|
6656
|
+
"molecule-must-compose": molecule_must_compose_default,
|
|
6657
|
+
"mutating-step-declares-retry": mutating_step_declares_retry_default,
|
|
6658
|
+
"plugin-route-namespaced": plugin_route_namespaced_default,
|
|
6659
|
+
"route-no-inline-mutation": route_no_inline_mutation_default,
|
|
6660
|
+
"unique-query-step-name": unique_query_step_name_default,
|
|
6661
|
+
"named-when-block": named_when_block_default,
|
|
6662
|
+
"next-route-segment-is-thin-delegate": next_route_segment_is_thin_delegate_default,
|
|
6663
|
+
"no-brand-names": no_brand_names_default,
|
|
6664
|
+
"no-card-shaped-div": no_card_shaped_div_default,
|
|
6665
|
+
"no-cross-feature-stores": no_cross_feature_stores_default,
|
|
6666
|
+
"no-d1-transaction": no_d1_transaction_default,
|
|
6667
|
+
"no-duplicate-jsx-patterns": no_duplicate_jsx_patterns_default,
|
|
6668
|
+
"no-hook-in-component-disguise": no_hook_in_component_disguise_default,
|
|
6669
|
+
"no-index-count-as-exact": no_index_count_as_exact_default,
|
|
6670
|
+
"no-inert-hidden-jsx": no_inert_hidden_jsx_default,
|
|
6671
|
+
"no-inline-data-in-jsx": no_inline_data_in_jsx_default,
|
|
6672
|
+
"no-invalid-feature-folders": no_invalid_feature_folders_default,
|
|
6673
|
+
"no-loader-side-effect-in-index-file": no_loader_side_effect_in_index_file_default,
|
|
6674
|
+
"no-logic-in-component-files": no_logic_in_component_files_default,
|
|
6675
|
+
"no-mock-db-in-integration": no_mock_db_in_integration_default,
|
|
6676
|
+
"no-orm-outside-db": no_orm_outside_db_default,
|
|
6677
|
+
"no-raw-html-atoms": no_raw_html_atoms_default,
|
|
6678
|
+
"no-raw-sql-outside-allowed": no_raw_sql_outside_allowed_default,
|
|
6679
|
+
"no-react-namespace": no_react_namespace_default,
|
|
6680
|
+
"no-renamed-html-props": no_renamed_html_props_default,
|
|
6681
|
+
"no-render-prop-reader": no_render_prop_reader_default,
|
|
6682
|
+
"no-trivial-wrapper-component": no_trivial_wrapper_component_default,
|
|
6683
|
+
"no-ts-in-bio-folders": no_ts_in_bio_folders_default,
|
|
6684
|
+
"no-type-definitions-in-components": no_type_definitions_in_components_default,
|
|
6685
|
+
"no-unregistered-plugin-dir": no_unregistered_plugin_dir_default,
|
|
6686
|
+
"no-void-port": no_void_port_default,
|
|
6687
|
+
"organelle-dependency": organelle_dependency_default,
|
|
6688
|
+
"organelle-single-source": organelle_single_source_default,
|
|
6689
|
+
"queries-require-org-scope": queries_require_org_scope_default,
|
|
6690
|
+
"queue-loop-is-the-library": queue_loop_is_the_library_default,
|
|
6691
|
+
"router-schema-parity": router_schema_parity_default,
|
|
6692
|
+
"service-name-not-reserved": service_name_not_reserved_default,
|
|
6693
|
+
"ssot-no-inline-facts": ssot_no_inline_facts_default,
|
|
6694
|
+
"ssot-no-process-env": ssot_no_process_env_default,
|
|
6695
|
+
"step-declares-compensation-or-none": step_declares_compensation_or_none_default,
|
|
6696
|
+
"step-opens-its-own-cell": step_opens_its_own_cell_default,
|
|
6697
|
+
"store-route-scopes-tenant-data": store_route_scopes_tenant_data_default,
|
|
6698
|
+
"subpath-over-barrel": subpath_over_barrel_default,
|
|
6699
|
+
"subscriber-declares-id": subscriber_declares_id_default,
|
|
6700
|
+
"tables-declare-their-plane": tables_declare_their_plane_default,
|
|
6701
|
+
"tenant-tables-carry-org-id": tenant_tables_carry_org_id_default,
|
|
6702
|
+
"time-through-the-door": time_through_the_door_default,
|
|
6703
|
+
"tokens-only-colors": tokens_only_colors_default,
|
|
6704
|
+
"tissue-must-compose": tissue_must_compose_default,
|
|
6705
|
+
"tissue-no-data-props": tissue_no_data_props_default,
|
|
6706
|
+
"tissue-no-hooks": tissue_no_hooks_default,
|
|
6707
|
+
"tissue-no-organelles": tissue_no_organelles_default,
|
|
6708
|
+
"tissue-no-stores": tissue_no_stores_default,
|
|
6709
|
+
"todo-names-its-plan": todo_names_its_plan_default,
|
|
6710
|
+
"worker-handles-are-scoped": worker_handles_are_scoped_default,
|
|
6711
|
+
"zustand-v5-best-practices": zustand_v5_best_practices_default
|
|
6712
|
+
})
|
|
6713
|
+
);
|
|
5970
6714
|
var { version } = createRequire(import.meta.url)("../package.json");
|
|
5971
6715
|
var plugin = {
|
|
5972
6716
|
configs: PRESETS,
|
|
@@ -5979,6 +6723,7 @@ var plugin = {
|
|
|
5979
6723
|
var index_default = plugin;
|
|
5980
6724
|
export {
|
|
5981
6725
|
PRESETS,
|
|
6726
|
+
RENAMED_TO,
|
|
5982
6727
|
index_default as default,
|
|
5983
6728
|
rules,
|
|
5984
6729
|
rulesOfPreset
|