@living-architecture/riviere-extract-conventions 0.3.24 → 0.3.26

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.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { DomainOpContainer, APIContainer, EventHandlerContainer, UseCase, Event, UI, DomainOp, APIEndpoint, EventHandler, Custom, Ignore, getCustomType, } from './decorators';
2
+ export type { HttpMethod, APIControllerDef, EventDef, EventHandlerDef, UIPageDef, DomainOpContainerDef, } from './interfaces';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EAErB,OAAO,EACP,KAAK,EACL,EAAE,EAEF,QAAQ,EACR,WAAW,EACX,YAAY,EAEZ,MAAM,EACN,MAAM,EAEN,aAAa,GACd,MAAM,cAAc,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EAErB,OAAO,EACP,KAAK,EACL,EAAE,EAEF,QAAQ,EACR,WAAW,EACX,YAAY,EAEZ,MAAM,EACN,MAAM,EAEN,aAAa,GACd,MAAM,cAAc,CAAA;AAGrB,YAAY,EACV,UAAU,EACV,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,SAAS,EACT,oBAAoB,GACrB,MAAM,cAAc,CAAA"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * HTTP methods supported for API controllers.
3
+ */
4
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
5
+ /**
6
+ * Interface for API controller classes.
7
+ * Classes implementing this interface represent HTTP endpoint handlers.
8
+ *
9
+ * Required properties:
10
+ * - route: The URL path for this endpoint
11
+ * - method: The HTTP method (GET, POST, PUT, PATCH, DELETE)
12
+ * - handle: The request handler function
13
+ */
14
+ export interface APIControllerDef {
15
+ readonly route: string;
16
+ readonly method: HttpMethod;
17
+ handle(req: Request, res: Response): void | Promise<void>;
18
+ }
19
+ /**
20
+ * Interface for domain event classes.
21
+ * Classes implementing this interface represent events that have occurred in the domain.
22
+ *
23
+ * Required properties:
24
+ * - type: A unique identifier for this event type (should be a string literal)
25
+ */
26
+ export interface EventDef {
27
+ readonly type: string;
28
+ }
29
+ /**
30
+ * Interface for event handler classes.
31
+ * Classes implementing this interface subscribe to and process domain events.
32
+ *
33
+ * Required properties:
34
+ * - subscribedEvents: Array of event type names this handler processes
35
+ * - handle: The event processing function
36
+ */
37
+ export interface EventHandlerDef {
38
+ readonly subscribedEvents: readonly string[];
39
+ handle(event: unknown): void | Promise<void>;
40
+ }
41
+ /**
42
+ * Interface for UI page classes.
43
+ * Classes implementing this interface represent routable UI pages.
44
+ *
45
+ * Required properties:
46
+ * - route: The URL path for this page
47
+ */
48
+ export interface UIPageDef {
49
+ readonly route: string;
50
+ }
51
+ /**
52
+ * Marker interface for domain operation container classes.
53
+ * Classes implementing this interface contain domain operations as methods.
54
+ * No required properties - the class itself serves as the container.
55
+ */
56
+ export interface DomainOpContainerDef {
57
+ readonly __brand?: 'DomainOpContainerDef';
58
+ }
59
+ //# sourceMappingURL=interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../src/interfaces.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAEpE;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5C,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,sBAAsB,CAAA;CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@living-architecture/riviere-extract-conventions",
3
- "version": "0.3.24",
3
+ "version": "0.3.26",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -32,6 +32,6 @@
32
32
  "!**/*.tsbuildinfo"
33
33
  ],
34
34
  "dependencies": {
35
- "@living-architecture/riviere-extract-config": "0.3.3"
35
+ "@living-architecture/riviere-extract-config": "0.3.5"
36
36
  }
37
37
  }
@@ -13,6 +13,9 @@
13
13
  "from": "@living-architecture/riviere-extract-conventions"
14
14
  }
15
15
  }
16
+ },
17
+ "extract": {
18
+ "apiType": { "literal": "REST" }
16
19
  }
17
20
  },
18
21
  "useCase": {
@@ -33,6 +36,9 @@
33
36
  "from": "@living-architecture/riviere-extract-conventions"
34
37
  }
35
38
  }
39
+ },
40
+ "extract": {
41
+ "operationName": { "fromMethodName": true }
36
42
  }
37
43
  },
38
44
  "event": {
@@ -42,6 +48,9 @@
42
48
  "name": "Event",
43
49
  "from": "@living-architecture/riviere-extract-conventions"
44
50
  }
