arkgate 2.13.0 → 3.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +37 -22
  3. package/bin/ark-check.mjs +62 -4
  4. package/bin/ark-mcp.mjs +108 -1
  5. package/bin/ark-shared.mjs +204 -149
  6. package/bin/ark.mjs +90 -25
  7. package/bin/lib/adapter-contract.mjs +93 -0
  8. package/bin/lib/agent-gates.mjs +1 -0
  9. package/bin/lib/analysis-engine.mjs +1171 -0
  10. package/bin/lib/architecture-scan.mjs +84 -135
  11. package/bin/lib/ci-and-commands.mjs +31 -0
  12. package/bin/lib/config-warnings.mjs +7 -205
  13. package/bin/lib/field-install.mjs +67 -10
  14. package/bin/lib/gate-files.mjs +42 -3
  15. package/bin/lib/graph-cycles.mjs +4 -54
  16. package/bin/lib/hook-templates.mjs +33 -1
  17. package/bin/lib/host-support-matrix.mjs +7 -1
  18. package/bin/lib/install-migrate.mjs +54 -16
  19. package/bin/lib/presets.mjs +42 -2
  20. package/bin/lib/safety-diagnostics.mjs +18 -17
  21. package/bin/lib/scan-files.mjs +12 -1
  22. package/bin/lib/skill-install.mjs +8 -1
  23. package/bin/lib/source-policy.mjs +36 -0
  24. package/bin/lib/start-preview.mjs +271 -0
  25. package/bin/lib/ts-resolve.mjs +11 -2
  26. package/bin/lib/write-path-capabilities.mjs +4 -0
  27. package/compat/nestjs.cjs +2 -0
  28. package/compat/nestjs.d.ts +2 -0
  29. package/compat/nestjs.js +1 -0
  30. package/compat/runtime.cjs +2 -0
  31. package/compat/runtime.d.ts +2 -0
  32. package/compat/runtime.js +1 -0
  33. package/dist/configContract-BxSIwVRo.d.cts +259 -0
  34. package/dist/configContract-BxSIwVRo.d.ts +259 -0
  35. package/dist/eslint/index.cjs +125 -48
  36. package/dist/eslint/index.d.cts +7 -1
  37. package/dist/eslint/index.d.ts +7 -1
  38. package/dist/eslint/index.js +125 -48
  39. package/dist/index.cjs +1248 -3302
  40. package/dist/index.d.cts +359 -483
  41. package/dist/index.d.ts +359 -483
  42. package/dist/index.js +1231 -3248
  43. package/docs/agent-guide.md +28 -16
  44. package/docs/ai-gates.md +30 -7
  45. package/docs/migrate-from-ark-runtime-kernel.md +2 -3
  46. package/docs/package-surface.md +8 -13
  47. package/docs/production-hardening.md +17 -4
  48. package/docs/typescript-support.md +27 -0
  49. package/package.json +33 -11
  50. package/schemas/ark.analysis-result.schema.json +91 -0
  51. package/server.json +2 -2
  52. package/templates/skills/ark-architect.md +3 -2
  53. package/dist/configContract-iBLxx5Tz.d.cts +0 -53
  54. package/dist/configContract-iBLxx5Tz.d.ts +0 -53
  55. package/dist/eslint/index.cjs.map +0 -1
  56. package/dist/eslint/index.js.map +0 -1
  57. package/dist/index.cjs.map +0 -1
  58. package/dist/index.js.map +0 -1
  59. package/dist/nestjs/index.cjs +0 -2606
  60. package/dist/nestjs/index.cjs.map +0 -1
  61. package/dist/nestjs/index.d.cts +0 -23
  62. package/dist/nestjs/index.d.ts +0 -23
  63. package/dist/nestjs/index.js +0 -2582
  64. package/dist/nestjs/index.js.map +0 -1
  65. package/dist/runtime/index.cjs +0 -4014
  66. package/dist/runtime/index.cjs.map +0 -1
  67. package/dist/runtime/index.d.cts +0 -3
  68. package/dist/runtime/index.d.ts +0 -3
  69. package/dist/runtime/index.js +0 -3925
  70. package/dist/runtime/index.js.map +0 -1
  71. package/dist/types-BxBwnBpC.d.cts +0 -1041
  72. package/dist/types-Wcs_l1_J.d.ts +0 -1041
