@crazyhappyone/auto-graph 0.1.0 → 0.1.2

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/dist/index.d.cts CHANGED
@@ -80,6 +80,14 @@ interface Diagnostic {
80
80
  path?: DiagnosticPathSegment[];
81
81
  detail?: JsonObject;
82
82
  }
83
+ /**
84
+ * Diagnostic codes that indicate the solver produced a degraded
85
+ * (non-deliverable) layout. Downstream consumers can gate on the
86
+ * {@link CoordinatedDiagram.degraded} flag or use the
87
+ * {@link SolveDiagramOptions.strict} option to promote these to
88
+ * errors.
89
+ */
90
+ declare const DELIVERABILITY_DIAGNOSTIC_CODES: ReadonlySet<string>;
83
91
 
84
92
  interface TextStyleOptions {
85
93
  fontFamily: string;
@@ -486,6 +494,8 @@ interface CoordinatedDiagram {
486
494
  evidencePanels?: CoordinatedEvidencePanel[];
487
495
  textAnnotations?: SolvedTextAnnotation[];
488
496
  diagnostics: Diagnostic[];
497
+ /** True when any deliverability-breaking diagnostic was emitted. */
498
+ degraded: boolean;
489
499
  bounds: Box;
490
500
  frame?: CoordinatedFrame;
491
501
  metadata?: DiagramMetadata;
@@ -668,7 +678,7 @@ interface InitialLayoutResult {
668
678
 
669
679
  declare function runDagreInitialLayout(input: DagreLayoutInput): InitialLayoutResult;
670
680
 
671
- type RouteKind = "orthogonal" | "straight";
681
+ type RouteKind = "orthogonal" | "straight" | "obstacle-avoiding";
672
682
  interface RouteEdgeInput {
673
683
  kind?: RouteKind;
674
684
  direction: DiagramDirection;
@@ -700,6 +710,8 @@ declare function stringifyCanonical(value: unknown, precision?: number): string;
700
710
  interface SolveDiagramOptions {
701
711
  routeKind?: RouteKind;
702
712
  obstacleMargin?: number | Insets;
713
+ /** Extra horizontal/vertical clearance reserved around nodes for edge corridors. */
714
+ routingGutter?: number;
703
715
  overlapSpacing?: number;
704
716
  minLaneGutter?: number;
705
717
  prefitLabelSize?: boolean;
@@ -715,6 +727,8 @@ interface SolveDiagramOptions {
715
727
  cjkFontFamily?: string | false;
716
728
  minCjkFontSize?: number | false;
717
729
  textMeasurer?: TextMeasurer;
730
+ /** When true, promote deliverability-breaking diagnostics to errors. */
731
+ strict?: boolean;
718
732
  }
719
733
  interface PortShiftingOptions {
720
734
  enabled?: boolean;
@@ -732,4 +746,4 @@ declare function solveDiagram(diagram: NormalizedDiagram, options?: SolveDiagram
732
746
  */
733
747
  declare function solveDiagramSafe(diagram: NormalizedDiagram, options?: SolveDiagramOptions): CoordinatedDiagram;
734
748
 
735
- export { type AlignConstraint, type AlignmentAxis, type AnchorName, type AnchorPoint, type Arrowhead, type Box, type CanonicalJson, type CanonicalizeOptions, type Constraint, type ConstraintBase, type ConstraintSolverInput, type ConstraintSolverResult, type ConstraintTarget, type ConstraintTargetKind, type ContainerGeometry, type ContainerGeometryInput, type ContainmentConstraint, type CoordinatedDiagram, type CoordinatedEdge, type CoordinatedEvidencePanel, type CoordinatedFrame, type CoordinatedGroup, type CoordinatedMatrixBlock, type CoordinatedNode, type CoordinatedPort, type CoordinatedTableBlock, DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, type DagreLayoutEdge, type DagreLayoutInput, type DagreLayoutNode, type DagreLayoutOptions, type DefaultTextMeasurerOptions, DeterministicTextMeasurer, type Diagnostic, type DiagnosticPathSegment, type DiagnosticSeverity, type DiagramDirection, type DiagramFrame, type DiagramMetadata, type DiagramStage, type DistributeConstraint, type DistributionAxis, type DslDiagnostic, type DslDiagnosticLayer, type DslOutputFormat, type EdgeArrowhead, type EdgeEndpoint, type EdgeStrokeStyle, type EvidenceCell, type EvidencePanel, type EvidencePanelItem, type EvidencePanelKind, type EvidenceTextLayout, type ExactPositionConstraint, type ExportFormat, type ExportOptions, type ExportResult, type InitialLayoutResult, type Insets, type IntentDiagram, type IntentEdge, type IntentGroup, type IntentNode, type JsonObject, type JsonPrimitive, type JsonValue, type Label, type LabelFitOptions, LabelFitter, type LabelLayout, type LabelLineLayout, type LayoutLock, type LayoutLockSource, type MatrixBlock, type NodeBase, type NodeCompartments, type NodePort, type NodeShape, type NormalizeDiagramDslOptions, type NormalizeDiagramDslResult, type NormalizedDiagram, type NormalizedEdge, type NormalizedGroup, type NormalizedNode, type ParseDiagramDslOptions, type ParseDiagramDslResult, type ParsedEdgeShorthand, type Point, type PortKind, type PortShiftingOptions, type PortSide, type PreparedText, PretextTextMeasurer, type RelativePositionConstraint, type RelativePositionRelation, type RenderDiagramDslOptions, type RenderDiagramDslResult, type RouteEdgeInput, type RouteEdgeResult, type RouteKind, type ShapeGeometry, type ShapeGeometryInput, type Size, type SolveDiagramOptions, type SolvedTextAnnotation, type Swimlane, type SwimlaneLane, type SwimlaneLayout, type SwimlaneOrientation, type TableBlock, type TableColumn, type TableRow, type TextCursor, type TextLayout, type TextLayoutLine, type TextMeasurementBackend, type TextMeasurer, type TextStyleOptions, type TextSurfaceKind, type VisualStyle, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
749
+ export { type AlignConstraint, type AlignmentAxis, type AnchorName, type AnchorPoint, type Arrowhead, type Box, type CanonicalJson, type CanonicalizeOptions, type Constraint, type ConstraintBase, type ConstraintSolverInput, type ConstraintSolverResult, type ConstraintTarget, type ConstraintTargetKind, type ContainerGeometry, type ContainerGeometryInput, type ContainmentConstraint, type CoordinatedDiagram, type CoordinatedEdge, type CoordinatedEvidencePanel, type CoordinatedFrame, type CoordinatedGroup, type CoordinatedMatrixBlock, type CoordinatedNode, type CoordinatedPort, type CoordinatedTableBlock, DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DELIVERABILITY_DIAGNOSTIC_CODES, type DagreLayoutEdge, type DagreLayoutInput, type DagreLayoutNode, type DagreLayoutOptions, type DefaultTextMeasurerOptions, DeterministicTextMeasurer, type Diagnostic, type DiagnosticPathSegment, type DiagnosticSeverity, type DiagramDirection, type DiagramFrame, type DiagramMetadata, type DiagramStage, type DistributeConstraint, type DistributionAxis, type DslDiagnostic, type DslDiagnosticLayer, type DslOutputFormat, type EdgeArrowhead, type EdgeEndpoint, type EdgeStrokeStyle, type EvidenceCell, type EvidencePanel, type EvidencePanelItem, type EvidencePanelKind, type EvidenceTextLayout, type ExactPositionConstraint, type ExportFormat, type ExportOptions, type ExportResult, type InitialLayoutResult, type Insets, type IntentDiagram, type IntentEdge, type IntentGroup, type IntentNode, type JsonObject, type JsonPrimitive, type JsonValue, type Label, type LabelFitOptions, LabelFitter, type LabelLayout, type LabelLineLayout, type LayoutLock, type LayoutLockSource, type MatrixBlock, type NodeBase, type NodeCompartments, type NodePort, type NodeShape, type NormalizeDiagramDslOptions, type NormalizeDiagramDslResult, type NormalizedDiagram, type NormalizedEdge, type NormalizedGroup, type NormalizedNode, type ParseDiagramDslOptions, type ParseDiagramDslResult, type ParsedEdgeShorthand, type Point, type PortKind, type PortShiftingOptions, type PortSide, type PreparedText, PretextTextMeasurer, type RelativePositionConstraint, type RelativePositionRelation, type RenderDiagramDslOptions, type RenderDiagramDslResult, type RouteEdgeInput, type RouteEdgeResult, type RouteKind, type ShapeGeometry, type ShapeGeometryInput, type Size, type SolveDiagramOptions, type SolvedTextAnnotation, type Swimlane, type SwimlaneLane, type SwimlaneLayout, type SwimlaneOrientation, type TableBlock, type TableColumn, type TableRow, type TextCursor, type TextLayout, type TextLayoutLine, type TextMeasurementBackend, type TextMeasurer, type TextStyleOptions, type TextSurfaceKind, type VisualStyle, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
package/dist/index.d.ts CHANGED
@@ -80,6 +80,14 @@ interface Diagnostic {
80
80
  path?: DiagnosticPathSegment[];
81
81
  detail?: JsonObject;
82
82
  }
83
+ /**
84
+ * Diagnostic codes that indicate the solver produced a degraded
85
+ * (non-deliverable) layout. Downstream consumers can gate on the
86
+ * {@link CoordinatedDiagram.degraded} flag or use the
87
+ * {@link SolveDiagramOptions.strict} option to promote these to
88
+ * errors.
89
+ */
90
+ declare const DELIVERABILITY_DIAGNOSTIC_CODES: ReadonlySet<string>;
83
91
 
84
92
  interface TextStyleOptions {
85
93
  fontFamily: string;
@@ -486,6 +494,8 @@ interface CoordinatedDiagram {
486
494
  evidencePanels?: CoordinatedEvidencePanel[];
487
495
  textAnnotations?: SolvedTextAnnotation[];
488
496
  diagnostics: Diagnostic[];
497
+ /** True when any deliverability-breaking diagnostic was emitted. */
498
+ degraded: boolean;
489
499
  bounds: Box;
490
500
  frame?: CoordinatedFrame;
491
501
  metadata?: DiagramMetadata;
@@ -668,7 +678,7 @@ interface InitialLayoutResult {
668
678
 
669
679
  declare function runDagreInitialLayout(input: DagreLayoutInput): InitialLayoutResult;
670
680
 
671
- type RouteKind = "orthogonal" | "straight";
681
+ type RouteKind = "orthogonal" | "straight" | "obstacle-avoiding";
672
682
  interface RouteEdgeInput {
673
683
  kind?: RouteKind;
674
684
  direction: DiagramDirection;
@@ -700,6 +710,8 @@ declare function stringifyCanonical(value: unknown, precision?: number): string;
700
710
  interface SolveDiagramOptions {
701
711
  routeKind?: RouteKind;
702
712
  obstacleMargin?: number | Insets;
713
+ /** Extra horizontal/vertical clearance reserved around nodes for edge corridors. */
714
+ routingGutter?: number;
703
715
  overlapSpacing?: number;
704
716
  minLaneGutter?: number;
705
717
  prefitLabelSize?: boolean;
@@ -715,6 +727,8 @@ interface SolveDiagramOptions {
715
727
  cjkFontFamily?: string | false;
716
728
  minCjkFontSize?: number | false;
717
729
  textMeasurer?: TextMeasurer;
730
+ /** When true, promote deliverability-breaking diagnostics to errors. */
731
+ strict?: boolean;
718
732
  }
719
733
  interface PortShiftingOptions {
720
734
  enabled?: boolean;
@@ -732,4 +746,4 @@ declare function solveDiagram(diagram: NormalizedDiagram, options?: SolveDiagram
732
746
  */
733
747
  declare function solveDiagramSafe(diagram: NormalizedDiagram, options?: SolveDiagramOptions): CoordinatedDiagram;
734
748
 
735
- export { type AlignConstraint, type AlignmentAxis, type AnchorName, type AnchorPoint, type Arrowhead, type Box, type CanonicalJson, type CanonicalizeOptions, type Constraint, type ConstraintBase, type ConstraintSolverInput, type ConstraintSolverResult, type ConstraintTarget, type ConstraintTargetKind, type ContainerGeometry, type ContainerGeometryInput, type ContainmentConstraint, type CoordinatedDiagram, type CoordinatedEdge, type CoordinatedEvidencePanel, type CoordinatedFrame, type CoordinatedGroup, type CoordinatedMatrixBlock, type CoordinatedNode, type CoordinatedPort, type CoordinatedTableBlock, DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, type DagreLayoutEdge, type DagreLayoutInput, type DagreLayoutNode, type DagreLayoutOptions, type DefaultTextMeasurerOptions, DeterministicTextMeasurer, type Diagnostic, type DiagnosticPathSegment, type DiagnosticSeverity, type DiagramDirection, type DiagramFrame, type DiagramMetadata, type DiagramStage, type DistributeConstraint, type DistributionAxis, type DslDiagnostic, type DslDiagnosticLayer, type DslOutputFormat, type EdgeArrowhead, type EdgeEndpoint, type EdgeStrokeStyle, type EvidenceCell, type EvidencePanel, type EvidencePanelItem, type EvidencePanelKind, type EvidenceTextLayout, type ExactPositionConstraint, type ExportFormat, type ExportOptions, type ExportResult, type InitialLayoutResult, type Insets, type IntentDiagram, type IntentEdge, type IntentGroup, type IntentNode, type JsonObject, type JsonPrimitive, type JsonValue, type Label, type LabelFitOptions, LabelFitter, type LabelLayout, type LabelLineLayout, type LayoutLock, type LayoutLockSource, type MatrixBlock, type NodeBase, type NodeCompartments, type NodePort, type NodeShape, type NormalizeDiagramDslOptions, type NormalizeDiagramDslResult, type NormalizedDiagram, type NormalizedEdge, type NormalizedGroup, type NormalizedNode, type ParseDiagramDslOptions, type ParseDiagramDslResult, type ParsedEdgeShorthand, type Point, type PortKind, type PortShiftingOptions, type PortSide, type PreparedText, PretextTextMeasurer, type RelativePositionConstraint, type RelativePositionRelation, type RenderDiagramDslOptions, type RenderDiagramDslResult, type RouteEdgeInput, type RouteEdgeResult, type RouteKind, type ShapeGeometry, type ShapeGeometryInput, type Size, type SolveDiagramOptions, type SolvedTextAnnotation, type Swimlane, type SwimlaneLane, type SwimlaneLayout, type SwimlaneOrientation, type TableBlock, type TableColumn, type TableRow, type TextCursor, type TextLayout, type TextLayoutLine, type TextMeasurementBackend, type TextMeasurer, type TextStyleOptions, type TextSurfaceKind, type VisualStyle, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
749
+ export { type AlignConstraint, type AlignmentAxis, type AnchorName, type AnchorPoint, type Arrowhead, type Box, type CanonicalJson, type CanonicalizeOptions, type Constraint, type ConstraintBase, type ConstraintSolverInput, type ConstraintSolverResult, type ConstraintTarget, type ConstraintTargetKind, type ContainerGeometry, type ContainerGeometryInput, type ContainmentConstraint, type CoordinatedDiagram, type CoordinatedEdge, type CoordinatedEvidencePanel, type CoordinatedFrame, type CoordinatedGroup, type CoordinatedMatrixBlock, type CoordinatedNode, type CoordinatedPort, type CoordinatedTableBlock, DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DELIVERABILITY_DIAGNOSTIC_CODES, type DagreLayoutEdge, type DagreLayoutInput, type DagreLayoutNode, type DagreLayoutOptions, type DefaultTextMeasurerOptions, DeterministicTextMeasurer, type Diagnostic, type DiagnosticPathSegment, type DiagnosticSeverity, type DiagramDirection, type DiagramFrame, type DiagramMetadata, type DiagramStage, type DistributeConstraint, type DistributionAxis, type DslDiagnostic, type DslDiagnosticLayer, type DslOutputFormat, type EdgeArrowhead, type EdgeEndpoint, type EdgeStrokeStyle, type EvidenceCell, type EvidencePanel, type EvidencePanelItem, type EvidencePanelKind, type EvidenceTextLayout, type ExactPositionConstraint, type ExportFormat, type ExportOptions, type ExportResult, type InitialLayoutResult, type Insets, type IntentDiagram, type IntentEdge, type IntentGroup, type IntentNode, type JsonObject, type JsonPrimitive, type JsonValue, type Label, type LabelFitOptions, LabelFitter, type LabelLayout, type LabelLineLayout, type LayoutLock, type LayoutLockSource, type MatrixBlock, type NodeBase, type NodeCompartments, type NodePort, type NodeShape, type NormalizeDiagramDslOptions, type NormalizeDiagramDslResult, type NormalizedDiagram, type NormalizedEdge, type NormalizedGroup, type NormalizedNode, type ParseDiagramDslOptions, type ParseDiagramDslResult, type ParsedEdgeShorthand, type Point, type PortKind, type PortShiftingOptions, type PortSide, type PreparedText, PretextTextMeasurer, type RelativePositionConstraint, type RelativePositionRelation, type RenderDiagramDslOptions, type RenderDiagramDslResult, type RouteEdgeInput, type RouteEdgeResult, type RouteKind, type ShapeGeometry, type ShapeGeometryInput, type Size, type SolveDiagramOptions, type SolvedTextAnnotation, type Swimlane, type SwimlaneLane, type SwimlaneLayout, type SwimlaneOrientation, type TableBlock, type TableColumn, type TableRow, type TextCursor, type TextLayout, type TextLayoutLine, type TextMeasurementBackend, type TextMeasurer, type TextStyleOptions, type TextSurfaceKind, type VisualStyle, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
package/dist/index.js CHANGED
@@ -2068,7 +2068,7 @@ function point(value) {
2068
2068
  return { x: value.x, y: value.y };
2069
2069
  }
2070
2070
  var directionSchema = z.enum(["TB", "LR", "BT", "RL"]);
2071
- var routeKindSchema = z.enum(["orthogonal", "straight"]);
2071
+ var routeKindSchema = z.enum(["orthogonal", "straight", "obstacle-avoiding"]);
2072
2072
  var outputFormatSchema = z.enum(["svg", "excalidraw"]);
2073
2073
  var edgeStrokeStyleSchema = z.enum(["solid", "dashed"]);
2074
2074
  var edgeArrowheadSchema = z.enum(["triangle", "hollowTriangle"]);
@@ -3747,6 +3747,15 @@ function indent(value) {
3747
3747
  function indentLines(values) {
3748
3748
  return values.map(indent);
3749
3749
  }
3750
+
3751
+ // src/ir/diagnostics.ts
3752
+ var DELIVERABILITY_DIAGNOSTIC_CODES = /* @__PURE__ */ new Set([
3753
+ "constraints.locked-target-not-moved",
3754
+ "routing.evidence.crossing_forbidden",
3755
+ "routing.obstacle.unavoidable",
3756
+ "route_obstacle_fallback",
3757
+ "routing.text-clearance.unresolved"
3758
+ ]);
3750
3759
  var DEFAULT_OPTIONS = {
3751
3760
  nodesep: 80,
3752
3761
  ranksep: 100,
@@ -3945,6 +3954,51 @@ function routeEdge(input) {
3945
3954
  )
3946
3955
  );
3947
3956
  if (hardClearCandidate !== void 0) {
3957
+ let bestPoints2 = hardClearCandidate.points;
3958
+ if (input.kind === "obstacle-avoiding") {
3959
+ const allObstacles = [...softObstacles, ...hardObstacles];
3960
+ for (const candidate of candidateRoutes) {
3961
+ if (routeCrossesBoxes(candidate.points, hardObstacles) || routeIntersectsEndpointInteriors(
3962
+ candidate.points,
3963
+ candidate.endpointObstacles
3964
+ )) {
3965
+ continue;
3966
+ }
3967
+ const rerouted2 = greedyRerouteAroundObstacles(
3968
+ candidate.points,
3969
+ allObstacles,
3970
+ 3
3971
+ );
3972
+ if (!routeCrossesBoxes(rerouted2, allObstacles) && !routeIntersectsEndpointInteriors(
3973
+ rerouted2,
3974
+ candidate.endpointObstacles
3975
+ )) {
3976
+ return {
3977
+ points: finalizeRoute(
3978
+ rerouted2,
3979
+ softObstacles,
3980
+ hardObstacles,
3981
+ diagnostics
3982
+ ),
3983
+ diagnostics
3984
+ };
3985
+ }
3986
+ }
3987
+ const rerouted = greedyRerouteAroundObstacles(
3988
+ bestPoints2,
3989
+ allObstacles,
3990
+ 3
3991
+ );
3992
+ const reroutedAvoidsEndpointInteriors = !routeIntersectsEndpointInteriors(
3993
+ rerouted,
3994
+ hardClearCandidate.endpointObstacles
3995
+ );
3996
+ if (reroutedAvoidsEndpointInteriors) {
3997
+ if (routeCrossesBoxes(rerouted, hardObstacles) && !routeCrossesBoxes(bestPoints2, hardObstacles)) ; else {
3998
+ bestPoints2 = rerouted;
3999
+ }
4000
+ }
4001
+ }
3948
4002
  diagnostics.push({
3949
4003
  severity: "warning",
3950
4004
  code: "routing.obstacle.unavoidable",
@@ -3952,7 +4006,7 @@ function routeEdge(input) {
3952
4006
  });
3953
4007
  return {
3954
4008
  points: finalizeRoute(
3955
- hardClearCandidate.points,
4009
+ bestPoints2,
3956
4010
  softObstacles,
3957
4011
  hardObstacles,
3958
4012
  diagnostics
@@ -3961,6 +4015,33 @@ function routeEdge(input) {
3961
4015
  };
3962
4016
  }
3963
4017
  if (hardObstacles.length > 0) {
4018
+ let bestPoints2 = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
4019
+ if (input.kind === "obstacle-avoiding") {
4020
+ const allObstacles = [...softObstacles, ...hardObstacles];
4021
+ for (const candidate of candidateRoutes) {
4022
+ const rerouted = greedyRerouteAroundObstacles(
4023
+ candidate.points,
4024
+ allObstacles,
4025
+ 5
4026
+ );
4027
+ if (!routeCrossesBoxes(rerouted, allObstacles)) {
4028
+ return {
4029
+ points: finalizeRoute(
4030
+ rerouted,
4031
+ softObstacles,
4032
+ hardObstacles,
4033
+ diagnostics
4034
+ ),
4035
+ diagnostics
4036
+ };
4037
+ }
4038
+ }
4039
+ bestPoints2 = greedyRerouteAroundObstacles(
4040
+ candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4041
+ allObstacles,
4042
+ 5
4043
+ );
4044
+ }
3964
4045
  diagnostics.push({
3965
4046
  severity: "error",
3966
4047
  code: "routing.evidence.crossing_forbidden",
@@ -3968,7 +4049,7 @@ function routeEdge(input) {
3968
4049
  });
3969
4050
  return {
3970
4051
  points: finalizeRoute(
3971
- candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4052
+ bestPoints2,
3972
4053
  softObstacles,
3973
4054
  hardObstacles,
3974
4055
  diagnostics
@@ -3976,6 +4057,33 @@ function routeEdge(input) {
3976
4057
  diagnostics
3977
4058
  };
3978
4059
  }
4060
+ let bestPoints = candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors);
4061
+ if (input.kind === "obstacle-avoiding") {
4062
+ const allObstacles = [...softObstacles, ...hardObstacles];
4063
+ for (const candidate of candidateRoutes) {
4064
+ const rerouted = greedyRerouteAroundObstacles(
4065
+ candidate.points,
4066
+ allObstacles,
4067
+ 5
4068
+ );
4069
+ if (!routeCrossesBoxes(rerouted, allObstacles)) {
4070
+ return {
4071
+ points: finalizeRoute(
4072
+ rerouted,
4073
+ softObstacles,
4074
+ hardObstacles,
4075
+ diagnostics
4076
+ ),
4077
+ diagnostics
4078
+ };
4079
+ }
4080
+ }
4081
+ bestPoints = greedyRerouteAroundObstacles(
4082
+ candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4083
+ allObstacles,
4084
+ 5
4085
+ );
4086
+ }
3979
4087
  diagnostics.push({
3980
4088
  severity: "warning",
3981
4089
  code: "routing.obstacle.unavoidable",
@@ -3983,7 +4091,7 @@ function routeEdge(input) {
3983
4091
  });
3984
4092
  return {
3985
4093
  points: finalizeRoute(
3986
- candidateRoutes[0]?.points ?? fallbackRoute(input, defaultAnchors),
4094
+ bestPoints,
3987
4095
  softObstacles,
3988
4096
  hardObstacles,
3989
4097
  diagnostics
@@ -3993,21 +4101,29 @@ function routeEdge(input) {
3993
4101
  }
3994
4102
  function finalizeRoute(points, softObstacles, hardObstacles, diagnostics) {
3995
4103
  const simplified = simplifyRoute(points);
4104
+ if (simplified.length >= 3) {
4105
+ return simplified;
4106
+ }
3996
4107
  const crossesHardObstacles = routeCrossesBoxes(simplified, hardObstacles);
3997
4108
  const crossesSoftObstacles = routeCrossesBoxes(simplified, softObstacles);
3998
- if (simplified.length < 3 && (crossesHardObstacles || crossesSoftObstacles)) {
4109
+ if (!crossesHardObstacles && !crossesSoftObstacles) {
4110
+ return simplified;
4111
+ }
4112
+ const expanded = expandFallbackRoute(simplified, [
4113
+ ...softObstacles,
4114
+ ...hardObstacles
4115
+ ]);
4116
+ const expandedCrossesHard = routeCrossesBoxes(expanded, hardObstacles);
4117
+ const expandedCrossesSoft = routeCrossesBoxes(expanded, softObstacles);
4118
+ if (expandedCrossesHard || expandedCrossesSoft) {
3999
4119
  diagnostics.push({
4000
- severity: crossesHardObstacles ? "error" : "warning",
4120
+ severity: expandedCrossesHard ? "error" : "warning",
4001
4121
  code: "route_obstacle_fallback",
4002
4122
  message: "Obstacle-aware routing fell back to fewer than three route points.",
4003
4123
  detail: { pointCount: simplified.length }
4004
4124
  });
4005
- return expandFallbackRoute(simplified, [
4006
- ...softObstacles,
4007
- ...hardObstacles
4008
- ]);
4009
4125
  }
4010
- return simplified;
4126
+ return expanded;
4011
4127
  }
4012
4128
  function expandFallbackRoute(points, obstacles) {
4013
4129
  if (points.length !== 2) {
@@ -4038,12 +4154,12 @@ function expandFallbackRoute(points, obstacles) {
4038
4154
  const hv = diagonalDetourHV(source, target, obstacles);
4039
4155
  const vh = diagonalDetourVH(source, target, obstacles);
4040
4156
  const viable = [hv, vh].filter((c) => !routeCrossesBoxes(c, obstacles));
4041
- if (viable.length > 0) {
4157
+ const [firstViable, ...remainingViable] = viable;
4158
+ if (firstViable !== void 0) {
4042
4159
  const directLen = Math.hypot(target.x - source.x, target.y - source.y);
4043
- let best = viable[0];
4044
- for (let i = 1; i < viable.length; i += 1) {
4045
- const cand = viable[i];
4046
- if (cand !== void 0 && pathLength(cand) - directLen < pathLength(best) - directLen) {
4160
+ let best = firstViable;
4161
+ for (const cand of remainingViable) {
4162
+ if (pathLength(cand) - directLen < pathLength(best) - directLen) {
4047
4163
  best = cand;
4048
4164
  }
4049
4165
  }
@@ -4133,6 +4249,70 @@ function insetBox(box, margin) {
4133
4249
  height: box.height - margin * 2
4134
4250
  };
4135
4251
  }
4252
+ function greedyRerouteAroundObstacles(points, obstacles, maxIterations) {
4253
+ let current = [...points];
4254
+ for (let iter = 0; iter < maxIterations; iter++) {
4255
+ const improved = pushRouteAwayFromObstacles(current, obstacles);
4256
+ if (improved === null) {
4257
+ break;
4258
+ }
4259
+ current = improved;
4260
+ if (!routeCrossesBoxes(current, obstacles)) {
4261
+ break;
4262
+ }
4263
+ }
4264
+ return current;
4265
+ }
4266
+ function pushRouteAwayFromObstacles(points, obstacles) {
4267
+ const result = [];
4268
+ let improved = false;
4269
+ for (let i = 0; i < points.length - 1; i++) {
4270
+ const a = points[i];
4271
+ const b = points[i + 1];
4272
+ if (a === void 0 || b === void 0) {
4273
+ result.push(a ?? b ?? { x: 0, y: 0 });
4274
+ continue;
4275
+ }
4276
+ result.push(a);
4277
+ const intersectors = obstacles.filter(
4278
+ (obs) => segmentIntersectsBox(a, b, obs)
4279
+ );
4280
+ if (intersectors.length === 0) {
4281
+ continue;
4282
+ }
4283
+ const mx = (a.x + b.x) / 2;
4284
+ const my = (a.y + b.y) / 2;
4285
+ const isHorizontal = a.y === b.y;
4286
+ const margin = 12;
4287
+ let bestWaypoint = null;
4288
+ let bestDist = Infinity;
4289
+ for (const obs of intersectors) {
4290
+ const candidates = isHorizontal ? [
4291
+ { x: mx, y: obs.y - margin },
4292
+ { x: mx, y: obs.y + obs.height + margin }
4293
+ ] : [
4294
+ { x: obs.x - margin, y: my },
4295
+ { x: obs.x + obs.width + margin, y: my }
4296
+ ];
4297
+ for (const wp of candidates) {
4298
+ const dist = Math.hypot(wp.x - mx, wp.y - my);
4299
+ if (dist < bestDist) {
4300
+ bestDist = dist;
4301
+ bestWaypoint = wp;
4302
+ }
4303
+ }
4304
+ }
4305
+ if (bestWaypoint !== null) {
4306
+ result.push(bestWaypoint);
4307
+ improved = true;
4308
+ }
4309
+ }
4310
+ const last = points[points.length - 1];
4311
+ if (last !== void 0) {
4312
+ result.push(last);
4313
+ }
4314
+ return improved ? result : null;
4315
+ }
4136
4316
  function fallbackRoute(input, defaultAnchors) {
4137
4317
  return [
4138
4318
  getEdgePort(
@@ -4629,6 +4809,7 @@ function solveDiagram(diagram, options = {}) {
4629
4809
  ];
4630
4810
  const initialContentBounds = layoutBoxes.length === 0 ? { x: 0, y: 0, width: 0} : unionBoxes(layoutBoxes);
4631
4811
  placeEvidenceBlocks(
4812
+ options.obstacleMargin ?? 0,
4632
4813
  [
4633
4814
  ...coordinatedMatrices,
4634
4815
  ...coordinatedTables,
@@ -4695,17 +4876,35 @@ function solveDiagram(diagram, options = {}) {
4695
4876
  ...baseTextAnnotations.filter(isPreRouteTextObstacle),
4696
4877
  ...frameTextAnnotation.filter(isPreRouteTextObstacle)
4697
4878
  ];
4879
+ const margin = options.obstacleMargin ?? 0;
4880
+ const softObstacles = [
4881
+ ...coordinatedTables.map((table) => expandBox(table.box, margin)),
4882
+ ...coordinatedEvidencePanels.map((panel) => expandBox(panel.box, margin))
4883
+ ];
4884
+ const hardObstacles = coordinatedMatrices.map(
4885
+ (matrix) => expandBox(matrix.box, margin)
4886
+ );
4887
+ const titleBarObstacles = [];
4888
+ if (frame !== void 0) {
4889
+ titleBarObstacles.push(expandBox(frame.titleBox, margin));
4890
+ }
4891
+ for (const swimlane of coordinatedSwimlanes) {
4892
+ for (const lane of swimlane.lanes) {
4893
+ if (lane.headerBox !== void 0 && lane.headerBox.width > 0 && lane.headerBox.height > 0) {
4894
+ titleBarObstacles.push(expandBox(lane.headerBox, margin));
4895
+ }
4896
+ }
4897
+ }
4698
4898
  const coordinatedEdges = coordinateEdges(
4699
4899
  styledEdges,
4700
4900
  nodeGeometryById,
4701
4901
  coordinatedNodes,
4702
- [...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
4703
- [
4704
- ...coordinatedTables.map((table) => table.box),
4705
- ...coordinatedEvidencePanels.map((panel) => panel.box)
4706
- ],
4902
+ [...nodeGeometryById.values()].map(
4903
+ (geometry) => options.routingGutter === void 0 ? geometry.obstacleBox : expandBox(geometry.obstacleBox, options.routingGutter)
4904
+ ),
4905
+ [...softObstacles, ...titleBarObstacles],
4707
4906
  routingTextObstacles,
4708
- coordinatedMatrices.map((matrix) => matrix.box),
4907
+ hardObstacles,
4709
4908
  diagram.direction,
4710
4909
  options,
4711
4910
  diagnostics
@@ -4740,6 +4939,16 @@ function solveDiagram(diagram, options = {}) {
4740
4939
  options.pageBounds
4741
4940
  )
4742
4941
  );
4942
+ let degraded = false;
4943
+ const resultDiagnostics = diagnostics.map((diagnostic) => {
4944
+ if (DELIVERABILITY_DIAGNOSTIC_CODES.has(diagnostic.code)) {
4945
+ degraded = true;
4946
+ if (options.strict) {
4947
+ return { ...diagnostic, severity: "error" };
4948
+ }
4949
+ }
4950
+ return diagnostic;
4951
+ });
4743
4952
  return {
4744
4953
  id: diagram.id,
4745
4954
  ...diagram.title === void 0 ? {} : { title: diagram.title },
@@ -4751,7 +4960,8 @@ function solveDiagram(diagram, options = {}) {
4751
4960
  ...coordinatedMatrices.length === 0 ? {} : { matrices: coordinatedMatrices },
4752
4961
  ...coordinatedTables.length === 0 ? {} : { tables: coordinatedTables },
4753
4962
  ...coordinatedEvidencePanels.length === 0 ? {} : { evidencePanels: coordinatedEvidencePanels },
4754
- diagnostics,
4963
+ diagnostics: resultDiagnostics,
4964
+ degraded,
4755
4965
  bounds: frame === void 0 ? unionBoxes(boundsBase) : unionBoxes([...boundsBase, frame.box, frame.titleBox]),
4756
4966
  ...frame === void 0 ? {} : { frame },
4757
4967
  ...textAnnotations.length === 0 ? {} : { textAnnotations },
@@ -6140,16 +6350,25 @@ function blockBox(block, defaultSize) {
6140
6350
  height: block.size?.height ?? defaultSize.height
6141
6351
  };
6142
6352
  }
6143
- function placeEvidenceBlocks(blocks, contentBounds) {
6353
+ function placeEvidenceBlocks(obstacleMargin, blocks, contentBounds) {
6354
+ const margin = normalizeInsets(obstacleMargin);
6355
+ const horizontalGap = Math.max(
6356
+ DEFAULT_EVIDENCE_BLOCK_GAP,
6357
+ margin.right + margin.left
6358
+ );
6359
+ const verticalGap = Math.max(
6360
+ DEFAULT_EVIDENCE_BLOCK_GAP,
6361
+ margin.bottom + margin.top
6362
+ );
6144
6363
  let nextY = contentBounds.y;
6145
- const x = contentBounds.x + contentBounds.width + DEFAULT_EVIDENCE_BLOCK_GAP;
6364
+ const x = contentBounds.x + contentBounds.width + horizontalGap;
6146
6365
  for (const block of blocks) {
6147
6366
  if (block.position !== void 0) {
6148
6367
  continue;
6149
6368
  }
6150
6369
  block.box.x = x;
6151
6370
  block.box.y = nextY;
6152
- nextY += block.box.height + DEFAULT_EVIDENCE_BLOCK_GAP;
6371
+ nextY += block.box.height + verticalGap;
6153
6372
  }
6154
6373
  }
6155
6374
  function columnXOffsets(table, box) {
@@ -7005,9 +7224,7 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
7005
7224
  { x: placement.x, y: placement.y + offset }
7006
7225
  );
7007
7226
  }
7008
- return candidates;
7009
- }
7010
- if (segment.start.x === segment.end.x) {
7227
+ } else if (segment.start.x === segment.end.x) {
7011
7228
  const needed = layout2.box.width / 2 + EDGE_LABEL_CLEARANCE;
7012
7229
  const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
7013
7230
  for (let step = 1; step <= maxSteps; step += 1) {
@@ -7017,7 +7234,83 @@ function edgeLabelAnchorCandidates(points, placement, layout2) {
7017
7234
  { x: placement.x - offset, y: placement.y }
7018
7235
  );
7019
7236
  }
7020
- return candidates;
7237
+ } else {
7238
+ const dx = segment.end.x - segment.start.x;
7239
+ const dy = segment.end.y - segment.start.y;
7240
+ const segLen = Math.hypot(dx, dy);
7241
+ if (segLen > 0) {
7242
+ const nx = -dy / segLen;
7243
+ const ny = dx / segLen;
7244
+ const needed = (Math.abs(nx) * layout2.box.width + Math.abs(ny) * layout2.box.height) / 2 + EDGE_LABEL_CLEARANCE;
7245
+ const maxSteps = Math.max(12, Math.ceil(needed / EDGE_LABEL_CLEARANCE));
7246
+ for (let step = 1; step <= maxSteps; step += 1) {
7247
+ const offset = EDGE_LABEL_CLEARANCE * step;
7248
+ candidates.push(
7249
+ { x: placement.x + nx * offset, y: placement.y + ny * offset },
7250
+ { x: placement.x - nx * offset, y: placement.y - ny * offset }
7251
+ );
7252
+ }
7253
+ }
7254
+ }
7255
+ const totalLen = points.reduce((sum, p, idx) => {
7256
+ if (idx === 0) return 0;
7257
+ const prev = points[idx - 1];
7258
+ return sum + Math.hypot((p?.x ?? 0) - (prev?.x ?? 0), (p?.y ?? 0) - (prev?.y ?? 0));
7259
+ }, 0);
7260
+ if (totalLen > 200) {
7261
+ for (const ratio of [0.25, 0.75]) {
7262
+ const qp = labelPlacementAtRatio(points, ratio, totalLen);
7263
+ if (qp !== void 0) {
7264
+ candidates.push(qp);
7265
+ const qTargetDist = totalLen * ratio;
7266
+ let qTravelled = 0;
7267
+ let seg;
7268
+ for (let si = 1; si < points.length; si++) {
7269
+ const sp = points[si - 1];
7270
+ const sc = points[si];
7271
+ if (sp === void 0 || sc === void 0) continue;
7272
+ const sl = Math.hypot(sc.x - sp.x, sc.y - sp.y);
7273
+ if (sl <= 0) continue;
7274
+ if (qTravelled + sl >= qTargetDist) {
7275
+ seg = { start: sp, end: sc, length: sl };
7276
+ break;
7277
+ }
7278
+ qTravelled += sl;
7279
+ }
7280
+ if (seg !== void 0) {
7281
+ const segLen = Math.hypot(
7282
+ seg.end.x - seg.start.x,
7283
+ seg.end.y - seg.start.y
7284
+ );
7285
+ const qpNeeded = seg.start.y === seg.end.y ? layout2.box.height / 2 + EDGE_LABEL_CLEARANCE : seg.start.x === seg.end.x ? layout2.box.width / 2 + EDGE_LABEL_CLEARANCE : (Math.abs(seg.start.y - seg.end.y) * layout2.box.width + Math.abs(seg.end.x - seg.start.x) * layout2.box.height) / (2 * segLen) + EDGE_LABEL_CLEARANCE;
7286
+ const qpMaxSteps = Math.max(
7287
+ 12,
7288
+ Math.ceil(qpNeeded / EDGE_LABEL_CLEARANCE)
7289
+ );
7290
+ for (let step = 1; step <= qpMaxSteps; step += 1) {
7291
+ const offset = EDGE_LABEL_CLEARANCE * step;
7292
+ if (seg.start.y === seg.end.y) {
7293
+ candidates.push(
7294
+ { x: qp.x, y: qp.y - offset },
7295
+ { x: qp.x, y: qp.y + offset }
7296
+ );
7297
+ } else if (seg.start.x === seg.end.x) {
7298
+ candidates.push(
7299
+ { x: qp.x - offset, y: qp.y },
7300
+ { x: qp.x + offset, y: qp.y }
7301
+ );
7302
+ } else {
7303
+ const nx = -(seg.end.y - seg.start.y) / segLen;
7304
+ const ny = (seg.end.x - seg.start.x) / segLen;
7305
+ candidates.push(
7306
+ { x: qp.x + nx * offset, y: qp.y + ny * offset },
7307
+ { x: qp.x - nx * offset, y: qp.y - ny * offset }
7308
+ );
7309
+ }
7310
+ }
7311
+ }
7312
+ }
7313
+ }
7021
7314
  }
7022
7315
  return candidates;
7023
7316
  }
@@ -7074,6 +7367,34 @@ function nonZeroSegments2(points) {
7074
7367
  }
7075
7368
  return segments;
7076
7369
  }
7370
+ function labelPlacementAtRatio(points, ratio, totalLength) {
7371
+ if (points.length < 2 || ratio < 0 || ratio > 1) {
7372
+ return void 0;
7373
+ }
7374
+ const targetDist = totalLength * ratio;
7375
+ let travelled = 0;
7376
+ for (let idx = 1; idx < points.length; idx++) {
7377
+ const prev = points[idx - 1];
7378
+ const curr = points[idx];
7379
+ if (prev === void 0 || curr === void 0) {
7380
+ continue;
7381
+ }
7382
+ const segLen = Math.hypot(curr.x - prev.x, curr.y - prev.y);
7383
+ if (segLen <= 0) {
7384
+ continue;
7385
+ }
7386
+ if (travelled + segLen >= targetDist) {
7387
+ const t = (targetDist - travelled) / segLen;
7388
+ const offset = labelOffset2({ start: prev, end: curr, length: segLen });
7389
+ return {
7390
+ x: prev.x + (curr.x - prev.x) * t + offset.x,
7391
+ y: prev.y + (curr.y - prev.y) * t + offset.y
7392
+ };
7393
+ }
7394
+ travelled += segLen;
7395
+ }
7396
+ return void 0;
7397
+ }
7077
7398
  function labelOffset2(segment) {
7078
7399
  const offset = 10;
7079
7400
  const dx = segment.end.x - segment.start.x;
@@ -7188,7 +7509,7 @@ function renderDiagramDsl(source, options = {}) {
7188
7509
  return { diagnostics };
7189
7510
  }
7190
7511
  const solved = solveDiagram(normalized.diagram, {
7191
- routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
7512
+ routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : normalized.diagram.metadata?.routeKind === "obstacle-avoiding" ? "obstacle-avoiding" : "orthogonal",
7192
7513
  ...solvePortShiftingOption(normalized.diagram.metadata?.portShifting),
7193
7514
  ...options.textMeasurer === void 0 ? {} : { textMeasurer: options.textMeasurer }
7194
7515
  });
@@ -7378,6 +7699,6 @@ function isPointLikeRecord(value) {
7378
7699
  return isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number";
7379
7700
  }
7380
7701
 
7381
- export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DeterministicTextMeasurer, LabelFitter, PretextTextMeasurer, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
7702
+ export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DELIVERABILITY_DIAGNOSTIC_CODES, DeterministicTextMeasurer, LabelFitter, PretextTextMeasurer, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, solveDiagramSafe, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
7382
7703
  //# sourceMappingURL=index.js.map
7383
7704
  //# sourceMappingURL=index.js.map