moeralib 0.15.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.
Files changed (80) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +8 -0
  3. package/lib/naming/index.js +14 -0
  4. package/lib/naming/naming.js +203 -0
  5. package/lib/naming/schemas.mjs +184 -0
  6. package/lib/naming/types.js +2 -0
  7. package/lib/naming/validate.js +9 -0
  8. package/lib/naming/validators.js +2065 -0
  9. package/lib/node/caller.js +267 -0
  10. package/lib/node/cartes.js +55 -0
  11. package/lib/node/index.js +13 -0
  12. package/lib/node/node.js +1405 -0
  13. package/lib/node/schemas.mjs +4582 -0
  14. package/lib/node/types.js +3 -0
  15. package/lib/node/validate.js +9 -0
  16. package/lib/node/validators.js +60225 -0
  17. package/lib/schema.js +20 -0
  18. package/lib/schemas-compile.mjs +42 -0
  19. package/lib/universal-location.js +164 -0
  20. package/lib/util.js +42 -0
  21. package/nodejs-moera-api/nodejs-moera-api +4 -0
  22. package/nodejs-moera-api/nodejsmoeraapi.py +578 -0
  23. package/package.json +65 -0
  24. package/src/naming/index.ts +12 -0
  25. package/src/naming/naming.ts +234 -0
  26. package/src/naming/schemas.mjs +194 -0
  27. package/src/naming/types.ts +39 -0
  28. package/src/naming/validate.ts +6 -0
  29. package/src/naming/validators.d.ts +3 -0
  30. package/src/naming/validators.js +2084 -0
  31. package/src/node/caller.ts +311 -0
  32. package/src/node/cartes.ts +51 -0
  33. package/src/node/index.ts +3 -0
  34. package/src/node/node.ts +1285 -0
  35. package/src/node/schemas.mjs +4715 -0
  36. package/src/node/types.ts +1544 -0
  37. package/src/node/validate.ts +6 -0
  38. package/src/node/validators.d.ts +3 -0
  39. package/src/node/validators.js +60484 -0
  40. package/src/schema.ts +30 -0
  41. package/src/schemas-compile.mjs +51 -0
  42. package/src/universal-location.ts +212 -0
  43. package/src/util.ts +42 -0
  44. package/tsconfig.json +112 -0
  45. package/typings/naming/index.d.ts +2 -0
  46. package/typings/naming/index.d.ts.map +1 -0
  47. package/typings/naming/naming.d.ts +31 -0
  48. package/typings/naming/naming.d.ts.map +1 -0
  49. package/typings/naming/schemas.d.mts +271 -0
  50. package/typings/naming/schemas.d.mts.map +1 -0
  51. package/typings/naming/types.d.ts +35 -0
  52. package/typings/naming/types.d.ts.map +1 -0
  53. package/typings/naming/validate.d.ts +3 -0
  54. package/typings/naming/validate.d.ts.map +1 -0
  55. package/typings/naming/validators.d.ts +73 -0
  56. package/typings/naming/validators.d.ts.map +1 -0
  57. package/typings/node/caller.d.ts +52 -0
  58. package/typings/node/caller.d.ts.map +1 -0
  59. package/typings/node/cartes.d.ts +13 -0
  60. package/typings/node/cartes.d.ts.map +1 -0
  61. package/typings/node/index.d.ts +4 -0
  62. package/typings/node/index.d.ts.map +1 -0
  63. package/typings/node/node.d.ts +176 -0
  64. package/typings/node/node.d.ts.map +1 -0
  65. package/typings/node/schemas.d.mts +6205 -0
  66. package/typings/node/schemas.d.mts.map +1 -0
  67. package/typings/node/types.d.ts +1340 -0
  68. package/typings/node/types.d.ts.map +1 -0
  69. package/typings/node/validate.d.ts +3 -0
  70. package/typings/node/validate.d.ts.map +1 -0
  71. package/typings/node/validators.d.ts +920 -0
  72. package/typings/node/validators.d.ts.map +1 -0
  73. package/typings/schema.d.ts +18 -0
  74. package/typings/schema.d.ts.map +1 -0
  75. package/typings/schemas-compile.d.mts +2 -0
  76. package/typings/schemas-compile.d.mts.map +1 -0
  77. package/typings/universal-location.d.ts +25 -0
  78. package/typings/universal-location.d.ts.map +1 -0
  79. package/typings/util.d.ts +6 -0
  80. package/typings/util.d.ts.map +1 -0
