glib-web 4.39.0 → 4.39.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.
- checksums.yaml +4 -4
- data/app/controllers/concerns/glib/auth/policy.rb +13 -0
- data/app/controllers/glib/api_docs_controller.rb +145 -0
- data/app/helpers/glib/json_ui/abstract_builder.rb +16 -0
- data/app/helpers/glib/json_ui/action_builder/dialogs.rb +4 -0
- data/app/helpers/glib/json_ui/list_builders.rb +2 -0
- data/app/helpers/glib/json_ui/view_builder/fields.rb +15 -0
- data/app/helpers/glib/json_ui/view_builder/panels.rb +450 -11
- data/app/helpers/glib/json_ui/view_builder.rb +1 -1
- data/app/views/glib/api_docs/component.json.jbuilder +215 -0
- data/app/views/glib/api_docs/index.json.jbuilder +103 -0
- data/app/views/glib/api_docs/show.json.jbuilder +111 -0
- data/app/views/json_ui/garage/actions/_dialogs.json.jbuilder +2 -2
- data/app/views/json_ui/garage/forms/dynamic_group.json.jbuilder +2 -2
- data/app/views/json_ui/garage/forms/file_upload_new.json.jbuilder +1 -1
- data/app/views/json_ui/garage/forms/partial_update.json.jbuilder +12 -12
- data/app/views/json_ui/garage/forms/selects.json.jbuilder +1 -1
- data/app/views/json_ui/garage/forms/show_hide.json.jbuilder +62 -7
- data/app/views/json_ui/garage/lists/edit_mode.json.jbuilder +4 -4
- data/app/views/json_ui/garage/lists/templating.json.jbuilder +68 -44
- data/app/views/json_ui/garage/pages/custom_style_class.json.jbuilder +1 -1
- data/app/views/json_ui/garage/pages/nav_buttons.json.jbuilder +31 -13
- data/app/views/json_ui/garage/panels/_styled.json.jbuilder +8 -8
- data/app/views/json_ui/garage/panels/hover.json.jbuilder +2 -2
- data/app/views/json_ui/garage/panels/timeline.json.jbuilder +5 -5
- data/app/views/json_ui/garage/panels/ul.json.jbuilder +1 -1
- data/app/views/json_ui/garage/tables/bulk_edit.json.jbuilder +1 -1
- data/app/views/json_ui/garage/test_page/file_upload_new.json.jbuilder +1 -1
- data/app/views/json_ui/garage/test_page/form.json.jbuilder +6 -6
- data/app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder +2 -2
- data/app/views/json_ui/garage/test_page/logics_set.json.jbuilder +94 -0
- data/app/views/json_ui/garage/views/components_replace.json.jbuilder +13 -13
- data/app/views/json_ui/garage/views/components_set.json.jbuilder +6 -6
- data/app/views/json_ui/garage/views/fields_focus.json.jbuilder +22 -22
- data/app/views/json_ui/garage/views/markdowns.json.jbuilder +2 -0
- data/config/routes.rb +4 -0
- data/lib/glib/doc_generator.rb +386 -0
- data/lib/glib/json_crawler/router.rb +45 -24
- data/lib/glib/rubocop/cops/json_ui/base_nested_parameter.rb +145 -0
- data/lib/glib/rubocop/cops/json_ui/nested_action_parameter.rb +55 -0
- data/lib/glib/rubocop/cops/json_ui/nested_block_parameter.rb +51 -0
- data/lib/glib/rubocop/cops/multiline_method_call_style.rb +74 -5
- data/lib/glib/rubocop/cops/test_name_parentheses.rb +33 -0
- data/lib/glib/rubocop.rb +4 -0
- data/lib/glib/test/parallel_coverage.rb +38 -0
- data/lib/glib/test_helpers.rb +12 -0
- data/lib/glib-web.rb +1 -0
- data/lib/tasks/db.rake +1 -1
- data/lib/tasks/docs.rake +59 -0
- metadata +13 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_nested_parameter'
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
module Cop
|
|
7
|
+
module Glib
|
|
8
|
+
module JsonUi
|
|
9
|
+
# Enforces using the immediate block parameter instead of outer block variables
|
|
10
|
+
# in nested view blocks (childViews, content) in JsonUi templates.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad - using outer variable
|
|
14
|
+
# scroll.panels_form childViews: ->(form) do
|
|
15
|
+
# scroll.label text: 'TEST' # using 'scroll' instead of 'form'
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# # good
|
|
19
|
+
# scroll.panels_form childViews: ->(form) do
|
|
20
|
+
# form.label text: 'TEST'
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# # bad - parameter shadowing
|
|
24
|
+
# scroll.panels_form childViews: ->(form) do
|
|
25
|
+
# form.panels_responsive childViews: ->(form) do # shadows outer 'form'
|
|
26
|
+
# form.fields_text name: 'user[field]'
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# # good
|
|
31
|
+
# scroll.panels_form childViews: ->(form) do
|
|
32
|
+
# form.panels_responsive childViews: ->(res) do
|
|
33
|
+
# res.fields_text name: 'user[field]'
|
|
34
|
+
# end
|
|
35
|
+
# end
|
|
36
|
+
class NestedBlockParameter < BaseNestedParameter
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def relevant_block?(block_node)
|
|
40
|
+
return false unless lambda_block?(block_node)
|
|
41
|
+
|
|
42
|
+
pair = hash_pair_parent(block_node)
|
|
43
|
+
return false unless pair
|
|
44
|
+
|
|
45
|
+
pair.key.sym_type? && [:childViews, :content].include?(pair.key.value)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -37,6 +37,7 @@ module RuboCop
|
|
|
37
37
|
return unless multi_line_call_with_args?(node)
|
|
38
38
|
return if proper_parentheses_style?(node)
|
|
39
39
|
return if proper_backslash_style?(node) && allow_backslash?
|
|
40
|
+
return if inside_correctable_parent?(node)
|
|
40
41
|
|
|
41
42
|
add_offense(node) do |corrector|
|
|
42
43
|
autocorrect(corrector, node)
|
|
@@ -122,12 +123,28 @@ module RuboCop
|
|
|
122
123
|
args
|
|
123
124
|
end
|
|
124
125
|
|
|
126
|
+
def inside_correctable_parent?(node)
|
|
127
|
+
# Check if this node is inside a block that belongs to a multi-line call
|
|
128
|
+
# that will also be corrected. If so, skip this node to avoid conflicts.
|
|
129
|
+
current = node.parent
|
|
130
|
+
|
|
131
|
+
while current
|
|
132
|
+
if current.send_type? || current.csend_type?
|
|
133
|
+
if multi_line_call_with_args?(current) && !proper_parentheses_style?(current)
|
|
134
|
+
return true
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
current = current.parent
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
false
|
|
141
|
+
end
|
|
142
|
+
|
|
125
143
|
def autocorrect(corrector, node)
|
|
126
144
|
method_end_pos = node.loc.selector.end_pos
|
|
127
145
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
base_indent = node.source_range.begin_pos - line_begin_pos
|
|
146
|
+
# Calculate indentation based on the node's actual column position
|
|
147
|
+
base_indent = node.loc.column
|
|
131
148
|
arg_indent = ' ' * (base_indent + 2)
|
|
132
149
|
|
|
133
150
|
# Build the new argument list
|
|
@@ -137,7 +154,9 @@ module RuboCop
|
|
|
137
154
|
|
|
138
155
|
if arg.hash_type?
|
|
139
156
|
arg.pairs.each do |pair|
|
|
140
|
-
|
|
157
|
+
# Re-indent pair if it contains a block
|
|
158
|
+
pair_source = reindent_pair_with_block(pair, arg_indent)
|
|
159
|
+
arg_parts << pair_source
|
|
141
160
|
end
|
|
142
161
|
else
|
|
143
162
|
arg_parts << arg.source
|
|
@@ -151,7 +170,7 @@ module RuboCop
|
|
|
151
170
|
|
|
152
171
|
# If there's a block, add it on the last argument line
|
|
153
172
|
if node.block_literal?
|
|
154
|
-
block_source = node.block_literal
|
|
173
|
+
block_source = reindent_block(node.block_literal, arg_indent)
|
|
155
174
|
replacement += " #{block_source}"
|
|
156
175
|
end
|
|
157
176
|
|
|
@@ -175,6 +194,56 @@ module RuboCop
|
|
|
175
194
|
|
|
176
195
|
corrector.replace(range, replacement)
|
|
177
196
|
end
|
|
197
|
+
|
|
198
|
+
def reindent_pair_with_block(pair, base_indent)
|
|
199
|
+
# Check if the pair's value is a block
|
|
200
|
+
return pair.source unless pair.value.block_type?
|
|
201
|
+
|
|
202
|
+
# Format: "key: ->(param) do ... end"
|
|
203
|
+
key_source = pair.key.source
|
|
204
|
+
block_source = reindent_block(pair.value, base_indent)
|
|
205
|
+
|
|
206
|
+
"#{key_source}: #{block_source}"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def reindent_block(block_node, base_indent)
|
|
210
|
+
# Get block source lines
|
|
211
|
+
source = block_node.source
|
|
212
|
+
lines = source.lines
|
|
213
|
+
|
|
214
|
+
return source if lines.length == 1 # Single line block, no reindent needed
|
|
215
|
+
|
|
216
|
+
# Find the current indentation of the block's first line
|
|
217
|
+
# (this is usually where "do" appears)
|
|
218
|
+
first_line = lines[0]
|
|
219
|
+
|
|
220
|
+
# Process the block:
|
|
221
|
+
# Line 0: "->(param) do" - keep as is
|
|
222
|
+
# Lines 1..-2: block body - re-indent to base_indent + 2
|
|
223
|
+
# Last line: "end" - indent to base_indent
|
|
224
|
+
|
|
225
|
+
result_lines = []
|
|
226
|
+
block_body_indent = base_indent + ' ' * 2
|
|
227
|
+
|
|
228
|
+
lines.each_with_index do |line, index|
|
|
229
|
+
if index == 0
|
|
230
|
+
# First line: "->(param) do"
|
|
231
|
+
result_lines << line.rstrip
|
|
232
|
+
elsif index == lines.length - 1
|
|
233
|
+
# Last line: "end"
|
|
234
|
+
result_lines << "#{base_indent}#{line.strip}"
|
|
235
|
+
else
|
|
236
|
+
# Body lines: re-indent
|
|
237
|
+
# Remove existing indentation and add new indentation
|
|
238
|
+
stripped = line.lstrip
|
|
239
|
+
next if stripped.empty? # Skip blank lines
|
|
240
|
+
|
|
241
|
+
result_lines << "#{block_body_indent}#{stripped.rstrip}"
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
result_lines.join("\n")
|
|
246
|
+
end
|
|
178
247
|
end
|
|
179
248
|
end
|
|
180
249
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Glib
|
|
6
|
+
# Prevents test method names from containing parentheses which can cause issues with test runners
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # bad
|
|
10
|
+
# test 'user creation (with email)' do
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# # good
|
|
14
|
+
# test 'user creation with email' do
|
|
15
|
+
# end
|
|
16
|
+
class TestNameParentheses < Base
|
|
17
|
+
MSG = "Test name should not contain parentheses because for some reason, this will produce: 'Syntax error: \"(\" unexpected'."
|
|
18
|
+
|
|
19
|
+
def_node_matcher :test_method?, <<~PATTERN
|
|
20
|
+
(send nil? :test (str $_) ...)
|
|
21
|
+
PATTERN
|
|
22
|
+
|
|
23
|
+
def on_send(node)
|
|
24
|
+
test_method?(node) do |test_name|
|
|
25
|
+
if test_name.include?('(') || test_name.include?(')')
|
|
26
|
+
add_offense(node)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/glib/rubocop.rb
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
require_relative 'rubocop/cops/localize'
|
|
2
2
|
require_relative 'rubocop/cops/multiline_method_call_style'
|
|
3
|
+
require_relative 'rubocop/cops/json_ui/base_nested_parameter'
|
|
4
|
+
require_relative 'rubocop/cops/json_ui/nested_block_parameter'
|
|
5
|
+
require_relative 'rubocop/cops/json_ui/nested_action_parameter'
|
|
6
|
+
require_relative 'rubocop/cops/test_name_parentheses'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glib
|
|
4
|
+
module Test
|
|
5
|
+
# ParallelCoverage configures Rails parallel testing to work properly with SimpleCov.
|
|
6
|
+
#
|
|
7
|
+
# Without this configuration, SimpleCov reports inaccurate coverage (often very low, like 1-2%)
|
|
8
|
+
# when running tests with parallelization enabled, because it only captures data from one worker
|
|
9
|
+
# instead of merging results from all parallel workers.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# class ActiveSupport::TestCase
|
|
13
|
+
# include Glib::Test::ParallelCoverage
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# This will:
|
|
17
|
+
# - Enable parallel test execution using all available processors
|
|
18
|
+
# - Configure SimpleCov to track each worker separately
|
|
19
|
+
# - Automatically merge coverage results from all workers
|
|
20
|
+
module ParallelCoverage
|
|
21
|
+
extend ActiveSupport::Concern
|
|
22
|
+
|
|
23
|
+
included do
|
|
24
|
+
# Set threshold for force parallelization.
|
|
25
|
+
parallelize(workers: :number_of_processors, threshold: 1)
|
|
26
|
+
|
|
27
|
+
# Configure SimpleCov to properly merge coverage from parallel workers
|
|
28
|
+
parallelize_setup do |worker|
|
|
29
|
+
SimpleCov.command_name "#{SimpleCov.command_name}-#{worker}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
parallelize_teardown do |_worker|
|
|
33
|
+
SimpleCov.result
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/glib/test_helpers.rb
CHANGED
|
@@ -83,6 +83,18 @@ module Glib
|
|
|
83
83
|
"http://#{HOST}/users/sign_out.json"
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
def glib_travel(*args, &block)
|
|
87
|
+
Timecop.travel(*args, &block)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def glib_travel_freeze(*args, &block)
|
|
91
|
+
Timecop.freeze(*args, &block)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def glib_travel_back
|
|
95
|
+
Timecop.return
|
|
96
|
+
end
|
|
97
|
+
|
|
86
98
|
private
|
|
87
99
|
def __execute_crawler(user, inspect_http:, log_file: nil, &block)
|
|
88
100
|
auth_token = login user
|
data/lib/glib-web.rb
CHANGED
data/lib/tasks/db.rake
CHANGED
data/lib/tasks/docs.rake
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
namespace :glib do
|
|
2
|
+
namespace :docs do
|
|
3
|
+
desc 'Generate component documentation in YAML format'
|
|
4
|
+
task :generate do
|
|
5
|
+
require_relative '../glib/doc_generator'
|
|
6
|
+
|
|
7
|
+
generator = Glib::DocGenerator.new
|
|
8
|
+
|
|
9
|
+
# Generate documentation for different component categories
|
|
10
|
+
categories = [
|
|
11
|
+
{
|
|
12
|
+
name: 'panels',
|
|
13
|
+
file: 'app/helpers/glib/json_ui/view_builder/panels.rb',
|
|
14
|
+
output: 'doc/components/panels.yml'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'fields',
|
|
18
|
+
file: 'app/helpers/glib/json_ui/view_builder/fields.rb',
|
|
19
|
+
output: 'doc/components/fields.yml'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'views',
|
|
23
|
+
file: 'app/helpers/glib/json_ui/view_builder.rb',
|
|
24
|
+
output: 'doc/components/views.yml'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'actions',
|
|
28
|
+
file: 'app/helpers/glib/json_ui/action_builder.rb',
|
|
29
|
+
output: 'doc/components/actions.yml'
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
categories.each do |category|
|
|
34
|
+
puts "Generating documentation for #{category[:name]}..."
|
|
35
|
+
generator.generate_for_file(category[:file], category[:output])
|
|
36
|
+
puts " -> #{category[:output]}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Also scan action subdirectories
|
|
40
|
+
Dir.glob('app/helpers/glib/json_ui/action_builder/*.rb').each do |file|
|
|
41
|
+
basename = File.basename(file, '.rb')
|
|
42
|
+
output = "doc/components/actions_#{basename}.yml"
|
|
43
|
+
puts "Generating documentation for actions/#{basename}..."
|
|
44
|
+
generator.generate_for_file(file, output)
|
|
45
|
+
puts " -> #{output}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
puts "\nDocumentation generation complete!"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
desc 'Validate YARD documentation'
|
|
52
|
+
task :validate do
|
|
53
|
+
puts "Validating YARD documentation..."
|
|
54
|
+
system('yard stats --list-undoc')
|
|
55
|
+
system('yard doc --fail-on-warning --no-output')
|
|
56
|
+
puts "\nValidation complete!"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: glib-web
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.39.
|
|
4
|
+
version: 4.39.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
@@ -156,6 +156,7 @@ files:
|
|
|
156
156
|
- app/controllers/concerns/glib/json/traversal.rb
|
|
157
157
|
- app/controllers/concerns/glib/json/ui.rb
|
|
158
158
|
- app/controllers/concerns/glib/json/validation.rb
|
|
159
|
+
- app/controllers/glib/api_docs_controller.rb
|
|
159
160
|
- app/controllers/glib/blob_url_generators_controller.rb
|
|
160
161
|
- app/controllers/glib/errors_controller.rb
|
|
161
162
|
- app/controllers/glib/glib_direct_uploads_controller.rb
|
|
@@ -220,6 +221,9 @@ files:
|
|
|
220
221
|
- app/validators/mutually_exclusive_with_validator.rb
|
|
221
222
|
- app/validators/presence_only_if_validator.rb
|
|
222
223
|
- app/validators/url_validator.rb
|
|
224
|
+
- app/views/glib/api_docs/component.json.jbuilder
|
|
225
|
+
- app/views/glib/api_docs/index.json.jbuilder
|
|
226
|
+
- app/views/glib/api_docs/show.json.jbuilder
|
|
223
227
|
- app/views/json_ui/garage/_nav_menu.json.jbuilder
|
|
224
228
|
- app/views/json_ui/garage/actions/_commands.json.jbuilder
|
|
225
229
|
- app/views/json_ui/garage/actions/_components.json.jbuilder
|
|
@@ -376,6 +380,7 @@ files:
|
|
|
376
380
|
- app/views/json_ui/garage/test_page/form.json.jbuilder
|
|
377
381
|
- app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder
|
|
378
382
|
- app/views/json_ui/garage/test_page/lifecycle.json.jbuilder
|
|
383
|
+
- app/views/json_ui/garage/test_page/logics_set.json.jbuilder
|
|
379
384
|
- app/views/json_ui/garage/test_page/multiupload.json.jbuilder
|
|
380
385
|
- app/views/json_ui/garage/test_page/selectable.json.jbuilder
|
|
381
386
|
- app/views/json_ui/garage/test_page/window.json.jbuilder
|
|
@@ -422,6 +427,7 @@ files:
|
|
|
422
427
|
- lib/glib-web.rb
|
|
423
428
|
- lib/glib/all_helpers.rb
|
|
424
429
|
- lib/glib/crypt/utils.rb
|
|
430
|
+
- lib/glib/doc_generator.rb
|
|
425
431
|
- lib/glib/dynamic_text.rb
|
|
426
432
|
- lib/glib/dynamic_text/config.rb
|
|
427
433
|
- lib/glib/engine.rb
|
|
@@ -442,15 +448,21 @@ files:
|
|
|
442
448
|
- lib/glib/json_crawler/router.rb
|
|
443
449
|
- lib/glib/mailer_tester.rb
|
|
444
450
|
- lib/glib/rubocop.rb
|
|
451
|
+
- lib/glib/rubocop/cops/json_ui/base_nested_parameter.rb
|
|
452
|
+
- lib/glib/rubocop/cops/json_ui/nested_action_parameter.rb
|
|
453
|
+
- lib/glib/rubocop/cops/json_ui/nested_block_parameter.rb
|
|
445
454
|
- lib/glib/rubocop/cops/localize.rb
|
|
446
455
|
- lib/glib/rubocop/cops/multiline_method_call_style.rb
|
|
456
|
+
- lib/glib/rubocop/cops/test_name_parentheses.rb
|
|
447
457
|
- lib/glib/snapshot.rb
|
|
458
|
+
- lib/glib/test/parallel_coverage.rb
|
|
448
459
|
- lib/glib/test_helpers.rb
|
|
449
460
|
- lib/glib/time_freezable_mailer.rb
|
|
450
461
|
- lib/glib/time_returning_mailer.rb
|
|
451
462
|
- lib/glib/value.rb
|
|
452
463
|
- lib/glib/version.rb
|
|
453
464
|
- lib/tasks/db.rake
|
|
465
|
+
- lib/tasks/docs.rake
|
|
454
466
|
homepage:
|
|
455
467
|
licenses: []
|
|
456
468
|
metadata: {}
|