@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/types.ts ADDED
@@ -0,0 +1,138 @@
1
+ export const GTS_PREFIX = 'gts.';
2
+ export const GTS_URI_PREFIX = 'gts://';
3
+ export const MAX_ID_LENGTH = 1024;
4
+
5
+ export interface GtsIDSegment {
6
+ num: number;
7
+ offset: number;
8
+ segment: string;
9
+ vendor: string;
10
+ package: string;
11
+ namespace: string;
12
+ type: string;
13
+ verMajor: number;
14
+ verMinor?: number;
15
+ isType: boolean;
16
+ isWildcard: boolean;
17
+ }
18
+
19
+ export interface GtsID {
20
+ id: string;
21
+ segments: GtsIDSegment[];
22
+ }
23
+
24
+ export interface ValidationResult {
25
+ id: string;
26
+ ok: boolean;
27
+ valid?: boolean;
28
+ error: string;
29
+ is_wildcard?: boolean;
30
+ }
31
+
32
+ export interface ParseResult {
33
+ ok: boolean;
34
+ segments: GtsIDSegment[];
35
+ error?: string;
36
+ is_schema?: boolean;
37
+ is_wildcard?: boolean;
38
+ }
39
+
40
+ export interface MatchResult {
41
+ match: boolean;
42
+ pattern: string;
43
+ candidate: string;
44
+ error?: string;
45
+ }
46
+
47
+ export interface UUIDResult {
48
+ id: string;
49
+ uuid: string;
50
+ error?: string;
51
+ }
52
+
53
+ export interface ExtractResult {
54
+ id: string;
55
+ schema_id: string | null;
56
+ selected_entity_field?: string;
57
+ selected_schema_id_field?: string;
58
+ is_schema: boolean;
59
+ error?: string;
60
+ }
61
+
62
+ export interface AttributeResult {
63
+ path: string;
64
+ resolved: boolean;
65
+ value?: any;
66
+ error?: string;
67
+ }
68
+
69
+ export interface QueryResult {
70
+ query: string;
71
+ count: number;
72
+ items: any[];
73
+ error?: string;
74
+ limit?: number;
75
+ }
76
+
77
+ export interface RelationshipResult {
78
+ id: string;
79
+ relationships: string[];
80
+ brokenReferences: string[];
81
+ error?: string;
82
+ }
83
+
84
+ export interface CompatibilityResult {
85
+ compatible: boolean;
86
+ oldId: string;
87
+ newId: string;
88
+ mode: 'backward' | 'forward' | 'full';
89
+ errors: string[];
90
+ warnings: string[];
91
+ }
92
+
93
+ export interface CastResult {
94
+ ok: boolean;
95
+ fromId: string;
96
+ toId: string;
97
+ result?: any;
98
+ error?: string;
99
+ }
100
+
101
+ export interface GtsConfig {
102
+ validateRefs: boolean;
103
+ strictMode: boolean;
104
+ }
105
+
106
+ export interface JsonEntity {
107
+ id: string;
108
+ schemaId: string | null;
109
+ content: Record<string, any>;
110
+ isSchema: boolean;
111
+ references: Set<string>;
112
+ }
113
+
114
+ export class InvalidGtsIDError extends Error {
115
+ constructor(
116
+ public gtsId: string,
117
+ public cause?: string
118
+ ) {
119
+ super(cause ? `Invalid GTS identifier: ${gtsId}: ${cause}` : `Invalid GTS identifier: ${gtsId}`);
120
+ this.name = 'InvalidGtsIDError';
121
+ }
122
+ }
123
+
124
+ export class InvalidSegmentError extends Error {
125
+ constructor(
126
+ public num: number,
127
+ public offset: number,
128
+ public segment: string,
129
+ public cause?: string
130
+ ) {
131
+ super(
132
+ cause
133
+ ? `Invalid GTS segment #${num} @ offset ${offset}: '${segment}': ${cause}`
134
+ : `Invalid GTS segment #${num} @ offset ${offset}: '${segment}'`
135
+ );
136
+ this.name = 'InvalidSegmentError';
137
+ }
138
+ }
@@ -0,0 +1,349 @@
1
+ /*
2
+ * x-gts-ref validation for GTS schemas
3
+ * Validates that string values match specified GTS ID patterns
4
+ */
5
+
6
+ import { Gts } from './gts';
7
+ import { GtsStore } from './store';
8
+
9
+ export interface XGtsRefValidationError {
10
+ fieldPath: string;
11
+ value: any;
12
+ refPattern: string;
13
+ reason: string;
14
+ }
15
+
16
+ export class XGtsRefValidator {
17
+ private store: GtsStore;
18
+
19
+ constructor(store: GtsStore) {
20
+ this.store = store;
21
+ }
22
+
23
+ /**
24
+ * Validate an instance against x-gts-ref constraints in schema
25
+ */
26
+ validateInstance(instance: any, schema: any, instancePath: string = ''): XGtsRefValidationError[] {
27
+ const errors: XGtsRefValidationError[] = [];
28
+ this.visitInstance(instance, schema, instancePath, schema, errors);
29
+ return errors;
30
+ }
31
+
32
+ /**
33
+ * Validate x-gts-ref fields in a schema definition
34
+ */
35
+ validateSchema(schema: any, schemaPath: string = '', rootSchema: any = null): XGtsRefValidationError[] {
36
+ if (!rootSchema) {
37
+ rootSchema = schema;
38
+ }
39
+ const errors: XGtsRefValidationError[] = [];
40
+ this.visitSchema(schema, schemaPath, rootSchema, errors);
41
+ return errors;
42
+ }
43
+
44
+ private visitInstance(
45
+ instance: any,
46
+ schema: any,
47
+ path: string,
48
+ rootSchema: any,
49
+ errors: XGtsRefValidationError[]
50
+ ): void {
51
+ if (!schema) return;
52
+
53
+ // Check for x-gts-ref constraint
54
+ if (schema['x-gts-ref'] !== undefined) {
55
+ if (typeof instance === 'string') {
56
+ const err = this.validateRefValue(instance, schema['x-gts-ref'], path, rootSchema);
57
+ if (err) {
58
+ errors.push(err);
59
+ }
60
+ }
61
+ }
62
+
63
+ // Recurse into object properties
64
+ if (schema.type === 'object' && schema.properties) {
65
+ if (instance && typeof instance === 'object') {
66
+ for (const propName in schema.properties) {
67
+ if (propName in instance) {
68
+ const propPath = path ? `${path}.${propName}` : propName;
69
+ this.visitInstance(instance[propName], schema.properties[propName], propPath, rootSchema, errors);
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ // Recurse into array items
76
+ if (schema.type === 'array' && schema.items) {
77
+ if (Array.isArray(instance)) {
78
+ instance.forEach((item, idx) => {
79
+ const itemPath = `${path}[${idx}]`;
80
+ this.visitInstance(item, schema.items, itemPath, rootSchema, errors);
81
+ });
82
+ }
83
+ }
84
+ }
85
+
86
+ private visitSchema(schema: any, path: string, rootSchema: any, errors: XGtsRefValidationError[]): void {
87
+ if (!schema || typeof schema !== 'object') return;
88
+
89
+ // Check for x-gts-ref field
90
+ if (schema['x-gts-ref'] !== undefined) {
91
+ const refPath = path ? `${path}/x-gts-ref` : 'x-gts-ref';
92
+ const err = this.validateRefPattern(schema['x-gts-ref'], refPath, rootSchema);
93
+ if (err) {
94
+ errors.push(err);
95
+ }
96
+ }
97
+
98
+ // Recurse into nested structures
99
+ for (const key in schema) {
100
+ if (key === 'x-gts-ref') continue;
101
+
102
+ const nestedPath = path ? `${path}/${key}` : key;
103
+ const value = schema[key];
104
+
105
+ if (value && typeof value === 'object') {
106
+ if (Array.isArray(value)) {
107
+ value.forEach((item, idx) => {
108
+ if (item && typeof item === 'object') {
109
+ this.visitSchema(item, `${nestedPath}[${idx}]`, rootSchema, errors);
110
+ }
111
+ });
112
+ } else {
113
+ this.visitSchema(value, nestedPath, rootSchema, errors);
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ private validateRefValue(
120
+ value: string,
121
+ refPattern: any,
122
+ fieldPath: string,
123
+ schema: any
124
+ ): XGtsRefValidationError | null {
125
+ if (typeof refPattern !== 'string') {
126
+ return {
127
+ fieldPath,
128
+ value,
129
+ refPattern: String(refPattern),
130
+ reason: `Value must be a string, got ${typeof refPattern}`,
131
+ };
132
+ }
133
+
134
+ let resolvedPattern = refPattern;
135
+
136
+ // Resolve pattern if it's a relative reference
137
+ if (refPattern.startsWith('/')) {
138
+ const resolved = this.resolvePointer(schema, refPattern);
139
+ if (!resolved) {
140
+ return {
141
+ fieldPath,
142
+ value,
143
+ refPattern,
144
+ reason: `Cannot resolve reference path '${refPattern}'`,
145
+ };
146
+ }
147
+
148
+ // Check if the resolved value is a pointer that needs further resolution
149
+ if (resolved.startsWith('/')) {
150
+ const furtherResolved = this.resolvePointer(schema, resolved);
151
+ if (!furtherResolved) {
152
+ return {
153
+ fieldPath,
154
+ value,
155
+ refPattern,
156
+ reason: `Cannot resolve nested reference '${refPattern}' -> '${resolved}'`,
157
+ };
158
+ }
159
+ resolvedPattern = furtherResolved;
160
+ } else {
161
+ resolvedPattern = resolved;
162
+ }
163
+
164
+ if (!resolvedPattern.startsWith('gts.')) {
165
+ return {
166
+ fieldPath,
167
+ value,
168
+ refPattern,
169
+ reason: `Resolved reference '${refPattern}' -> '${resolvedPattern}' is not a GTS pattern`,
170
+ };
171
+ }
172
+ }
173
+
174
+ // Validate against GTS pattern
175
+ return this.validateGtsPattern(value, resolvedPattern, fieldPath);
176
+ }
177
+
178
+ private validateRefPattern(refPattern: any, fieldPath: string, rootSchema: any): XGtsRefValidationError | null {
179
+ if (typeof refPattern !== 'string') {
180
+ return {
181
+ fieldPath,
182
+ value: refPattern,
183
+ refPattern: '',
184
+ reason: `x-gts-ref value must be a string, got ${typeof refPattern}`,
185
+ };
186
+ }
187
+
188
+ // Case 1: Absolute GTS pattern
189
+ if (refPattern.startsWith('gts.')) {
190
+ return this.validateGtsIDOrPattern(refPattern, fieldPath);
191
+ }
192
+
193
+ // Case 2: Relative reference
194
+ if (refPattern.startsWith('/')) {
195
+ const resolved = this.resolvePointer(rootSchema, refPattern);
196
+ if (!resolved) {
197
+ return {
198
+ fieldPath,
199
+ value: refPattern,
200
+ refPattern,
201
+ reason: `Cannot resolve reference path '${refPattern}'`,
202
+ };
203
+ }
204
+ if (!Gts.isValidGtsID(resolved)) {
205
+ return {
206
+ fieldPath,
207
+ value: refPattern,
208
+ refPattern,
209
+ reason: `Resolved reference '${refPattern}' -> '${resolved}' is not a valid GTS identifier`,
210
+ };
211
+ }
212
+ return null;
213
+ }
214
+
215
+ return {
216
+ fieldPath,
217
+ value: refPattern,
218
+ refPattern,
219
+ reason: `Invalid x-gts-ref value: '${refPattern}' must start with 'gts.' or '/'`,
220
+ };
221
+ }
222
+
223
+ private validateGtsIDOrPattern(pattern: string, fieldPath: string): XGtsRefValidationError | null {
224
+ if (pattern === 'gts.*') {
225
+ return null; // Valid wildcard
226
+ }
227
+
228
+ if (pattern.includes('*')) {
229
+ // Wildcard pattern - validate prefix
230
+ const prefix = pattern.replace('*', '');
231
+ if (!prefix.startsWith('gts.')) {
232
+ return {
233
+ fieldPath,
234
+ value: pattern,
235
+ refPattern: pattern,
236
+ reason: `Invalid GTS wildcard pattern: ${pattern}`,
237
+ };
238
+ }
239
+ return null;
240
+ }
241
+
242
+ // Specific GTS ID
243
+ if (!Gts.isValidGtsID(pattern)) {
244
+ return {
245
+ fieldPath,
246
+ value: pattern,
247
+ refPattern: pattern,
248
+ reason: `Invalid GTS identifier: ${pattern}`,
249
+ };
250
+ }
251
+ return null;
252
+ }
253
+
254
+ private validateGtsPattern(value: string, pattern: string, fieldPath: string): XGtsRefValidationError | null {
255
+ // Validate it's a valid GTS ID
256
+ if (!Gts.isValidGtsID(value)) {
257
+ return {
258
+ fieldPath,
259
+ value,
260
+ refPattern: pattern,
261
+ reason: `Value '${value}' is not a valid GTS identifier`,
262
+ };
263
+ }
264
+
265
+ // Check pattern match
266
+ if (pattern === 'gts.*') {
267
+ // Any valid GTS ID matches
268
+ } else if (pattern.endsWith('*')) {
269
+ const prefix = pattern.slice(0, -1);
270
+ if (!value.startsWith(prefix)) {
271
+ return {
272
+ fieldPath,
273
+ value,
274
+ refPattern: pattern,
275
+ reason: `Value '${value}' does not match pattern '${pattern}'`,
276
+ };
277
+ }
278
+ } else if (!value.startsWith(pattern)) {
279
+ return {
280
+ fieldPath,
281
+ value,
282
+ refPattern: pattern,
283
+ reason: `Value '${value}' does not match pattern '${pattern}'`,
284
+ };
285
+ }
286
+
287
+ // Optionally check if entity exists in store
288
+ if (this.store) {
289
+ const entity = this.store.get(value);
290
+ if (!entity) {
291
+ return {
292
+ fieldPath,
293
+ value,
294
+ refPattern: pattern,
295
+ reason: `Referenced entity '${value}' not found in registry`,
296
+ };
297
+ }
298
+ }
299
+
300
+ return null;
301
+ }
302
+
303
+ /**
304
+ * Strip the "gts://" prefix from a value if present
305
+ */
306
+ private stripGtsURIPrefix(value: string): string {
307
+ return value.replace(/^gts:\/\//, '');
308
+ }
309
+
310
+ /**
311
+ * Resolve a JSON Pointer in the schema
312
+ * Note: For /$id references, the gts:// prefix is stripped from the value
313
+ */
314
+ private resolvePointer(schema: any, pointer: string): string {
315
+ const path = pointer.startsWith('/') ? pointer.slice(1) : pointer;
316
+ if (!path) return '';
317
+
318
+ const parts = path.split('/');
319
+ let current: any = schema;
320
+
321
+ for (const part of parts) {
322
+ if (!current || typeof current !== 'object') {
323
+ return '';
324
+ }
325
+ current = current[part];
326
+ if (current === undefined) {
327
+ return '';
328
+ }
329
+ }
330
+
331
+ // If current is a string, return it (stripping gts:// prefix if present)
332
+ if (typeof current === 'string') {
333
+ return this.stripGtsURIPrefix(current);
334
+ }
335
+
336
+ // If current is a dict with x-gts-ref, resolve it
337
+ if (current && typeof current === 'object' && current['x-gts-ref']) {
338
+ const xGtsRef = current['x-gts-ref'];
339
+ if (typeof xGtsRef === 'string') {
340
+ if (xGtsRef.startsWith('/')) {
341
+ return this.resolvePointer(schema, xGtsRef);
342
+ }
343
+ return xGtsRef;
344
+ }
345
+ }
346
+
347
+ return '';
348
+ }
349
+ }