aws-iam-language-server 0.0.13 → 0.0.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.
Files changed (54) hide show
  1. package/package.json +1 -1
  2. package/readme.md +12 -0
  3. package/src/handlers/completion/action-value.d.ts +2 -0
  4. package/src/handlers/completion/action-value.js +1 -1
  5. package/src/handlers/completion/condition-key.d.ts +2 -0
  6. package/src/handlers/completion/condition-key.js +3 -3
  7. package/src/handlers/completion/index.d.ts +2 -2
  8. package/src/handlers/completion/index.js +4 -4
  9. package/src/handlers/completion/principal-identifier-completions.d.ts +1 -1
  10. package/src/handlers/diagnostics/diagnostics.js +4 -2
  11. package/src/handlers/diagnostics/resource.js +4 -4
  12. package/src/handlers/diagnostics/sid.d.ts +1 -1
  13. package/src/handlers/diagnostics/sid.js +11 -2
  14. package/src/handlers/diagnostics/utils.js +1 -13
  15. package/src/handlers/document-link/document-link.js +1 -3
  16. package/src/handlers/hover/action-value.d.ts +3 -0
  17. package/src/handlers/hover/action-value.js +21 -0
  18. package/src/handlers/hover/condition-block.d.ts +3 -0
  19. package/src/handlers/hover/condition-block.js +18 -0
  20. package/src/handlers/hover/condition-key.d.ts +3 -0
  21. package/src/handlers/hover/condition-key.js +53 -0
  22. package/src/handlers/hover/condition-operator.d.ts +3 -0
  23. package/src/handlers/hover/condition-operator.js +14 -0
  24. package/src/handlers/hover/effect-value.d.ts +3 -0
  25. package/src/handlers/hover/effect-value.js +17 -0
  26. package/src/handlers/hover/index.d.ts +3 -0
  27. package/src/handlers/hover/index.js +69 -0
  28. package/src/handlers/hover/principal-block.d.ts +3 -0
  29. package/src/handlers/hover/principal-block.js +17 -0
  30. package/src/handlers/hover/principal-type.d.ts +3 -0
  31. package/src/handlers/hover/principal-type.js +25 -0
  32. package/src/handlers/hover/principal-typed-value.d.ts +3 -0
  33. package/src/handlers/hover/principal-typed-value.js +118 -0
  34. package/src/handlers/hover/principal-value.d.ts +3 -0
  35. package/src/handlers/hover/principal-value.js +13 -0
  36. package/src/handlers/hover/resource-value.d.ts +3 -0
  37. package/src/handlers/hover/resource-value.js +45 -0
  38. package/src/handlers/hover/statement-block.d.ts +3 -0
  39. package/src/handlers/hover/statement-block.js +14 -0
  40. package/src/handlers/hover/statement-key.d.ts +3 -0
  41. package/src/handlers/hover/statement-key.js +14 -0
  42. package/src/lib/iam-policy/arn.d.ts +17 -0
  43. package/src/lib/iam-policy/arn.js +77 -0
  44. package/src/lib/iam-policy/location.d.ts +31 -1
  45. package/src/lib/iam-policy/location.js +56 -19
  46. package/src/lib/iam-policy/reference/services.d.ts +5 -3
  47. package/src/lib/iam-policy/reference/services.js +58 -19
  48. package/src/lib/iam-policy/reference/types.d.ts +5 -0
  49. package/src/lib/treesitter/base.d.ts +3 -8
  50. package/src/lib/treesitter/base.js +3 -3
  51. package/src/lib/treesitter/hcl.js +32 -24
  52. package/src/lib/treesitter/json.js +14 -11
  53. package/src/lib/treesitter/yaml.js +30 -27
  54. package/src/server.js +5 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-iam-language-server",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "type": "module",
5
5
  "bin": "./src/server.js",
6
6
  "publisher": "MichaelBarney",
