@nfq/eslint-config 4.0.0-beta.14 → 4.0.0-beta.15

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.
@@ -18,7 +18,7 @@ const hexagonalDependencyDirection = createRule$8({
18
18
  missingServiceClass: 'Client services must export a class implementing an adapter interface.',
19
19
  missingServiceImplements: 'Client services classes must implement an adapter interface.',
20
20
  serverDirection: 'Server dependency direction is domain → errors → services → controllers.',
21
- uiApplicationImport: 'UI may only import from application/useCases, application/configs, or application/utils.'
21
+ uiApplicationImport: 'UI may only import from UI, shared, domain, or application useCases/configs/utils.'
22
22
  },
23
23
  schema: [],
24
24
  type: 'problem'
@@ -153,6 +153,9 @@ const hexagonalDependencyDirection = createRule$8({
153
153
  return;
154
154
  }
155
155
  if (currentLayer === 'client-application' || currentLayer === 'client-application-configs' || currentLayer === 'client-application-services' || currentLayer === 'client-application-usecases' || currentLayer === 'client-application-utils') {
156
+ if (targetLayer === 'client-shared') {
157
+ return;
158
+ }
156
159
  if (targetLayer === 'client-ui') {
157
160
  context.report({
158
161
  messageId: 'crossBoundary',
@@ -162,7 +165,7 @@ const hexagonalDependencyDirection = createRule$8({
162
165
  return;
163
166
  }
164
167
  if (currentLayer === 'client-ui') {
165
- const allowed = targetLayer === 'client-ui' || targetLayer === 'client-shared' || targetLayer === 'client-application-configs' || targetLayer === 'client-application-usecases' || targetLayer === 'client-application-utils';
168
+ const allowed = targetLayer === 'client-ui' || targetLayer === 'client-shared' || targetLayer === 'client-domain' || targetLayer === 'client-application-configs' || targetLayer === 'client-application-usecases' || targetLayer === 'client-application-utils';
166
169
  if (!allowed) {
167
170
  context.report({
168
171
  messageId: 'uiApplicationImport',
@@ -1 +1 @@
1
- {"version":3,"file":"hexagonal-dependency-direction.js","sources":["../../../../src/rules/custom/hexagonal-dependency-direction.ts"],"sourcesContent":["import {ESLintUtils} from '@typescript-eslint/utils';\n\nimport {\n buildAliasMap,\n getLayer,\n isClientLayer,\n isClientUtils,\n isConfigLayer,\n isDomainLayer,\n isMobxStoreFile,\n isServerLayer,\n isServerUtils,\n normalizePath,\n readAliasesFromSettings,\n resolveAliasImport,\n resolveRelativeImport\n} from './utils/hexagonal-dependency-direction-utils';\n\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype RuleOptions = [];\n\ntype MessageIds = 'apiImport' | 'clientApiImport' | 'crossBoundary' | 'domainImport' | 'domainUtilsImport'\n | 'missingServiceClass' | 'missingServiceImplements' | 'serverDirection' | 'uiApplicationImport';\n\nconst requiredAliases = [\n 'Domain',\n 'Application',\n 'UI',\n 'ApiRoutes',\n 'Controllers',\n 'ServerDomain',\n 'Services'\n];\n\nexport const hexagonalDependencyDirection = createRule<RuleOptions, MessageIds>({\n defaultOptions: [],\n meta: {\n docs: {description: 'Enforce hexagonal dependency direction and boundary imports.'},\n messages: {\n apiImport: 'API routes may only import controllers, middleware, or server utils.',\n clientApiImport: 'Only client application services may import API routes.',\n crossBoundary: 'Client and server layers must not import from each other.',\n domainImport: 'Domain layers must not import from other layers.',\n domainUtilsImport: 'Domain layers must not import from utils.',\n missingServiceClass: 'Client services must export a class implementing an adapter interface.',\n missingServiceImplements: 'Client services classes must implement an adapter interface.',\n serverDirection: 'Server dependency direction is domain → errors → services → controllers.',\n // eslint-disable-next-line @stylistic/max-len\n uiApplicationImport: 'UI may only import from application/useCases, application/configs, or application/utils.'\n },\n schema: [],\n type: 'problem'\n },\n name: 'hexagonal-dependency-direction',\n /**\n * This function creates the ESLint rule listeners for validating hexagonal dependency direction.\n * It initializes alias resolution, determines the current file layer, and wires up import checks.\n * It also validates service class exports and adapter implementations for client services.\n *\n * @param context The rule context used for reporting and settings.\n * @returns A rule listener map for import declarations and program validation.\n *\n * @example\n * ```tsx\n * const rule = hexagonalDependencyDirection.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const aliases = readAliasesFromSettings(context.settings);\n const aliasMap = buildAliasMap(aliases);\n\n const missing = requiredAliases.filter(alias => !aliasMap.has(alias));\n\n if (missing.length > 0) {\n return {};\n }\n\n const {filename} = context;\n const normalizedFile = normalizePath(filename);\n const currentLayer = getLayer(normalizedFile);\n\n if (currentLayer === 'unknown') {\n return {};\n }\n\n const isClient = isClientLayer(currentLayer);\n const isServer = isServerLayer(currentLayer);\n\n /**\n * This function validates that client application services export at least one class and that the class\n * implements an adapter interface. It scans the program body for class declarations and named exports\n * containing classes. It reports rule violations when no class is found or when a class lacks implemented\n * interfaces.\n *\n * @param program The program node to analyze for service class exports.\n *\n * @example\n * ```tsx\n * reportInvalidService(program);\n * ```\n */\n const reportInvalidService = (program: TSESTree.Program) => {\n if (currentLayer !== 'client-application-services') {\n return;\n }\n\n if (isMobxStoreFile(program)) {\n return;\n }\n\n const classes: (TSESTree.ClassDeclaration | TSESTree.ClassExpression)[] = [];\n\n for (const node of program.body) {\n // @ts-expect-error\n if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {\n classes.push(node);\n continue;\n }\n\n if (node.type === 'ExportNamedDeclaration' && node.declaration) {\n const {declaration} = node;\n\n // @ts-expect-error\n if (declaration.type === 'ClassDeclaration' || declaration.type === 'ClassExpression') {\n classes.push(declaration);\n }\n }\n }\n\n if (classes.length === 0) {\n context.report({\n messageId: 'missingServiceClass',\n node: program\n });\n\n return;\n }\n\n for (const classNode of classes) {\n if (classNode.implements.length === 0) {\n context.report({\n messageId: 'missingServiceImplements',\n node: classNode\n });\n }\n }\n };\n\n /**\n * Resolves an import source to a normalized path using aliases and relative paths. It first attempts to match\n * the source against configured aliases to ensure project-wide consistency. If no alias applies, it falls back\n * to resolving a relative import against the current file for local references.\n *\n * @param source The raw import source string from the import declaration.\n * @returns The resolved path string when resolution succeeds, or undefined when it cannot be resolved.\n *\n * @example\n * ```tsx\n * const resolved = resolveImport('@app/domain');\n * ```\n */\n const resolveImport = (source: string) => {\n const aliasResolved = resolveAliasImport(source, aliasMap);\n\n if (aliasResolved) {\n return aliasResolved;\n }\n\n return resolveRelativeImport(source, filename);\n };\n\n /**\n * Validates an import declaration against the hexagonal dependency rules for the current file.\n * It resolves the import source into a target layer and checks for forbidden cross-boundary or layer-specific dependencies.\n * It reports violations using the configured ESLint messages when a rule is broken.\n *\n * @param node The import declaration node to validate.\n *\n * @example\n * ```tsx\n * checkImport(node);\n * ```\n */\n // eslint-disable-next-line complexity\n const checkImport = (node: TSESTree.ImportDeclaration) => {\n const source = node.source.value;\n\n if (typeof source !== 'string') {\n return;\n }\n\n const resolved = resolveImport(source);\n\n if (!resolved) {\n return;\n }\n\n const targetLayer = getLayer(resolved);\n\n if (targetLayer === 'unknown') {\n return;\n }\n\n if (isClient && isServerLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (isServer && isClientLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (currentLayer === 'api') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'apiImport',\n node\n });\n }\n\n return;\n }\n\n if (targetLayer === 'api' && currentLayer !== 'client-application-services') {\n context.report({\n messageId: 'clientApiImport',\n node\n });\n\n return;\n }\n\n if (isDomainLayer(currentLayer)) {\n if (targetLayer === 'client-shared' || targetLayer === 'server-configs') {\n return;\n }\n\n if (isConfigLayer(targetLayer)) {\n return;\n }\n\n if (isClientUtils(targetLayer) || isServerUtils(targetLayer)) {\n context.report({\n messageId: 'domainUtilsImport',\n node\n });\n\n return;\n }\n\n if (targetLayer !== currentLayer) {\n context.report({\n messageId: 'domainImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-application' || currentLayer === 'client-application-configs'\n || currentLayer === 'client-application-services'\n || currentLayer === 'client-application-usecases'\n || currentLayer === 'client-application-utils') {\n if (targetLayer === 'client-ui') {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-ui') {\n const allowed = targetLayer === 'client-ui'\n || targetLayer === 'client-shared'\n || targetLayer === 'client-application-configs'\n || targetLayer === 'client-application-usecases'\n || targetLayer === 'client-application-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'uiApplicationImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-errors') {\n const allowed = targetLayer === 'server-errors'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-services') {\n const allowed = targetLayer === 'server-services'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-controllers') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-services'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n }\n };\n\n return {\n ImportDeclaration: checkImport,\n /**\n * Inspects constructor method definitions to detect assignments where a class method\n * is bound to `this` via `.bind(this)`, and reports such bindings for further handling.\n * This listener is triggered for the entire program to validate service class exports and adapter implementations\n * for client application services. It ensures that the hexagonal architecture rules are enforced at the program level.\n *\n * @param program The program node to analyze for service class export and implementation validation.\n */\n Program(program) {\n reportInvalidService(program);\n }\n } satisfies TSESLint.RuleListener;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","requiredAliases","hexagonalDependencyDirection","defaultOptions","meta","docs","description","messages","apiImport","clientApiImport","crossBoundary","domainImport","domainUtilsImport","missingServiceClass","missingServiceImplements","serverDirection","uiApplicationImport","schema","type","create","context","aliases","readAliasesFromSettings","settings","aliasMap","buildAliasMap","missing","filter","alias","has","length","filename","normalizedFile","normalizePath","currentLayer","getLayer","isClient","isClientLayer","isServer","isServerLayer","reportInvalidService","program","isMobxStoreFile","classes","node","body","push","declaration","report","messageId","classNode","implements","resolveImport","source","aliasResolved","resolveAliasImport","resolveRelativeImport","checkImport","value","resolved","targetLayer","allowed","isDomainLayer","isConfigLayer","isClientUtils","isServerUtils","ImportDeclaration","Program"],"mappings":"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"}
1
+ {"version":3,"file":"hexagonal-dependency-direction.js","sources":["../../../../src/rules/custom/hexagonal-dependency-direction.ts"],"sourcesContent":["import {ESLintUtils} from '@typescript-eslint/utils';\n\nimport {\n buildAliasMap,\n getLayer,\n isClientLayer,\n isClientUtils,\n isConfigLayer,\n isDomainLayer,\n isMobxStoreFile,\n isServerLayer,\n isServerUtils,\n normalizePath,\n readAliasesFromSettings,\n resolveAliasImport,\n resolveRelativeImport\n} from './utils/hexagonal-dependency-direction-utils';\n\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype RuleOptions = [];\n\ntype MessageIds = 'apiImport' | 'clientApiImport' | 'crossBoundary' | 'domainImport' | 'domainUtilsImport'\n | 'missingServiceClass' | 'missingServiceImplements' | 'serverDirection' | 'uiApplicationImport';\n\nconst requiredAliases = [\n 'Domain',\n 'Application',\n 'UI',\n 'ApiRoutes',\n 'Controllers',\n 'ServerDomain',\n 'Services'\n];\n\nexport const hexagonalDependencyDirection = createRule<RuleOptions, MessageIds>({\n defaultOptions: [],\n meta: {\n docs: {description: 'Enforce hexagonal dependency direction and boundary imports.'},\n messages: {\n apiImport: 'API routes may only import controllers, middleware, or server utils.',\n clientApiImport: 'Only client application services may import API routes.',\n crossBoundary: 'Client and server layers must not import from each other.',\n domainImport: 'Domain layers must not import from other layers.',\n domainUtilsImport: 'Domain layers must not import from utils.',\n missingServiceClass: 'Client services must export a class implementing an adapter interface.',\n missingServiceImplements: 'Client services classes must implement an adapter interface.',\n serverDirection: 'Server dependency direction is domain → errors → services → controllers.',\n // eslint-disable-next-line @stylistic/max-len\n uiApplicationImport: 'UI may only import from UI, shared, domain, or application useCases/configs/utils.'\n },\n schema: [],\n type: 'problem'\n },\n name: 'hexagonal-dependency-direction',\n /**\n * This function creates the ESLint rule listeners for validating hexagonal dependency direction.\n * It initializes alias resolution, determines the current file layer, and wires up import checks.\n * It also validates service class exports and adapter implementations for client services.\n *\n * @param context The rule context used for reporting and settings.\n * @returns A rule listener map for import declarations and program validation.\n *\n * @example\n * ```tsx\n * const rule = hexagonalDependencyDirection.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const aliases = readAliasesFromSettings(context.settings);\n const aliasMap = buildAliasMap(aliases);\n\n const missing = requiredAliases.filter(alias => !aliasMap.has(alias));\n\n if (missing.length > 0) {\n return {};\n }\n\n const {filename} = context;\n const normalizedFile = normalizePath(filename);\n const currentLayer = getLayer(normalizedFile);\n\n if (currentLayer === 'unknown') {\n return {};\n }\n\n const isClient = isClientLayer(currentLayer);\n const isServer = isServerLayer(currentLayer);\n\n /**\n * This function validates that client application services export at least one class and that the class\n * implements an adapter interface. It scans the program body for class declarations and named exports\n * containing classes. It reports rule violations when no class is found or when a class lacks implemented\n * interfaces.\n *\n * @param program The program node to analyze for service class exports.\n *\n * @example\n * ```tsx\n * reportInvalidService(program);\n * ```\n */\n const reportInvalidService = (program: TSESTree.Program) => {\n if (currentLayer !== 'client-application-services') {\n return;\n }\n\n if (isMobxStoreFile(program)) {\n return;\n }\n\n const classes: (TSESTree.ClassDeclaration | TSESTree.ClassExpression)[] = [];\n\n for (const node of program.body) {\n // @ts-expect-error\n if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {\n classes.push(node);\n continue;\n }\n\n if (node.type === 'ExportNamedDeclaration' && node.declaration) {\n const {declaration} = node;\n\n // @ts-expect-error\n if (declaration.type === 'ClassDeclaration' || declaration.type === 'ClassExpression') {\n classes.push(declaration);\n }\n }\n }\n\n if (classes.length === 0) {\n context.report({\n messageId: 'missingServiceClass',\n node: program\n });\n\n return;\n }\n\n for (const classNode of classes) {\n if (classNode.implements.length === 0) {\n context.report({\n messageId: 'missingServiceImplements',\n node: classNode\n });\n }\n }\n };\n\n /**\n * Resolves an import source to a normalized path using aliases and relative paths. It first attempts to match\n * the source against configured aliases to ensure project-wide consistency. If no alias applies, it falls back\n * to resolving a relative import against the current file for local references.\n *\n * @param source The raw import source string from the import declaration.\n * @returns The resolved path string when resolution succeeds, or undefined when it cannot be resolved.\n *\n * @example\n * ```tsx\n * const resolved = resolveImport('@app/domain');\n * ```\n */\n const resolveImport = (source: string) => {\n const aliasResolved = resolveAliasImport(source, aliasMap);\n\n if (aliasResolved) {\n return aliasResolved;\n }\n\n return resolveRelativeImport(source, filename);\n };\n\n /**\n * Validates an import declaration against the hexagonal dependency rules for the current file.\n * It resolves the import source into a target layer and checks for forbidden cross-boundary or layer-specific dependencies.\n * It reports violations using the configured ESLint messages when a rule is broken.\n *\n * @param node The import declaration node to validate.\n *\n * @example\n * ```tsx\n * checkImport(node);\n * ```\n */\n // eslint-disable-next-line complexity\n const checkImport = (node: TSESTree.ImportDeclaration) => {\n const source = node.source.value;\n\n if (typeof source !== 'string') {\n return;\n }\n\n const resolved = resolveImport(source);\n\n if (!resolved) {\n return;\n }\n\n const targetLayer = getLayer(resolved);\n\n if (targetLayer === 'unknown') {\n return;\n }\n\n if (isClient && isServerLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (isServer && isClientLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (currentLayer === 'api') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'apiImport',\n node\n });\n }\n\n return;\n }\n\n if (targetLayer === 'api' && currentLayer !== 'client-application-services') {\n context.report({\n messageId: 'clientApiImport',\n node\n });\n\n return;\n }\n\n if (isDomainLayer(currentLayer)) {\n if (targetLayer === 'client-shared' || targetLayer === 'server-configs') {\n return;\n }\n\n if (isConfigLayer(targetLayer)) {\n return;\n }\n\n if (isClientUtils(targetLayer) || isServerUtils(targetLayer)) {\n context.report({\n messageId: 'domainUtilsImport',\n node\n });\n\n return;\n }\n\n if (targetLayer !== currentLayer) {\n context.report({\n messageId: 'domainImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-application' || currentLayer === 'client-application-configs'\n || currentLayer === 'client-application-services'\n || currentLayer === 'client-application-usecases'\n || currentLayer === 'client-application-utils') {\n if (targetLayer === 'client-shared') {\n return;\n }\n\n if (targetLayer === 'client-ui') {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-ui') {\n const allowed = targetLayer === 'client-ui'\n || targetLayer === 'client-shared'\n || targetLayer === 'client-domain'\n || targetLayer === 'client-application-configs'\n || targetLayer === 'client-application-usecases'\n || targetLayer === 'client-application-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'uiApplicationImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-errors') {\n const allowed = targetLayer === 'server-errors'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-services') {\n const allowed = targetLayer === 'server-services'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-controllers') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-services'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n }\n };\n\n return {\n ImportDeclaration: checkImport,\n /**\n * Inspects constructor method definitions to detect assignments where a class method\n * is bound to `this` via `.bind(this)`, and reports such bindings for further handling.\n * This listener is triggered for the entire program to validate service class exports and adapter implementations\n * for client application services. It ensures that the hexagonal architecture rules are enforced at the program level.\n *\n * @param program The program node to analyze for service class export and implementation validation.\n */\n Program(program) {\n reportInvalidService(program);\n }\n } satisfies TSESLint.RuleListener;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","requiredAliases","hexagonalDependencyDirection","defaultOptions","meta","docs","description","messages","apiImport","clientApiImport","crossBoundary","domainImport","domainUtilsImport","missingServiceClass","missingServiceImplements","serverDirection","uiApplicationImport","schema","type","create","context","aliases","readAliasesFromSettings","settings","aliasMap","buildAliasMap","missing","filter","alias","has","length","filename","normalizedFile","normalizePath","currentLayer","getLayer","isClient","isClientLayer","isServer","isServerLayer","reportInvalidService","program","isMobxStoreFile","classes","node","body","push","declaration","report","messageId","classNode","implements","resolveImport","source","aliasResolved","resolveAliasImport","resolveRelativeImport","checkImport","value","resolved","targetLayer","allowed","isDomainLayer","isConfigLayer","isClientUtils","isServerUtils","ImportDeclaration","Program"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3117,7 +3117,7 @@ const hexagonalDependencyDirection = createRule$8({
3117
3117
  missingServiceClass: 'Client services must export a class implementing an adapter interface.',
3118
3118
  missingServiceImplements: 'Client services classes must implement an adapter interface.',
3119
3119
  serverDirection: 'Server dependency direction is domain → errors → services → controllers.',
3120
- uiApplicationImport: 'UI may only import from application/useCases, application/configs, or application/utils.'
3120
+ uiApplicationImport: 'UI may only import from UI, shared, domain, or application useCases/configs/utils.'
3121
3121
  },
3122
3122
  schema: [],
3123
3123
  type: 'problem'
@@ -3252,6 +3252,9 @@ const hexagonalDependencyDirection = createRule$8({
3252
3252
  return;
3253
3253
  }
3254
3254
  if (currentLayer === 'client-application' || currentLayer === 'client-application-configs' || currentLayer === 'client-application-services' || currentLayer === 'client-application-usecases' || currentLayer === 'client-application-utils') {
3255
+ if (targetLayer === 'client-shared') {
3256
+ return;
3257
+ }
3255
3258
  if (targetLayer === 'client-ui') {
3256
3259
  context.report({
3257
3260
  messageId: 'crossBoundary',
@@ -3261,7 +3264,7 @@ const hexagonalDependencyDirection = createRule$8({
3261
3264
  return;
3262
3265
  }
3263
3266
  if (currentLayer === 'client-ui') {
3264
- const allowed = targetLayer === 'client-ui' || targetLayer === 'client-shared' || targetLayer === 'client-application-configs' || targetLayer === 'client-application-usecases' || targetLayer === 'client-application-utils';
3267
+ const allowed = targetLayer === 'client-ui' || targetLayer === 'client-shared' || targetLayer === 'client-domain' || targetLayer === 'client-application-configs' || targetLayer === 'client-application-usecases' || targetLayer === 'client-application-utils';
3265
3268
  if (!allowed) {
3266
3269
  context.report({
3267
3270
  messageId: 'uiApplicationImport',