@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,525 @@
1
+ import { GTS, isValidGtsID, validateGtsID, parseGtsID, matchIDPattern, idToUUID, extractID } from '../src';
2
+
3
+ describe('GTS Core Operations', () => {
4
+ describe('OP#1 - ID Validation', () => {
5
+ test('validates correct GTS IDs', () => {
6
+ expect(isValidGtsID('gts.vendor.pkg.ns.type.v1~')).toBe(true);
7
+ // v0.7: Single-segment instances are prohibited, must use chained IDs
8
+ expect(isValidGtsID('gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0')).toBe(true);
9
+ // Chained identifiers per spec section 2.2
10
+ expect(isValidGtsID('gts.x.core.events.type.v1~ven.app._.custom_event.v1~')).toBe(true);
11
+ expect(isValidGtsID('gts.x.core.events.topic.v1~ven.app._.custom_event_topic.v1.2')).toBe(true);
12
+ });
13
+
14
+ test('rejects invalid GTS IDs', () => {
15
+ expect(isValidGtsID('invalid')).toBe(false);
16
+ expect(isValidGtsID('GTS.vendor.pkg.ns.type.v1~')).toBe(false);
17
+ expect(isValidGtsID('gts.vendor-pkg.ns.type.v1~')).toBe(false);
18
+ expect(isValidGtsID('gts.vendor.pkg.ns.type')).toBe(false);
19
+ });
20
+
21
+ test('rejects single-segment instance IDs (v0.7)', () => {
22
+ // Single-segment instance IDs are prohibited in v0.7
23
+ expect(isValidGtsID('gts.vendor.pkg.ns.type.v1.0')).toBe(false);
24
+ expect(isValidGtsID('gts.vendor.pkg.ns.type.v1.2')).toBe(false);
25
+ });
26
+
27
+ test('validateGtsID returns detailed validation result', () => {
28
+ const validResult = validateGtsID('gts.vendor.pkg.ns.type.v1~');
29
+ expect(validResult.ok).toBe(true);
30
+ expect(validResult.valid).toBe(true);
31
+ expect(validResult.error).toBe('');
32
+
33
+ const invalidResult = validateGtsID('invalid.id');
34
+ expect(invalidResult.ok).toBe(false);
35
+ expect(invalidResult.valid).toBe(false);
36
+ expect(invalidResult.error).toContain('Invalid GTS identifier');
37
+ });
38
+ });
39
+
40
+ describe('OP#2 - ID Extraction', () => {
41
+ test('extracts GTS ID from instance', () => {
42
+ const instance = {
43
+ gtsId: 'gts.vendor.pkg.ns.type.v1.0',
44
+ name: 'Test Instance',
45
+ };
46
+
47
+ const result = extractID(instance);
48
+ expect(result.id).toBe('gts.vendor.pkg.ns.type.v1.0');
49
+ expect(result.is_schema).toBe(false);
50
+ });
51
+
52
+ test('extracts GTS ID from schema', () => {
53
+ const schema = {
54
+ $$id: 'gts.vendor.pkg.ns.type.v1~',
55
+ $$schema: 'http://json-schema.org/draft-07/schema#',
56
+ type: 'object',
57
+ properties: {},
58
+ };
59
+
60
+ const result = extractID(schema);
61
+ expect(result.id).toBe('gts.vendor.pkg.ns.type.v1~');
62
+ expect(result.is_schema).toBe(true);
63
+ });
64
+
65
+ test('handles GTS URI prefix', () => {
66
+ const schema = {
67
+ $id: 'gts://gts.vendor.pkg.ns.type.v1~',
68
+ $schema: 'http://json-schema.org/draft-07/schema#',
69
+ type: 'object',
70
+ };
71
+
72
+ const result = extractID(schema);
73
+ expect(result.id).toBe('gts.vendor.pkg.ns.type.v1~');
74
+ expect(result.is_schema).toBe(true);
75
+ });
76
+ });
77
+
78
+ describe('OP#3 - ID Parsing', () => {
79
+ test('parses GTS ID into segments', () => {
80
+ const result = parseGtsID('gts.vendor.pkg.ns.type.v1~');
81
+ expect(result.ok).toBe(true);
82
+ expect(result.segments).toHaveLength(1);
83
+
84
+ const segment = result.segments[0];
85
+ expect(segment.vendor).toBe('vendor');
86
+ expect(segment.package).toBe('pkg');
87
+ expect(segment.namespace).toBe('ns');
88
+ expect(segment.type).toBe('type');
89
+ expect(segment.verMajor).toBe(1);
90
+ expect(segment.verMinor).toBeUndefined();
91
+ expect(segment.isType).toBe(true);
92
+ });
93
+
94
+ test('parses instance ID with minor version', () => {
95
+ const result = parseGtsID('gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.2');
96
+ expect(result.ok).toBe(true);
97
+
98
+ const segment = result.segments[1];
99
+ expect(segment.verMajor).toBe(1);
100
+ expect(segment.verMinor).toBe(2);
101
+ expect(segment.isType).toBe(false);
102
+ });
103
+
104
+ test('parses chained identifiers', () => {
105
+ const result = parseGtsID('gts.x.core.events.type.v1~ven.app._.custom_event.v1~');
106
+ expect(result.ok).toBe(true);
107
+ expect(result.segments).toHaveLength(2);
108
+
109
+ // First segment - base type
110
+ expect(result.segments[0].vendor).toBe('x');
111
+ expect(result.segments[0].package).toBe('core');
112
+ expect(result.segments[0].namespace).toBe('events');
113
+ expect(result.segments[0].type).toBe('type');
114
+ expect(result.segments[0].verMajor).toBe(1);
115
+ expect(result.segments[0].isType).toBe(true);
116
+
117
+ // Second segment - derived type
118
+ expect(result.segments[1].vendor).toBe('ven');
119
+ expect(result.segments[1].package).toBe('app');
120
+ expect(result.segments[1].namespace).toBe('_'); // placeholder
121
+ expect(result.segments[1].type).toBe('custom_event');
122
+ expect(result.segments[1].verMajor).toBe(1);
123
+ expect(result.segments[1].isType).toBe(true);
124
+ });
125
+ });
126
+
127
+ describe('OP#4 - Pattern Matching', () => {
128
+ test('matches exact patterns', () => {
129
+ const candidate = 'gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0';
130
+ const pattern = 'gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0';
131
+ const result = matchIDPattern(candidate, pattern);
132
+ expect(result.match).toBe(true);
133
+ });
134
+
135
+ test('matches wildcard patterns', () => {
136
+ const candidate = 'gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0';
137
+ const pattern = 'gts.vendor.pkg.*';
138
+ const result = matchIDPattern(candidate, pattern);
139
+ expect(result.match).toBe(true);
140
+ });
141
+
142
+ test('rejects non-matching patterns', () => {
143
+ const candidate = 'gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0';
144
+ const pattern = 'gts.other.pkg.*';
145
+ const result = matchIDPattern(candidate, pattern);
146
+ expect(result.match).toBe(false);
147
+ });
148
+
149
+ test('matches partial wildcards', () => {
150
+ const candidate = 'gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0';
151
+ const pattern = 'gts.vendor.pkg.ns.*';
152
+ const result = matchIDPattern(candidate, pattern);
153
+ expect(result.match).toBe(true);
154
+ });
155
+ });
156
+
157
+ describe('OP#5 - UUID Generation', () => {
158
+ test('generates deterministic UUID from GTS ID', () => {
159
+ const result1 = idToUUID('gts.vendor.pkg.ns.type.v1~');
160
+ const result2 = idToUUID('gts.vendor.pkg.ns.type.v1~');
161
+
162
+ expect(result1.uuid).toBe(result2.uuid);
163
+ expect(result1.uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
164
+ });
165
+
166
+ test('generates different UUIDs for different IDs', () => {
167
+ const result1 = idToUUID('gts.vendor.pkg.ns.type.v1~');
168
+ const result2 = idToUUID('gts.vendor.pkg.ns.type.v2~');
169
+
170
+ expect(result1.uuid).not.toBe(result2.uuid);
171
+ });
172
+ });
173
+ });
174
+
175
+ describe('GTS Store Operations', () => {
176
+ let gts: GTS;
177
+
178
+ beforeEach(() => {
179
+ gts = new GTS({ validateRefs: false });
180
+ });
181
+
182
+ describe('OP#6 - Schema Validation', () => {
183
+ test('validates instance against schema', () => {
184
+ const schema = {
185
+ $$id: 'gts.test.pkg.ns.person.v1~',
186
+ $$schema: 'http://json-schema.org/draft-07/schema#',
187
+ type: 'object',
188
+ properties: {
189
+ name: { type: 'string' },
190
+ age: { type: 'number' },
191
+ },
192
+ required: ['name'],
193
+ };
194
+
195
+ const validInstance = {
196
+ gtsId: 'gts.test.pkg.ns.person.v1~test.pkg.ns.john.v1.0',
197
+ $schema: 'gts.test.pkg.ns.person.v1~',
198
+ name: 'John Doe',
199
+ age: 30,
200
+ };
201
+
202
+ const invalidInstance = {
203
+ gtsId: 'gts.test.pkg.ns.person.v1~test.pkg.ns.jane.v1.1',
204
+ $schema: 'gts.test.pkg.ns.person.v1~',
205
+ age: 30,
206
+ };
207
+
208
+ gts.register(schema);
209
+ gts.register(validInstance);
210
+ gts.register(invalidInstance);
211
+
212
+ const validResult = gts.validateInstance('gts.test.pkg.ns.person.v1~test.pkg.ns.john.v1.0');
213
+ expect(validResult.ok).toBe(true);
214
+
215
+ const invalidResult = gts.validateInstance('gts.test.pkg.ns.person.v1~test.pkg.ns.jane.v1.1');
216
+ expect(invalidResult.ok).toBe(false);
217
+ expect(invalidResult.error).toContain('required');
218
+ });
219
+ });
220
+
221
+ describe('OP#7 - Relationship Resolution', () => {
222
+ test('resolves relationships between entities', () => {
223
+ const schema = {
224
+ $$id: 'gts.test.pkg.ns.person.v1~',
225
+ $$schema: 'http://json-schema.org/draft-07/schema#',
226
+ type: 'object',
227
+ properties: {
228
+ name: { type: 'string' },
229
+ friend: { $ref: 'gts://gts.test.pkg.ns.person.v1~' },
230
+ },
231
+ };
232
+
233
+ const instance = {
234
+ gtsId: 'gts.test.pkg.ns.person.v1~test.pkg.ns.john.v1.0',
235
+ $schema: 'gts.test.pkg.ns.person.v1~',
236
+ name: 'John',
237
+ friend: { $ref: 'gts.test.pkg.ns.person.v1~test.pkg.ns.jane.v1.1' },
238
+ };
239
+
240
+ gts.register(schema);
241
+ gts.register(instance);
242
+
243
+ const result = gts.resolveRelationships('gts.test.pkg.ns.person.v1~test.pkg.ns.john.v1.0');
244
+ expect(result.relationships).toContain('gts.test.pkg.ns.person.v1~');
245
+ expect(result.brokenReferences).toContain('gts.test.pkg.ns.person.v1~test.pkg.ns.jane.v1.1');
246
+ });
247
+ });
248
+
249
+ describe('OP#8 - Compatibility Checking', () => {
250
+ test('checks backward compatibility', () => {
251
+ const schemaV1 = {
252
+ $$id: 'gts.test.pkg.ns.person.v1~',
253
+ $$schema: 'http://json-schema.org/draft-07/schema#',
254
+ type: 'object',
255
+ properties: {
256
+ name: { type: 'string' },
257
+ age: { type: 'number' },
258
+ },
259
+ required: ['name'],
260
+ };
261
+
262
+ const schemaV2 = {
263
+ $$id: 'gts.test.pkg.ns.person.v2~',
264
+ $$schema: 'http://json-schema.org/draft-07/schema#',
265
+ type: 'object',
266
+ properties: {
267
+ name: { type: 'string' },
268
+ age: { type: 'number' },
269
+ email: { type: 'string' },
270
+ },
271
+ required: ['name'],
272
+ };
273
+
274
+ gts.register(schemaV1);
275
+ gts.register(schemaV2);
276
+
277
+ const result = gts.checkCompatibility('gts.test.pkg.ns.person.v1~', 'gts.test.pkg.ns.person.v2~', 'backward');
278
+ expect(result.compatible).toBe(true);
279
+ });
280
+
281
+ test('detects incompatible changes', () => {
282
+ const schemaV1 = {
283
+ $$id: 'gts.test.pkg.ns.person.v1~',
284
+ $$schema: 'http://json-schema.org/draft-07/schema#',
285
+ type: 'object',
286
+ properties: {
287
+ name: { type: 'string' },
288
+ },
289
+ required: ['name'],
290
+ };
291
+
292
+ const schemaV2 = {
293
+ $$id: 'gts.test.pkg.ns.person.v2~',
294
+ $$schema: 'http://json-schema.org/draft-07/schema#',
295
+ type: 'object',
296
+ properties: {
297
+ fullName: { type: 'string' },
298
+ },
299
+ required: ['fullName'],
300
+ };
301
+
302
+ gts.register(schemaV1);
303
+ gts.register(schemaV2);
304
+
305
+ const result = gts.checkCompatibility('gts.test.pkg.ns.person.v1~', 'gts.test.pkg.ns.person.v2~', 'backward');
306
+ expect(result.compatible).toBe(false);
307
+ expect(result.errors.length).toBeGreaterThan(0);
308
+ });
309
+ });
310
+
311
+ describe('OP#9 - Version Casting', () => {
312
+ test('casts instance between compatible versions', () => {
313
+ const schemaV1 = {
314
+ $$id: 'gts.test.pkg.ns.person.v1~',
315
+ $$schema: 'http://json-schema.org/draft-07/schema#',
316
+ type: 'object',
317
+ properties: {
318
+ name: { type: 'string' },
319
+ age: { type: 'number' },
320
+ },
321
+ required: ['name'],
322
+ };
323
+
324
+ const schemaV2 = {
325
+ $$id: 'gts.test.pkg.ns.person.v2~',
326
+ $$schema: 'http://json-schema.org/draft-07/schema#',
327
+ type: 'object',
328
+ properties: {
329
+ name: { type: 'string' },
330
+ age: { type: 'number' },
331
+ email: { type: 'string', default: '' },
332
+ },
333
+ required: ['name'],
334
+ };
335
+
336
+ const instance = {
337
+ gtsId: 'gts.test.pkg.ns.person.v1~test.pkg.ns.john.v1.0',
338
+ $schema: 'gts.test.pkg.ns.person.v1~',
339
+ name: 'John',
340
+ age: 30,
341
+ };
342
+
343
+ gts.register(schemaV1);
344
+ gts.register(schemaV2);
345
+ gts.register(instance);
346
+
347
+ const result = gts.castInstance('gts.test.pkg.ns.person.v1~test.pkg.ns.john.v1.0', 'gts.test.pkg.ns.person.v2~');
348
+
349
+ expect(result.ok).toBe(true);
350
+ expect(result.result).toBeDefined();
351
+ expect(result.result.gtsId).toContain('v2');
352
+ expect(result.result.email).toBe('');
353
+ });
354
+ });
355
+
356
+ describe('OP#10 - Query Execution', () => {
357
+ test('queries entities with patterns', () => {
358
+ gts.register({
359
+ gtsId: 'gts.vendor.pkg1.ns.type.v1~vendor.pkg1.ns.instance.v1.0',
360
+ data: 'test1',
361
+ });
362
+ gts.register({
363
+ gtsId: 'gts.vendor.pkg2.ns.type.v1~vendor.pkg2.ns.instance.v1.0',
364
+ data: 'test2',
365
+ });
366
+ gts.register({
367
+ gtsId: 'gts.other.pkg.ns.type.v1~other.pkg.ns.instance.v1.0',
368
+ data: 'test3',
369
+ });
370
+
371
+ const result = gts.query('gts.vendor.*');
372
+ expect(result.count).toBe(2);
373
+ const ids = result.items.map((item: any) => item.gtsId);
374
+ expect(ids).toContain('gts.vendor.pkg1.ns.type.v1~vendor.pkg1.ns.instance.v1.0');
375
+ expect(ids).toContain('gts.vendor.pkg2.ns.type.v1~vendor.pkg2.ns.instance.v1.0');
376
+ });
377
+
378
+ test('supports wildcard patterns', () => {
379
+ gts.register({ gtsId: 'gts.a.b.c.d.v1~a.b.c.d.v1.0' });
380
+ gts.register({ gtsId: 'gts.a.b.c.e.v1~a.b.c.e.v1.0' });
381
+ gts.register({ gtsId: 'gts.a.x.c.d.v1~a.x.c.d.v1.0' });
382
+
383
+ const result = gts.query('gts.a.b.*');
384
+ expect(result.count).toBe(2);
385
+ const ids = result.items.map((item: any) => item.gtsId);
386
+ expect(ids).toContain('gts.a.b.c.d.v1~a.b.c.d.v1.0');
387
+ expect(ids).toContain('gts.a.b.c.e.v1~a.b.c.e.v1.0');
388
+ });
389
+ });
390
+
391
+ describe('OP#11 - Attribute Access', () => {
392
+ test('retrieves attribute values', () => {
393
+ const instance = {
394
+ gtsId: 'gts.test.pkg.ns.person.v1.0',
395
+ name: 'John Doe',
396
+ address: {
397
+ city: 'New York',
398
+ country: 'USA',
399
+ },
400
+ };
401
+
402
+ gts.register(instance);
403
+
404
+ const nameResult = gts.getAttribute('gts.test.pkg.ns.person.v1.0@name');
405
+ expect(nameResult.resolved).toBe(true);
406
+ expect(nameResult.value).toBe('John Doe');
407
+
408
+ const cityResult = gts.getAttribute('gts.test.pkg.ns.person.v1.0@address.city');
409
+ expect(cityResult.resolved).toBe(true);
410
+ expect(cityResult.value).toBe('New York');
411
+
412
+ const missingResult = gts.getAttribute('gts.test.pkg.ns.person.v1.0@missing');
413
+ expect(missingResult.resolved).toBe(false);
414
+ });
415
+ });
416
+
417
+ describe('OP#12 - Wildcard Validation (v0.7)', () => {
418
+ test('validates wildcard patterns', () => {
419
+ const result = validateGtsID('gts.vendor.pkg.*');
420
+ expect(result.ok).toBe(true);
421
+ expect(result.is_wildcard).toBe(true);
422
+ });
423
+
424
+ test('rejects wildcards not at token boundaries', () => {
425
+ expect(isValidGtsID('gts.vendor.pkg.a*')).toBe(false);
426
+ expect(isValidGtsID('gts.vendor.pkg.*a')).toBe(false);
427
+ });
428
+
429
+ test('rejects wildcards in middle of chain', () => {
430
+ expect(isValidGtsID('gts.vendor.*.ns.type.v1~')).toBe(false);
431
+ });
432
+
433
+ test('allows wildcards at end of chain', () => {
434
+ expect(isValidGtsID('gts.vendor.pkg.ns.type.v1~vendor.*')).toBe(true);
435
+ });
436
+ });
437
+
438
+ describe('OP#13 - Schema Detection (v0.7)', () => {
439
+ test('detects schema with $schema field', () => {
440
+ const schema = {
441
+ $id: 'gts.vendor.pkg.ns.type.v1~',
442
+ $schema: 'http://json-schema.org/draft-07/schema#',
443
+ type: 'object',
444
+ };
445
+ const result = extractID(schema);
446
+ expect(result.is_schema).toBe(true);
447
+ });
448
+
449
+ test('does not detect schema without $schema field', () => {
450
+ const notSchema = {
451
+ $id: 'gts.vendor.pkg.ns.type.v1~',
452
+ type: 'object',
453
+ properties: {},
454
+ };
455
+ const result = extractID(notSchema);
456
+ expect(result.is_schema).toBe(false);
457
+ });
458
+
459
+ test('detects schema with GTS $schema reference', () => {
460
+ const schema = {
461
+ $id: 'gts.vendor.pkg.ns.derived.v1~',
462
+ $schema: 'gts://gts.vendor.pkg.ns.type.v1~',
463
+ type: 'object',
464
+ };
465
+ const result = extractID(schema);
466
+ expect(result.is_schema).toBe(true);
467
+ });
468
+ });
469
+
470
+ describe('OP#14 - Schema ID Extraction (v0.7)', () => {
471
+ test('extracts schema_id from chain for instances without explicit schema field', () => {
472
+ const instance = {
473
+ gtsId: 'gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0',
474
+ data: 'test',
475
+ };
476
+ const result = extractID(instance);
477
+ // v0.7: schema_id is extracted from the chain
478
+ expect(result.schema_id).toBe('gts.vendor.pkg.ns.type.v1~');
479
+ });
480
+
481
+ test('extracts schema_id from chained instance ID', () => {
482
+ const instance = {
483
+ gtsId: 'gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0',
484
+ $schema: 'gts.vendor.pkg.ns.type.v1~',
485
+ data: 'test',
486
+ };
487
+ const result = extractID(instance);
488
+ expect(result.schema_id).toBe('gts.vendor.pkg.ns.type.v1~');
489
+ });
490
+
491
+ test('extracts parent type from derived schema chain', () => {
492
+ const schema = {
493
+ $id: 'gts.x.core.events.type.v1~x.commerce.orders.order_placed.v1.0~',
494
+ $schema: 'gts://gts.x.core.events.type.v1~',
495
+ type: 'object',
496
+ };
497
+ const result = extractID(schema);
498
+ expect(result.schema_id).toBe('gts.x.core.events.type.v1~');
499
+ });
500
+ });
501
+
502
+ describe('OP#15 - ParseResult Fields (v0.7)', () => {
503
+ test('parseGtsID successfully parses type IDs', () => {
504
+ const result = parseGtsID('gts.vendor.pkg.ns.type.v1~');
505
+ expect(result.ok).toBe(true);
506
+ expect(result.segments).toHaveLength(1);
507
+ expect(result.segments[0].isType).toBe(true);
508
+ });
509
+
510
+ test('parseGtsID successfully parses wildcard patterns', () => {
511
+ const result = parseGtsID('gts.vendor.pkg.*');
512
+ expect(result.ok).toBe(true);
513
+ expect(result.segments).toHaveLength(1);
514
+ expect(result.segments[0].isWildcard).toBe(true);
515
+ });
516
+
517
+ test('parseGtsID successfully parses chained instance IDs', () => {
518
+ const result = parseGtsID('gts.vendor.pkg.ns.type.v1~vendor.pkg.ns.instance.v1.0');
519
+ expect(result.ok).toBe(true);
520
+ expect(result.segments).toHaveLength(2);
521
+ expect(result.segments[0].isType).toBe(true);
522
+ expect(result.segments[1].isType).toBe(false);
523
+ });
524
+ });
525
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "sourceMap": true,
15
+ "resolveJsonModule": true,
16
+ "moduleResolution": "node",
17
+ "allowSyntheticDefaultImports": true,
18
+ "noImplicitAny": true,
19
+ "strictNullChecks": true,
20
+ "strictFunctionTypes": true,
21
+ "strictBindCallApply": true,
22
+ "strictPropertyInitialization": true,
23
+ "noImplicitThis": true,
24
+ "alwaysStrict": true,
25
+ "noUnusedLocals": true,
26
+ "noUnusedParameters": true,
27
+ "noImplicitReturns": true,
28
+ "noFallthroughCasesInSwitch": true
29
+ },
30
+ "include": ["src/**/*"],
31
+ "exclude": ["node_modules", "dist", "tests"]
32
+ }