@@ -475,6 +475,63 @@ function parseArkConfigJson(json, source = "ark.config.json") {
475
475
  return loadArkConfigContract(input, source);
476
476
  }
477
477
 
478
+ // src/domain/adapterContract.ts
479
+ function text(value) {
480
+ return typeof value === "string" && value.length > 0 ? value : void 0;
481
+ }
482
+ function positiveInteger(value, fallback) {
483
+ return Number.isInteger(value) && Number(value) > 0 ? Number(value) : fallback;
484
+ }
485
+ function toAdapterDiagnostic(violation, fallbackSeverity = "error") {
486
+ const ruleId = text(violation.ruleId) ?? text(violation.code) ?? "ARK_UNKNOWN";
487
+ const severity = violation.severity === "warning" ? "warning" : fallbackSeverity;
488
+ const evidence = {
489
+ ...text(violation.target) ? { target: text(violation.target) } : {},
490
+ ...text(violation.fromLayer) ? { fromLayer: text(violation.fromLayer) } : {},
491
+ ...text(violation.toLayer) ? { toLayer: text(violation.toLayer) } : {},
492
+ ...typeof violation.typeOnly === "boolean" ? { typeOnly: violation.typeOnly } : {}
493
+ };
494
+ return {
495
+ ruleId,
496
+ severity,
497
+ message: text(violation.message) ?? ruleId,
498
+ location: {
499
+ file: text(violation.file) ?? "<unknown>",
500
+ line: positiveInteger(violation.line, 1),
501
+ column: positiveInteger(violation.column, 1)
502
+ },
503
+ evidence
504
+ };
505
+ }
506
+
507
+ // src/domain/sourcePolicy.ts
508
+ var SOURCE_POLICY_MESSAGES = {
509
+ RAW_EVENT_PUBLISH: "Publish through a registered intent creator; raw event objects or intent strings bypass Ark contracts and tooling.",
510
+ PUBLISH_MISSING_SOURCE: "Strict Ark publish calls must include metadata.source."
511
+ };
512
+ function looksLikeArkIntent(value) {
513
+ return /^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(
514
+ value
515
+ );
516
+ }
517
+ function classifyPublishFacts(facts) {
518
+ if (!facts.publishCall) return [];
519
+ const findings = [];
520
+ if (facts.rawIntentName !== void 0 && looksLikeArkIntent(facts.rawIntentName) || facts.objectHasIntent) {
521
+ findings.push({
522
+ ruleId: "RAW_EVENT_PUBLISH",
523
+ message: SOURCE_POLICY_MESSAGES.RAW_EVENT_PUBLISH
524
+ });
525
+ }
526
+ if (facts.arkPublishCandidate && !facts.hasSource) {
527
+ findings.push({
528
+ ruleId: "PUBLISH_MISSING_SOURCE",
529
+ message: SOURCE_POLICY_MESSAGES.PUBLISH_MISSING_SOURCE
530
+ });
531
+ }
532
+ return findings;
533
+ }
534
+
478
535
  // src/eslint/index.ts