51
+ },
52
+ "extract": {
53
+ "eventName": { "fromClassName": true }
45
54
  }
46
55
  },
47
56
  "eventHandler": {
@@ -53,6 +62,9 @@
53
62
  "from": "@living-architecture/riviere-extract-conventions"
54
63
  }
55
64
  }
65
+ },
66
+ "extract": {
67
+ "subscribedEvents": { "fromGenericArg": { "interface": "IEventHandler", "position": 0 } }
56
68
  }
57
69
  },
58
70
  "ui": {
@@ -62,6 +74,9 @@
62
74
  "name": "UI",
63
75
  "from": "@living-architecture/riviere-extract-conventions"
64
76
  }
77
+ },
78
+ "extract": {
79
+ "route": { "fromProperty": { "name": "route", "kind": "static" } }
65
80
  }
66
81
  }
67
82
  }
@@ -0,0 +1,85 @@
1
+ const {
2
+ implementsInterface,
3
+ findInstanceProperty,
4
+ hasStringLiteralValue,
5
+ getLiteralValue,
6
+ getValueTypeDescription,
7
+ } = require('./interface-ast-predicates.cjs')
8
+
9
+ const VALID_HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
10
+
11
+ module.exports = {
12
+ meta: {
13
+ type: 'problem',
14
+ docs: { description: 'Require APIControllerDef implementations to have route and method with literal values' },
15
+ schema: [],
16
+ messages: {
17
+ missingRoute: "Class '{{className}}' implements APIControllerDef but is missing 'route' property",
18
+ missingMethod: "Class '{{className}}' implements APIControllerDef but is missing 'method' property",
19
+ routeNotLiteral: "Class '{{className}}' has 'route' property but value must be a string literal, not {{actualType}}",
20
+ methodNotLiteral: "Class '{{className}}' has 'method' property but value must be a string literal, not {{actualType}}",
21
+ invalidHttpMethod: "Class '{{className}}' has invalid HTTP method '{{value}}'. Must be one of: GET, POST, PUT, PATCH, DELETE",
22
+ },
23
+ },
24
+ create(context) {
25
+ return {
26
+ ClassDeclaration(node) {
27
+ /* v8 ignore next -- ClassDeclaration always has id (name) */
28
+ if (!node.id) return
29
+ if (!implementsInterface(node, 'APIControllerDef')) return
30
+
31
+ const className = node.id.name
32
+ const routeProperty = findInstanceProperty(node, 'route')
33
+ const methodProperty = findInstanceProperty(node, 'method')
34
+
35
+ // Check route property
36
+ if (!routeProperty) {
37
+ context.report({
38
+ node: node.id,
39
+ messageId: 'missingRoute',
40
+ data: { className },
41
+ })
42
+ } else if (!hasStringLiteralValue(routeProperty)) {
43
+ context.report({
44
+ node: routeProperty,
45
+ messageId: 'routeNotLiteral',
46
+ data: {
47
+ className,
48
+ actualType: getValueTypeDescription(routeProperty),
49
+ },
50
+ })
51
+ }
52
+
53
+ // Check method property
54
+ if (!methodProperty) {
55
+ context.report({
56
+ node: node.id,
57
+ messageId: 'missingMethod',
58
+ data: { className },
59
+ })
60
+ } else if (!hasStringLiteralValue(methodProperty)) {
61
+ context.report({
62
+ node: methodProperty,
63
+ messageId: 'methodNotLiteral',
64
+ data: {
65
+ className,
66
+ actualType: getValueTypeDescription(methodProperty),
67
+ },
68
+ })
69
+ } else {
70
+ const methodValue = getLiteralValue(methodProperty)
71
+ if (!VALID_HTTP_METHODS.includes(methodValue)) {
72
+ context.report({
73
+ node: methodProperty,
74
+ messageId: 'invalidHttpMethod',
75
+ data: {
76
+ className,
77
+ value: methodValue,
78
+ },
79
+ })
80
+ }
81
+ }
82
+ },
83
+ }
84
+ },
85
+ }
@@ -0,0 +1,6 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils'
2
+
3
+ type MessageIds = 'missingRoute' | 'missingMethod' | 'routeNotLiteral' | 'methodNotLiteral' | 'invalidHttpMethod'
4
+
5
+ declare const rule: TSESLint.RuleModule<MessageIds>
6
+ export default rule
@@ -0,0 +1,47 @@
1
+ const {
2
+ implementsInterface,
3
+ findInstanceProperty,
4
+ hasLiteralArrayValue,
5
+ getValueTypeDescription,
6
+ } = require('./interface-ast-predicates.cjs')
7
+
8
+ module.exports = {
9
+ meta: {
10
+ type: 'problem',
11
+ docs: { description: 'Require EventHandlerDef implementations to have subscribedEvents array with literal values' },
12
+ schema: [],
13
+ messages: {
14
+ missingSubscribedEvents: "Class '{{className}}' implements EventHandlerDef but is missing 'subscribedEvents' property",
15
+ subscribedEventsNotLiteralArray: "Class '{{className}}' has 'subscribedEvents' property but value must be an array of string literals, not {{actualType}}",
16
+ },
17
+ },
18
+ create(context) {
19
+ return {
20
+ ClassDeclaration(node) {
21
+ /* v8 ignore next -- ClassDeclaration always has id (name) */
22
+ if (!node.id) return
23
+ if (!implementsInterface(node, 'EventHandlerDef')) return
24
+
25
+ const className = node.id.name
26
+ const subscribedEventsProperty = findInstanceProperty(node, 'subscribedEvents')
27
+
28
+ if (!subscribedEventsProperty) {
29
+ context.report({
30
+ node: node.id,
31
+ messageId: 'missingSubscribedEvents',
32
+ data: { className },
33
+ })
34
+ } else if (!hasLiteralArrayValue(subscribedEventsProperty)) {
35
+ context.report({
36
+ node: subscribedEventsProperty,
37
+ messageId: 'subscribedEventsNotLiteralArray',
38
+ data: {
39
+ className,
40
+ actualType: getValueTypeDescription(subscribedEventsProperty),
41
+ },
42
+ })
43
+ }
44
+ },
45
+ }
46
+ },
47
+ }
@@ -0,0 +1,6 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils'
2
+
3
+ type MessageIds = 'missingSubscribedEvents' | 'subscribedEventsNotLiteralArray'
4
+
5
+ declare const rule: TSESLint.RuleModule<MessageIds>
6
+ export default rule
@@ -0,0 +1,47 @@
1
+ const {
2
+ implementsInterface,
3
+ findInstanceProperty,
4
+ hasStringLiteralValue,
5
+ getValueTypeDescription,
6
+ } = require('./interface-ast-predicates.cjs')
7
+
8
+ module.exports = {
9
+ meta: {
10
+ type: 'problem',
11
+ docs: { description: 'Require EventDef implementations to have type property with literal value' },
12
+ schema: [],
13
+ messages: {
14
+ missingType: "Class '{{className}}' implements EventDef but is missing 'type' property",
15
+ typeNotLiteral: "Class '{{className}}' has 'type' property but value must be a string literal, not {{actualType}}",
16
+ },
17
+ },
18
+ create(context) {
19
+ return {
20
+ ClassDeclaration(node) {
21
+ /* v8 ignore next -- ClassDeclaration always has id (name) */
22
+ if (!node.id) return
23
+ if (!implementsInterface(node, 'EventDef')) return
24
+
25
+ const className = node.id.name
26
+ const typeProperty = findInstanceProperty(node, 'type')
27
+
28
+ if (!typeProperty) {
29
+ context.report({
30
+ node: node.id,
31
+ messageId: 'missingType',
32
+ data: { className },
33
+ })
34
+ } else if (!hasStringLiteralValue(typeProperty)) {
35
+ context.report({
36
+ node: typeProperty,
37
+ messageId: 'typeNotLiteral',
38
+ data: {
39
+ className,
40
+ actualType: getValueTypeDescription(typeProperty),
41
+ },
42
+ })
43
+ }
44
+ },
45
+ }
46
+ },
47
+ }
@@ -0,0 +1,6 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils'
2
+
3
+ type MessageIds = 'missingType' | 'typeNotLiteral'
4
+
5
+ declare const rule: TSESLint.RuleModule<MessageIds>
6
+ export default rule
@@ -1,3 +1,15 @@
1
1
  const requireComponentDecorator = require('./require-component-decorator.cjs')
