@globaltypesystem/gts-ts 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.
Files changed (81) hide show
  1. package/.eslintrc.json +16 -0
  2. package/.github/workflows/ci.yml +198 -0
  3. package/.gitmodules +3 -0
  4. package/.prettierrc +7 -0
  5. package/LICENSE +201 -0
  6. package/Makefile +64 -0
  7. package/README.md +298 -0
  8. package/dist/cast.d.ts +9 -0
  9. package/dist/cast.d.ts.map +1 -0
  10. package/dist/cast.js +153 -0
  11. package/dist/cast.js.map +1 -0
  12. package/dist/cli/index.d.ts +3 -0
  13. package/dist/cli/index.d.ts.map +1 -0
  14. package/dist/cli/index.js +318 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/compatibility.d.ts +11 -0
  17. package/dist/compatibility.d.ts.map +1 -0
  18. package/dist/compatibility.js +176 -0
  19. package/dist/compatibility.js.map +1 -0
  20. package/dist/extract.d.ts +13 -0
  21. package/dist/extract.d.ts.map +1 -0
  22. package/dist/extract.js +194 -0
  23. package/dist/extract.js.map +1 -0
  24. package/dist/gts.d.ts +18 -0
  25. package/dist/gts.d.ts.map +1 -0
  26. package/dist/gts.js +472 -0
  27. package/dist/gts.js.map +1 -0
  28. package/dist/index.d.ts +29 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +97 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/query.d.ts +10 -0
  33. package/dist/query.d.ts.map +1 -0
  34. package/dist/query.js +171 -0
  35. package/dist/query.js.map +1 -0
  36. package/dist/relationships.d.ts +7 -0
  37. package/dist/relationships.d.ts.map +1 -0
  38. package/dist/relationships.js +80 -0
  39. package/dist/relationships.js.map +1 -0
  40. package/dist/server/index.d.ts +2 -0
  41. package/dist/server/index.d.ts.map +1 -0
  42. package/dist/server/index.js +132 -0
  43. package/dist/server/index.js.map +1 -0
  44. package/dist/server/server.d.ts +33 -0
  45. package/dist/server/server.d.ts.map +1 -0
  46. package/dist/server/server.js +678 -0
  47. package/dist/server/server.js.map +1 -0
  48. package/dist/server/types.d.ts +61 -0
  49. package/dist/server/types.d.ts.map +1 -0
  50. package/dist/server/types.js +3 -0
  51. package/dist/server/types.js.map +1 -0
  52. package/dist/store.d.ts +39 -0
  53. package/dist/store.d.ts.map +1 -0
  54. package/dist/store.js +1026 -0
  55. package/dist/store.js.map +1 -0
  56. package/dist/types.d.ts +111 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +29 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/x-gts-ref.d.ts +35 -0
  61. package/dist/x-gts-ref.d.ts.map +1 -0
  62. package/dist/x-gts-ref.js +304 -0
  63. package/dist/x-gts-ref.js.map +1 -0
  64. package/jest.config.js +13 -0
  65. package/package.json +54 -0
  66. package/src/cast.ts +179 -0
  67. package/src/cli/index.ts +315 -0
  68. package/src/compatibility.ts +201 -0
  69. package/src/extract.ts +213 -0
  70. package/src/gts.ts +550 -0
  71. package/src/index.ts +97 -0
  72. package/src/query.ts +191 -0
  73. package/src/relationships.ts +91 -0
  74. package/src/server/index.ts +112 -0
  75. package/src/server/server.ts +771 -0
  76. package/src/server/types.ts +74 -0
  77. package/src/store.ts +1178 -0
  78. package/src/types.ts +138 -0
  79. package/src/x-gts-ref.ts +349 -0
  80. package/tests/gts.test.ts +525 -0
  81. package/tsconfig.json +32 -0