@@ -0,0 +1,578 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ import sys
5
+ from enum import Enum
6
+ from typing import Any, TextIO
7
+
8
+ import yaml
9
+ from camel_converter import to_pascal
10
+
11
+
12
+ def ind(n: int) -> str:
13
+ return n * 4 * ' '
14
+
15
+
16
+ def read_api(ifname: str) -> Any:
17
+ with open(ifname, 'r') as ifile:
18
+ return yaml.safe_load(ifile)
19
+
20
+
21
+ def generate_enum(enum: Any, tfile: TextIO) -> None:
22
+ s = f'\nexport type {enum["name"]} = '
23
+ first = True
24
+ for item in enum['values']:
25
+ c = f'"{item["name"]}"'
26
+ if first:
27
+ s += c
28
+ first = False
29
+ else:
30
+ c = ' | ' + c
31
+ if len(s) + len(c) > 120:
32
+ s += '\n'
33
+ tfile.write(s)
34
+ s = ' '
35
+ s += c
36
+ s += ';\n'
37
+ tfile.write(s)
38
+
39
+
40
+ def schema_type(sfile: TextIO, indent: int, a_type: str, struct: bool = False, nullable: bool = False,
41
+ default: Any = None, min: float | None = None, max: float | None = None) -> None:
42
+ sfile.write('{\n')
43
+ if struct:
44
+ if nullable:
45
+ sfile.write(ind(indent + 1) + 'anyOf: [\n')
46
+ sfile.write(ind(indent + 2) + '{\n')
47
+ sfile.write(ind(indent + 3) + f'$ref: "node#/definitions/{a_type}",\n')
48
+ sfile.write(ind(indent + 3) + 'type: "object",\n')
49
+ sfile.write(ind(indent + 3) + 'nullable: true\n')
50
+ sfile.write(ind(indent + 2) + '},\n')
51
+ sfile.write(ind(indent + 2) + '{\n')
52
+ sfile.write(ind(indent + 3) + 'type: "null"\n')
53
+ sfile.write(ind(indent + 2) + '}\n')
54
+ sfile.write(ind(indent + 1) + ']')
55
+ else:
56
+ sfile.write(ind(indent + 1) + f'$ref: "node#/definitions/{a_type}"')
57
+ else:
58
+ sfile.write(ind(indent + 1) + f'type: "{a_type}"')
59
+ if nullable:
60
+ sfile.write(',\n')
61
+ sfile.write(ind(indent + 1) + 'nullable: true')
62
+ if default is not None:
63
+ sfile.write(',\n')
64
+ sfile.write(ind(indent + 1) + f'default: {default}')
65
+ if min is not None:
66
+ sfile.write(',\n')
67
+ sfile.write(ind(indent + 1) + f'minimum: {min}')
68
+ if max is not None:
69
+ sfile.write(',\n')
70
+ sfile.write(ind(indent + 1) + f'maximum: {max}')
71
+ sfile.write('\n')
72
+ sfile.write(ind(indent) + '}')
73
+
74
+
75
+ def schema_array(sfile: TextIO, indent: int, a_type: str, struct: bool = False, nullable: bool = False,
76
+ default: Any = None, min_items: int | None = None, max_items: int | None = None,
77
+ min: float | None = None, max: float | None = None) -> None:
78
+ sfile.write('{\n')
79
+ sfile.write(ind(indent + 1) + 'type: "array",\n')
80
+ sfile.write(ind(indent + 1) + 'items: ')
81
+ schema_type(sfile, indent + 1, a_type, struct=struct, nullable=False, min=min, max=max)
82
+ if nullable:
83
+ sfile.write(',\n')
84
+ sfile.write(ind(indent + 1) + 'nullable: true')
85
+ if default is not None:
86
+ sfile.write(',\n')
87
+ sfile.write(ind(indent + 1) + f'default: {default}')
88
+ if min_items is not None:
89
+ sfile.write(',\n')
90
+ sfile.write(ind(indent + 1) + f'minItems: {min_items}')
91
+ if max_items is not None:
92
+ sfile.write(',\n')
93
+ sfile.write(ind(indent + 1) + f'maxItems: {max_items}')
94
+ sfile.write('\n')
95
+ sfile.write(ind(indent) + '}')
96
+
97
+
98
+ def schema_map_string_int(sfile: TextIO, indent: int, nullable: bool = False) -> None:
99
+ sfile.write('{\n')
100
+ sfile.write(ind(indent + 1) + 'type: "object",\n')
101
+ sfile.write(ind(indent + 1) + 'patternProperties: {\n')
102
+ sfile.write(ind(indent + 2) + '"^.*$": ')
103
+ schema_type(sfile, indent + 2, 'integer')
104
+ sfile.write('\n')
105
+ sfile.write(ind(indent + 1) + '}')
106
+ if nullable:
107
+ sfile.write(',\n')
108
+ sfile.write(ind(indent + 1) + 'nullable: true')
109
+ sfile.write('\n')
110
+ sfile.write(ind(indent) + '}')
111
+
112
+
113
+ def generate_operations(operations: Any, tfile: TextIO, sfile: TextIO) -> None:
114
+ tfile.write('\n')
115
+ tfile.write(f'export interface {operations["name"]} {{\n')
116
+ for field in operations['fields']:
117
+ tfile.write(f' {field["name"]}?: PrincipalValue | null;\n')
118
+ tfile.write('}\n')
119
+
120
+ sfile.write('\n')
121
+ sfile.write(f'{ind(2)}{operations["name"]}: {{\n')
122
+ sfile.write(f'{ind(3)}type: "object",\n')
123
+ sfile.write(f'{ind(3)}properties: {{\n')
124
+ for field in operations['fields']:
125
+ sfile.write(f'{ind(4)}"{field["name"]}": ')
126
+ schema_type(sfile, 4, "string", nullable=True)
127
+ sfile.write(',\n')
128
+ sfile.write(f'{ind(3)}}},\n')
129
+ sfile.write(f'{ind(3)}additionalProperties: false\n')
130
+ sfile.write(f'{ind(2)}}},\n')
131
+
132
+
133
+ JS_TYPES = {
134
+ 'String': 'string',
135
+ 'String[]': 'string[]',
136
+ 'int': 'number',
137
+ 'float': 'number',
138
+ 'boolean': 'boolean',
139
+ 'timestamp': 'number',
140
+ 'byte[]': 'string',
141
+ 'UUID': 'string',
142
+ 'String -> int': 'Partial<Record<string, number>>'
143
+ }
144
+
145
+
146
+ def to_js_type(api_type: str) -> str:
147
+ js_type = JS_TYPES.get(api_type)
148
+ if js_type is None:
149
+ print('Unrecognized field type: ' + api_type)
150
+ exit(1)
151
+ return js_type
152
+
153
+
154
+ SCHEMA_TYPES = {
155
+ 'String': ('string', False),
156
+ 'String[]': ('string', True),
157
+ 'int': ('integer', False),
158
+ 'float': ('number', False),
159
+ 'boolean': ('boolean', False),
160
+ 'timestamp': ('integer', False),
161
+ 'byte[]': ('string', False),
162
+ 'UUID': ('string', False),
163
+ 'String -> int': schema_map_string_int
164
+ }
165
+
166
+
167
+ class Interface:
168
+ data: Any
169
+ output_array: bool = False
170
+
171
+ def __init__(self, data: Any) -> None:
172
+ self.data = data
173
+
174
+ def get_name(self) -> str:
175
+ raise NotImplementedError
176
+
177
+ def get_schema_fields(self) -> list[Any]:
178
+ return self.data.get('fields', [])
179
+
180
+ def generate_schema(self, sfile: TextIO) -> None:
181
+ class_name = self.get_name()
182
+ sfile.write(f'\n{ind(2)}{class_name}: {{\n')
183
+ sfile.write(f'{ind(3)}type: "object",\n')
184
+ sfile.write(f'{ind(3)}properties: {{\n')
185
+ required: list[str] = []
186
+ for field in self.get_schema_fields():
187
+ if field.get('type') == 'any':
188
+ continue
189
+
190
+ sfile.write(f'{ind(4)}"{field["name"]}": ')
191
+ default = field.get('js-default')
192
+ optional = field.get('optional', False) and default is None
193
+ array = field.get('array', False)
194
+ if not optional:
195
+ required.append(field['name'])
196
+ struct = False
197
+ if 'struct' in field:
198
+ if field['struct'] == 'Body':
199
+ t = 'string'
200
+ else:
201
+ t = field['struct']
202
+ struct = True
203
+ elif 'enum' in field:
204
+ t = 'string'
205
+ else:
206
+ s_type = SCHEMA_TYPES.get(field['type'])
207
+ if callable(s_type):
208
+ t = None
209
+ s_type(sfile, 4, nullable=optional)
210
+ else:
211
+ assert isinstance(s_type, tuple)
212
+ t, array = s_type
213
+ if t is not None:
214
+ if array:
215
+ schema_array(sfile, 4, t, struct=struct, nullable=optional, default=default,
216
+ min_items=field.get('min-items'), max_items=field.get('max-items'),
217
+ min=field.get('min'), max=field.get('max'))
218
+ else:
219
+ schema_type(sfile, 4, t, struct=struct, nullable=optional, default=default,
220
+ min=field.get('min'), max=field.get('max'))
221
+ sfile.write(',\n')
222
+ sfile.write(f'{ind(3)}}},\n')
223
+ if len(required) > 0:
224
+ sfile.write(f'{ind(3)}required: [\n')
225
+ for name in required:
226
+ sfile.write(f'{ind(4)}"{name}",\n')
227
+ sfile.write(f'{ind(3)}],\n')
228
+ sfile.write(f'{ind(3)}additionalProperties: false\n')
229
+ sfile.write(f'{ind(2)}}},\n')
230
+
231
+ if self.output_array:
232
+ sfile.write(f'\n{ind(2)}{class_name}Array: ')
233
+ schema_array(sfile, 2, class_name, struct=True)
234
+ sfile.write(',\n')
235
+
236
+
237
+ class Structure(Interface):
238
+ generated: bool = False
239
+ depends: list[str]
240
+ uses_body: bool = False
241
+ output: bool = False
242
+
243
+ def __init__(self, data: Any) -> None:
244
+ super().__init__(data)
245
+ self.depends = [field['struct'] for field in data['fields'] if 'struct' in field]
246
+
247
+ def get_name(self) -> str:
248
+ return self.data["name"]
249
+
250
+ @property
251
+ def generic(self) -> bool:
252
+ return self.uses_body and self.output
253
+
254
+ def generate_class(self, tfile: TextIO, structs: dict[str, Structure]) -> None:
255
+ if self.generic:
256
+ tfile.write(f'\nexport interface {self.data["name"]}Base<B> {{\n')
257
+ else:
258
+ tfile.write(f'\nexport interface {self.data["name"]} {{\n')
259
+ for field in self.data['fields']:
260
+ if field.get('optional', False) and 'js-default' not in field:
261
+ tmpl = ' %s?: %s | null;\n'
262
+ else:
263
+ tmpl = ' %s: %s;\n'
264
+ if 'struct' in field:
265
+ if field['struct'] == 'Body':
266
+ t = 'B' if self.generic else 'string'
267
+ else:
268
+ t = field['struct']
269
+ if self.generic and field['struct'] in structs and structs[field['struct']].generic:
270
+ t += 'Base<B>'
271
+ elif 'enum' in field:
272
+ t = field['enum']
273
+ else:
274
+ if field['type'] == 'any':
275
+ continue
276
+ t = to_js_type(field['type'])
277
+ if field.get('array', False):
278
+ t += '[]'
279
+ tfile.write(tmpl % (field['name'], t))
280
+ tfile.write('}\n')
281
+ if self.generic:
282
+ tfile.write('\nexport type Encoded{name} = {name}Base<string>;\n'.format(name=self.data['name']))
283
+ tfile.write('export type {name} = {name}Base<Body>;\n'.format(name=self.data['name']))
284
+
285
+ def generate(self, tfile: TextIO, sfile: TextIO, structs: dict[str, Structure]) -> None:
286
+ self.generate_class(tfile, structs)
287
+ if self.output:
288
+ self.generate_schema(sfile)
289
+ self.generated = True
290
+
291
+
292
+ def scan_body_usage(structs: dict[str, Structure]) -> None:
293
+ for struct in structs.values():
294
+ if 'Body' in struct.depends:
295
+ struct.uses_body = True
296
+
297
+ modified = True
298
+ while modified:
299
+ modified = False
300
+ for struct in structs.values():
301
+ if struct.uses_body:
302
+ continue
303
+ for dep in struct.depends:
304
+ if dep in structs and structs[dep].uses_body:
305
+ struct.uses_body = True
306
+ modified = True
307
+
308
+
309
+ def scan_output_usage(api: Any, structs: dict[str, Structure]) -> None:
310
+ for obj in api['objects']:
311
+ for request in obj.get('requests', []):
312
+ if 'out' not in request:
313
+ continue
314
+ if 'struct' not in request['out']:
315
+ continue
316
+ struct = request['out']['struct']
317
+ if struct not in structs:
318
+ continue
319
+ structs[struct].output = True
320
+ structs[struct].output_array |= request['out'].get('array', False)
321
+
322
+ modified = True
323
+ while modified:
324
+ modified = False
325
+ for struct in structs.values():
326
+ if not struct.output:
327
+ continue
328
+ for dep in struct.depends:
329
+ if dep in structs and not structs[dep].output:
330
+ structs[dep].output = True
331
+ modified = True
332
+
333
+
334
+ def scan_structures(api: Any) -> dict[str, Structure]:
335
+ structs: dict[str, Structure] = {struct['name']: Structure(struct) for struct in api['structures']}
336
+ scan_body_usage(structs)
337
+ scan_output_usage(api, structs)
338
+ return structs
339
+
340
+
341
+ def generate_structures(structs: dict[str, Structure], tfile: TextIO, sfile: TextIO) -> None:
342
+ gen = True
343
+ while gen:
344
+ gen = False
345
+ for struct in structs.values():
346
+ if struct.generated:
347
+ continue
348
+ if any(not structs[d].generated for d in struct.depends if d in structs):
349
+ continue
350
+ struct.generate(tfile, sfile, structs)
351
+ gen = True
352
+ loop = [s.data['name'] for s in structs.values() if not s.generated]
353
+ if len(loop) > 0:
354
+ print('Dependency loop in structures: ' + ', '.join(loop))
355
+ exit(1)
356
+
357
+
358
+ def comma_wrap(s: str, indent: int) -> str:
359
+ max_length = 120 - indent * 4
360
+ result = ''
361
+ while True:
362
+ if len(s) <= max_length:
363
+ result += s
364
+ break
365
+ pos = 0
366
+ while True:
367
+ next = s.find(', ', pos + 1)
368
+ if next < 0 or next > max_length:
369
+ break
370
+ pos = next
371
+ result += s[:pos] + ',\n' + ind(indent)
372
+ s = s[pos + 2:]
373
+ return result
374
+
375
+
376
+ def params_wrap(template: str, substitute: str, indent: int) -> str:
377
+ line = template % substitute
378
+ if len(line) > 120:
379
+ line = template % ('\n' + ind(indent) + comma_wrap(substitute, indent) + '\n' + ind(indent - 1))
380
+ return line
381
+
382
+
383
+ class AuthType(Enum):
384
+ NONE = 0
385
+ OPTIONAL = 1
386
+ REQUIRED = 2
387
+
388
+
389
+ def auth_type(auth: str) -> AuthType:
390
+ variants = auth.split(" or ")
391
+ if variants == ['none'] or variants == ['signature']:
392
+ return AuthType.NONE
393
+ if 'none' in variants or 'signature' in variants or 'optional' in variants:
394
+ return AuthType.OPTIONAL
395
+ return AuthType.REQUIRED
396
+
397
+
398
+ def generate_calls(api: Any, structs: dict[str, Structure], afile: TextIO) -> None:
399
+ for obj in api['objects']:
400
+ for request in obj.get('requests', []):
401
+ if 'function' not in request:
402
+ continue
403
+
404
+ params: list[str] = []
405
+ tail_params: list[str] = []
406
+ url_params: dict[str, str] = {}
407
+ flag_name: str | None = None
408
+ flag_js_name: str | None = None
409
+ flags: list[str] = []
410
+ for param in request.get('params', []) + request.get('query', []):
411
+ if 'name' not in param:
412
+ print('Missing name of parameter of the request "{method} {url}"'
413
+ .format(method=request['type'], url=request['url']))
414
+ exit(1)
415
+ name = param['name']
416
+ js_name = 'remoteNodeName' if name == 'nodeName' else name
417
+ url_params[name] = js_name
418
+ if 'enum' in param:
419
+ js_type = 'API.' + param['enum']
420
+ else:
421
+ js_type = to_js_type(param['type'])
422
+ if 'flags' in param:
423
+ flag_name = name
424
+ flag_js_name = js_name
425
+ flags = [flag['name'] for flag in param['flags']]
426
+ for flag in flags:
427
+ params.append('with{name}: boolean = false'.format(name=flag.capitalize()))
428
+ else:
429
+ if param.get('optional', False):
430
+ tail_params.append(f'{js_name}: {js_type} | null = null')
431
+ else:
432
+ params.append(f'{js_name}: {js_type}')
433
+ body = ''
434
+ if 'in' in request:
435
+ if 'type' in request['in']:
436
+ if request['in']['type'] != 'blob':
437
+ print('Unrecognised type "{type}" of the input body of the request "{method} {url}"'
438
+ .format(type=request['in']['type'], method=request['type'], url=request['url']))
439
+ exit(1)
440
+ body = ', body'
441
+ params.append('body: Buffer')
442
+ else:
443
+ if 'name' not in request['in']:
444
+ print('Missing name of body of the request "{method} {url}"'
445
+ .format(method=request['type'], url=request['url']))
446
+ exit(1)
447
+ name = request['in']['name']
448
+ js_type = 'API.' + request['in']['struct']
449
+ if request['in'].get('array', False):
450
+ js_type += '[]'
451
+ body = f', body: {name}'
452
+ params.append(f'{name}: {js_type}')
453
+ params += tail_params
454
+
455
+ method = request['type']
456
+ location: str = request['url']
457
+ if len(url_params) > 0:
458
+ p = re.compile(r'{(\w+)}')
459
+ for name in p.findall(location):
460
+ if name not in url_params:
461
+ print('Unknown parameter "{param}" referenced in location "{url}"'
462
+ .format(param=name, url=request['url']))
463
+ exit(1)
464
+ location = location.replace(f'{{{name}}}', f'${{{url_params[name]}}}')
465
+ del url_params[name]
466
+ location = f'ut`{location}`'
467
+ else:
468
+ location = f'"{location}"'
469
+ subs = []
470
+ for name, js_name in url_params.items():
471
+ if name == js_name:
472
+ subs.append(name)
473
+ else:
474
+ subs.append(f'{name}: {js_name}')
475
+
476
+ result = 'API.Result'
477
+ result_schema = 'Result'
478
+ result_body = False
479
+ if 'out' in request:
480
+ if 'type' in request['out']:
481
+ if request['out']['type'] != 'blob':
482
+ print('Unrecognised type "{type}" of the output body of the request "{method} {url}"'
483
+ .format(type=request['out']['type'], method=request['type'], url=request['url']))
484
+ exit(1)
485
+ result = 'Blob'
486
+ result_schema = 'blob'
487
+ else:
488
+ struct = request['out']['struct']
489
+ result = 'API.' + struct
490
+ result_schema = struct
491
+ if struct in structs and structs[struct].uses_body:
492
+ result_body = True
493
+ if request['out'].get('array', False):
494
+ result += '[]'
495
+ result_schema += 'Array'
496
+
497
+ name = request['function']
498
+ afile.write(params_wrap(f'\n{ind(1)}async {name}(%s): Promise<{result}> {{\n', ', '.join(params), 2))
499
+ if flag_name is not None:
500
+ items = ', '.join('"%s": with%s' % (flag, flag.capitalize()) for flag in flags)
501
+ afile.write(f'{ind(2)}const {flag_js_name} = commaSeparatedFlags({{{items}}});\n')
502
+ afile.write(f'{ind(2)}const location = {location};\n')
503
+ query_params = ''
504
+ if len(subs) > 0:
505
+ afile.write(f'{ind(2)}const params = {{{", ".join(subs)}}};\n')
506
+ query_params = ', params'
507
+ afile.write(f'{ind(2)}return await this.call("{name}", location, {{\n')
508
+ decode_bodies = ''
509
+ if result_body:
510
+ decode_bodies = ', bodies: true'
511
+ call_params = f'{ind(3)}method: "{method}"{query_params}{body}, schema: "{result_schema}"{decode_bodies}\n'
512
+ afile.write(comma_wrap(call_params, 2))
513
+ afile.write(f'{ind(2)}}}) as {result};\n')
514
+ afile.write(f'{ind(1)}}}\n')
515
+
516
+
517
+ PREAMBLE_TYPES = '''// This file is generated
518
+
519
+ export type PrincipalValue = "none" | "private" | "admin" | "owner" | "secret" | "senior" | "enigma" | "major"
520
+ | "signed" | "subscribed" | "public" | "unset" | string;
521
+ '''
522
+
523
+ PREAMBLE_SCHEMAS = '''// This file is generated for schema compiler only, do not use directly
524
+
525
+ export const NODE_API_SCHEMAS = {
526
+ $id: "node",
527
+ definitions: {
528
+ '''
529
+
530
+ PREAMBLE_CALLS = '''// This file is generated
531
+
532
+ import { Caller } from "./caller";
533
+ import * as API from "./types";
534
+ import { commaSeparatedFlags, ut } from "../util";
535
+
536
+ export class MoeraNode extends Caller {
537
+
538
+ constructor(nodeUrl: string | null = null) {
539
+ super();
540
+ if (nodeUrl != null) {
541
+ this.nodeUrl(nodeUrl);
542
+ }
543
+ }
544
+ '''
545
+
546
+
547
+ def generate_types(api: Any, outdir: str) -> None:
548
+ structs = scan_structures(api)
549
+
550
+ with open(outdir + '/node/types.ts', 'w+') as tfile:
551
+ with open(outdir + '/node/schemas.mjs', 'w+') as sfile:
552
+ tfile.write(PREAMBLE_TYPES)
553
+ sfile.write(PREAMBLE_SCHEMAS)
554
+ for enum in api['enums']:
555
+ generate_enum(enum, tfile)
556
+ for operations in api['operations']:
557
+ generate_operations(operations, tfile, sfile)
558
+ generate_structures(structs, tfile, sfile)
559
+ sfile.write('\n }\n')
560
+ sfile.write('}\n')
561
+
562
+ with open(outdir + '/node/node.ts', 'w+') as afile:
563
+ afile.write(PREAMBLE_CALLS)
564
+ generate_calls(api, structs, afile)
565
+ afile.write('\n}\n')
566
+
567
+
568
+ def generate_code(outdir: str) -> None:
569
+ node_api = read_api(sys.argv[1])
570
+ generate_types(node_api, outdir)
571
+
572
+
573
+ if len(sys.argv) < 2 or sys.argv[1] == '':
574
+ print("Usage: nodejs-moera-api <node_api.yml file path> <output directory>")
575
+ exit(1)
576
+
577
+ outdir = sys.argv[2] if len(sys.argv) >= 3 else '.'
578
+ generate_code(outdir)
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "moeralib",
3
+ "version": "0.15.0",
4
+ "description": "Modern Telegram Bot Framework",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/MoeraOrg/typescript-moeralib.git"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/MoeraOrg/moera-issues/issues"
11
+ },
12
+ "author": "Shmuel Leib Melamud <balu@moera.org>",
13
+ "homepage": "https://moera.org",
14
+ "license": "Apache-2.0",
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "clean": "rm -r lib/* typings/*"
18
+ },
19
+ "type": "commonjs",
20
+ "engines": {
21
+ "node": ">=18.10.0"
22
+ },
23
+ "types": "./typings/index.d.ts",
24
+ "dependencies": {
25
+ "@types/lodash.clonedeep": "^4.5.9",
26
+ "ajv": "^8.16.0",
27
+ "ajv-formats": "^3.0.1",
28
+ "change-case": "^5.4.4",
29
+ "json-rpc-2.0": "^1.7.0",
30
+ "lodash.clonedeep": "^4.5.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^20.14.2",
34
+ "typescript": "^5.4.5"
35
+ },
36
+ "exports": {
37
+ "./naming": {
38
+ "types": "./typings/naming/index.d.ts",
39
+ "default": "./lib/naming/index.js"
40
+ },
41
+ "./naming/types": {
42
+ "types": "./typings/naming/types/index.d.ts",
43
+ "default": "./lib/naming/types/index.js"
44
+ },
45
+ "./node": {
46
+ "types": "./typings/node/index.d.ts",
47
+ "default": "./lib/node/index.js"
48
+ },
49
+ "./node/types": {
50
+ "types": "./typings/node/types/index.d.ts",
51
+ "default": "./lib/node/types/index.js"
52
+ },
53
+ "./universal-location": {
54
+ "types": "./typings/universal-location.d.ts",
55
+ "default": "./lib/universal-location.js"
56
+ }
57
+ },
58
+ "keywords": [
59
+ "moera",
60
+ "social",
61
+ "network",
62
+ "api",
63
+ "decentralized"
64
+ ]
65
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ MoeraNaming,
3
+ MoeraNamingError,
4
+ MoeraNamingApiError,
5
+ MoeraNamingConnectionError,
6
+ MAIN_NAMING_SERVER,
7
+ DEV_NAMING_SERVER,
8
+ parseNodeName,
9
+ expand,
10
+ resolve,
11
+ shorten
12
+ } from "./naming";