2
+ const apiControllerRequiresRouteAndMethod = require('./api-controller-requires-route-and-method.cjs')
3
+ const eventRequiresTypeProperty = require('./event-requires-type-property.cjs')
4
+ const eventHandlerRequiresSubscribedEvents = require('./event-handler-requires-subscribed-events.cjs')
5
+ const uiPageRequiresRoute = require('./ui-page-requires-route.cjs')
2
6
 
3
- module.exports = { rules: { 'require-component-decorator': requireComponentDecorator } }
7
+ module.exports = {
8
+ rules: {
9
+ 'require-component-decorator': requireComponentDecorator,
10
+ 'api-controller-requires-route-and-method': apiControllerRequiresRouteAndMethod,
11
+ 'event-requires-type-property': eventRequiresTypeProperty,
12
+ 'event-handler-requires-subscribed-events': eventHandlerRequiresSubscribedEvents,
13
+ 'ui-page-requires-route': uiPageRequiresRoute,
14
+ },
15
+ }
@@ -3,6 +3,18 @@ import type { TSESLint } from '@typescript-eslint/utils'
3
3
  interface Plugin {
4
4
  rules: {
5
5
  'require-component-decorator': TSESLint.RuleModule<'missingDecorator'>
6
+ 'api-controller-requires-route-and-method': TSESLint.RuleModule<
7
+ | 'missingRoute'
8
+ | 'missingMethod'
9
+ | 'routeNotLiteral'
10
+ | 'methodNotLiteral'
11
+ | 'invalidHttpMethod'
12
+ >
13
+ 'event-requires-type-property': TSESLint.RuleModule<'missingType' | 'typeNotLiteral'>
14
+ 'event-handler-requires-subscribed-events': TSESLint.RuleModule<
15
+ 'missingSubscribedEvents' | 'subscribedEventsNotLiteralArray'
16
+ >
17
+ 'ui-page-requires-route': TSESLint.RuleModule<'missingRoute' | 'routeNotLiteral'>
6
18
  }
7
19
  }
