@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
package/src/gts.ts ADDED
@@ -0,0 +1,550 @@
1
+ import { v5 as uuidv5 } from 'uuid';
2
+ import {
3
+ GTS_PREFIX,
4
+ MAX_ID_LENGTH,
5
+ GtsID,
6
+ GtsIDSegment,
7
+ InvalidGtsIDError,
8
+ InvalidSegmentError,
9
+ ValidationResult,
10
+ ParseResult,
11
+ MatchResult,
12
+ UUIDResult,
13
+ } from './types';
14
+
15
+ const GTS_NAMESPACE = uuidv5('gts', uuidv5.URL);
16
+
17
+ const SEGMENT_TOKEN_REGEX = /^[a-z_][a-z0-9_]*$/;
18
+
19
+ export class Gts {
20
+ static parseGtsID(id: string): GtsID {
21
+ // If ID contains wildcard, validate as wildcard first
22
+ if (id.includes('*')) {
23
+ return this.validateWildcard(id);
24
+ }
25
+ return this.parseGtsIDInternal(id, false);
26
+ }
27
+
28
+ private static splitPreservingTilde(s: string): string[] {
29
+ const parts: string[] = [];
30
+ let current = '';
31
+
32
+ for (let i = 0; i < s.length; i++) {
33
+ if (s[i] === '~') {
34
+ // Add the segment with the tilde
35
+ parts.push(current + '~');
36
+ current = '';
37
+ } else {
38
+ current += s[i];
39
+ }
40
+ }
41
+
42
+ // Add any remaining content (instance without trailing ~)
43
+ if (current) {
44
+ parts.push(current);
45
+ }
46
+
47
+ return parts.filter((p) => p !== '~'); // Remove any standalone tildes
48
+ }
49
+
50
+ private static parseSegment(num: number, offset: number, segment: string): GtsIDSegment {
51
+ const seg: GtsIDSegment = {
52
+ num,
53
+ offset,
54
+ segment: segment.trim(),
55
+ vendor: '',
56
+ package: '',
57
+ namespace: '',
58
+ type: '',
59
+ verMajor: 0,
60
+ verMinor: undefined,
61
+ isType: false,
62
+ isWildcard: false,
63
+ };
64
+
65
+ let workingSegment = seg.segment;
66
+
67
+ // Check for empty segment
68
+ if (!workingSegment || workingSegment === '~') {
69
+ throw new InvalidSegmentError(num, offset, segment, 'Empty segment');
70
+ }
71
+
72
+ const tildeCount = (workingSegment.match(/~/g) || []).length;
73
+ if (tildeCount > 0) {
74
+ if (tildeCount > 1) {
75
+ throw new InvalidSegmentError(num, offset, segment, "Too many '~' characters");
76
+ }
77
+ if (workingSegment.endsWith('~')) {
78
+ seg.isType = true;
79
+ workingSegment = workingSegment.slice(0, -1);
80
+ } else {
81
+ throw new InvalidSegmentError(num, offset, segment, " '~' must be at the end");
82
+ }
83
+ }
84
+
85
+ // Check for empty tokens (double dots)
86
+ if (workingSegment.includes('..')) {
87
+ throw new InvalidSegmentError(num, offset, segment, 'Empty token (double dots)');
88
+ }
89
+
90
+ const tokens = workingSegment.split('.');
91
+
92
+ // Check for empty tokens
93
+ for (const token of tokens) {
94
+ if (token === '') {
95
+ throw new InvalidSegmentError(num, offset, segment, 'Empty token');
96
+ }
97
+ }
98
+
99
+ if (tokens.length > 6) {
100
+ throw new InvalidSegmentError(num, offset, segment, 'Too many tokens');
101
+ }
102
+
103
+ if (!workingSegment.endsWith('*')) {
104
+ if (tokens.length < 5) {
105
+ throw new InvalidSegmentError(num, offset, segment, 'Too few tokens');
106
+ }
107
+
108
+ for (let t = 0; t < 4; t++) {
109
+ if (!SEGMENT_TOKEN_REGEX.test(tokens[t])) {
110
+ throw new InvalidSegmentError(num, offset, segment, 'Invalid segment token: ' + tokens[t]);
111
+ }
112
+ }
113
+ }
114
+
115
+ if (tokens.length > 0) {
116
+ if (tokens[0] === '*') {
117
+ seg.isWildcard = true;
118
+ return seg;
119
+ }
120
+ seg.vendor = tokens[0];
121
+ }
122
+
123
+ if (tokens.length > 1) {
124
+ if (tokens[1] === '*') {
125
+ seg.isWildcard = true;
126
+ return seg;
127
+ }
128
+ seg.package = tokens[1];
129
+ }
130
+
131
+ if (tokens.length > 2) {
132
+ if (tokens[2] === '*') {
133
+ seg.isWildcard = true;
134
+ return seg;
135
+ }
136
+ seg.namespace = tokens[2];
137
+ }
138
+
139
+ if (tokens.length > 3) {
140
+ if (tokens[3] === '*') {
141
+ seg.isWildcard = true;
142
+ return seg;
143
+ }
144
+ seg.type = tokens[3];
145
+ }
146
+
147
+ if (tokens.length > 4) {
148
+ if (tokens[4] === '*') {
149
+ seg.isWildcard = true;
150
+ return seg;
151
+ }
152
+
153
+ if (!tokens[4].startsWith('v')) {
154
+ throw new InvalidSegmentError(num, offset, segment, "Major version must start with 'v'");
155
+ }
156
+
157
+ const majorStr = tokens[4].substring(1);
158
+ const major = parseInt(majorStr, 10);
159
+
160
+ if (isNaN(major)) {
161
+ throw new InvalidSegmentError(num, offset, segment, 'Major version must be an integer');
162
+ }
163
+
164
+ if (major < 0) {
165
+ throw new InvalidSegmentError(num, offset, segment, 'Major version must be >= 0');
166
+ }
167
+
168
+ if (major.toString() !== majorStr) {
169
+ throw new InvalidSegmentError(num, offset, segment, 'Major version must be an integer');
170
+ }
171
+
172
+ seg.verMajor = major;
173
+ }
174
+
175
+ if (tokens.length > 5) {
176
+ if (tokens[5] === '*') {
177
+ seg.isWildcard = true;
178
+ return seg;
179
+ }
180
+
181
+ const minor = parseInt(tokens[5], 10);
182
+
183
+ if (isNaN(minor)) {
184
+ throw new InvalidSegmentError(num, offset, segment, 'Minor version must be an integer');
185
+ }
186
+
187
+ if (minor < 0) {
188
+ throw new InvalidSegmentError(num, offset, segment, 'Minor version must be >= 0');
189
+ }
190
+
191
+ if (minor.toString() !== tokens[5]) {
192
+ throw new InvalidSegmentError(num, offset, segment, 'Minor version must be an integer');
193
+ }
194
+
195
+ seg.verMinor = minor;
196
+ }
197
+
198
+ return seg;
199
+ }
200
+
201
+ static isValidGtsID(id: string): boolean {
202
+ if (!id.startsWith(GTS_PREFIX)) {
203
+ return false;
204
+ }
205
+ try {
206
+ this.parseGtsID(id);
207
+ return true;
208
+ } catch {
209
+ return false;
210
+ }
211
+ }
212
+
213
+ static validateGtsID(id: string): ValidationResult {
214
+ const isWildcard = id.includes('*');
215
+ try {
216
+ if (isWildcard) {
217
+ // For wildcard patterns, use validateWildcard
218
+ this.validateWildcard(id);
219
+ } else {
220
+ this.parseGtsID(id);
221
+ }
222
+ return {
223
+ id,
224
+ ok: true,
225
+ valid: true,
226
+ error: '',
227
+ is_wildcard: isWildcard,
228
+ };
229
+ } catch (error) {
230
+ return {
231
+ id,
232
+ ok: false,
233
+ valid: false,
234
+ error: error instanceof Error ? error.message : String(error),
235
+ is_wildcard: isWildcard,
236
+ };
237
+ }
238
+ }
239
+
240
+ static parseID(id: string): ParseResult {
241
+ try {
242
+ const gtsId = this.parseGtsID(id);
243
+ return {
244
+ ok: true,
245
+ segments: gtsId.segments,
246
+ };
247
+ } catch (error) {
248
+ return {
249
+ ok: false,
250
+ segments: [],
251
+ error: error instanceof Error ? error.message : String(error),
252
+ };
253
+ }
254
+ }
255
+
256
+ static isType(id: string): boolean {
257
+ return id.endsWith('~');
258
+ }
259
+
260
+ static toUUID(id: string): string {
261
+ return uuidv5(id, GTS_NAMESPACE);
262
+ }
263
+
264
+ static idToUUID(id: string): UUIDResult {
265
+ try {
266
+ this.parseGtsID(id);
267
+ return {
268
+ id,
269
+ uuid: this.toUUID(id),
270
+ };
271
+ } catch (error) {
272
+ return {
273
+ id,
274
+ uuid: '',
275
+ error: error instanceof Error ? error.message : String(error),
276
+ };
277
+ }
278
+ }
279
+
280
+ static matchIDPattern(candidate: string, pattern: string): MatchResult {
281
+ try {
282
+ // Validate and parse candidate
283
+ // If candidate contains '*', validate it as a wildcard pattern first
284
+ // This catches malformed wildcards like 'a*' (wildcard not on token boundary)
285
+ let candidateId: GtsID;
286
+ try {
287
+ if (candidate.includes('*')) {
288
+ // Validate candidate as a wildcard pattern first
289
+ this.validateWildcard(candidate);
290
+ }
291
+ candidateId = this.parseGtsID(candidate);
292
+ } catch (error) {
293
+ return {
294
+ match: false,
295
+ pattern,
296
+ candidate,
297
+ error: error instanceof Error ? error.message : String(error),
298
+ };
299
+ }
300
+
301
+ // Validate and parse pattern (allow wildcards)
302
+ let patternId: GtsID;
303
+ try {
304
+ patternId = this.validateWildcard(pattern);
305
+ } catch (error) {
306
+ return {
307
+ match: false,
308
+ pattern,
309
+ candidate,
310
+ error: error instanceof Error ? error.message : String(error),
311
+ };
312
+ }
313
+
314
+ // Perform matching
315
+ const match = this.wildcardMatch(candidateId, patternId);
316
+
317
+ return {
318
+ match,
319
+ pattern,
320
+ candidate,
321
+ };
322
+ } catch (error) {
323
+ return {
324
+ match: false,
325
+ pattern,
326
+ candidate,
327
+ error: error instanceof Error ? error.message : String(error),
328
+ };
329
+ }
330
+ }
331
+
332
+ private static validateWildcard(pattern: string): GtsID {
333
+ const p = pattern.trim();
334
+
335
+ // Must start with gts.
336
+ if (!p.startsWith(GTS_PREFIX)) {
337
+ throw new InvalidGtsIDError(pattern, `Does not start with '${GTS_PREFIX}'`);
338
+ }
339
+
340
+ // Count wildcards
341
+ const wildcardCount = (p.match(/\*/g) || []).length;
342
+ if (wildcardCount > 1) {
343
+ throw new InvalidGtsIDError(pattern, "The wildcard '*' token is allowed only once");
344
+ }
345
+
346
+ // If wildcard exists, validate its position
347
+ if (wildcardCount === 1) {
348
+ // Wildcard must be at a token boundary at the end (either .* or ~*)
349
+ // Pattern like "gts.a.b.c.d.v1~a.*" is valid (wildcard at end after .)
350
+ // Pattern like "gts.a.b.c.d.v1~a.*~" is invalid (wildcard not at end of pattern)
351
+ // Pattern like "gts.a.b.c.d.v1~a*" is invalid (wildcard not at token boundary)
352
+ // Pattern like "gts.a.b.c.*.v1~a.*" is invalid (wildcard in middle of segment)
353
+
354
+ const wildcardIndex = p.indexOf('*');
355
+
356
+ // Check if wildcard is at the very end
357
+ if (wildcardIndex !== p.length - 1) {
358
+ throw new InvalidGtsIDError(pattern, "The wildcard '*' token is allowed only at the end of the pattern");
359
+ }
360
+
361
+ // Check if wildcard is preceded by . or ~ (token boundary)
362
+ if (wildcardIndex > 0 && p[wildcardIndex - 1] !== '.' && p[wildcardIndex - 1] !== '~') {
363
+ throw new InvalidGtsIDError(pattern, "The wildcard '*' must be preceded by '.' or '~' (token boundary)");
364
+ }
365
+
366
+ // Check that there's no wildcard in the middle of a segment (before a ~)
367
+ // Split by ~ to get segments, check if any segment except the last has a wildcard
368
+ const segments = p.split('~');
369
+ for (let i = 0; i < segments.length - 1; i++) {
370
+ if (segments[i].includes('*')) {
371
+ throw new InvalidGtsIDError(pattern, "The wildcard '*' token cannot appear in the middle of a chained ID");
372
+ }
373
+ }
374
+ }
375
+
376
+ // Parse the pattern - parseGtsID will handle the segment validation
377
+ return this.parseGtsIDInternal(p, true);
378
+ }
379
+
380
+ // Internal parse method that can be called with wildcard mode
381
+ private static parseGtsIDInternal(id: string, allowWildcard: boolean = false): GtsID {
382
+ const raw = id.trim();
383
+
384
+ if (raw !== raw.toLowerCase()) {
385
+ throw new InvalidGtsIDError(id, 'Must be lower case');
386
+ }
387
+
388
+ if (raw.includes('-')) {
389
+ throw new InvalidGtsIDError(id, "Must not contain '-'");
390
+ }
391
+
392
+ if (!raw.startsWith(GTS_PREFIX)) {
393
+ throw new InvalidGtsIDError(id, `Does not start with '${GTS_PREFIX}'`);
394
+ }
395
+
396
+ if (raw.length > MAX_ID_LENGTH) {
397
+ throw new InvalidGtsIDError(id, 'Too long');
398
+ }
399
+
400
+ // Additional validation
401
+ if (raw.includes('..')) {
402
+ throw new InvalidGtsIDError(id, 'Double dots not allowed');
403
+ }
404
+ if (raw.endsWith('.') && !raw.endsWith('.*')) {
405
+ throw new InvalidGtsIDError(id, 'Cannot end with a dot');
406
+ }
407
+ if (raw.includes('~~')) {
408
+ throw new InvalidGtsIDError(id, 'Double tildes not allowed');
409
+ }
410
+ if (raw === GTS_PREFIX || raw === GTS_PREFIX + '~') {
411
+ throw new InvalidGtsIDError(id, 'ID cannot be just the prefix');
412
+ }
413
+
414
+ const gtsId: GtsID = {
415
+ id: raw,
416
+ segments: [],
417
+ };
418
+
419
+ const remainder = raw.substring(GTS_PREFIX.length);
420
+ const parts = this.splitPreservingTilde(remainder);
421
+
422
+ let offset = GTS_PREFIX.length;
423
+ for (let i = 0; i < parts.length; i++) {
424
+ const part = parts[i];
425
+ if (part === '') {
426
+ continue;
427
+ }
428
+
429
+ const segment = this.parseSegment(i + 1, offset, part);
430
+ gtsId.segments.push(segment);
431
+ offset += part.length;
432
+ }
433
+
434
+ // Ensure we have at least one segment
435
+ if (gtsId.segments.length === 0) {
436
+ throw new InvalidGtsIDError(id, 'No valid segments found');
437
+ }
438
+
439
+ // v0.7: Single-segment instance IDs are prohibited (skip for wildcard patterns)
440
+ if (!allowWildcard && !raw.includes('*')) {
441
+ const lastSegment = gtsId.segments[gtsId.segments.length - 1];
442
+ if (!lastSegment.isType && gtsId.segments.length === 1) {
443
+ throw new InvalidGtsIDError(
444
+ id,
445
+ 'Single-segment instance IDs are prohibited. Instance IDs must be chained with a type segment (e.g., gts.vendor.pkg.ns.type.v1~instance.segment.v1)'
446
+ );
447
+ }
448
+ }
449
+
450
+ return gtsId;
451
+ }
452
+
453
+ private static wildcardMatch(candidate: GtsID, pattern: GtsID): boolean {
454
+ if (!candidate || !pattern) {
455
+ return false;
456
+ }
457
+
458
+ // If no wildcard in pattern, perform exact match with version flexibility
459
+ if (!pattern.id.includes('*')) {
460
+ return this.matchSegments(pattern.segments, candidate.segments);
461
+ }
462
+
463
+ // Wildcard case
464
+ if ((pattern.id.match(/\*/g) || []).length > 1 || !pattern.id.endsWith('*')) {
465
+ return false;
466
+ }
467
+
468
+ // Use segment matching for wildcard patterns too
469
+ return this.matchSegments(pattern.segments, candidate.segments);
470
+ }
471
+
472
+ private static matchSegments(patternSegs: GtsIDSegment[], candidateSegs: GtsIDSegment[]): boolean {
473
+ // If pattern is longer than candidate, no match
474
+ if (patternSegs.length > candidateSegs.length) {
475
+ return false;
476
+ }
477
+
478
+ for (let i = 0; i < patternSegs.length; i++) {
479
+ const pSeg = patternSegs[i];
480
+ const cSeg = candidateSegs[i];
481
+
482
+ // If pattern segment is a wildcard, check non-wildcard fields first
483
+ if (pSeg.isWildcard) {
484
+ // Check the fields that are set (non-empty) in the wildcard pattern
485
+ if (pSeg.vendor && pSeg.vendor !== cSeg.vendor) {
486
+ return false;
487
+ }
488
+ if (pSeg.package && pSeg.package !== cSeg.package) {
489
+ return false;
490
+ }
491
+ if (pSeg.namespace && pSeg.namespace !== cSeg.namespace) {
492
+ return false;
493
+ }
494
+ if (pSeg.type && pSeg.type !== cSeg.type) {
495
+ return false;
496
+ }
497
+ // Check version fields if they are set in the pattern
498
+ if (pSeg.verMajor !== 0 && pSeg.verMajor !== cSeg.verMajor) {
499
+ return false;
500
+ }
501
+ if (pSeg.verMinor !== undefined && (cSeg.verMinor === undefined || pSeg.verMinor !== cSeg.verMinor)) {
502
+ return false;
503
+ }
504
+ // Check is_type flag if set
505
+ if (pSeg.isType && pSeg.isType !== cSeg.isType) {
506
+ return false;
507
+ }
508
+ // Wildcard matches - accept anything after this point
509
+ return true;
510
+ }
511
+
512
+ // Non-wildcard segment - all fields must match
513
+ if (pSeg.vendor !== cSeg.vendor) {
514
+ return false;
515
+ }
516
+ if (pSeg.package !== cSeg.package) {
517
+ return false;
518
+ }
519
+ if (pSeg.namespace !== cSeg.namespace) {
520
+ return false;
521
+ }
522
+ if (pSeg.type !== cSeg.type) {
523
+ return false;
524
+ }
525
+
526
+ // Check version matching
527
+ // Major version must match
528
+ if (pSeg.verMajor !== cSeg.verMajor) {
529
+ return false;
530
+ }
531
+
532
+ // Minor version: if pattern has no minor version, accept any minor in candidate
533
+ // If pattern has minor version, it must match exactly
534
+ if (pSeg.verMinor !== undefined) {
535
+ if (cSeg.verMinor === undefined || pSeg.verMinor !== cSeg.verMinor) {
536
+ return false;
537
+ }
538
+ }
539
+ // else: pattern has no minor version, so any minor version in candidate is OK
540
+
541
+ // Check is_type flag matches
542
+ if (pSeg.isType !== cSeg.isType) {
543
+ return false;
544
+ }
545
+ }
546
+
547
+ // If we've matched all pattern segments, it's a match
548
+ return true;
549
+ }
550
+ }
package/src/index.ts ADDED
@@ -0,0 +1,97 @@
1
+ export * from './types';
2
+ export { Gts } from './gts';
3
+ export { GtsExtractor } from './extract';
4
+ export { GtsStore, createJsonEntity } from './store';
5
+ export { GtsRelationships } from './relationships';
6
+ export { GtsCompatibility } from './compatibility';
7
+ export { GtsCast } from './cast';
8
+ export { GtsQuery } from './query';
9
+
10
+ import { Gts } from './gts';
11
+ import { GtsExtractor } from './extract';
12
+ import { GtsStore, createJsonEntity } from './store';
13
+ import { GtsRelationships } from './relationships';
14
+ import { GtsCompatibility } from './compatibility';
15
+ import { GtsCast } from './cast';
16
+ import { GtsQuery } from './query';
17
+ import {
18
+ ValidationResult,
19
+ ParseResult,
20
+ MatchResult,
21
+ UUIDResult,
22
+ ExtractResult,
23
+ AttributeResult,
24
+ QueryResult,
25
+ RelationshipResult,
26
+ CompatibilityResult,
27
+ CastResult,
28
+ GtsConfig,
29
+ } from './types';
30
+
31
+ export const isValidGtsID = (id: string): boolean => Gts.isValidGtsID(id);
32
+ export const validateGtsID = (id: string): ValidationResult => Gts.validateGtsID(id);
33
+ export const parseGtsID = (id: string): ParseResult => Gts.parseID(id);
34
+ export const matchIDPattern = (candidate: string, pattern: string): MatchResult =>
35
+ Gts.matchIDPattern(candidate, pattern);
36
+ export const idToUUID = (id: string): UUIDResult => Gts.idToUUID(id);
37
+ export const extractID = (content: any, schemaContent?: any): ExtractResult =>
38
+ GtsExtractor.extractID(content, schemaContent);
39
+
40
+ export class GTS {
41
+ private store: GtsStore;
42
+
43
+ constructor(config?: Partial<GtsConfig>) {
44
+ this.store = new GtsStore(config);
45
+ }
46
+
47
+ register(content: any): void {
48
+ const entity = createJsonEntity(content);
49
+ this.store.register(entity);
50
+ }
51
+
52
+ get(id: string): any {
53
+ const entity = this.store.get(id);
54
+ return entity?.content;
55
+ }
56
+
57
+ validateInstance(id: string): ValidationResult {
58
+ return this.store.validateInstance(id);
59
+ }
60
+
61
+ getAttribute(path: string): AttributeResult {
62
+ // Parse the combined path format: gts_id@attr_path
63
+ const atIndex = path.indexOf('@');
64
+ if (atIndex === -1) {
65
+ return {
66
+ path,
67
+ resolved: false,
68
+ error: 'Invalid attribute path: missing @',
69
+ };
70
+ }
71
+ const gtsId = path.substring(0, atIndex);
72
+ const attrPath = path.substring(atIndex + 1);
73
+ return this.store.getAttribute(gtsId, attrPath);
74
+ }
75
+
76
+ query(expression: string, limit?: number): QueryResult {
77
+ return GtsQuery.query(this.store, expression, limit);
78
+ }
79
+
80
+ resolveRelationships(id: string): RelationshipResult {
81
+ return GtsRelationships.resolveRelationships(this.store, id);
82
+ }
83
+
84
+ checkCompatibility(
85
+ oldId: string,
86
+ newId: string,
87
+ mode: 'backward' | 'forward' | 'full' = 'full'
88
+ ): CompatibilityResult {
89
+ return GtsCompatibility.checkCompatibility(this.store, oldId, newId, mode);
90
+ }
91
+
92
+ castInstance(fromId: string, toSchemaId: string): CastResult {
93
+ return GtsCast.castInstance(this.store, fromId, toSchemaId);
94
+ }
95
+ }
96
+
97
+ export default GTS;