package/readme.md CHANGED
@@ -59,6 +59,17 @@ This language server provides completion on:
59
59
  - condition operators (`StringLike`, `ForAnyValue:*`, etc)
60
60
  - condition keys (global keys like `aws:RequestTag/${TagKey}`, action-specific keys like `s3:TlsVersion`)
61
61
 
62
+ ### Hover
63
+
64
+ Hovering over elements within a policy document will show contextual documentation:
65
+
66
+ - actions (access level, resource types, condition keys, and dependent actions)
67
+ - resources (matched resource type from the service reference with ARN format and condition keys)
68
+ - principal types (description of `AWS`, `Service`, `Federated`, `CanonicalUser`)
69
+ - principal values (identifies account IDs, role/user ARNs, service principals, federated providers)
70
+ - condition operators (description of what each operator does, like `StringEquals`, `ArnLike`, `IpAddress`, etc.)
71
+ - condition keys (documentation for global keys like `aws:SourceIp` and service-specific keys like `s3:prefix`)
72
+
62
73
  ### Diagnostics
63
74
 
64
75
  This language server will provide diagnostics for some IAM policy issues, including:
@@ -67,6 +78,7 @@ This language server will provide diagnostics for some IAM policy issues, includ
67
78
  - no missing keys in a statement, (effect, action, resource or effect, action, principal)
68
79
  - no duplicate keys in a statement (including "not" variants like action/not action)
69
80
  - ensuring `Sid` uniqueness within a policy document
81
+ - `Sid` values are valid (alphanumeric for identity policies, allow spaces in resource policies)
70
82
  - effect has a valid value
71
83
  - defined actions are valid, or wildcards resolve to valid actions
72
84
  - arn parts are valid (partition, region, account id)
@@ -1,4 +1,6 @@
1
1
  import type { CompletionList } from 'vscode-languageserver';
2
2
  import type { ActionValueLocation } from '../../lib/iam-policy/location.ts';
3
+ import type { Action } from '../../lib/iam-policy/reference/types.ts';
3
4
  import { type CompletionContext } from './index.ts';
4
5
  export declare function completeActionValue(location: ActionValueLocation, context: CompletionContext): CompletionList;
6
+ export declare function formatActionDocumentation(action: Action): string;
@@ -44,7 +44,7 @@ export function completeActionValue(location, context) {
44
44
  }
45
45
  return { items, isIncomplete: false };
46
46
  }
47
- function formatActionDocumentation(action) {
47
+ export function formatActionDocumentation(action) {
48
48
  const parts = [];
49
49
  if (action.accessLevel)
50
50
  parts.push(`**Access Level**: ${action.accessLevel}`);
@@ -1,4 +1,6 @@
1
1
  import type { CompletionList } from 'vscode-languageserver';
2
2
  import type { ConditionKeyLocation } from '../../lib/iam-policy/location.ts';
3
+ import type { ConditionKey, GlobalConditionKey } from '../../lib/iam-policy/reference/types.ts';
3
4
  import { type CompletionContext } from './index.ts';
4
5
  export declare function completeConditionKey(location: ConditionKeyLocation, context: CompletionContext): CompletionList;
6
+ export declare function formatConditionKeyDocumentation(types: string[], global: GlobalConditionKey | undefined, conditionKey?: ConditionKey): string;
@@ -39,7 +39,7 @@ export function completeConditionKey(location, context) {
39
39
  textEdit: { range, newText: key.name },
40
40
  documentation: {
41
41
  kind: MarkupKind.Markdown,
42
- value: formatDocumentation(key.types, global, conditionKeyData),
42
+ value: formatConditionKeyDocumentation(key.types, global, conditionKeyData),
43
43
  },
44
44
  });
45
45
  }
@@ -59,13 +59,13 @@ export function completeConditionKey(location, context) {
59
59
  textEdit: { range, newText: global.name },
60
60
  documentation: {
61
61
  kind: MarkupKind.Markdown,
62
- value: formatDocumentation([], global),
62
+ value: formatConditionKeyDocumentation([], global),
63
63
  },
64
64
  });