8
20
 
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Shared utilities for ESLint rules that check interface implementations.
3
+ */
4
+
5
+ /**
6
+ * Checks if a class implements a specific interface by name.
7
+ * @param {object} node - ClassDeclaration AST node
8
+ * @param {string} interfaceName - Name of interface to check
9
+ * @returns {boolean} True if class implements the interface
10
+ */
11
+ function implementsInterface(node, interfaceName) {
12
+ if (!node.implements || node.implements.length === 0) {
13
+ return false
14
+ }
15
+ return node.implements.some((impl) => {
16
+ // Handle simple identifier: implements APIControllerDef
17
+ if (impl.expression && impl.expression.type === 'Identifier') {
18
+ return impl.expression.name === interfaceName
19
+ }
20
+ // Handle qualified name: implements SomeModule.APIControllerDef
21
+ /* v8 ignore next 4 -- defensive check: implements array always has expression */
22
+ if (impl.expression && impl.expression.type === 'TSQualifiedName') {
23
+ return impl.expression.right.name === interfaceName
24
+ }
25
+ return false
26
+ })
27
+ }
28
+
29
+ /**
30
+ * Finds an instance property (non-static) by name in class body.
31
+ * @param {object} classNode - ClassDeclaration AST node
32
+ * @param {string} propertyName - Name of property to find
33
+ * @returns {object|null} PropertyDefinition node or null
34
+ */
35
+ function findInstanceProperty(classNode, propertyName) {
36
+ /* v8 ignore next 3 -- defensive check: ClassDeclaration always has body in practice */
37
+ if (!classNode.body || !classNode.body.body) {
38
+ return null
39
+ }
40
+ return classNode.body.body.find((member) => {
41
+ return (
42
+ member.type === 'PropertyDefinition' &&
43
+ member.static !== true &&
44
+ member.key &&
45
+ member.key.type === 'Identifier' &&
46
+ member.key.name === propertyName
47
+ )
48
+ }) || null
49
+ }
50
+
51
+ /**
52
+ * Checks if a property has a literal value (string, number, boolean).
53
+ * @param {object} property - PropertyDefinition AST node
54
+ * @returns {boolean} True if value is a literal
55
+ */
56
+ /* v8 ignore start -- exported utility for consumers, not used internally */
57
+ function hasLiteralValue(property) {
58
+ if (!property || !property.value) {
59
+ return false
60
+ }
61
+ return property.value.type === 'Literal'
62
+ }
63
+ /* v8 ignore stop */
64
+
65
+ /**
66
+ * Checks if a property has a string literal value specifically.
67
+ * @param {object} property - PropertyDefinition AST node
68
+ * @returns {boolean} True if value is a string literal
69
+ */
70
+ function hasStringLiteralValue(property) {
71
+ /* v8 ignore next 3 -- defensive check: rules always check property existence first */
72
+ if (!property || !property.value) {
73
+ return false
74
+ }
75
+ return property.value.type === 'Literal' && typeof property.value.value === 'string'
76
+ }
77
+
78
+ /**
79
+ * Checks if a property has an array literal value with only literal elements.
80
+ * @param {object} property - PropertyDefinition AST node
81
+ * @returns {boolean} True if value is an array of literals
82
+ */
83
+ function hasLiteralArrayValue(property) {
84
+ /* v8 ignore next 3 -- defensive check: rules always check property existence first */
85
+ if (!property || !property.value) {
86
+ return false
87
+ }
88
+ if (property.value.type !== 'ArrayExpression') {
89
+ return false
90
+ }
91
+ return property.value.elements.every(
92
+ (element) => element && element.type === 'Literal'
93
+ )
94
+ }
95
+
96
+ /**
97
+ * Gets the literal value from a property, or null if not a literal.
98
+ * @param {object} property - PropertyDefinition AST node
99
+ * @returns {*} The literal value or null
100
+ */
101
+ function getLiteralValue(property) {
102
+ /* v8 ignore next 3 -- defensive check: rules always check property existence first */
103
+ if (!property || !property.value || property.value.type !== 'Literal') {
104
+ return null
105
+ }
106
+ return property.value.value
107
+ }
108
+
109
+ /**
110
+ * Gets a human-readable type description for error messages.
111
+ * @param {object} property - PropertyDefinition AST node
112
+ * @returns {string} Type description
113
+ */
114
+ function getValueTypeDescription(property) {
115
+ /* v8 ignore next 3 -- defensive check: only called when property exists */
116
+ if (!property || !property.value) {
117
+ return 'undefined'
118
+ }
119
+ const valueType = property.value.type
120
+ if (valueType === 'Identifier') {
121
+ return `variable reference '${property.value.name}'`
122
+ }
123
+ if (valueType === 'MemberExpression') {
124
+ return 'member expression (possibly an enum)'
125
+ }
126
+ if (valueType === 'CallExpression') {
127
+ return 'function call'
128
+ }
129
+ if (valueType === 'TemplateLiteral') {
130
+ return 'template literal'
131
+ }
132
+ return valueType
133
+ }
134
+
135
+ module.exports = {
136
+ implementsInterface,
137
+ findInstanceProperty,
138
+ hasLiteralValue,
139
+ hasStringLiteralValue,
140
+ hasLiteralArrayValue,
141
+ getLiteralValue,
142
+ getValueTypeDescription,
143
+ }
@@ -0,0 +1,47 @@
1
+ const {
2
+ implementsInterface,
3
+ findInstanceProperty,
4
+ hasStringLiteralValue,
5
+ getValueTypeDescription,
6
+ } = require('./interface-ast-predicates.cjs')
7
+
8
+ module.exports = {
9
+ meta: {
10
+ type: 'problem',
11
+ docs: { description: 'Require UIPageDef implementations to have route property with literal value' },
12
+ schema: [],
13
+ messages: {
14
+ missingRoute: "Class '{{className}}' implements UIPageDef but is missing 'route' property",
15
+ routeNotLiteral: "Class '{{className}}' has 'route' property but value must be a string literal, not {{actualType}}",
16
+ },
17
+ },
18
+ create(context) {
19
+ return {
20
+ ClassDeclaration(node) {
21
+ /* v8 ignore next -- ClassDeclaration always has id (name) */
22
+ if (!node.id) return
23
+ if (!implementsInterface(node, 'UIPageDef')) return
24
+
25
+ const className = node.id.name
26
+ const routeProperty = findInstanceProperty(node, 'route')
27
+
28
+ if (!routeProperty) {
29
+ context.report({
30
+ node: node.id,
31
+ messageId: 'missingRoute',
32
+ data: { className },
33
+ })
34
+ } else if (!hasStringLiteralValue(routeProperty)) {
35
+ context.report({
36
+ node: routeProperty,
37
+ messageId: 'routeNotLiteral',
38
+ data: {
39
+ className,
40
+ actualType: getValueTypeDescription(routeProperty),
41
+ },
42
+ })
43
+ }
44
+ },
45
+ }
46
+ },
47
+ }
@@ -0,0 +1,6 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils'
2
+
3
+ type MessageIds = 'missingRoute' | 'routeNotLiteral'
4
+
5
+ declare const rule: TSESLint.RuleModule<MessageIds>
6
+ export default rule