naider 1.24.5 → 2.0.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.
package/src/preprocess.js DELETED
@@ -1,308 +0,0 @@
1
- const TYPE_MAP = { s: 'str', i: 'int', n: 'num', b: 'bool', l: 'list', m: 'map', a: 'any', j: 'json', v: 'void' };
2
- const TYPE_CHARS = new Set(Object.keys(TYPE_MAP));
3
-
4
- function expandType(ch) { return TYPE_MAP[ch] || ch; }
5
-
6
- function expandParams(paramStr) {
7
- if (!paramStr) return '';
8
- return paramStr.split(',').map(p => {
9
- p = p.trim();
10
- if (!p) return p;
11
- const m = p.match(/^(\.\.\.)?([ sinblmaj]):(.+)$/);
12
- if (m) return `${m[1] || ''}${expandType(m[2])} ${m[3]}`;
13
- const m2 = p.match(/^(\.\.\.)?([ sinblmaj])(\w+)$/);
14
- if (m2) return `${m2[1] || ''}${expandType(m2[2])} ${m2[3]}`;
15
- return p;
16
- }).join(', ');
17
- }
18
-
19
- function transformFunc(rest, isAsync, isPublic) {
20
- const pub = isPublic ? 'pub ' : '';
21
- const fn = isAsync ? 'fn.async' : 'fn';
22
- const nameM = rest.match(/^(\w+)/);
23
- if (!nameM) return `${pub}${fn} ${rest}:`;
24
- const name = nameM[1];
25
- let rem = rest.slice(name.length);
26
-
27
- let params = '';
28
- if (rem.startsWith('(')) {
29
- let depth = 1, i = 1;
30
- while (i < rem.length && depth > 0) {
31
- if (rem[i] === '(') depth++;
32
- if (rem[i] === ')') depth--;
33
- i++;
34
- }
35
- params = expandParams(rem.slice(1, i - 1));
36
- rem = rem.slice(i).trim();
37
- }
38
-
39
- let retType = '';
40
- if (rem.length > 0 && TYPE_CHARS.has(rem[0]) && (rem.length === 1 || !rem[1].match(/\w/))) {
41
- retType = ` -> ${expandType(rem[0])}`;
42
- }
43
-
44
- return `${pub}${fn} ${name}(${params})${retType}:`;
45
- }
46
-
47
- function transformRoute(method, rest) {
48
- const m = rest.match(/^("[^"]*")\s*(\[[^\]]*\])?\s*(\([^)]*\))?\s*(.*)$/);
49
- if (!m) return `${method} ${rest}:`;
50
- const path = m[1];
51
- const middleware = m[2] ? ` ${m[2]}` : '';
52
- const routeParams = m[3] ? ` ${m[3]}` : '';
53
- const inline = m[4] ? m[4].trim() : '';
54
-
55
- if (inline) {
56
- const body = transformContent(inline);
57
- return `${method} ${path}${middleware}${routeParams}:\n ${body}`;
58
- }
59
- return `${method} ${path}${middleware}${routeParams}:`;
60
- }
61
-
62
- function transformAwait(text) {
63
- // ~~ → await.all
64
- text = text.replace(/~~/g, 'await.all ');
65
- // ~ before identifier or ( → await (but not ~f, ~TYPE: which are handled at line level)
66
- text = text.replace(/~(\w)/g, (_, ch) => `await ${ch}`);
67
- text = text.replace(/~\(/g, 'await (');
68
- return text;
69
- }
70
-
71
- function transformContent(line) {
72
- // Log shorthands
73
- line = line.replace(/\blog\.e\b/g, 'log.error');
74
- line = line.replace(/\blog\.w\b/g, 'log.warn');
75
- line = line.replace(/\blog\.i\b/g, 'log.info');
76
- line = line.replace(/\blog\.d\b/g, 'log.debug');
77
- // log"msg" → log "msg"
78
- line = line.replace(/\blog"/g, 'log "');
79
- line = line.replace(/\blog\.error"/g, 'log.error "');
80
- line = line.replace(/\blog\.warn"/g, 'log.warn "');
81
- line = line.replace(/\blog\.info"/g, 'log.info "');
82
- line = line.replace(/\blog\.debug"/g, 'log.debug "');
83
- // Await transforms
84
- line = transformAwait(line);
85
- return line;
86
- }
87
-
88
- export function preprocess(source) {
89
- const lines = source.split('\n');
90
- const result = [];
91
-
92
- for (let raw of lines) {
93
- const indentM = raw.match(/^(\s*)/);
94
- const indent = indentM ? indentM[1] : '';
95
- let line = raw.slice(indent.length);
96
-
97
- // Empty line
98
- if (!line.trim()) { result.push(''); continue; }
99
-
100
- // Comment: -- → #
101
- if (line.startsWith('--')) { result.push(indent + '#' + line.slice(2)); continue; }
102
-
103
- // Keep # comments as-is
104
- if (line.startsWith('#') && !line.startsWith('#=')) { result.push(raw); continue; }
105
-
106
- const first = line[0];
107
- const second = line[1] || '';
108
- let out = '';
109
-
110
- // > return
111
- if (first === '>' && second !== '=' && second !== '>') {
112
- const rest = line.slice(1).trim();
113
- if (rest.startsWith('.s ') || rest.startsWith('.s(')) {
114
- out = 'ret.status ' + transformContent(rest.slice(3));
115
- } else if (rest.startsWith('.r ')) {
116
- out = 'ret.redirect ' + transformContent(rest.slice(3));
117
- } else if (rest.startsWith('.h ')) {
118
- out = 'ret.html ' + transformContent(rest.slice(3));
119
- } else if (rest.startsWith('.t ')) {
120
- out = 'ret.text ' + transformContent(rest.slice(3));
121
- } else if (rest.startsWith('.f ')) {
122
- out = 'ret.file ' + transformContent(rest.slice(3));
123
- } else if (rest.startsWith('.v ')) {
124
- out = 'ret.render ' + transformContent(rest.slice(3));
125
- } else if (rest.startsWith('.d ')) {
126
- out = 'ret.download ' + transformContent(rest.slice(3));
127
- } else {
128
- out = rest ? 'ret ' + transformContent(rest) : 'ret';
129
- }
130
- }
131
- // ? if
132
- else if (first === '?' && second !== '.' && second !== '?') {
133
- out = 'if ' + transformContent(line.slice(1).trim()) + ':';
134
- }
135
- // | elif (but not |>)
136
- else if (first === '|' && second !== '>') {
137
- out = 'elif ' + transformContent(line.slice(1).trim()) + ':';
138
- }
139
- // : else (alone on line)
140
- else if (line.trim() === ':') {
141
- out = 'else:';
142
- }
143
- // @ loop
144
- else if (first === '@') {
145
- const rest = line.slice(1);
146
- const loopM = rest.match(/^(\w+(?:,\w+)?)<(.+)$/);
147
- if (loopM) {
148
- const vars = loopM[1];
149
- const col = transformContent(loopM[2]);
150
- if (col.includes('..')) {
151
- const [start, end] = col.split('..');
152
- out = `for ${vars} in ${start}..${end}:`;
153
- } else if (vars.includes(',')) {
154
- const [k, v] = vars.split(',');
155
- out = `each ${k}, ${v} in ${col}:`;
156
- } else {
157
- out = `each ${vars} in ${col}:`;
158
- }
159
- } else {
160
- out = transformContent(line);
161
- }
162
- }
163
- // * while
164
- else if (first === '*') {
165
- out = 'while ' + transformContent(line.slice(1).trim()) + ':';
166
- }
167
- // !!! ensure/finally (before !! and ! checks)
168
- else if (first === '!' && second === '!' && (line[2] || '') === '!') {
169
- out = 'ensure:';
170
- }
171
- // !! catch (before ! try check)
172
- else if (first === '!' && second === '!') {
173
- const rest = line.slice(2).trim();
174
- out = rest ? `fail ${rest}:` : 'fail:';
175
- }
176
- // ! try (alone)
177
- else if (line.trim() === '!') {
178
- out = 'try:';
179
- }
180
- // $ server
181
- else if (first === '$') {
182
- const rest = line.slice(1);
183
- const srvM = rest.match(/^(\w+):(.+)$/);
184
- if (srvM) {
185
- out = `server ${srvM[1]} port ${transformContent(srvM[2].trim())}:`;
186
- } else {
187
- out = `server ${rest.trim()}:`;
188
- }
189
- }
190
- // HTTP routes: G P U D
191
- else if (first === 'G' && second === '"') { out = transformRoute('get', line.slice(1)); }
192
- else if (first === 'P' && second === '"') { out = transformRoute('post', line.slice(1)); }
193
- else if (first === 'U' && second === '"') { out = transformRoute('put', line.slice(1)); }
194
- else if (first === 'D' && second === '"') { out = transformRoute('del', line.slice(1)); }
195
- else if (first === 'X' && second === '"') { out = transformRoute('patch', line.slice(1)); }
196
- // ^ model
197
- else if (first === '^') {
198
- const rest = line.slice(1).trim();
199
- const extM = rest.match(/^(\w+)<(\w+)$/);
200
- if (extM) {
201
- out = `model ${extM[1]} extends ${extM[2]}:`;
202
- } else {
203
- out = `model ${rest}:`;
204
- }
205
- }
206
- // < import
207
- else if (first === '<') {
208
- const rest = line.slice(1);
209
- if (rest.startsWith('{')) {
210
- const impM = rest.match(/^\{([^}]+)\}"(.+)"$/);
211
- if (impM) {
212
- out = `use {${impM[1]}} from "${impM[2]}"`;
213
- } else {
214
- out = transformContent(line);
215
- }
216
- } else {
217
- const fromM = rest.match(/^(\w+)"(.+)"$/);
218
- if (fromM) {
219
- out = `use ${fromM[1]} from "${fromM[2]}"`;
220
- } else {
221
- out = `use ${rest.trim()}`;
222
- }
223
- }
224
- }
225
- // % match
226
- else if (first === '%') {
227
- out = 'match ' + transformContent(line.slice(1).trim()) + ':';
228
- }
229
- // + export prefix
230
- else if (first === '+') {
231
- const inner = line.slice(1);
232
- if (inner.startsWith('~f ')) {
233
- out = transformFunc(inner.slice(3), true, true);
234
- } else if (inner.startsWith('f ')) {
235
- out = transformFunc(inner.slice(2), false, true);
236
- } else {
237
- const tvM = inner.match(/^([sinblmaj]):(\w+)=(.+)$/);
238
- if (tvM) {
239
- out = `pub ${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
240
- } else {
241
- out = 'pub ' + transformContent(inner);
242
- }
243
- }
244
- }
245
- // ~f async function
246
- else if (first === '~' && second === 'f' && line[2] === ' ') {
247
- out = transformFunc(line.slice(3), true, false);
248
- }
249
- // ~TYPE:name=value → mutable
250
- else if (first === '~' && TYPE_CHARS.has(second) && line[2] === ':') {
251
- const rest = line.slice(1);
252
- const tvM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
253
- if (tvM) {
254
- out = `mut ${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
255
- } else {
256
- // Mutable field without assignment: ~s:name
257
- const fieldM = rest.match(/^([sinblmaj]):(\w+)$/);
258
- if (fieldM) {
259
- out = `mut ${expandType(fieldM[1])} ${fieldM[2]}`;
260
- } else {
261
- out = transformContent(line);
262
- }
263
- }
264
- }
265
- // f function
266
- else if (first === 'f' && second === ' ') {
267
- out = transformFunc(line.slice(2), false, false);
268
- }
269
- // TYPE:name=value → typed variable
270
- else if (TYPE_CHARS.has(first) && second === ':') {
271
- const rest = line;
272
- const tvM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
273
- if (tvM) {
274
- out = `${expandType(tvM[1])} ${tvM[2]} = ${transformContent(tvM[3])}`;
275
- } else {
276
- // Could be a type field in model: s:name (no assignment)
277
- const fieldM = rest.match(/^([sinblmaj]):(\w+)$/);
278
- if (fieldM) {
279
- out = `${expandType(fieldM[1])} ${fieldM[2]}`;
280
- } else {
281
- // type:name=... with complex value
282
- const fieldDefM = rest.match(/^([sinblmaj]):(\w+)=(.+)$/);
283
- if (fieldDefM) {
284
- out = `${expandType(fieldDefM[1])} ${fieldDefM[2]} = ${transformContent(fieldDefM[3])}`;
285
- } else {
286
- out = transformContent(line);
287
- }
288
- }
289
- }
290
- }
291
- // Default: apply inline transforms
292
- else {
293
- out = transformContent(line);
294
- }
295
-
296
- // Handle multi-line output from route transform
297
- if (out.includes('\n')) {
298
- const subLines = out.split('\n');
299
- for (const sl of subLines) {
300
- result.push(indent + sl);
301
- }
302
- } else {
303
- result.push(indent + out);
304
- }
305
- }
306
-
307
- return result.join('\n');
308
- }
@@ -1,32 +0,0 @@
1
- {
2
- "comments": {
3
- "lineComment": "--"
4
- },
5
- "brackets": [
6
- ["{", "}"],
7
- ["[", "]"],
8
- ["(", ")"]
9
- ],
10
- "autoClosingPairs": [
11
- { "open": "{", "close": "}" },
12
- { "open": "[", "close": "]" },
13
- { "open": "(", "close": ")" },
14
- { "open": "\"", "close": "\"" },
15
- { "open": "'", "close": "'" }
16
- ],
17
- "surroundingPairs": [
18
- { "open": "{", "close": "}" },
19
- { "open": "[", "close": "]" },
20
- { "open": "(", "close": ")" },
21
- { "open": "\"", "close": "\"" },
22
- { "open": "'", "close": "'" }
23
- ],
24
- "indentationRules": {
25
- "increaseIndentPattern": "^\\s*(fn|fn\\.async|if|elif|else|each|for|while|match|try|fail|ensure|server|model|on|schema|crud|auth|env|every|watch|ws|group|upload|queue|job|test|get|post|put|del|patch|error|sse|view|cache|mid|validate|openapi|static|session|cookie|cors|limit)\\b.*:\\s*$",
26
- "decreaseIndentPattern": "^\\s*(elif|else|fail|ensure)\\b"
27
- },
28
- "folding": {
29
- "offSide": true
30
- },
31
- "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)"
32
- }
Binary file
@@ -1,287 +0,0 @@
1
- {
2
- "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
3
- "name": "NAIDE-X",
4
- "scopeName": "source.naidex",
5
- "patterns": [
6
- { "include": "#comments" },
7
- { "include": "#strings" },
8
- { "include": "#numbers" },
9
- { "include": "#constants" },
10
- { "include": "#storage" },
11
- { "include": "#types-short" },
12
- { "include": "#types" },
13
- { "include": "#nx-prefixes" },
14
- { "include": "#keywords-control" },
15
- { "include": "#keywords-server" },
16
- { "include": "#keywords-declaration" },
17
- { "include": "#keywords-operator" },
18
- { "include": "#schema-modifiers" },
19
- { "include": "#builtins" },
20
- { "include": "#function-def" },
21
- { "include": "#operators" },
22
- { "include": "#punctuation" }
23
- ],
24
- "repository": {
25
- "comments": {
26
- "match": "--.*$",
27
- "name": "comment.line.double-dash.naidex"
28
- },
29
- "strings": {
30
- "patterns": [
31
- {
32
- "name": "string.quoted.double.naidex",
33
- "begin": "\"",
34
- "end": "\"",
35
- "patterns": [
36
- {
37
- "name": "constant.character.escape.naidex",
38
- "match": "\\\\."
39
- },
40
- {
41
- "name": "meta.embedded.expression.naidex",
42
- "begin": "\\{",
43
- "end": "\\}",
44
- "beginCaptures": { "0": { "name": "punctuation.definition.interpolation.begin.naidex" } },
45
- "endCaptures": { "0": { "name": "punctuation.definition.interpolation.end.naidex" } },
46
- "patterns": [
47
- { "include": "#numbers" },
48
- { "include": "#constants" },
49
- {
50
- "name": "variable.other.naidex",
51
- "match": "[a-zA-Z_][a-zA-Z0-9_.]*"
52
- }
53
- ]
54
- }
55
- ]
56
- },
57
- {
58
- "name": "string.quoted.single.naidex",
59
- "begin": "'",
60
- "end": "'",
61
- "patterns": [
62
- {
63
- "name": "constant.character.escape.naidex",
64
- "match": "\\\\."
65
- }
66
- ]
67
- }
68
- ]
69
- },
70
- "numbers": {
71
- "match": "\\b\\d+(\\.\\d+)?\\b",
72
- "name": "constant.numeric.naidex"
73
- },
74
- "constants": {
75
- "match": "\\b(true|false|null|self)\\b",
76
- "name": "constant.language.naidex"
77
- },
78
- "storage": {
79
- "match": "\\b(mut|pub)\\b|(?<=^\\s*)~(?=[a-z]:)",
80
- "name": "storage.modifier.naidex"
81
- },
82
- "types-short": {
83
- "match": "(?<=^\\s*~?)([sinblma])(?=:)",
84
- "name": "support.type.naidex"
85
- },
86
- "types": {
87
- "match": "\\b(str|int|num|bool|list|map|any|json|void)\\b",
88
- "name": "support.type.naidex"
89
- },
90
- "nx-prefixes": {
91
- "patterns": [
92
- {
93
- "match": "^\\s*>",
94
- "name": "keyword.control.return.naidex"
95
- },
96
- {
97
- "match": "^\\s*\\?(?!\\.)",
98
- "name": "keyword.control.if.naidex"
99
- },
100
- {
101
- "match": "^\\s*\\|(?!>)",
102
- "name": "keyword.control.elif.naidex"
103
- },
104
- {
105
- "match": "^\\s*:(?!=)",
106
- "name": "keyword.control.else.naidex"
107
- },
108
- {
109
- "match": "^\\s*@",
110
- "name": "keyword.control.loop.naidex"
111
- },
112
- {
113
- "match": "^\\s*\\*",
114
- "name": "keyword.control.while.naidex"
115
- },
116
- {
117
- "match": "^\\s*!!!",
118
- "name": "keyword.control.ensure.naidex"
119
- },
120
- {
121
- "match": "^\\s*!!",
122
- "name": "keyword.control.catch.naidex"
123
- },
124
- {
125
- "match": "^\\s*!(?!!)",
126
- "name": "keyword.control.try.naidex"
127
- },
128
- {
129
- "match": "^\\s*\\$",
130
- "name": "keyword.other.server.naidex"
131
- },
132
- {
133
- "match": "^\\s*\\^",
134
- "name": "keyword.declaration.model.naidex"
135
- },
136
- {
137
- "match": "^\\s*<",
138
- "name": "keyword.declaration.import.naidex"
139
- },
140
- {
141
- "match": "^\\s*%",
142
- "name": "keyword.control.match.naidex"
143
- },
144
- {
145
- "match": "^\\s*\\+",
146
- "name": "storage.modifier.export.naidex"
147
- },
148
- {
149
- "match": "^\\s*~f\\b",
150
- "name": "keyword.declaration.async-function.naidex"
151
- },
152
- {
153
- "match": "^\\s*f\\b",
154
- "name": "keyword.declaration.function.naidex"
155
- },
156
- {
157
- "match": "^\\s*G(?=\")",
158
- "name": "keyword.other.get.naidex"
159
- },
160
- {
161
- "match": "^\\s*P(?=\")",
162
- "name": "keyword.other.post.naidex"
163
- },
164
- {
165
- "match": "^\\s*U(?=\")",
166
- "name": "keyword.other.put.naidex"
167
- },
168
- {
169
- "match": "^\\s*D(?=\")",
170
- "name": "keyword.other.delete.naidex"
171
- },
172
- {
173
- "match": "^\\s*X(?=\")",
174
- "name": "keyword.other.patch.naidex"
175
- }
176
- ]
177
- },
178
- "keywords-control": {
179
- "match": "\\b(if|elif|else|each|for|while|in|match|try|fail|ensure|ret|break|continue|throw|then|not|and|or|typeof|instanceof)\\b",
180
- "name": "keyword.control.naidex"
181
- },
182
- "keywords-server": {
183
- "match": "\\b(server|get|post|put|del|patch|mid|cors|limit|auth|crud|static|ws|sse|group|upload|cookie|session|view|cache|validate|openapi|error|on)\\b",
184
- "name": "keyword.other.naidex"
185
- },
186
- "keywords-declaration": {
187
- "match": "\\b(fn|fn\\.async|use|from|as|model|extends|schema|env|db|every|watch|test|assert|queue|job|new)\\b",
188
- "name": "keyword.declaration.naidex"
189
- },
190
- "keywords-operator": {
191
- "match": "\\b(await|await\\.all)\\b|~~|~(?=[a-zA-Z(])",
192
- "name": "keyword.operator.naidex"
193
- },
194
- "schema-modifiers": {
195
- "match": "\\b(required|optional|unique|auto|timestamp)\\b",
196
- "name": "support.other.naidex"
197
- },
198
- "builtins": {
199
- "patterns": [
200
- {
201
- "match": "\\b(log|log\\.e|log\\.w|log\\.i|log\\.d|log\\.error|log\\.warn|log\\.info|log\\.debug)\\b",
202
- "name": "support.function.naidex"
203
- },
204
- {
205
- "match": "\\b(uuid|hash|verify|sign)\\b(?=\\s*\\()",
206
- "name": "support.function.naidex"
207
- },
208
- {
209
- "match": "\\b(api)\\.(get|post|put|del|raw)\\b",
210
- "name": "support.function.naidex"
211
- },
212
- {
213
- "match": ">\\.([shrtvdf])\\b",
214
- "name": "keyword.control.naidex"
215
- },
216
- {
217
- "match": "\\b(min|max|default|email|url|enum)\\b(?=\\s*\\()",
218
- "name": "support.function.naidex"
219
- },
220
- {
221
- "match": "\\b(send|broadcast|next)\\b(?=\\s*\\()",
222
- "name": "support.function.naidex"
223
- }
224
- ]
225
- },
226
- "function-def": {
227
- "match": "(?<=\\bf\\s|\\bfn\\s|\\bfn\\.async\\s|~f\\s)([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=\\()",
228
- "captures": {
229
- "1": { "name": "entity.name.function.naidex" }
230
- }
231
- },
232
- "operators": {
233
- "patterns": [
234
- {
235
- "match": "\\|>",
236
- "name": "keyword.operator.pipe.naidex"
237
- },
238
- {
239
- "match": "=>",
240
- "name": "keyword.operator.arrow.naidex"
241
- },
242
- {
243
- "match": "->",
244
- "name": "keyword.operator.return-type.naidex"
245
- },
246
- {
247
- "match": "\\?\\.|\\.\\.",
248
- "name": "keyword.operator.naidex"
249
- },
250
- {
251
- "match": "\\?\\?",
252
- "name": "keyword.operator.nullish.naidex"
253
- },
254
- {
255
- "match": "\\.\\.\\.",
256
- "name": "keyword.operator.spread.naidex"
257
- },
258
- {
259
- "match": "==|!=|>=|<=|>|<",
260
- "name": "keyword.operator.comparison.naidex"
261
- },
262
- {
263
- "match": "\\+=|-=",
264
- "name": "keyword.operator.assignment.compound.naidex"
265
- },
266
- {
267
- "match": "=",
268
- "name": "keyword.operator.assignment.naidex"
269
- },
270
- {
271
- "match": "[+\\-*/%]",
272
- "name": "keyword.operator.arithmetic.naidex"
273
- }
274
- ]
275
- },
276
- "punctuation": {
277
- "patterns": [
278
- { "match": ":", "name": "punctuation.separator.naidex" },
279
- { "match": ",", "name": "punctuation.separator.comma.naidex" },
280
- { "match": "\\.", "name": "punctuation.accessor.naidex" },
281
- { "match": "[\\(\\)]", "name": "punctuation.paren.naidex" },
282
- { "match": "[\\[\\]]", "name": "punctuation.bracket.naidex" },
283
- { "match": "[\\{\\}]", "name": "punctuation.brace.naidex" }
284
- ]
285
- }
286
- }
287
- }