479
536
  function lintedFilename(context) {
480
537
  if (typeof context.physicalFilename === "string" && context.physicalFilename.length > 0) {
@@ -492,6 +549,15 @@ function lintedFilename(context) {
492
549
  }
493
550
  return "";
494
551
  }
552
+ function reportAdapterDiagnostic(context, node, messageId, violation, data) {
553
+ const diagnostic = toAdapterDiagnostic({
554
+ ...violation,
555
+ line: violation.line ?? node.loc?.start?.line,
556
+ column: violation.column ?? (typeof node.loc?.start?.column === "number" ? node.loc.start.column + 1 : void 0)
557
+ });
558
+ context.report({ node, messageId, ...data ? { data } : {}, diagnostic });
559
+ return diagnostic;
560
+ }
495
561
  function findConfigPath(startFile) {
496
562
  if (!startFile || startFile === "<input>" || startFile.startsWith("stdin")) return null;
497
563
  let dir = path.dirname(path.resolve(startFile));
@@ -593,38 +659,14 @@ function objectHasMetadataSource(node) {
593
659
  const metadata = objectProperty(node, "metadata")?.value;
594
660
  return objectHasProperty(metadata, "source");
595
661
  }
596
- function looksLikeIntent(value) {
597
- return /^(Domain|Application|Adapter|Workflow|Job|Presentation|Reporting|Metadata|Security|Audit|Observability|Kernel)\.[A-Za-z0-9_.]+$/.test(
598
- value
599
- );
600
- }
601
662
  function isPublishCall(node) {
602
663
  return calleePropertyName(node) === "publish";
603
664
  }
604
- function isDomainFileHeuristic(filename) {
605
- const normalized = filename.split("\\").join("/").toLowerCase();
606
- return normalized.includes("/domain/") || normalized.endsWith("/domain.ts");
607
- }
608
- function isInfraImportHeuristic(specifier) {
609
- const normalized = specifier.toLowerCase();
610
- return [
611
- "adapter",
612
- "adapters",
613
- "infrastructure",
614
- "persistence",
615
- "repository",
616
- "repositories",
617
- "integration",
618
- "database",
619
- "db"
620
- ].some((token) => normalized.includes(token));
621
- }
622
- var DEFAULT_FORBIDDEN_GLOBALS = ["fetch", "process", "Date.now", "Math.random"];
623
665
  var noDomainInfraImports = {
624
666
  meta: {
625
667
  type: "problem",
626
668
  docs: {
627
- description: "Disallow imports that violate ark.config.json layer rules (same contract as arkgate-check). Falls back to domain\u2192infra path heuristics when no config is found."
669
+ description: "Disallow imports that violate ark.config.json layer rules (same contract as arkgate-check)."
628
670
  },
629
671
  messages: {
630
672
  forbiddenImport: "Architecture: {{fromLayer}} must not import {{toLayer}} (ark.config.json). Specifier: {{specifier}}",
@@ -656,17 +698,24 @@ var noDomainInfraImports = {
656
698
  toPath: relTarget,
657
699
  layers: config.layers
658
700
  })) {
659
- context.report({
701
+ reportAdapterDiagnostic(
702
+ context,
660
703
  node,
661
- messageId: "forbiddenImport",
662
- data: { fromLayer, toLayer, specifier: source }
663
- });
704
+ "forbiddenImport",
705
+ {
706
+ ruleId: "LAYER_IMPORT_VIOLATION",
707
+ file: relFile,
708
+ fromLayer,
709
+ toLayer,
710
+ target: relTarget,
711
+ ...node.importKind === "type" ? { typeOnly: true } : {},
712
+ message: `${fromLayer} must not import ${toLayer}.`
713
+ },
714
+ { fromLayer, toLayer, specifier: source }
715
+ );
664
716
  }
665
717
  return;
666
718
  }
667
- if (isDomainFileHeuristic(filename) && isInfraImportHeuristic(source)) {
668
- context.report({ node, messageId: "forbiddenImportHeuristic" });
669
- }
670
719
  };
671
720
  return {
672
721
  ImportDeclaration: check,
@@ -689,11 +738,21 @@ var noRawEventPublish = {
689
738
  create(context) {
690
739
  return {
691
740
  CallExpression(node) {
692
- if (!isPublishCall(node)) return;
693
741
  const firstArg = node.arguments?.[0];
694
742
  const firstValue = stringValue(firstArg);
695
- if (firstValue && looksLikeIntent(firstValue) || objectHasProperty(firstArg, "intent")) {
696
- context.report({ node, messageId: "rawPublish" });
743
+ const findings = classifyPublishFacts({
744
+ publishCall: isPublishCall(node),
745
+ rawIntentName: firstValue,
746
+ objectHasIntent: objectHasProperty(firstArg, "intent"),
747
+ arkPublishCandidate: false,
748
+ hasSource: true
749
+ });
750
+ if (findings.some((finding) => finding.ruleId === "RAW_EVENT_PUBLISH")) {
751
+ const finding = findings.find((item) => item.ruleId === "RAW_EVENT_PUBLISH");
752
+ reportAdapterDiagnostic(context, node, "rawPublish", {
753
+ ...finding,
754
+ file: lintedFilename(context)
755
+ });
697
756
  }
698
757
  }
699
758
  };
@@ -713,13 +772,22 @@ var requirePublishSource = {
713
772
  create(context) {
714
773
  return {
715
774
  CallExpression(node) {
716
- if (!isPublishCall(node)) return;
717
775
  const firstArg = node.arguments?.[0];
718
776
  const metadataArg = node.arguments?.[2];
719
- if (objectHasMetadataSource(firstArg) || objectHasProperty(metadataArg, "source")) {
720
- return;
777
+ const findings = classifyPublishFacts({
778
+ publishCall: isPublishCall(node),
779
+ rawIntentName: stringValue(firstArg),
780
+ objectHasIntent: objectHasProperty(firstArg, "intent"),
781
+ arkPublishCandidate: true,
782
+ hasSource: objectHasMetadataSource(firstArg) || objectHasProperty(metadataArg, "source")
783
+ });
784
+ const finding = findings.find((item) => item.ruleId === "PUBLISH_MISSING_SOURCE");
785
+ if (finding) {
786
+ reportAdapterDiagnostic(context, node, "missingSource", {
787
+ ...finding,
788
+ file: lintedFilename(context)
789
+ });
721
790
  }
722
- context.report({ node, messageId: "missingSource" });
723
791
  }
724
792
  };
725
793
  }
@@ -728,7 +796,7 @@ var noForbiddenGlobals = {
728
796
  meta: {
729
797
  type: "problem",
730
798
  docs: {
731
- description: "Disallow ambient globals from the layer\u2019s forbiddenGlobals in ark.config.json (same purity surface as arkgate-check). Option `globals` overrides. Without config, defaults apply only on domain-like paths."
799
+ description: "Disallow ambient globals from the layer\u2019s forbiddenGlobals in ark.config.json (same purity surface as ark-check). Option `globals` explicitly overrides."
732
800
  },
733
801
  messages: {
734
802
  forbiddenGlobal: 'Ambient global "{{name}}" is forbidden in {{layer}} (ark.config.json); inject the capability through a port instead.',
@@ -766,18 +834,28 @@ var noForbiddenGlobals = {
766
834
  } else {
767
835
  globals = null;
768
836
  }
769
- } else if (isDomainFileHeuristic(filename)) {
770
- globals = new Set(DEFAULT_FORBIDDEN_GLOBALS);
771
837
  }
772
838
  if (!globals) {
773
839
  return {};
774
840
  }
775
841
  const scopeAware = typeof sourceCodeFor(context)?.getScope === "function";
776
- const report = (node, name) => context.report({
777
- node,
778
- messageId: config ? "forbiddenGlobal" : "forbiddenGlobalDefault",
779
- data: { name, layer: layerName }
780
- });
842
+ const report = (node, name) => {
843
+ const absFile = path.isAbsolute(filename) ? filename : path.resolve(filename);
844
+ const reportFile = root ? path.relative(root, absFile).split(path.sep).join("/") : filename;
845
+ reportAdapterDiagnostic(
846
+ context,
847
+ node,
848
+ config ? "forbiddenGlobal" : "forbiddenGlobalDefault",
849
+ {
850
+ ruleId: "FORBIDDEN_GLOBAL",
851
+ file: reportFile,
852
+ fromLayer: layerName,
853
+ target: name,
854
+ message: `${layerName} must not use the ambient global "${name}".`
855
+ },
856
+ { name, layer: layerName }
857
+ );
858
+ };
781
859
  return {
782
860
  MemberExpression(node) {
783
861
  if (node.parent?.type === "MemberExpression" && node.parent.object === node) return;
@@ -851,4 +929,3 @@ export {
851
929
  requirePublishSource,
852
930
  resolveRelativeImport
853
931
  };
854
- //# sourceMappingURL=index.js.map