archsight 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +24 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +186 -0
- data/Dockerfile +39 -0
- data/LICENSE.txt +201 -0
- data/README.md +170 -0
- data/SECURITY.md +27 -0
- data/exe/archsight +9 -0
- data/lib/archsight/annotations/aggregators.rb +109 -0
- data/lib/archsight/annotations/annotation.rb +168 -0
- data/lib/archsight/annotations/architecture_annotations.rb +59 -0
- data/lib/archsight/annotations/backup_annotations.rb +21 -0
- data/lib/archsight/annotations/computed.rb +264 -0
- data/lib/archsight/annotations/email_recipient.rb +35 -0
- data/lib/archsight/annotations/generated_annotations.rb +17 -0
- data/lib/archsight/annotations/git_annotations.rb +21 -0
- data/lib/archsight/annotations/relation_resolver.rb +160 -0
- data/lib/archsight/cli.rb +120 -0
- data/lib/archsight/configuration.rb +36 -0
- data/lib/archsight/database.rb +183 -0
- data/lib/archsight/documentation.rb +171 -0
- data/lib/archsight/graph.rb +113 -0
- data/lib/archsight/helpers.rb +210 -0
- data/lib/archsight/linter.rb +77 -0
- data/lib/archsight/mcp/analyze_resource_tool.rb +222 -0
- data/lib/archsight/mcp/base.rb +48 -0
- data/lib/archsight/mcp/query_tool.rb +113 -0
- data/lib/archsight/mcp/resource_doc_tool.rb +87 -0
- data/lib/archsight/mcp.rb +6 -0
- data/lib/archsight/query/ast.rb +279 -0
- data/lib/archsight/query/errors.rb +39 -0
- data/lib/archsight/query/evaluator.rb +707 -0
- data/lib/archsight/query/lexer.rb +289 -0
- data/lib/archsight/query/parser.rb +506 -0
- data/lib/archsight/query.rb +68 -0
- data/lib/archsight/renderer.rb +134 -0
- data/lib/archsight/resources/application_component.rb +346 -0
- data/lib/archsight/resources/application_interface.rb +54 -0
- data/lib/archsight/resources/application_service.rb +222 -0
- data/lib/archsight/resources/base.rb +300 -0
- data/lib/archsight/resources/business_actor.rb +195 -0
- data/lib/archsight/resources/business_constraint.rb +32 -0
- data/lib/archsight/resources/business_process.rb +37 -0
- data/lib/archsight/resources/business_product.rb +206 -0
- data/lib/archsight/resources/business_requirement.rb +56 -0
- data/lib/archsight/resources/compliance_evidence.rb +42 -0
- data/lib/archsight/resources/data_object.rb +49 -0
- data/lib/archsight/resources/motivation_goal.rb +37 -0
- data/lib/archsight/resources/motivation_outcome.rb +33 -0
- data/lib/archsight/resources/motivation_stakeholder.rb +38 -0
- data/lib/archsight/resources/strategy_capability.rb +38 -0
- data/lib/archsight/resources/technology_artifact.rb +154 -0
- data/lib/archsight/resources/technology_interface.rb +34 -0
- data/lib/archsight/resources/technology_node.rb +42 -0
- data/lib/archsight/resources/technology_service.rb +35 -0
- data/lib/archsight/resources/technology_system_software.rb +37 -0
- data/lib/archsight/resources/view.rb +51 -0
- data/lib/archsight/resources.rb +49 -0
- data/lib/archsight/template.rb +49 -0
- data/lib/archsight/version.rb +5 -0
- data/lib/archsight/web/application.rb +290 -0
- data/lib/archsight/web/doc/archimate.md +215 -0
- data/lib/archsight/web/doc/computed_annotations.md +316 -0
- data/lib/archsight/web/doc/icons.md +303 -0
- data/lib/archsight/web/doc/index.md.erb +74 -0
- data/lib/archsight/web/doc/modeling.md +200 -0
- data/lib/archsight/web/doc/search.md +227 -0
- data/lib/archsight/web/doc/togaf.md +255 -0
- data/lib/archsight/web/doc/tool.md +90 -0
- data/lib/archsight/web/public/css/artifact.css +985 -0
- data/lib/archsight/web/public/css/base.css +201 -0
- data/lib/archsight/web/public/css/graph.css +106 -0
- data/lib/archsight/web/public/css/highlight.min.css +10 -0
- data/lib/archsight/web/public/css/iconoir.css +22 -0
- data/lib/archsight/web/public/css/instance.css +329 -0
- data/lib/archsight/web/public/css/layout.css +421 -0
- data/lib/archsight/web/public/css/mermaid-layers.css +188 -0
- data/lib/archsight/web/public/css/pico.min.css +4 -0
- data/lib/archsight/web/public/favicon.ico +0 -0
- data/lib/archsight/web/public/img/archimate.png +0 -0
- data/lib/archsight/web/public/img/togaf-high-level.png +0 -0
- data/lib/archsight/web/public/js/graph-zoom.js +18 -0
- data/lib/archsight/web/public/js/highlight.min.js +3899 -0
- data/lib/archsight/web/public/js/htmx.min.js +1 -0
- data/lib/archsight/web/public/js/mermaid-init.js +88 -0
- data/lib/archsight/web/public/js/mermaid.min.js +2811 -0
- data/lib/archsight/web/public/js/sparkline.js +42 -0
- data/lib/archsight/web/public/js/svg-pan-zoom.min.js +3 -0
- data/lib/archsight/web/public/js/svg-zoom-controls.js +93 -0
- data/lib/archsight/web/views/index.haml +12 -0
- data/lib/archsight/web/views/partials/artifact/_activity.haml +55 -0
- data/lib/archsight/web/views/partials/artifact/_agentic.haml +25 -0
- data/lib/archsight/web/views/partials/artifact/_deployment.haml +29 -0
- data/lib/archsight/web/views/partials/artifact/_git_info.haml +16 -0
- data/lib/archsight/web/views/partials/artifact/_language_stats.haml +53 -0
- data/lib/archsight/web/views/partials/artifact/_links.haml +24 -0
- data/lib/archsight/web/views/partials/artifact/_project_estimate.haml +26 -0
- data/lib/archsight/web/views/partials/artifact/_repositories.haml +55 -0
- data/lib/archsight/web/views/partials/artifact/_team.haml +83 -0
- data/lib/archsight/web/views/partials/artifact/_workflow.haml +69 -0
- data/lib/archsight/web/views/partials/components/_activity.haml +37 -0
- data/lib/archsight/web/views/partials/components/_git.haml +17 -0
- data/lib/archsight/web/views/partials/components/_jira.haml +18 -0
- data/lib/archsight/web/views/partials/components/_languages.haml +29 -0
- data/lib/archsight/web/views/partials/components/_owner.haml +15 -0
- data/lib/archsight/web/views/partials/components/_repositories.haml +37 -0
- data/lib/archsight/web/views/partials/components/_status.haml +23 -0
- data/lib/archsight/web/views/partials/instance/_detail.haml +99 -0
- data/lib/archsight/web/views/partials/instance/_graph.haml +6 -0
- data/lib/archsight/web/views/partials/instance/_list.haml +84 -0
- data/lib/archsight/web/views/partials/instance/_relations.haml +43 -0
- data/lib/archsight/web/views/partials/instance/_requirements.haml +41 -0
- data/lib/archsight/web/views/partials/instance/_view_detail.haml +57 -0
- data/lib/archsight/web/views/partials/layout/_content.haml +40 -0
- data/lib/archsight/web/views/partials/layout/_error.haml +22 -0
- data/lib/archsight/web/views/partials/layout/_head.haml +24 -0
- data/lib/archsight/web/views/partials/layout/_navigation.haml +20 -0
- data/lib/archsight/web/views/partials/layout/_sidebar.haml +27 -0
- data/lib/archsight/web/views/search.haml +53 -0
- data/lib/archsight.rb +17 -0
- metadata +311 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors"
|
|
4
|
+
|
|
5
|
+
class Archsight::Query::Lexer
|
|
6
|
+
class Token
|
|
7
|
+
attr_reader :type, :value, :position
|
|
8
|
+
|
|
9
|
+
def initialize(type, value, position)
|
|
10
|
+
@type = type
|
|
11
|
+
@value = value
|
|
12
|
+
@position = position
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_s
|
|
16
|
+
"Token(#{@type}, #{@value.inspect}, pos=#{@position})"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Keywords are case-insensitive
|
|
21
|
+
KEYWORDS = {
|
|
22
|
+
"and" => :AND,
|
|
23
|
+
"or" => :OR,
|
|
24
|
+
"not" => :NOT,
|
|
25
|
+
"kind" => :KIND,
|
|
26
|
+
"name" => :NAME,
|
|
27
|
+
"none" => :NONE,
|
|
28
|
+
"in" => :IN
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
# Two-character operators (must be checked before single-character)
|
|
32
|
+
TWO_CHAR_OPERATORS = {
|
|
33
|
+
"==" => :EQ,
|
|
34
|
+
"!=" => :NEQ,
|
|
35
|
+
"=~" => :MATCH,
|
|
36
|
+
">=" => :GTE,
|
|
37
|
+
"<=" => :LTE,
|
|
38
|
+
"->" => :OUTGOING_DIRECT,
|
|
39
|
+
"~>" => :OUTGOING_TRANSITIVE,
|
|
40
|
+
"<-" => :INCOMING_DIRECT,
|
|
41
|
+
"<~" => :INCOMING_TRANSITIVE
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
# Single-character operators
|
|
45
|
+
SINGLE_CHAR_OPERATORS = {
|
|
46
|
+
">" => :GT,
|
|
47
|
+
"<" => :LT,
|
|
48
|
+
"&" => :AND,
|
|
49
|
+
"|" => :OR,
|
|
50
|
+
"!" => :NOT
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
53
|
+
def initialize(input)
|
|
54
|
+
@input = input
|
|
55
|
+
@position = 0
|
|
56
|
+
@tokens = []
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def tokenize
|
|
60
|
+
@tokens = []
|
|
61
|
+
while @position < @input.length
|
|
62
|
+
skip_whitespace
|
|
63
|
+
break if @position >= @input.length
|
|
64
|
+
|
|
65
|
+
token = scan_token
|
|
66
|
+
@tokens << token if token
|
|
67
|
+
end
|
|
68
|
+
@tokens << Token.new(:EOF, nil, @position)
|
|
69
|
+
@tokens
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def skip_whitespace
|
|
75
|
+
@position += 1 while @position < @input.length && @input[@position] =~ /\s/
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def scan_token
|
|
79
|
+
char = @input[@position]
|
|
80
|
+
start_pos = @position
|
|
81
|
+
|
|
82
|
+
case char
|
|
83
|
+
when "("
|
|
84
|
+
@position += 1
|
|
85
|
+
Token.new(:LPAREN, "(", start_pos)
|
|
86
|
+
when ")"
|
|
87
|
+
@position += 1
|
|
88
|
+
Token.new(:RPAREN, ")", start_pos)
|
|
89
|
+
when ":"
|
|
90
|
+
@position += 1
|
|
91
|
+
Token.new(:COLON, ":", start_pos)
|
|
92
|
+
when "$"
|
|
93
|
+
@position += 1
|
|
94
|
+
Token.new(:DOLLAR, "$", start_pos)
|
|
95
|
+
when "?"
|
|
96
|
+
@position += 1
|
|
97
|
+
Token.new(:QUESTION, "?", start_pos)
|
|
98
|
+
when ","
|
|
99
|
+
@position += 1
|
|
100
|
+
Token.new(:COMMA, ",", start_pos)
|
|
101
|
+
when "{"
|
|
102
|
+
@position += 1
|
|
103
|
+
Token.new(:LBRACE, "{", start_pos)
|
|
104
|
+
when "}"
|
|
105
|
+
@position += 1
|
|
106
|
+
Token.new(:RBRACE, "}", start_pos)
|
|
107
|
+
when '"'
|
|
108
|
+
scan_string
|
|
109
|
+
when "'"
|
|
110
|
+
scan_single_quoted_string
|
|
111
|
+
when "/"
|
|
112
|
+
scan_regex
|
|
113
|
+
when "-", "~", "=", "!", ">", "<", "&", "|"
|
|
114
|
+
scan_operator
|
|
115
|
+
else
|
|
116
|
+
if char =~ /[a-zA-Z_]/
|
|
117
|
+
scan_identifier
|
|
118
|
+
elsif char =~ /\d/
|
|
119
|
+
scan_number
|
|
120
|
+
else
|
|
121
|
+
raise Archsight::Query::LexerError.new("Unexpected character '#{char}'", position: @position, source: @input)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def scan_string
|
|
127
|
+
start_pos = @position
|
|
128
|
+
@position += 1 # skip opening quote
|
|
129
|
+
value = ""
|
|
130
|
+
|
|
131
|
+
while @position < @input.length && @input[@position] != '"'
|
|
132
|
+
if @input[@position] == "\\"
|
|
133
|
+
@position += 1
|
|
134
|
+
value += @input[@position] if @position < @input.length
|
|
135
|
+
else
|
|
136
|
+
value += @input[@position]
|
|
137
|
+
end
|
|
138
|
+
@position += 1
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
raise Archsight::Query::LexerError.new("Unterminated string", position: start_pos, source: @input) if @position >= @input.length
|
|
142
|
+
|
|
143
|
+
@position += 1 # skip closing quote
|
|
144
|
+
Token.new(:STRING, value, start_pos)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def scan_single_quoted_string
|
|
148
|
+
start_pos = @position
|
|
149
|
+
@position += 1 # skip opening quote
|
|
150
|
+
value = ""
|
|
151
|
+
|
|
152
|
+
while @position < @input.length && @input[@position] != "'"
|
|
153
|
+
value += @input[@position]
|
|
154
|
+
@position += 1
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
if @position >= @input.length
|
|
158
|
+
raise Archsight::Query::LexerError.new("Unterminated single-quoted string", position: start_pos,
|
|
159
|
+
source: @input)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
@position += 1 # skip closing quote
|
|
163
|
+
Token.new(:STRING, value, start_pos)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def scan_regex
|
|
167
|
+
start_pos = @position
|
|
168
|
+
@position += 1 # skip opening /
|
|
169
|
+
pattern = ""
|
|
170
|
+
|
|
171
|
+
while @position < @input.length && @input[@position] != "/"
|
|
172
|
+
pattern += @input[@position]
|
|
173
|
+
if @input[@position] == "\\"
|
|
174
|
+
@position += 1
|
|
175
|
+
pattern += @input[@position] if @position < @input.length
|
|
176
|
+
end
|
|
177
|
+
@position += 1
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
raise Archsight::Query::LexerError.new("Unterminated regex", position: start_pos, source: @input) if @position >= @input.length
|
|
181
|
+
|
|
182
|
+
@position += 1 # skip closing /
|
|
183
|
+
|
|
184
|
+
# Check for flags
|
|
185
|
+
flags = ""
|
|
186
|
+
while @position < @input.length && @input[@position] =~ /[imx]/
|
|
187
|
+
flags += @input[@position]
|
|
188
|
+
@position += 1
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
Token.new(:REGEX, { pattern: pattern, flags: flags }, start_pos)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def scan_operator
|
|
195
|
+
start_pos = @position
|
|
196
|
+
one_char = @input[@position]
|
|
197
|
+
next_char = @position + 1 < @input.length ? @input[@position + 1] : nil
|
|
198
|
+
|
|
199
|
+
# Check for verb filter syntax: -{, ~{, <{ should NOT be consumed as two-char operators
|
|
200
|
+
# Instead, emit single char tokens so parser can handle verb filters
|
|
201
|
+
if next_char == "{"
|
|
202
|
+
case one_char
|
|
203
|
+
when "-"
|
|
204
|
+
@position += 1
|
|
205
|
+
return Token.new(:DASH, "-", start_pos)
|
|
206
|
+
when "~"
|
|
207
|
+
@position += 1
|
|
208
|
+
return Token.new(:TILDE, "~", start_pos)
|
|
209
|
+
when "<"
|
|
210
|
+
@position += 1
|
|
211
|
+
return Token.new(:LT, "<", start_pos)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Try two-character operators
|
|
216
|
+
if @position + 1 < @input.length
|
|
217
|
+
two_char = @input[@position, 2]
|
|
218
|
+
if TWO_CHAR_OPERATORS[two_char]
|
|
219
|
+
@position += 2
|
|
220
|
+
return Token.new(TWO_CHAR_OPERATORS[two_char], two_char, start_pos)
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Single character operators
|
|
225
|
+
if SINGLE_CHAR_OPERATORS[one_char]
|
|
226
|
+
@position += 1
|
|
227
|
+
return Token.new(SINGLE_CHAR_OPERATORS[one_char], one_char, start_pos)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Handle standalone - and ~ (for verb filter closing: }- and }~)
|
|
231
|
+
if one_char == "-"
|
|
232
|
+
@position += 1
|
|
233
|
+
return Token.new(:DASH, "-", start_pos)
|
|
234
|
+
elsif one_char == "~"
|
|
235
|
+
@position += 1
|
|
236
|
+
return Token.new(:TILDE, "~", start_pos)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
raise Archsight::Query::LexerError.new("Unknown operator '#{one_char}'", position: @position, source: @input)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def scan_identifier
|
|
243
|
+
start_pos = @position
|
|
244
|
+
value = ""
|
|
245
|
+
|
|
246
|
+
# Allow letters, digits, underscores, hyphens, slashes, and dots in identifiers
|
|
247
|
+
# This supports annotation paths like "activity/status" and "scc/language/Go/loc"
|
|
248
|
+
# and resource names like "teleport.e"
|
|
249
|
+
while @position < @input.length && @input[@position] =~ %r{[a-zA-Z0-9_\-/.]}
|
|
250
|
+
value += @input[@position]
|
|
251
|
+
@position += 1
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Check if it's a keyword (case-insensitive)
|
|
255
|
+
lower = value.downcase
|
|
256
|
+
return Token.new(KEYWORDS[lower], value, start_pos) if KEYWORDS.key?(lower)
|
|
257
|
+
|
|
258
|
+
Token.new(:IDENTIFIER, value, start_pos)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def scan_number
|
|
262
|
+
start_pos = @position
|
|
263
|
+
value = ""
|
|
264
|
+
|
|
265
|
+
# Optional negative sign (handled separately as operator, but support here too)
|
|
266
|
+
if @input[@position] == "-"
|
|
267
|
+
value += "-"
|
|
268
|
+
@position += 1
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Integer part
|
|
272
|
+
while @position < @input.length && @input[@position] =~ /\d/
|
|
273
|
+
value += @input[@position]
|
|
274
|
+
@position += 1
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Decimal part
|
|
278
|
+
if @position < @input.length && @input[@position] == "."
|
|
279
|
+
value += "."
|
|
280
|
+
@position += 1
|
|
281
|
+
while @position < @input.length && @input[@position] =~ /\d/
|
|
282
|
+
value += @input[@position]
|
|
283
|
+
@position += 1
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
Token.new(:NUMBER, value.to_f, start_pos)
|
|
288
|
+
end
|
|
289
|
+
end
|