diff --git a/library/schema/schema.ts b/library/schema/schema.ts index 98df35b..7916326 100644 --- a/library/schema/schema.ts +++ b/library/schema/schema.ts @@ -16,7 +16,7 @@ export type SchemaPrototypeProperties = { }; export type SchemaPrototypeType = { - "@type": string | readonly string[]; + "@type"?: string | readonly string[]; }; export type SchemaPrototype = SchemaPrototypeProperties & SchemaPrototypeType; diff --git a/library/schema/utils.ts b/library/schema/utils.ts index b49e2ed..45d13d5 100644 --- a/library/schema/utils.ts +++ b/library/schema/utils.ts @@ -1,3 +1,4 @@ +import rdf from "../namespaces/rdf.ts"; import xsd from "../namespaces/xsd.ts"; import type { @@ -13,9 +14,15 @@ export const expandSchema = (schemaPrototype: SchemaPrototype) => { } if (Object.keys(schemaPrototype).length === 0) { - throw new Error(`Invalid schema, empty object, expected "@type" key or property definition`); + throw new Error( + `Invalid schema, empty object, expected "@type" key or property definition`, + ); } + const expandShortcut = (value: string) => { + return value === "@type" ? rdf.type : value; + }; + const expandArray = (stringOrStrings: T | readonly T[]) => { return Array.isArray(stringOrStrings) ? stringOrStrings : [stringOrStrings]; }; @@ -25,7 +32,7 @@ export const expandSchema = (schemaPrototype: SchemaPrototype) => { ) => { if (typeof stringOrProperty === "string") { return { - "@id": stringOrProperty, + "@id": expandShortcut(stringOrProperty), "@type": xsd.string, }; } @@ -52,6 +59,8 @@ export const expandSchema = (schemaPrototype: SchemaPrototype) => { const expandedProperty = Object.keys(property).reduce((acc, key) => { if (key === "@context") { acc[key] = expandSchema(property[key]!); + } else if (key === "@id") { + acc[key] = expandShortcut(property[key]); } else if (validKeys.includes(key as keyof PropertyPrototype)) { acc[key] = property[key as keyof PropertyPrototype] as unknown; } @@ -71,7 +80,7 @@ export const expandSchema = (schemaPrototype: SchemaPrototype) => { return Object.keys(schemaPrototype).reduce((acc, key) => { if (key === "@type") { - acc[key] = expandArray(schemaPrototype[key]); + acc[key] = expandArray(schemaPrototype[key]!); } else { acc[key] = expandSchemaProperty( schemaPrototype[key] as string | PropertyPrototype, diff --git a/tests/schema.test.ts b/tests/schema.test.ts index 56d24d0..e6732f8 100644 --- a/tests/schema.test.ts +++ b/tests/schema.test.ts @@ -3,15 +3,16 @@ import { Equals, x } from "./test_utils.ts"; import { expandSchema, + Property, type Schema, type SchemaInterface, type SchemaPrototype, } from "../library/schema/mod.ts"; import { xsd } from "../library/namespaces/mod.ts"; +import rdf from "../library/namespaces/rdf.ts"; type ThingType = { $id: string; - $type: string[]; required: string; optional: string | undefined; array: string[]; @@ -22,7 +23,6 @@ type ThingType = { date: Date; nested: { $id: string; - $type: string[]; nestedValue: string; }; }; @@ -137,3 +137,19 @@ Deno.test("Schema / should have at least one property or @type restriction", () expandSchema({} as unknown as SchemaPrototype); }); }); + +Deno.test("Schema / should expand @type shortcut definition", () => { + const schema = { + "type": "@type", + }; + const expandedSchema = expandSchema(schema); + assertEquals((expandedSchema["type"] as Property)["@id"], rdf.type); +}); + +Deno.test("Schema / should expand @type property definition", () => { + const schema = { + "type": { "@id": "@type" }, + }; + const expandedSchema = expandSchema(schema); + assertEquals((expandedSchema["type"] as Property)["@id"], rdf.type); +});