Skip to content

Make the gts-ts re-usable in gts-kit (VS Code plugin and GTS viewer web/desktop app) #19

Description

@Artifizer

Making @globaltypesystem/gts-ts the single source of truth

Goal: let gts-ts own parsing, entity extraction, casting, and — most
importantly — schema/instance validation, so gts-kit stops maintaining its
own Ajv pipeline in packages/shared/src/registry.ts.

This document records what gts-ts already provides, what is missing, and the
exact API changes needed to complete the switch.


1. What gts-ts already provides (already used by gts-kit)

Concern gts-ts API Used in gts-kit
GTS ID validate / parse / match / uuid isValidGtsID, validateGtsID, parseGtsID, matchIDPattern, idToUUID packages/shared/src/entities.ts
Schema-vs-instance detection + id/type extraction extractIDExtractResult entities.ts (createEntity, applyGtsExtraction)
Registry / store GtsStore, createJsonEntity registry.ts (getGtsStore)
Modifier declaration errors (§9.11.1) GtsStore.register() throws registry.ts
Derivation / trait compatibility (§9.12 / §9.7) GtsStore.validateSchemaAgainstParent(id) registry.ts
x-gts-ref schema + instance assertions (§9.6) XGtsRefValidator registry.ts
Abstract-type guard (§9.11.3) GtsModifiers.isAbstract, checkInstanceRules registry.ts
Cast (OP#9) castInstance, castInstanceRaw available
Compatibility / query / relationships / attributes checkCompatibility, query, resolveRelationships, getAttribute available

So gts-kit already delegates identity, extraction, and the GTS-specific
structural rules. The remaining thing it does itself is the actual JSON
Schema validation of instances and schemas (its own Ajv in
registry.ts::createAjvInstance).


2. Why gts-kit still keeps its own Ajv (the gaps)

Gap A — Formats are disabled, with no opt-in

GtsStore builds Ajv with validateFormats: false and never registers
ajv-formats:

// node_modules/@globaltypesystem/gts-ts/dist/store.js
this.ajv = new Ajv({ strict:false, validateSchema:false,
                     validateFormats: false }); // "match Go lenient validation"

The config type only exposes { validateRefs, strictMode } — there is no
switch to enable formats. As a result date-time, email, uuid, uri,
ipv4/6, hostname, regex are not enforced at all through gts-ts.

gts-kit needs strict formats — in particular RFC 3339 date/time (see the
OP#6 conformance table in .gts-spec/tests/test_op6_schema_validation.py).

Gap B — Errors are a single string, not structured

GtsStore.validateInstance returns ValidationResult:

interface ValidationResult { id: string; ok: boolean; valid?: boolean; error: string }

error is a joined string ("${instancePath} ${message}; ..."). The VS
Code extension needs an array of structured errors to place squigglies and
specialise messages. It relies on this shape
(packages/shared/src/entities.ts::ValidationError):

interface ValidationError {
  instancePath: string   // "/dateTimeValue"
  schemaPath: string     // "#/properties/dateTimeValue/format"
  keyword: string        // "format" | "required" | "additionalProperties" | ...
  message: string
  params: Record<string, any>  // e.g. { format }, { missingProperty }, { additionalProperty }
  data?: any
}

See apps/vscode-extension/src/validation.ts (findErrorPosition,
findValueRangeAtInstancePath) — every branch keys off instancePath,
keyword, and params.

Gap C — No schema meta-validation with structured errors

gts-kit validates the schema document itself as JSON Schema
(compileAsync(schema.content)) and surfaces per-error paths. gts-ts's
register() only throws for §9.11.1 declaration errors; there is no
"meta-validate this schema and return structured errors" entry point.

Gap D — Synchronous compile / $ref resolution

validateInstance uses this.ajv.compile (sync). gts-kit uses compileAsync
with a loadSchema that resolves GTS IDs and the json-schema.org meta-schema.
Cross-schema $$ref/gts:// references are not guaranteed to resolve the same
way under sync compile.

Gap E — Parsing lives outside gts-ts

gts-ts consumes already-parsed JS objects (register(content),
createJsonEntity(content)). JSONC/YAML parsing, comments, trailing commas, and
.jsonc/.yaml selection live in @gts/shared (parse.ts, jsonc.ts,
yaml.ts). To have gts-ts "own parsing" it needs a text-in entry point.


3. Proposed gts-ts API changes

Priority order — A and B are blockers; C–E are follow-ups.

A. Enable standard + strict formats (blocker)

Add a config flag and bundle ajv-formats:

new GtsStore({ validateFormats: true })   // default true; false = legacy lenient

Requirements:

  • date-time, date, time must be strict RFC 3339:
    • date-time requires the T/t separator (reject the space form),
    • requires a time-offset (Z/z or ±HH:MM),
    • validates real calendar/clock ranges (leap day, leap second :60).
  • email, uuid, uri, ipv4, ipv6, hostname, regex (ECMA-262) enforced.

Prefer the standard ajv-formats validators over any hand-rolled regex.
gts-kit's interim fix (§5) shows the pattern: compose the library's own
fast and full format definitions (via addFormats.get(name, mode)) so a
value must satisfy both the strict grammar (fast) and the real value-range
checks (full). The same composition can live behind validateFormats: true
in gts-ts.

B. Structured validation errors (blocker)

Extend the result (keep error for back-compat, add an array):

interface ValidationIssue {
  instancePath: string
  schemaPath: string
  keyword: string
  message: string
  params: Record<string, any>
  data?: unknown
}

interface ValidationResult {
  id: string
  ok: boolean
  valid?: boolean
  error: string               // keep: joined summary
  errors?: ValidationIssue[]  // NEW: structured, ~ raw Ajv ErrorObject[]
}

validateInstance / validateEntity should populate errors from
validate.errors (plus the abstract-type and x-gts-ref findings, each mapped
to an issue with its own instancePath/keyword).

C. Schema meta-validation

GtsStore.validateSchema(id: string): ValidationResult  // structured `errors[]`

Compile the schema as JSON Schema and return structured meta-errors (what
gts-kit does today in registry.ts for JsonSchema entities).

D. Async, ref-resolving validate (optional)

GtsStore.validateInstanceAsync(id): Promise<ValidationResult>

Uses compileAsync + the store's loadSchema so cross-schema
$$ref/gts:// references resolve exactly like gts-kit's current loader.

E. File-text parsing entry point (optional)

parseGtsFile(fileName: string, text: string): { entities: JsonEntity[]; error?: string }

JSONC + YAML aware. (May reasonably stay in gts-kit; listed for completeness.)


4. gts-kit changes once the APIs land

In packages/shared/src/registry.ts:

  • Delete createAjvInstance, addFormats, matchesFormat, and the local
    formatValidationErrors Ajv plumbing.
  • In validateEntity:
    • schema entities → store.validateSchema(id),
    • instance entities → store.validateInstance(id) (or the async variant),
    • map ValidationIssue[] straight onto ValidationError[] (shapes already
      align).
  • Keep using GtsStore for §9.11.1 / derivation / traits / x-gts-ref (already
    wired), now returned through the same structured errors[].

Net effect: one validation engine (gts-ts), consistent results across the CLI,
HTTP server, and the VS Code extension, and RFC 3339 behaviour owned by the
library.


5. Interim fix already applied in gts-kit

Until Gap A ships in gts-ts, registry.ts keeps its own Ajv but tightens the
temporal formats using only the standard ajv-formats validators — no
hand-rolled RFC logic. After addFormats(ajv), for date, time, and
date-time it composes the library's fast and full definitions
(addFormats.get(name, mode), evaluated by the matchesFormat helper) so a
value must satisfy both:

  • the fast grammar — strict T separator and a mandatory time-offset, and
  • the full validator — real calendar/clock ranges (month, day, leap second,
    offset ≤ 23:59).

This makes gts-kit reject the space form (e.g. "2008-10-12 10:30:00Z") and
every other OP#6 invalid format case while still accepting the valid ones. The
same composition moves into gts-ts under Gap A.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions