Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gts-spec
Submodule .gts-spec updated 53 files
+3 −1 .gitignore
+257 −35 README.md
+6 −0 examples/events/README.md
+24 −0 ...s/events/instances/gts.x.core.events.type_combined_id.v1~x.commerce.orders.order_placed.v1.0~.examples.json
+16 −7 examples/events/schemas/gts.x.core.events.type.v1~.schema.json
+4 −0 examples/events/schemas/gts.x.core.events.type.v1~x.commerce.orders.order_placed.v1.0~.schema.json
+4 −0 examples/events/schemas/gts.x.core.events.type.v1~x.commerce.orders.order_placed.v1.1~.schema.json
+4 −3 examples/events/schemas/gts.x.core.events.type.v1~x.core.idp.contact_created.v1~.schema.json
+88 −0 examples/events/schemas/gts.x.core.events.type_combined.v1~.schema.json
+33 −0 examples/events/schemas/gts.x.core.events.type_combined.v1~x.commerce.orders.order_placed.v1.0~.schema.json
+16 −6 examples/modules/instances/gts.x.core.modules.module.v1~x.webstore._.catalog.v1.json
+7 −5 examples/modules/instances/gts.x.core.modules.module.v1~x.webstore._.chat.v1.json
+7 −4 examples/modules/schemas/gts.x.core.modules.capability.v1~.schema.json
+78 −0 examples/typespec/vms/README.md
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.migrating.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.paused.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.rebooting.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.running.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.starting.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.stopped.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.stopping.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.suspended.v1.json
+6 −0 examples/typespec/vms/instances/states/gts.x.infra.compute.vm_state.v1~x.infra._.suspending.v1.json
+28 −0 examples/typespec/vms/instances/vms/gts.x.infra.compute.vm.v1~nutanix.ahv._.vm.v1~db-server-01.json
+24 −0 examples/typespec/vms/instances/vms/gts.x.infra.compute.vm.v1~vmware.esxi._.vm.v1~web-server-01.json
+26 −0 examples/typespec/vms/instances/vms/gts.x.infra.compute.vm.v1~vz.vz._.vm.v1~app-server-01.json
+169 −0 examples/typespec/vms/schemas/common.tsp
+72 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~.schema.json
+83 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~.tsp
+76 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~nutanix.ahv._.vm.v1~.schema.json
+47 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~nutanix.ahv._.vm.v1~.tsp
+73 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~vmware.esxi._.vm.v1~.schema.json
+47 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~vmware.esxi._.vm.v1~.tsp
+72 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~vz.vz._.vm.v1~.schema.json
+47 −0 examples/typespec/vms/schemas/gts.x.infra.compute.vm.v1~vz.vz._.vm.v1~.tsp
+30 −0 examples/typespec/vms/schemas/states/gts.x.infra.compute.vm_state.v1~.schema.json
+36 −0 examples/typespec/vms/schemas/states/gts.x.infra.compute.vm_state.v1~.tsp
+138 −0 examples/yaml/ui/README.md
+114 −0 examples/yaml/ui/instances/gts.x.ui.core.item.v1~x.ui.components.grid.v1~users_list.yaml
+27 −0 examples/yaml/ui/instances/gts.x.ui.core.item.v1~x.ui.components.menu_item.v1~main_dashboard.yaml
+33 −0 examples/yaml/ui/instances/gts.x.ui.core.item.v1~x.ui.components.menu_item.v1~user_settings.yaml
+86 −0 examples/yaml/ui/schemas/gts.x.ui.core.item.v1~.schema.yaml
+185 −0 examples/yaml/ui/schemas/gts.x.ui.core.item.v1~x.ui.components.grid.v1~.schema.yaml
+106 −0 examples/yaml/ui/schemas/gts.x.ui.core.item.v1~x.ui.components.menu_item.v1~.schema.yaml
+13 −0 tests/conftest.py
+228 −0 tests/test_op12_schema_vs_schema_validation.py
+2,147 −0 tests/test_op13_schema_traits_validation.py
+10 −0 tests/test_op1_id_validation.py
+44 −0 tests/test_op2_id_extraction.py
+28 −0 tests/test_op2_schema_id_priority.py
+36 −0 tests/test_op3_id_parsing.py
+24 −0 tests/test_op5_id_uuid.py
+319 −90 tests/test_op6_schema_validation.py
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@globaltypesystem/gts-ts",
"version": "0.2.0",
"version": "0.3.0",
"description": "TypeScript library for working with GTS (Global Type System) identifiers and JSON/JSON Schema artifacts",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
45 changes: 40 additions & 5 deletions src/gts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
const GTS_NAMESPACE = uuidv5('gts', uuidv5.URL);

const SEGMENT_TOKEN_REGEX = /^[a-z_][a-z0-9_]*$/;
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
Comment thread
GeraBart marked this conversation as resolved.

export class Gts {
static parseGtsID(id: string): GtsID {
Expand All @@ -40,6 +41,7 @@ export class Gts {
}

// Add any remaining content (instance without trailing ~)
// This could be a regular instance segment or a UUID tail
if (current) {
parts.push(current);
}
Expand All @@ -60,6 +62,7 @@ export class Gts {
verMinor: undefined,
isType: false,
isWildcard: false,
isUuidTail: false,
};

let workingSegment = seg.segment;
Expand Down Expand Up @@ -385,10 +388,6 @@ export class Gts {
throw new InvalidGtsIDError(id, 'Must be lower case');
}

if (raw.includes('-')) {
throw new InvalidGtsIDError(id, "Must not contain '-'");
}

if (!raw.startsWith(GTS_PREFIX)) {
throw new InvalidGtsIDError(id, `Does not start with '${GTS_PREFIX}'`);
}
Expand Down Expand Up @@ -426,6 +425,33 @@ export class Gts {
continue;
}

// Check if this is a UUID tail (last part, no tilde, matches UUID format)
if (i > 0 && i === parts.length - 1 && !part.endsWith('~') && UUID_REGEX.test(part)) {
// UUID tail segment for combined anonymous instances
const seg: GtsIDSegment = {
num: i + 1,
offset,
segment: part,
vendor: '',
package: '',
namespace: '',
type: '',
verMajor: 0,
verMinor: undefined,
isType: false,
isWildcard: false,
isUuidTail: true,
};
gtsId.segments.push(seg);
offset += part.length;
continue;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Regular segments must not contain hyphens
if (part.includes('-')) {
throw new InvalidGtsIDError(id, "Must not contain '-'");
}

const segment = this.parseSegment(i + 1, offset, part);
gtsId.segments.push(segment);
offset += part.length;
Expand All @@ -437,9 +463,10 @@ export class Gts {
}

// v0.7: Single-segment instance IDs are prohibited (skip for wildcard patterns)
// Exception: combined anonymous instances (UUID tail) are always valid
if (!allowWildcard && !raw.includes('*')) {
const lastSegment = gtsId.segments[gtsId.segments.length - 1];
if (!lastSegment.isType && gtsId.segments.length === 1) {
if (!lastSegment.isType && !lastSegment.isUuidTail && gtsId.segments.length === 1) {
throw new InvalidGtsIDError(
id,
'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)'
Expand Down Expand Up @@ -509,6 +536,14 @@ export class Gts {
return true;
}

// Non-wildcard UUID tail - compare raw segment string
if (pSeg.isUuidTail) {
if (pSeg.segment !== cSeg.segment) {
return false;
}
continue;
}

// Non-wildcard segment - all fields must match
if (pSeg.vendor !== cSeg.vendor) {
return false;
Expand Down
22 changes: 22 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,28 @@ export class GTS {
castInstance(fromId: string, toSchemaId: string): CastResult {
return GtsCast.castInstance(this.store, fromId, toSchemaId);
}

validateEntity(id: string): ValidationResult & { entity_type: string } {
const entity = this.store.get(id);
if (!entity) {
return { id, ok: false, error: `Entity not found: ${id}`, entity_type: 'unknown' };
}

if (entity.isSchema) {
const result = this.store.validateSchemaAgainstParent(id);
if (!result.ok) {
return { ...result, entity_type: 'schema' };
}
const traitsResult = this.store.validateEntityTraits(id);
if (!traitsResult.ok) {
return { ...traitsResult, entity_type: 'schema' };
}
return { ...result, entity_type: 'schema' };
} else {
const result = this.store.validateInstance(id);
return { ...result, entity_type: 'instance' };
}
}
}

export default GTS;
13 changes: 1 addition & 12 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -614,18 +614,7 @@ export class GtsServer {
return { ok: false, error: 'Missing required field: entity_id or gts_id' };
}

const entity = this.store['store'].get(id);
if (!entity) {
return { ok: false, error: `Entity not found: ${id}` };
}

if (entity.isSchema) {
const result = this.store['store'].validateSchemaAgainstParent(id);
return { ...result, entity_type: 'schema' };
} else {
const result = this.store.validateInstance(id);
return { ...result, entity_type: 'instance' };
}
return this.store.validateEntity(id);
}

// OpenAPI Specification
Expand Down
Loading
Loading