arkgate 2.12.0 → 2.13.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/CHANGELOG.md +83 -0
- package/README.md +55 -31
- package/bin/ark-check.mjs +95 -36
- package/bin/ark-mcp.mjs +11 -5
- package/bin/ark-shared.mjs +88 -56
- package/bin/ark.mjs +45 -10
- package/bin/lib/agent-gates.mjs +12 -0
- package/bin/lib/architecture-scan.mjs +8 -0
- package/bin/lib/ci-and-commands.mjs +9 -3
- package/bin/lib/codex-home.mjs +7 -0
- package/bin/lib/config-contract.mjs +331 -0
- package/bin/lib/doctor-plan.mjs +43 -16
- package/bin/lib/enforcement-profiles.mjs +97 -0
- package/bin/lib/host-support-matrix.mjs +77 -0
- package/bin/lib/install-migrate.mjs +45 -14
- package/bin/lib/mcp-adoption.mjs +35 -3
- package/bin/lib/open-html.mjs +75 -0
- package/bin/lib/presets.mjs +3 -2
- package/bin/lib/safety-diagnostics.mjs +31 -11
- package/bin/lib/skill-install.mjs +64 -0
- package/bin/lib/ts-resolve.mjs +2 -1
- package/bin/lib/weakest-link.mjs +417 -0
- package/bin/lib/write-path-capabilities.mjs +182 -0
- package/bin/lib/write-path-detect.mjs +62 -99
- package/dist/configContract-iBLxx5Tz.d.cts +53 -0
- package/dist/configContract-iBLxx5Tz.d.ts +53 -0
- package/dist/eslint/index.cjs +375 -13
- package/dist/eslint/index.cjs.map +1 -1
- package/dist/eslint/index.d.cts +30 -20
- package/dist/eslint/index.d.ts +30 -20
- package/dist/eslint/index.js +375 -13
- package/dist/eslint/index.js.map +1 -1
- package/dist/index.cjs +723 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -5
- package/dist/index.d.ts +95 -5
- package/dist/index.js +716 -61
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.cjs +150 -42
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +2 -1
- package/dist/nestjs/index.d.ts +2 -1
- package/dist/nestjs/index.js +150 -42
- package/dist/nestjs/index.js.map +1 -1
- package/dist/runtime/index.cjs +723 -61
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +3 -2
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.js +716 -61
- package/dist/runtime/index.js.map +1 -1
- package/dist/{types-BZ17b9i5.d.cts → types-BxBwnBpC.d.cts} +9 -36
- package/dist/{types-BZ17b9i5.d.ts → types-Wcs_l1_J.d.ts} +9 -36
- package/docs/agent-guide.md +32 -20
- package/docs/ai-gates.md +53 -18
- package/docs/configuration.md +97 -0
- package/docs/enthusiast/README.md +3 -3
- package/docs/enthusiast/how-to-agent-gates.md +7 -3
- package/docs/migrate-from-ark-runtime-kernel.md +3 -0
- package/docs/package-surface.md +14 -9
- package/docs/production-hardening.md +15 -2
- package/docs/threat-model.md +65 -0
- package/docs/typescript-support.md +3 -3
- package/package.json +15 -2
- package/schemas/ark.config.schema.json +750 -0
- package/server.json +2 -2
- package/templates/hooks/pre-commit-ark +37 -0
- package/templates/skills/ark-coverage.md +2 -2
- package/templates/skills/ark-runtime.md +8 -5
- package/templates/skills/ark-upgrade.md +36 -16
- package/tests/fixtures/ts-consumer/ark.config.json +2 -0
package/dist/eslint/index.cjs
CHANGED
|
@@ -216,6 +216,311 @@ function isEdgeDenied(rules2, from, to, options) {
|
|
|
216
216
|
return findDeniedEdgeRule(rules2, from, to, options) !== void 0;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
// src/domain/configContract.ts
|
|
220
|
+
var ARK_CONFIG_SCHEMA_VERSION = "1.0";
|
|
221
|
+
var ARK_CONFIG_SCHEMA_URL = "https://unpkg.com/arkgate@2/schemas/ark.config.schema.json";
|
|
222
|
+
var DEFAULT_LAYER_NAMES = [
|
|
223
|
+
"DomainModel",
|
|
224
|
+
"ApplicationOrchestration",
|
|
225
|
+
"PersistenceAdapters",
|
|
226
|
+
"IntegrationAdapters",
|
|
227
|
+
"WorkflowSagaEngine",
|
|
228
|
+
"BackgroundJobsScheduling",
|
|
229
|
+
"PresentationAdapters",
|
|
230
|
+
"ReportingReadModels",
|
|
231
|
+
"ExtensibilityMetadata",
|
|
232
|
+
"SecurityAuditObservability",
|
|
233
|
+
"Kernel"
|
|
234
|
+
];
|
|
235
|
+
var DEFAULT_ALLOWED_FLOWS = /* @__PURE__ */ new Set([
|
|
236
|
+
"PresentationAdapters->ApplicationOrchestration",
|
|
237
|
+
"ApplicationOrchestration->DomainModel",
|
|
238
|
+
"WorkflowSagaEngine->ApplicationOrchestration",
|
|
239
|
+
"WorkflowSagaEngine->DomainModel",
|
|
240
|
+
"BackgroundJobsScheduling->ApplicationOrchestration"
|
|
241
|
+
]);
|
|
242
|
+
function createDefaultRules() {
|
|
243
|
+
const rules2 = [];
|
|
244
|
+
for (const from of DEFAULT_LAYER_NAMES) {
|
|
245
|
+
for (const to of DEFAULT_LAYER_NAMES) {
|
|
246
|
+
if (from === to || DEFAULT_ALLOWED_FLOWS.has(`${from}->${to}`)) continue;
|
|
247
|
+
rules2.push({ from, to, allowed: false });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return rules2;
|
|
251
|
+
}
|
|
252
|
+
var DEFAULT_ARK_CONFIG_RULES = createDefaultRules();
|
|
253
|
+
var stringArraySchema = {
|
|
254
|
+
type: "array",
|
|
255
|
+
items: { type: "string", minLength: 1 },
|
|
256
|
+
uniqueItems: true
|
|
257
|
+
};
|
|
258
|
+
var ARK_CONFIG_SCHEMA = {
|
|
259
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
260
|
+
$id: ARK_CONFIG_SCHEMA_URL,
|
|
261
|
+
title: "ArkGate architecture contract",
|
|
262
|
+
description: "Versioned contract consumed identically by ArkGate CLI, MCP, and ESLint surfaces.",
|
|
263
|
+
type: "object",
|
|
264
|
+
additionalProperties: false,
|
|
265
|
+
required: ["$schema", "schemaVersion", "include", "layers", "rules"],
|
|
266
|
+
properties: {
|
|
267
|
+
$schema: {
|
|
268
|
+
type: "string",
|
|
269
|
+
minLength: 1,
|
|
270
|
+
default: ARK_CONFIG_SCHEMA_URL,
|
|
271
|
+
description: "Editor-facing URL or local path for this JSON Schema."
|
|
272
|
+
},
|
|
273
|
+
schemaVersion: {
|
|
274
|
+
type: "string",
|
|
275
|
+
const: ARK_CONFIG_SCHEMA_VERSION,
|
|
276
|
+
default: ARK_CONFIG_SCHEMA_VERSION
|
|
277
|
+
},
|
|
278
|
+
name: { type: "string", minLength: 1 },
|
|
279
|
+
include: { ...stringArraySchema, minItems: 1, default: ["src"] },
|
|
280
|
+
exclude: { ...stringArraySchema, default: [] },
|
|
281
|
+
excludeGenerated: { type: "boolean", default: true },
|
|
282
|
+
frameworkOverlay: { type: "string", minLength: 1 },
|
|
283
|
+
layers: {
|
|
284
|
+
type: "array",
|
|
285
|
+
default: [],
|
|
286
|
+
items: { $ref: "#/$defs/layer" }
|
|
287
|
+
},
|
|
288
|
+
rules: {
|
|
289
|
+
type: "array",
|
|
290
|
+
default: DEFAULT_ARK_CONFIG_RULES,
|
|
291
|
+
items: { $ref: "#/$defs/rule" }
|
|
292
|
+
},
|
|
293
|
+
cyclePolicy: {
|
|
294
|
+
type: "string",
|
|
295
|
+
enum: ["strict", "soft", "framework-soft", "off"],
|
|
296
|
+
default: "strict"
|
|
297
|
+
},
|
|
298
|
+
dynamicImportAllowlist: { ...stringArraySchema, default: [] },
|
|
299
|
+
safety: {
|
|
300
|
+
$ref: "#/$defs/safety",
|
|
301
|
+
default: {
|
|
302
|
+
maxTsSuppressions: 0,
|
|
303
|
+
maxAnyCasts: 0,
|
|
304
|
+
allowInMemory: false,
|
|
305
|
+
allowDisabledPeerIsolation: false
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
$defs: {
|
|
310
|
+
layer: {
|
|
311
|
+
type: "object",
|
|
312
|
+
additionalProperties: false,
|
|
313
|
+
required: ["name", "patterns"],
|
|
314
|
+
properties: {
|
|
315
|
+
name: { type: "string", minLength: 1 },
|
|
316
|
+
patterns: { ...stringArraySchema, minItems: 1 },
|
|
317
|
+
exclude: stringArraySchema,
|
|
318
|
+
intentPrefixes: stringArraySchema,
|
|
319
|
+
description: { type: "string", minLength: 1 },
|
|
320
|
+
forbiddenGlobals: stringArraySchema,
|
|
321
|
+
mayImportInfrastructure: { type: "boolean" },
|
|
322
|
+
optional: { type: "boolean" }
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
rule: {
|
|
326
|
+
type: "object",
|
|
327
|
+
additionalProperties: false,
|
|
328
|
+
required: ["from", "to", "allowed"],
|
|
329
|
+
properties: {
|
|
330
|
+
from: { type: "string", minLength: 1 },
|
|
331
|
+
to: { type: "string", minLength: 1 },
|
|
332
|
+
allowed: { type: "boolean" },
|
|
333
|
+
message: { type: "string", minLength: 1 },
|
|
334
|
+
peerIsolation: { type: "boolean" },
|
|
335
|
+
sliceFolders: { ...stringArraySchema, minItems: 1 }
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
safety: {
|
|
339
|
+
type: "object",
|
|
340
|
+
additionalProperties: false,
|
|
341
|
+
properties: {
|
|
342
|
+
maxTsSuppressions: { type: "integer", minimum: 0, default: 0 },
|
|
343
|
+
maxAnyCasts: { type: "integer", minimum: 0, default: 0 },
|
|
344
|
+
allowInMemory: { type: "boolean", default: false },
|
|
345
|
+
allowDisabledPeerIsolation: { type: "boolean", default: false }
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
var ArkConfigValidationError = class extends Error {
|
|
351
|
+
issues;
|
|
352
|
+
source;
|
|
353
|
+
constructor(source, issues) {
|
|
354
|
+
super(
|
|
355
|
+
`Invalid ArkGate config (${source}):
|
|
356
|
+
${issues.map((issue) => `- ${issue.path}: ${issue.message}`).join("\n")}`
|
|
357
|
+
);
|
|
358
|
+
this.name = "ArkConfigValidationError";
|
|
359
|
+
this.source = source;
|
|
360
|
+
this.issues = issues;
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
function isObject(value) {
|
|
364
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
365
|
+
}
|
|
366
|
+
function propertyPath(parent, key) {
|
|
367
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? `${parent}.${key}` : `${parent}[${JSON.stringify(key)}]`;
|
|
368
|
+
}
|
|
369
|
+
function valueType(value) {
|
|
370
|
+
if (value === null) return "null";
|
|
371
|
+
if (Array.isArray(value)) return "array";
|
|
372
|
+
return typeof value;
|
|
373
|
+
}
|
|
374
|
+
function resolveSchemaRef(ref, root) {
|
|
375
|
+
const prefix = "#/$defs/";
|
|
376
|
+
if (!ref.startsWith(prefix)) return void 0;
|
|
377
|
+
return root.$defs[ref.slice(prefix.length)];
|
|
378
|
+
}
|
|
379
|
+
function validateNode(value, schema, path2, root, issues) {
|
|
380
|
+
if (schema.$ref) {
|
|
381
|
+
const referenced = resolveSchemaRef(schema.$ref, root);
|
|
382
|
+
if (!referenced) {
|
|
383
|
+
issues.push({ path: path2, message: `schema reference ${schema.$ref} cannot be resolved` });
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
validateNode(value, referenced, path2, root, issues);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (schema.const !== void 0 && !Object.is(value, schema.const)) {
|
|
390
|
+
issues.push({ path: path2, message: `must equal ${JSON.stringify(schema.const)}` });
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (schema.enum && !schema.enum.some((candidate) => Object.is(candidate, value))) {
|
|
394
|
+
issues.push({ path: path2, message: `must be one of ${schema.enum.map(String).join(", ")}` });
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (schema.type === "object") {
|
|
398
|
+
if (!isObject(value)) {
|
|
399
|
+
issues.push({ path: path2, message: `must be an object; received ${valueType(value)}` });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const properties = schema.properties ?? {};
|
|
403
|
+
for (const key of schema.required ?? []) {
|
|
404
|
+
if (value[key] === void 0) {
|
|
405
|
+
issues.push({ path: propertyPath(path2, key), message: "is required" });
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (schema.additionalProperties === false) {
|
|
409
|
+
for (const key of Object.keys(value)) {
|
|
410
|
+
if (!(key in properties)) {
|
|
411
|
+
issues.push({ path: propertyPath(path2, key), message: "unknown field" });
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (const [key, childSchema] of Object.entries(properties)) {
|
|
416
|
+
if (value[key] !== void 0) {
|
|
417
|
+
validateNode(value[key], childSchema, propertyPath(path2, key), root, issues);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (schema.type === "array") {
|
|
423
|
+
if (!Array.isArray(value)) {
|
|
424
|
+
issues.push({ path: path2, message: `must be an array; received ${valueType(value)}` });
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (schema.minItems !== void 0 && value.length < schema.minItems) {
|
|
428
|
+
issues.push({ path: path2, message: `must contain at least ${schema.minItems} item(s)` });
|
|
429
|
+
}
|
|
430
|
+
if (schema.uniqueItems) {
|
|
431
|
+
const serialized = value.map((entry) => JSON.stringify(entry));
|
|
432
|
+
if (new Set(serialized).size !== serialized.length) {
|
|
433
|
+
issues.push({ path: path2, message: "must not contain duplicate items" });
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (schema.items) {
|
|
437
|
+
value.forEach(
|
|
438
|
+
(entry, index) => validateNode(entry, schema.items, `${path2}[${index}]`, root, issues)
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (schema.type === "string") {
|
|
444
|
+
if (typeof value !== "string") {
|
|
445
|
+
issues.push({ path: path2, message: `must be a string; received ${valueType(value)}` });
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (schema.minLength !== void 0 && value.length < schema.minLength) {
|
|
449
|
+
issues.push({ path: path2, message: `must contain at least ${schema.minLength} character(s)` });
|
|
450
|
+
}
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
if (schema.type === "boolean") {
|
|
454
|
+
if (typeof value !== "boolean") {
|
|
455
|
+
issues.push({ path: path2, message: `must be a boolean; received ${valueType(value)}` });
|
|
456
|
+
}
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (schema.type === "integer") {
|
|
460
|
+
if (!Number.isInteger(value)) {
|
|
461
|
+
issues.push({ path: path2, message: `must be an integer; received ${valueType(value)}` });
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (schema.minimum !== void 0 && value < schema.minimum) {
|
|
465
|
+
issues.push({ path: path2, message: `must be at least ${schema.minimum}` });
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function defaultedConfig(input) {
|
|
470
|
+
return {
|
|
471
|
+
...input,
|
|
472
|
+
$schema: input.$schema === void 0 ? ARK_CONFIG_SCHEMA_URL : input.$schema,
|
|
473
|
+
schemaVersion: input.schemaVersion === void 0 ? ARK_CONFIG_SCHEMA_VERSION : input.schemaVersion,
|
|
474
|
+
include: input.include === void 0 ? ["src"] : input.include,
|
|
475
|
+
layers: input.layers === void 0 ? [] : input.layers,
|
|
476
|
+
rules: input.rules === void 0 ? DEFAULT_ARK_CONFIG_RULES.map((rule) => ({ ...rule })) : input.rules
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function migrateArkConfig(input, source = "ark.config.json") {
|
|
480
|
+
if (!isObject(input)) {
|
|
481
|
+
throw new ArkConfigValidationError(source, [
|
|
482
|
+
{ path: "$", message: `must be an object; received ${valueType(input)}` }
|
|
483
|
+
]);
|
|
484
|
+
}
|
|
485
|
+
const migratedFrom = input.schemaVersion === void 0 ? "unversioned" : null;
|
|
486
|
+
if (input.schemaVersion !== void 0 && input.schemaVersion !== ARK_CONFIG_SCHEMA_VERSION) {
|
|
487
|
+
throw new ArkConfigValidationError(source, [
|
|
488
|
+
{
|
|
489
|
+
path: "$.schemaVersion",
|
|
490
|
+
message: `unsupported version ${JSON.stringify(input.schemaVersion)}; expected ${ARK_CONFIG_SCHEMA_VERSION}`
|
|
491
|
+
}
|
|
492
|
+
]);
|
|
493
|
+
}
|
|
494
|
+
return { candidate: defaultedConfig(input), migratedFrom };
|
|
495
|
+
}
|
|
496
|
+
function loadArkConfigContract(input, source = "ark.config.json") {
|
|
497
|
+
const { candidate, migratedFrom } = migrateArkConfig(input, source);
|
|
498
|
+
const issues = [];
|
|
499
|
+
validateNode(
|
|
500
|
+
candidate,
|
|
501
|
+
ARK_CONFIG_SCHEMA,
|
|
502
|
+
"$",
|
|
503
|
+
ARK_CONFIG_SCHEMA,
|
|
504
|
+
issues
|
|
505
|
+
);
|
|
506
|
+
if (issues.length > 0) throw new ArkConfigValidationError(source, issues);
|
|
507
|
+
return { config: candidate, migratedFrom };
|
|
508
|
+
}
|
|
509
|
+
function parseArkConfigJson(json, source = "ark.config.json") {
|
|
510
|
+
let input;
|
|
511
|
+
try {
|
|
512
|
+
input = JSON.parse(json);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
throw new ArkConfigValidationError(source, [
|
|
515
|
+
{
|
|
516
|
+
path: "$",
|
|
517
|
+
message: `invalid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
518
|
+
}
|
|
519
|
+
]);
|
|
520
|
+
}
|
|
521
|
+
return loadArkConfigContract(input, source);
|
|
522
|
+
}
|
|
523
|
+
|
|
219
524
|
// src/eslint/index.ts
|
|
220
525
|
function lintedFilename(context) {
|
|
221
526
|
if (typeof context.physicalFilename === "string" && context.physicalFilename.length > 0) {
|
|
@@ -247,14 +552,10 @@ function findConfigPath(startFile) {
|
|
|
247
552
|
var _configCache = /* @__PURE__ */ new Map();
|
|
248
553
|
function loadArkConfig(configPath) {
|
|
249
554
|
if (_configCache.has(configPath)) return _configCache.get(configPath) ?? null;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
} catch {
|
|
255
|
-
_configCache.set(configPath, null);
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
555
|
+
if (!import_node_fs.default.existsSync(configPath)) return null;
|
|
556
|
+
const config = parseArkConfigJson(import_node_fs.default.readFileSync(configPath, "utf8"), configPath).config;
|
|
557
|
+
_configCache.set(configPath, config);
|
|
558
|
+
return config;
|
|
258
559
|
}
|
|
259
560
|
function resolveRelativeImport(fromFile, specifier) {
|
|
260
561
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -285,6 +586,46 @@ function stringValue(node) {
|
|
|
285
586
|
function propertyName(node) {
|
|
286
587
|
return node?.name ?? stringValue(node);
|
|
287
588
|
}
|
|
589
|
+
function sourceCodeFor(context) {
|
|
590
|
+
return context.sourceCode ?? context.getSourceCode?.();
|
|
591
|
+
}
|
|
592
|
+
function referenceFor(context, node) {
|
|
593
|
+
let scope = sourceCodeFor(context)?.getScope?.(node);
|
|
594
|
+
while (scope) {
|
|
595
|
+
const reference = scope.references?.find((candidate) => candidate.identifier === node);
|
|
596
|
+
if (reference) return reference;
|
|
597
|
+
scope = scope.upper ?? void 0;
|
|
598
|
+
}
|
|
599
|
+
return void 0;
|
|
600
|
+
}
|
|
601
|
+
function isLocallyBound(context, node, name) {
|
|
602
|
+
const reference = referenceFor(context, node);
|
|
603
|
+
if (reference?.resolved) return (reference.resolved.defs?.length ?? 0) > 0;
|
|
604
|
+
let scope = sourceCodeFor(context)?.getScope?.(node);
|
|
605
|
+
while (scope) {
|
|
606
|
+
const variable = scope.set?.get(name);
|
|
607
|
+
if (variable) return (variable.defs?.length ?? 0) > 0;
|
|
608
|
+
scope = scope.upper ?? void 0;
|
|
609
|
+
}
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
function isValueIdentifierReference(context, node) {
|
|
613
|
+
const reference = referenceFor(context, node);
|
|
614
|
+
if (reference) return reference.isValueReference !== false;
|
|
615
|
+
return node.parent?.type === "VariableDeclarator" && node.parent.init === node;
|
|
616
|
+
}
|
|
617
|
+
function memberExpressionPath(node) {
|
|
618
|
+
if (node?.type === "Identifier" && node.name) {
|
|
619
|
+
return { root: node, segments: [node.name] };
|
|
620
|
+
}
|
|
621
|
+
if (!node) return void 0;
|
|
622
|
+
const memberLike = node.type === "MemberExpression" || Boolean(node.object && node.property);
|
|
623
|
+
if (!memberLike || node.computed === true) return void 0;
|
|
624
|
+
const base = memberExpressionPath(node.object);
|
|
625
|
+
const property = propertyName(node.property);
|
|
626
|
+
if (!base || !property) return void 0;
|
|
627
|
+
return { root: base.root, segments: [...base.segments, property] };
|
|
628
|
+
}
|
|
288
629
|
function calleePropertyName(node) {
|
|
289
630
|
return propertyName(node.callee?.property);
|
|
290
631
|
}
|
|
@@ -477,6 +818,7 @@ var noForbiddenGlobals = {
|
|
|
477
818
|
if (!globals) {
|
|
478
819
|
return {};
|
|
479
820
|
}
|
|
821
|
+
const scopeAware = typeof sourceCodeFor(context)?.getScope === "function";
|
|
480
822
|
const report = (node, name) => context.report({
|
|
481
823
|
node,
|
|
482
824
|
messageId: config ? "forbiddenGlobal" : "forbiddenGlobalDefault",
|
|
@@ -484,19 +826,39 @@ var noForbiddenGlobals = {
|
|
|
484
826
|
});
|
|
485
827
|
return {
|
|
486
828
|
MemberExpression(node) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
829
|
+
if (node.parent?.type === "MemberExpression" && node.parent.object === node) return;
|
|
830
|
+
const path2 = memberExpressionPath(node);
|
|
831
|
+
if (!path2 || isLocallyBound(context, path2.root, path2.segments[0])) return;
|
|
832
|
+
const explicitGlobalThis = path2.segments[0] === "globalThis";
|
|
833
|
+
const normalized = explicitGlobalThis ? path2.segments.slice(1) : path2.segments;
|
|
834
|
+
let match;
|
|
835
|
+
for (let length = normalized.length; length >= (explicitGlobalThis ? 1 : 2); length -= 1) {
|
|
836
|
+
const candidate = normalized.slice(0, length).join(".");
|
|
837
|
+
if (globals.has(candidate)) {
|
|
838
|
+
match = candidate;
|
|
839
|
+
break;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (match) report(node, match);
|
|
843
|
+
else if (!scopeAware && globals.has(path2.segments[0])) {
|
|
844
|
+
report(node, path2.segments[0]);
|
|
845
|
+
}
|
|
492
846
|
},
|
|
493
847
|
CallExpression(node) {
|
|
848
|
+
if (scopeAware) return;
|
|
494
849
|
const callee = node.callee?.type === "Identifier" ? node.callee.name : void 0;
|
|
495
850
|
if (callee && globals.has(callee)) report(node, callee);
|
|
496
851
|
},
|
|
497
852
|
NewExpression(node) {
|
|
853
|
+
if (scopeAware) return;
|
|
498
854
|
const callee = node.callee?.type === "Identifier" ? node.callee.name : void 0;
|
|
499
855
|
if (callee && globals.has(callee)) report(node, callee);
|
|
856
|
+
},
|
|
857
|
+
Identifier(node) {
|
|
858
|
+
if (!scopeAware || !node.name || !globals.has(node.name) || !isValueIdentifierReference(context, node) || isLocallyBound(context, node, node.name)) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
report(node, node.name);
|
|
500
862
|
}
|
|
501
863
|
};
|
|
502
864
|
}
|