@@ -0,0 +1,678 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.GtsServer = void 0;
40
+ const fastify_1 = __importDefault(require("fastify"));
41
+ const index_1 = require("../index");
42
+ const x_gts_ref_1 = require("../x-gts-ref");
43
+ const gts = __importStar(require("../index"));
44
+ class GtsServer {
45
+ constructor(config) {
46
+ this.config = config;
47
+ this.store = new index_1.GTS({ validateRefs: false });
48
+ this.fastify = (0, fastify_1.default)({
49
+ logger: config.verbose > 0
50
+ ? {
51
+ level: config.verbose >= 2 ? 'debug' : 'info',
52
+ }
53
+ : false,
54
+ });
55
+ this.setupMiddleware();
56
+ this.registerRoutes();
57
+ }
58
+ setupMiddleware() {
59
+ // Enable CORS manually
60
+ this.fastify.addHook('onRequest', async (_request, reply) => {
61
+ reply.header('Access-Control-Allow-Origin', '*');
62
+ reply.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
63
+ reply.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
64
+ });
65
+ // Handle OPTIONS requests
66
+ this.fastify.options('*', async (_request, reply) => {
67
+ reply.status(204).send();
68
+ });
69
+ }
70
+ registerRoutes() {
71
+ // Health check
72
+ this.fastify.get('/health', async () => ({
73
+ status: 'ok',
74
+ timestamp: new Date().toISOString(),
75
+ }));
76
+ // Entity management
77
+ this.fastify.get('/entities', this.handleGetEntities.bind(this));
78
+ this.fastify.get('/entities/:id', this.handleGetEntity.bind(this));
79
+ this.fastify.post('/entities', this.handleAddEntity.bind(this));
80
+ this.fastify.post('/entities/bulk', this.handleAddEntities.bind(this));
81
+ this.fastify.post('/schemas', this.handleAddSchema.bind(this));
82
+ // OP#1 - Validate ID
83
+ this.fastify.get('/validate-id', this.handleValidateID.bind(this));
84
+ // OP#2 - Extract ID
85
+ this.fastify.post('/extract-id', this.handleExtractID.bind(this));
86
+ // OP#3 - Parse ID
87
+ this.fastify.get('/parse-id', this.handleParseID.bind(this));
88
+ // OP#4 - Match ID Pattern
89
+ this.fastify.get('/match-id-pattern', this.handleMatchIDPattern.bind(this));
90
+ // OP#5 - UUID
91
+ this.fastify.get('/uuid', this.handleUUID.bind(this));
92
+ // OP#6 - Validate Instance
93
+ this.fastify.post('/validate-instance', this.handleValidateInstance.bind(this));
94
+ // OP#7 - Resolve Relationships
95
+ this.fastify.get('/resolve-relationships', this.handleResolveRelationships.bind(this));
96
+ // OP#8 - Compatibility
97
+ this.fastify.get('/compatibility', this.handleCompatibility.bind(this));
98
+ // OP#9 - Cast
99
+ this.fastify.post('/cast', this.handleCast.bind(this));
100
+ // OP#10 - Query
101
+ this.fastify.get('/query', this.handleQuery.bind(this));
102
+ // OP#11 - Attribute Access
103
+ this.fastify.get('/attr', this.handleAttribute.bind(this));
104
+ // OpenAPI spec
105
+ this.fastify.get('/openapi', this.handleOpenAPI.bind(this));
106
+ }
107
+ // Entity Management Handlers
108
+ async handleGetEntities(request, _reply) {
109
+ let limit = parseInt(request.query.limit || '100', 10);
110
+ if (limit < 1)
111
+ limit = 1;
112
+ if (limit > 1000)
113
+ limit = 1000;
114
+ const items = this.store['store']
115
+ .getAll()
116
+ .slice(0, limit)
117
+ .map((e) => e.id);
118
+ return {
119
+ count: items.length,
120
+ items,
121
+ };
122
+ }
123
+ async handleGetEntity(request, reply) {
124
+ const entity = this.store.get(request.params.id);
125
+ if (!entity) {
126
+ reply.code(404);
127
+ throw new Error(`Entity not found: ${request.params.id}`);
128
+ }
129
+ return {
130
+ id: request.params.id,
131
+ content: entity,
132
+ };
133
+ }
134
+ async handleAddEntity(request, reply) {
135
+ try {
136
+ const content = request.body;
137
+ const validate = request.query.validate === 'true' || request.query.validation === 'true';
138
+ const entity = (0, index_1.createJsonEntity)(content);
139
+ // Strict validation for schemas when validate=true
140
+ if (validate && entity.isSchema) {
141
+ const validationError = this.validateSchemaStrict(content);
142
+ if (validationError) {
143
+ reply.code(422);
144
+ return {
145
+ ok: false,
146
+ error: validationError,
147
+ };
148
+ }
149
+ }
150
+ // Strict validation for instances when validate=true
151
+ if (validate && !entity.isSchema) {
152
+ // Check if entity has recognizable GTS fields
153
+ if (!entity.id || !gts.isValidGtsID(entity.id)) {
154
+ // Check if there's a valid type field
155
+ const hasValidType = entity.schemaId && gts.isValidGtsID(entity.schemaId);
156
+ if (!hasValidType) {
157
+ reply.code(422);
158
+ return {
159
+ ok: false,
160
+ error: 'Instance must have a valid GTS ID or type field',
161
+ };
162
+ }
163
+ }
164
+ }
165
+ if (!entity.id) {
166
+ return {
167
+ ok: false,
168
+ error: 'Unable to extract GTS ID from entity',
169
+ };
170
+ }
171
+ // Validate schema with x-gts-ref if it's a schema
172
+ // x-gts-ref validation always returns 422 on failure (not just when validate=true)
173
+ if (entity.isSchema) {
174
+ const xGtsRefValidator = new x_gts_ref_1.XGtsRefValidator(this.store['store']);
175
+ const xGtsRefErrors = xGtsRefValidator.validateSchema(content);
176
+ if (xGtsRefErrors.length > 0) {
177
+ const errorMsgs = xGtsRefErrors.map((err) => `${err.fieldPath}: ${err.reason}`).join('; ');
178
+ reply.code(422);
179
+ return {
180
+ ok: false,
181
+ error: `x-gts-ref validation failed: ${errorMsgs}`,
182
+ };
183
+ }
184
+ }
185
+ // Register the entity
186
+ this.store.register(content);
187
+ // Validate instance if requested
188
+ if (validate && !entity.isSchema) {
189
+ const result = this.store.validateInstance(entity.id);
190
+ if (!result.ok) {
191
+ return {
192
+ ok: false,
193
+ error: result.error,
194
+ };
195
+ }
196
+ }
197
+ return {
198
+ ok: true,
199
+ id: entity.id,
200
+ };
201
+ }
202
+ catch (error) {
203
+ return {
204
+ ok: false,
205
+ error: error instanceof Error ? error.message : String(error),
206
+ };
207
+ }
208
+ }
209
+ validateSchemaStrict(content) {
210
+ // Check for $id or $$id
211
+ const schemaId = content['$id'] || content['$$id'];
212
+ if (!schemaId) {
213
+ return 'Schema must have a $id or $$id field';
214
+ }
215
+ // Normalize the ID
216
+ let normalizedId = schemaId;
217
+ if (typeof normalizedId === 'string') {
218
+ // Check if it starts with gts:// prefix
219
+ if (normalizedId.startsWith('gts://')) {
220
+ normalizedId = normalizedId.substring(6);
221
+ }
222
+ else if (normalizedId.startsWith('gts.')) {
223
+ // Plain gts. prefix without gts:// is not allowed for JSON Schema $id
224
+ return 'Schema $id with GTS identifier must use gts:// URI format (e.g., gts://gts.vendor.pkg.ns.type.v1~)';
225
+ }
226
+ else {
227
+ // Non-GTS $id is not allowed
228
+ return 'Schema $id must be a valid GTS identifier with gts:// URI format';
229
+ }
230
+ // Check for wildcards in schema ID
231
+ if (normalizedId.includes('*')) {
232
+ return 'Schema $id cannot contain wildcards';
233
+ }
234
+ // Validate the GTS ID
235
+ if (!gts.isValidGtsID(normalizedId)) {
236
+ return `Schema $id is not a valid GTS identifier: ${normalizedId}`;
237
+ }
238
+ }
239
+ // Validate $ref fields
240
+ const refErrors = this.validateSchemaRefs(content, '');
241
+ if (refErrors.length > 0) {
242
+ return refErrors[0];
243
+ }
244
+ return null;
245
+ }
246
+ validateSchemaRefs(obj, path) {
247
+ const errors = [];
248
+ if (!obj || typeof obj !== 'object') {
249
+ return errors;
250
+ }
251
+ // Check $ref or $$ref
252
+ const ref = obj['$ref'] || obj['$$ref'];
253
+ if (typeof ref === 'string') {
254
+ const refPath = path ? `${path}/$ref` : '$ref';
255
+ // Local refs (#/...) are allowed
256
+ if (ref.startsWith('#')) {
257
+ // OK - local ref
258
+ }
259
+ else if (ref.startsWith('gts://')) {
260
+ // gts:// URI is allowed - validate the GTS ID
261
+ const normalizedRef = ref.substring(6);
262
+ // Check for wildcards
263
+ if (normalizedRef.includes('*')) {
264
+ errors.push(`Invalid $ref at ${refPath}: wildcards are not allowed in $ref`);
265
+ }
266
+ else if (!gts.isValidGtsID(normalizedRef)) {
267
+ errors.push(`Invalid $ref at ${refPath}: ${normalizedRef} is not a valid GTS identifier`);
268
+ }
269
+ }
270
+ else if (ref.startsWith('gts.')) {
271
+ // Plain gts. prefix without gts:// is not allowed
272
+ errors.push(`Invalid $ref at ${refPath}: GTS references must use gts:// URI format`);
273
+ }
274
+ else if (ref.startsWith('http://') || ref.startsWith('https://')) {
275
+ // External HTTP refs are not allowed (except json-schema.org for $schema)
276
+ if (!ref.includes('json-schema.org')) {
277
+ errors.push(`Invalid $ref at ${refPath}: external HTTP references are not allowed`);
278
+ }
279
+ }
280
+ }
281
+ // Recurse into nested objects
282
+ for (const [key, value] of Object.entries(obj)) {
283
+ if (key === '$ref' || key === '$$ref')
284
+ continue;
285
+ if (value && typeof value === 'object') {
286
+ const nestedPath = path ? `${path}/${key}` : key;
287
+ if (Array.isArray(value)) {
288
+ value.forEach((item, idx) => {
289
+ if (item && typeof item === 'object') {
290
+ errors.push(...this.validateSchemaRefs(item, `${nestedPath}[${idx}]`));
291
+ }
292
+ });
293
+ }
294
+ else {
295
+ errors.push(...this.validateSchemaRefs(value, nestedPath));
296
+ }
297
+ }
298
+ }
299
+ return errors;
300
+ }
301
+ async handleAddEntities(request, _reply) {
302
+ try {
303
+ const entities = request.body;
304
+ if (!Array.isArray(entities)) {
305
+ return {
306
+ ok: false,
307
+ error: 'Request body must be an array of entities',
308
+ };
309
+ }
310
+ const registered = [];
311
+ const errors = [];
312
+ for (const content of entities) {
313
+ try {
314
+ const entity = (0, index_1.createJsonEntity)(content);
315
+ if (entity.id) {
316
+ this.store.register(content);
317
+ registered.push(entity.id);
318
+ }
319
+ else {
320
+ errors.push('Unable to extract GTS ID from entity');
321
+ }
322
+ }
323
+ catch (err) {
324
+ errors.push(err instanceof Error ? err.message : String(err));
325
+ }
326
+ }
327
+ return {
328
+ ok: errors.length === 0,
329
+ registered,
330
+ errors: errors.length > 0 ? errors : undefined,
331
+ };
332
+ }
333
+ catch (error) {
334
+ return {
335
+ ok: false,
336
+ error: error instanceof Error ? error.message : String(error),
337
+ };
338
+ }
339
+ }
340
+ async handleAddSchema(request, reply) {
341
+ return this.handleAddEntity(request, reply);
342
+ }
343
+ // OP#1 - Validate ID
344
+ async handleValidateID(request, reply) {
345
+ // Support both 'gts_id' (test spec) and 'id' (fallback)
346
+ const id = request.query.gts_id || request.query.id;
347
+ if (!id) {
348
+ reply.code(400);
349
+ throw new Error('Missing required parameter: gts_id or id');
350
+ }
351
+ return gts.validateGtsID(id);
352
+ }
353
+ // OP#2 - Extract ID
354
+ async handleExtractID(request, _reply) {
355
+ // The body itself is the content
356
+ return gts.extractID(request.body);
357
+ }
358
+ // OP#3 - Parse ID
359
+ async handleParseID(request, reply) {
360
+ const id = request.query.gts_id || request.query.id;
361
+ if (!id) {
362
+ reply.code(400);
363
+ throw new Error('Missing required parameter: gts_id or id');
364
+ }
365
+ const result = gts.parseGtsID(id);
366
+ const isWildcard = id.includes('*');
367
+ // Convert segments to match test format (snake_case)
368
+ const segments = result.segments?.map((seg) => ({
369
+ vendor: seg.vendor,
370
+ package: seg.package,
371
+ namespace: seg.namespace,
372
+ type: seg.type,
373
+ ver_major: seg.verMajor,
374
+ ver_minor: seg.verMinor ?? null,
375
+ is_type: seg.isType,
376
+ })) || [];
377
+ // is_schema: true if ends with ~ and not a wildcard ending with ~*
378
+ const isSchema = id.endsWith('~') && !isWildcard;
379
+ return {
380
+ id,
381
+ ok: result.ok,
382
+ segments,
383
+ error: result.error || '',
384
+ is_schema: isSchema,
385
+ is_wildcard: isWildcard,
386
+ };
387
+ }
388
+ // OP#4 - Match ID Pattern
389
+ async handleMatchIDPattern(request, reply) {
390
+ const { pattern, candidate } = request.query;
391
+ if (!pattern || !candidate) {
392
+ reply.code(400);
393
+ throw new Error('Missing required parameters: pattern, candidate');
394
+ }
395
+ return gts.matchIDPattern(candidate, pattern);
396
+ }
397
+ // OP#5 - UUID
398
+ async handleUUID(request, reply) {
399
+ const id = request.query.gts_id || request.query.id;
400
+ if (!id) {
401
+ reply.code(400);
402
+ throw new Error('Missing required parameter: gts_id or id');
403
+ }
404
+ return gts.idToUUID(id);
405
+ }
406
+ // OP#6 - Validate Instance
407
+ async handleValidateInstance(request, reply) {
408
+ const { instance_id } = request.body;
409
+ if (!instance_id) {
410
+ reply.code(400);
411
+ throw new Error('Missing required field: instance_id');
412
+ }
413
+ return this.store.validateInstance(instance_id);
414
+ }
415
+ // OP#7 - Resolve Relationships
416
+ async handleResolveRelationships(request, reply) {
417
+ const { gts_id } = request.query;
418
+ if (!gts_id) {
419
+ reply.code(400);
420
+ throw new Error('Missing required parameter: gts_id');
421
+ }
422
+ return this.store.resolveRelationships(gts_id);
423
+ }
424
+ // OP#8 - Compatibility
425
+ async handleCompatibility(request, reply) {
426
+ const { old_schema_id, new_schema_id, mode = 'full' } = request.query;
427
+ if (!old_schema_id || !new_schema_id) {
428
+ reply.code(400);
429
+ throw new Error('Missing required parameters: old_schema_id, new_schema_id');
430
+ }
431
+ // Call the store's checkCompatibility directly to get the correct response format
432
+ return this.store['store'].checkCompatibility(old_schema_id, new_schema_id, mode);
433
+ }
434
+ // OP#9 - Cast
435
+ async handleCast(request, reply) {
436
+ const { instance_id, to_schema_id } = request.body;
437
+ if (!instance_id || !to_schema_id) {
438
+ reply.code(400);
439
+ throw new Error('Missing required fields: instance_id, to_schema_id');
440
+ }
441
+ // Call the store's castInstance directly to get the correct response format
442
+ return this.store['store'].castInstance(instance_id, to_schema_id);
443
+ }
444
+ // OP#10 - Query
445
+ async handleQuery(request, reply) {
446
+ const { expr, limit } = request.query;
447
+ if (!expr) {
448
+ reply.code(400);
449
+ throw new Error('Missing required parameter: expr');
450
+ }
451
+ const queryLimit = limit !== undefined ? Number(limit) : 100;
452
+ const result = this.store.query(expr, queryLimit);
453
+ // Tests expect 'results' not 'items', 'error' field first if present, and 'limit' field always
454
+ const response = {};
455
+ if (result.error) {
456
+ response.error = result.error;
457
+ }
458
+ else {
459
+ response.error = '';
460
+ }
461
+ response.count = result.count;
462
+ response.limit = queryLimit;
463
+ response.results = result.items;
464
+ return response;
465
+ }
466
+ // OP#11 - Attribute Access
467
+ async handleAttribute(request, reply) {
468
+ // Handle both formats: gts_with_path or separate gts_id + path
469
+ let gtsId;
470
+ let path;
471
+ if (request.query.gts_with_path) {
472
+ // Split on @ symbol to extract gts_id and path
473
+ const parts = request.query.gts_with_path.split('@');
474
+ if (parts.length !== 2) {
475
+ // If no @ symbol, treat the whole thing as gts_id with empty path
476
+ // This will result in resolved=false
477
+ gtsId = request.query.gts_with_path;
478
+ path = '';
479
+ }
480
+ else {
481
+ gtsId = parts[0];
482
+ path = parts[1];
483
+ }
484
+ }
485
+ else if (request.query.gts_id && request.query.path) {
486
+ gtsId = request.query.gts_id;
487
+ path = request.query.path;
488
+ }
489
+ else {
490
+ reply.code(400);
491
+ throw new Error('Missing required parameters: gts_with_path or (gts_id, path)');
492
+ }
493
+ return this.store['store'].getAttribute(gtsId, path);
494
+ }
495
+ // OpenAPI Specification
496
+ async handleOpenAPI() {
497
+ return {
498
+ openapi: '3.0.0',
499
+ info: {
500
+ title: 'GTS Server',
501
+ version: '0.1.0',
502
+ description: 'GTS (Global Type System) HTTP API',
503
+ },
504
+ servers: [
505
+ {
506
+ url: `http://${this.config.host}:${this.config.port}`,
507
+ description: 'GTS Server',
508
+ },
509
+ ],
510
+ paths: this.getOpenAPIPaths(),
511
+ components: this.getOpenAPIComponents(),
512
+ };
513
+ }
514
+ getOpenAPIPaths() {
515
+ return {
516
+ '/entities': {
517
+ get: {
518
+ summary: 'Get all entities in the registry',
519
+ operationId: 'getEntities',
520
+ parameters: [
521
+ {
522
+ name: 'limit',
523
+ in: 'query',
524
+ description: 'Maximum number of entities to return',
525
+ schema: { type: 'integer', default: 100, minimum: 1, maximum: 1000 },
526
+ },
527
+ ],
528
+ responses: {
529
+ 200: {
530
+ description: 'List of entity IDs',
531
+ content: {
532
+ 'application/json': {
533
+ schema: {
534
+ type: 'object',
535
+ properties: {
536
+ count: { type: 'integer' },
537
+ items: { type: 'array', items: { type: 'string' } },
538
+ },
539
+ },
540
+ },
541
+ },
542
+ },
543
+ },
544
+ },
545
+ post: {
546
+ summary: 'Add a new entity',
547
+ operationId: 'addEntity',
548
+ requestBody: {
549
+ required: true,
550
+ content: {
551
+ 'application/json': {
552
+ schema: { type: 'object' },
553
+ },
554
+ },
555
+ },
556
+ responses: {
557
+ 200: {
558
+ description: 'Operation result',
559
+ content: {
560
+ 'application/json': {
561
+ schema: { $ref: '#/components/schemas/OperationResult' },
562
+ },
563
+ },
564
+ },
565
+ },
566
+ },
567
+ },
568
+ '/validate-id': {
569
+ get: {
570
+ summary: 'Validate a GTS ID',
571
+ operationId: 'validateID',
572
+ parameters: [
573
+ {
574
+ name: 'id',
575
+ in: 'query',
576
+ required: true,
577
+ description: 'GTS ID to validate',
578
+ schema: { type: 'string' },
579
+ },
580
+ ],
581
+ responses: {
582
+ 200: {
583
+ description: 'Validation result',
584
+ content: {
585
+ 'application/json': {
586
+ schema: { $ref: '#/components/schemas/ValidationResult' },
587
+ },
588
+ },
589
+ },
590
+ },
591
+ },
592
+ },
593
+ '/query': {
594
+ get: {
595
+ summary: 'Query entities using GTS query language',
596
+ operationId: 'query',
597
+ parameters: [
598
+ {
599
+ name: 'expr',
600
+ in: 'query',
601
+ required: true,
602
+ description: 'Query expression',
603
+ schema: { type: 'string' },
604
+ },
605
+ {
606
+ name: 'limit',
607
+ in: 'query',
608
+ description: 'Maximum number of results',
609
+ schema: { type: 'integer' },
610
+ },
611
+ ],
612
+ responses: {
613
+ 200: {
614
+ description: 'Query results',
615
+ content: {
616
+ 'application/json': {
617
+ schema: { $ref: '#/components/schemas/QueryResult' },
618
+ },
619
+ },
620
+ },
621
+ },
622
+ },
623
+ },
624
+ };
625
+ }
626
+ getOpenAPIComponents() {
627
+ return {
628
+ schemas: {
629
+ OperationResult: {
630
+ type: 'object',
631
+ properties: {
632
+ ok: { type: 'boolean' },
633
+ error: { type: 'string' },
634
+ },
635
+ required: ['ok'],
636
+ },
637
+ ValidationResult: {
638
+ type: 'object',
639
+ properties: {
640
+ id: { type: 'string' },
641
+ ok: { type: 'boolean' },
642
+ valid: { type: 'boolean' },
643
+ error: { type: 'string' },
644
+ },
645
+ required: ['id', 'ok'],
646
+ },
647
+ QueryResult: {
648
+ type: 'object',
649
+ properties: {
650
+ query: { type: 'string' },
651
+ count: { type: 'integer' },
652
+ items: { type: 'array', items: { type: 'string' } },
653
+ error: { type: 'string' },
654
+ },
655
+ required: ['query', 'count', 'items'],
656
+ },
657
+ },
658
+ };
659
+ }
660
+ async start() {
661
+ try {
662
+ const address = await this.fastify.listen({
663
+ port: this.config.port,
664
+ host: this.config.host,
665
+ });
666
+ console.log(`GTS server listening on ${address}`);
667
+ }
668
+ catch (err) {
669
+ console.error('Error starting server:', err);
670
+ process.exit(1);
671
+ }
672
+ }
673
+ async stop() {
674
+ await this.fastify.close();
675
+ }
676
+ }
677
+ exports.GtsServer = GtsServer;
678
+ //# sourceMappingURL=server.js.map