65
65
  }
66
66
  return { items, isIncomplete: false };
67
67
  }
68
- function formatDocumentation(types, global, conditionKey) {
68
+ export function formatConditionKeyDocumentation(types, global, conditionKey) {
69
69
  const parts = [];
70
70
  const description = global?.description ?? conditionKey?.description;
71
71
  if (description)
@@ -8,12 +8,12 @@ export type CompletionContext = {
8
8
  uri: string;
9
9
  position: {
10
10
  line: number;
11
- column: number;
11
+ character: number;
12
12
  };
13
13
  };
14
14
  export declare function partialRange(position: {
15
15
  line: number;
16
- column: number;
16
+ character: number;
17
17
  }, partialLength: number): {
18
18
  start: {
19
19
  line: number;
@@ -17,15 +17,15 @@ import { completeStatementKey } from "./statement-key.js";
17
17
  const emptyResult = { items: [], isIncomplete: false };
18
18
  export function partialRange(position, partialLength) {
19
19
  return {
20
- start: { line: position.line, character: position.column - partialLength },
21
- end: { line: position.line, character: position.column },
20
+ start: { line: position.line, character: position.character - partialLength },
21
+ end: { line: position.line, character: position.character },
22
22
  };
23
23
  }
24
24
  export async function handleCompletionRequest(params, _documents, treeManager, connection) {
25
25
  const handler = treeManager.getLanguageHandler(params.textDocument.uri);
26
26
  if (!handler)
27
27
  return emptyResult;
28
- const position = { line: params.position.line, column: params.position.character };
28
+ const position = params.position;
29
29
  const cursorContext = handler.getCursorContext(params.textDocument.uri, position);
30
30
  if (!cursorContext)
31
31
  return emptyResult;
@@ -42,7 +42,7 @@ export async function handleCompletionRequest(params, _documents, treeManager, c
42
42
  position,
43
43
  });
44
44
  result.items = result.items.map(toSnippet);
45
- connection.console.debug(`Found ${result.items.length} completion items for ${params.textDocument.uri} at line ${position.line}, column ${position.column}`);
45
+ connection.console.debug(`Found ${result.items.length} completion items for ${params.textDocument.uri} at line ${position.line}, character ${position.character}`);
46
46
  return result;
47
47
  }
48
48
  function handleLocationCompletion(location, context) {
@@ -1,5 +1,5 @@
1
1
  import type { CompletionList } from 'vscode-languageserver';
2
2
  export declare function completePrincipalIdentifier(principalType: string | null, partial: string, position: {
3
3
  line: number;
4
- column: number;
4
+ character: number;
5
5
  }): CompletionList;
@@ -34,9 +34,10 @@ async function handleStandardDiagnostics(policyDocument) {
34
34
  const resourceValidator = new ResourceValidator();
35
35
  const conditionValidator = new ConditionValidator();
36
36
  for (const statement of policyDocument.statements) {
37
+ const isResourcePolicy = statement.entries.some((e) => e.key === 'Principal' || e.key === 'NotPrincipal');
37
38
  for (const entry of statement.entries) {
38
39
  if (entry.key === 'Sid') {
39
- diagnostics = diagnostics.concat(sidValidator.validate(entry));
40
+ diagnostics = diagnostics.concat(sidValidator.validate(entry, isResourcePolicy));
40
41
  }
41
42
  else if (entry.key === 'Effect') {
42
43
  diagnostics = diagnostics.concat(effectValidator.validate(entry));
@@ -81,9 +82,10 @@ async function handleHclBlockDiagnostics(policyDocument) {
81
82
  const resourceValidator = new ResourceValidator();
82
83
  const conditionValidator = new ConditionValidator();
83
84
  for (const statement of policyDocument.statements) {
85
+ const isResourcePolicy = statement.entries.some((e) => e.key === 'principals' || e.key === 'not_principals');
84
86
  for (const entry of statement.entries) {
85
87
  if (entry.key === 'sid') {
86
- diagnostics = diagnostics.concat(sidValidator.validate(entry));
88
+ diagnostics = diagnostics.concat(sidValidator.validate(entry, isResourcePolicy));
87
89
  }
88
90
  else if (entry.key === 'effect') {
89
91
  diagnostics = diagnostics.concat(effectValidator.validate(entry));
@@ -9,11 +9,11 @@ function segmentRange(value, segmentIndex) {
9
9
  for (let i = 0; i < segmentIndex; i++) {
10
10
  offset += segments[i].length + 1;
11
11
  }
12
- const startColumn = value.range.start.column + offset;
13
- const endColumn = startColumn + segments[segmentIndex].length;
12
+ const startCharacter = value.range.start.character + offset;
13
+ const endCharacter = startCharacter + segments[segmentIndex].length;
14
14
  return {
15
- start: { line: value.range.start.line, column: startColumn },
16
- end: { line: value.range.start.line, column: endColumn },
15
+ start: { line: value.range.start.line, character: startCharacter },
16
+ end: { line: value.range.start.line, character: endCharacter },
17
17
  };
18
18
  }
19
19
  function validateArn(value) {
@@ -4,5 +4,5 @@ import { ElementValidator } from './base.ts';
4
4
  export declare class SidValidator extends ElementValidator {
5
5
  #private;
6
6
  constructor();
7
- validate(entry: StatementEntry): Array<Diagnostic>;
7
+ validate(entry: StatementEntry, isResourcePolicy?: boolean): Array<Diagnostic>;
8
8
  }
@@ -1,21 +1,30 @@
1
1
  import { ElementValidator } from "./base.js";
2
2
  import { createDiagnostic } from "./utils.js";
3
+ const strictSidPattern = /^[A-Za-z0-9]*$/;
4
+ const resourcePolicySidPattern = /^[A-Za-z0-9 ]*$/;
3
5
  export class SidValidator extends ElementValidator {
4
6
  #sids = {};
5
7
  constructor() {
6
8
  super();
7
9
  this.#sids = {};
8
10
  }
9
- validate(entry) {
11
+ validate(entry, isResourcePolicy = false) {
10
12
  const diagnostics = super.validate(entry);
11
13
  const sidValue = entry.values[0]?.text;
12
14
  if (!sidValue)
13
- return [];
15
+ return diagnostics;
14
16
  if (sidValue in this.#sids) {
15
17
  diagnostics.push(createDiagnostic(`Duplicate statement id value "${sidValue}"`, entry.valueRange));
16
18
  diagnostics.push(createDiagnostic(`Duplicate statement id value "${sidValue}"`, this.#sids[sidValue].valueRange));
17
19
  }
18
20
  this.#sids[sidValue] = entry;
21
+ const pattern = isResourcePolicy ? resourcePolicySidPattern : strictSidPattern;
22
+ if (!pattern.test(sidValue)) {
23
+ const message = isResourcePolicy
24
+ ? 'Sid must contain only ASCII letters (A-Z, a-z), digits (0-9), and spaces'
25
+ : 'Sid must contain only ASCII letters (A-Z, a-z) and digits (0-9)';
26
+ diagnostics.push(createDiagnostic(message, entry.values[0].range));
27
+ }
19
28
  return diagnostics;
20
29
  }
21
30
  }
@@ -2,18 +2,6 @@ export function createDiagnostic(message, range) {
2
2
  return {
3
3
  source: 'aws-iam-language-server',
4
4
  message,
5
- range: convertRangeToDiagnosticRange(range),
6
- };
7
- }
8
- function convertRangeToDiagnosticRange(range) {
9
- return {
10
- start: {
11
- character: range.start.column,
12
- line: range.start.line,
13
- },
14
- end: {
15
- character: range.end.column,
16
- line: range.end.line,
17
- },
5
+ range,
18
6
  };
19
7
  }
@@ -21,9 +21,7 @@ export function documentLinkHandler(params, documents, treeManager, connection)
21
21
  const action = ServiceReference.getAction(value.text);
22
22
  if (!action)
23
23
  continue;
24
- const start = { line: value.range.start.line, character: value.range.start.column };
25
- const end = { line: value.range.end.line, character: value.range.end.column };
26
- const range = { start, end };
24
+ const range = value.range;
27
25
  if (action.iamUrl) {
28
26
  links.push({
29
27
  range,
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ActionValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleActionValueHover(location: ActionValueLocation): Hover | null;
@@ -0,0 +1,21 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { ServiceReference } from "../../lib/iam-policy/reference/services.js";
3
+ import { formatActionDocumentation } from "../completion/action-value.js";
4
+ export function handleActionValueHover(location) {
5
+ const action = ServiceReference.getAction(location.value);
6
+ if (!action)
7
+ return null;
8
+ const lines = [`**${action.service}:${action.name}**`];
9
+ if (action.description)
10
+ lines.push(action.description);
11
+ const docs = formatActionDocumentation(action);
12
+ if (docs)
13
+ lines.push(docs);
14
+ return {
15
+ range: location.range,
16
+ contents: {
17
+ kind: MarkupKind.Markdown,
18
+ value: lines.join('\n\n'),
19
+ },
20
+ };
21
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ConditionBlockLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleConditionBlockHover(location: ConditionBlockLocation): Hover | null;
@@ -0,0 +1,18 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ const conditionBlockAttributes = {
3
+ test: 'The condition operator to evaluate (e.g., `StringEquals`, `ArnLike`, `IpAddress`).',
4
+ variable: 'The condition key to evaluate (e.g., `aws:SourceIp`, `s3:prefix`).',
5
+ values: 'List of values to compare against the condition key.',
6
+ };
7
+ export function handleConditionBlockHover(location) {
8
+ const description = conditionBlockAttributes[location.value];
9
+ if (!description)
10
+ return null;
11
+ return {
12
+ range: location.range,
13
+ contents: {
14
+ kind: MarkupKind.Markdown,
15
+ value: description,
16
+ },
17
+ };
18
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ConditionKeyLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleConditionKeyHover(location: ConditionKeyLocation): Hover | null;
@@ -0,0 +1,53 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { ServiceReference } from "../../lib/iam-policy/reference/services.js";
3
+ import { formatConditionKeyDocumentation } from "../completion/condition-key.js";
4
+ const placeholderPattern = /\$\{[^}]+\}/g;
5
+ function patternToRegex(pattern) {
6
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
7
+ const withPlaceholders = escaped.replace(/\\\$\\\{[^}]+\\\}/g, '.+');
8
+ return new RegExp(`^${withPlaceholders}$`);
9
+ }
10
+ function findByPattern(keyName, globals, service) {
11
+ for (const global of globals) {
12
+ if (placeholderPattern.test(global.name) && patternToRegex(global.name).test(keyName)) {
13
+ const conditionKey = service ? ServiceReference.getConditionKey(service, global.name) : undefined;
14
+ return { global, conditionKey };
15
+ }
16
+ }
17
+ if (service) {
18
+ const serviceData = ServiceReference.getServiceData(service);
19
+ if (serviceData) {
20
+ for (const name of Object.keys(serviceData.conditionKeys)) {
21
+ if (placeholderPattern.test(name) && patternToRegex(name).test(keyName)) {
22
+ return { conditionKey: serviceData.conditionKeys[name] };
23
+ }
24
+ }
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ export function handleConditionKeyHover(location) {
30
+ const keyName = location.value;
31
+ if (!keyName)
32
+ return null;
33
+ const globalKeys = ServiceReference.getGlobalConditionKeys();
34
+ const service = keyName.split(':')[0];
35
+ let global = globalKeys.find((k) => k.name === keyName);
36
+ let conditionKey = service ? ServiceReference.getConditionKey(service, keyName) : undefined;
37
+ if (!global && !conditionKey) {
38
+ const match = findByPattern(keyName, globalKeys, service);
39
+ if (!match)
40
+ return null;
41
+ global = match.global;
42
+ conditionKey = match.conditionKey;
43
+ }
44
+ const types = conditionKey?.types ?? [];
45
+ const docs = formatConditionKeyDocumentation(types, global, conditionKey);
46
+ return {
47
+ range: location.range,
48
+ contents: {
49
+ kind: MarkupKind.Markdown,
50
+ value: `**${keyName}**\n\n${docs}`,
51
+ },
52
+ };
53
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { ConditionOperatorLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleConditionOperatorHover(location: ConditionOperatorLocation): Hover | null;
@@ -0,0 +1,14 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { conditionOperators } from "../../lib/iam-policy/condition-operators.js";
3
+ export function handleConditionOperatorHover(location) {
4
+ const operator = conditionOperators[location.value];
5
+ if (!operator)
6
+ return null;
7
+ return {
8
+ range: location.range,
9
+ contents: {
10
+ kind: MarkupKind.Markdown,
11
+ value: operator.description,
12
+ },
13
+ };
14
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { EffectValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handleEffectValueHover(location: EffectValueLocation): Hover | null;
@@ -0,0 +1,17 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ const effectDescriptions = {
3
+ Allow: 'Grants access to the specified resources and actions.\n\nBy default, all requests are implicitly denied. An `Allow` overrides implicit denies but never overrides an explicit `Deny`.',
4
+ Deny: 'Explicitly denies access to the specified resources and actions.\n\nAn explicit `Deny` always takes precedence — it overrides any `Allow` statements, regardless of where they appear.',
5
+ };
6
+ export function handleEffectValueHover(location) {
7
+ const description = effectDescriptions[location.value];
8
+ if (!description)
9
+ return null;
10
+ return {
11
+ range: location.range,
12
+ contents: {
13
+ kind: MarkupKind.Markdown,
14
+ value: description,
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,3 @@
1
+ import type { Connection, Hover, HoverParams } from 'vscode-languageserver';
2
+ import type { TreeManager } from '../../lib/treesitter/manager.ts';
3
+ export declare function hoverHandler(params: HoverParams, treeManager: TreeManager, connection: Connection): Hover | null;
@@ -0,0 +1,69 @@
1
+ import { resolvePolicyLocation } from "../../lib/iam-policy/location.js";
2
+ import { handleActionValueHover } from "./action-value.js";
3
+ import { handleConditionBlockHover } from "./condition-block.js";
4
+ import { handleConditionKeyHover } from "./condition-key.js";
5
+ import { handleConditionOperatorHover } from "./condition-operator.js";
6
+ import { handleEffectValueHover } from "./effect-value.js";
7
+ import { handlePrincipalBlockHover } from "./principal-block.js";
8
+ import { handlePrincipalTypeHover } from "./principal-type.js";
9
+ import { handlePrincipalTypedValueHover } from "./principal-typed-value.js";
10
+ import { handlePrincipalValueHover } from "./principal-value.js";
11
+ import { handleResourceValueHover } from "./resource-value.js";
12
+ import { handleStatementBlockHover } from "./statement-block.js";
13
+ import { handleStatementKeyHover } from "./statement-key.js";
14
+ export function hoverHandler(params, treeManager, connection) {
15
+ const handler = treeManager.getLanguageHandler(params.textDocument.uri);
16
+ if (!handler)
17
+ return null;
18
+ const position = params.position;
19
+ const cursorContext = handler.getCursorContext(params.textDocument.uri, position);
20
+ if (!cursorContext)
21
+ return null;
22
+ const location = resolvePolicyLocation(cursorContext);
23
+ connection.console.debug(`Hover debug: ${JSON.stringify({
24
+ uri: params.textDocument.uri,
25
+ cursorContext,
26
+ location,
27
+ position: params.position,
28
+ })}`);
29
+ return handleLocationHover(connection, location);
30
+ }
31
+ function handleLocationHover(connection, location) {
32
+ if (location.type === 'statement-key') {
33
+ return handleStatementKeyHover(connection, location);
34
+ }
35
+ else if (location.type === 'statement-block') {
36
+ return handleStatementBlockHover(location);
37
+ }
38
+ else if (location.type === 'effect-value') {
39
+ return handleEffectValueHover(location);
40
+ }
41
+ else if (location.type === 'action-value') {
42
+ return handleActionValueHover(location);
43
+ }
44
+ else if (location.type === 'resource-value') {
45
+ return handleResourceValueHover(location);
46
+ }
47
+ else if (location.type === 'principal-value') {
48
+ return handlePrincipalValueHover(location);
49
+ }
50
+ else if (location.type === 'principal-type' || location.type === 'principal-block-type') {
51
+ return handlePrincipalTypeHover(location);
52
+ }
53
+ else if (location.type === 'principal-block') {
54
+ return handlePrincipalBlockHover(location);
55
+ }
56
+ else if (location.type === 'principal-typed-value' || location.type === 'principal-block-identifier') {
57
+ return handlePrincipalTypedValueHover(location);
58
+ }
59
+ else if (location.type === 'condition-block') {
60
+ return handleConditionBlockHover(location);
61
+ }
62
+ else if (location.type === 'condition-operator') {
63
+ return handleConditionOperatorHover(location);
64
+ }
65
+ else if (location.type === 'condition-key') {
66
+ return handleConditionKeyHover(location);
67
+ }
68
+ return null;
69
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { PrincipalBlockLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handlePrincipalBlockHover(location: PrincipalBlockLocation): Hover | null;
@@ -0,0 +1,17 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ const principalBlockAttributes = {
3
+ type: 'The type of principal. Valid values: `*`, `AWS`, `Service`, `Federated`, `CanonicalUser`.',
4
+ identifiers: 'List of principal identifiers. The format depends on the `type` attribute.',
5
+ };
6
+ export function handlePrincipalBlockHover(location) {
7
+ const description = principalBlockAttributes[location.value];
8
+ if (!description)
9
+ return null;
10
+ return {
11
+ range: location.range,
12
+ contents: {
13
+ kind: MarkupKind.Markdown,
14
+ value: description,
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { PrincipalBlockTypeLocation, PrincipalTypeLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handlePrincipalTypeHover(location: PrincipalTypeLocation | PrincipalBlockTypeLocation): Hover | null;
@@ -0,0 +1,25 @@
1
+ import { MarkupKind } from 'vscode-languageserver';
2
+ import { principalTypes } from "../../lib/iam-policy/principals.js";
3
+ export function handlePrincipalTypeHover(location) {
4
+ if (location.value === '*') {
5
+ return {
6
+ range: location.range,
7
+ contents: {
8
+ kind: MarkupKind.Markdown,
9
+ value: '**Public (unauthenticated) access**\n\nMatches all principals, including anonymous users.',
10
+ },
11
+ };
12
+ }
13
+ for (const principalType of Object.values(principalTypes)) {
14
+ if (principalType.value === location.value) {
15
+ return {
16
+ range: location.range,
17
+ contents: {
18
+ kind: MarkupKind.Markdown,
19
+ value: principalType.description,
20
+ },
21
+ };
22
+ }
23
+ }
24
+ return null;
25
+ }
@@ -0,0 +1,3 @@
1
+ import { type Hover } from 'vscode-languageserver';
2
+ import type { PrincipalBlockIdentifierLocation, PrincipalTypedValueLocation } from '../../lib/iam-policy/location.ts';
3
+ export declare function handlePrincipalTypedValueHover(location: PrincipalTypedValueLocation | PrincipalBlockIdentifierLocation): Hover | null;