@ronin/schema
Advanced tools
Comparing version 0.1.4 to 0.1.5
@@ -109,2 +109,3 @@ /** | ||
type ModelFieldCollation = 'BINARY' | 'NOCASE' | 'RTRIM'; | ||
type ModelFieldLinkAction = 'CASCADE' | 'RESTRICT' | 'SET NULL' | 'SET DEFAULT' | 'NO ACTION'; | ||
type ModelFieldBasics = { | ||
@@ -143,27 +144,45 @@ /** The label that should be used when displaying the field on the RONIN dashboard. */ | ||
check?: Expression; | ||
/** | ||
* If the field is of type `string`, setting this attribute defines the collation | ||
* sequence to use for the field value. | ||
*/ | ||
}; | ||
type ModelField = ModelFieldBasics & ({ | ||
/** The kind of value that should be stored inside the field. */ | ||
type?: never; | ||
} | { | ||
/** The kind of value that should be stored inside the field. */ | ||
type: 'boolean'; | ||
} | { | ||
/** The kind of value that should be stored inside the field. */ | ||
type: 'date'; | ||
} | { | ||
/** The kind of value that should be stored inside the field. */ | ||
type: 'json'; | ||
} | { | ||
/** The kind of value that should be stored inside the field. */ | ||
type: 'blob'; | ||
} | { | ||
/** The kind of value that should be stored inside the field. */ | ||
type: 'string'; | ||
/** The collation sequence to use for the field value. */ | ||
collation?: ModelFieldCollation; | ||
} | { | ||
/** The kind of value that should be stored inside the field. */ | ||
type: 'number'; | ||
/** | ||
* If the field is of type `number`, setting this attribute will automatically increment | ||
* the value of the field with every new record that gets inserted. | ||
* Automatically increments the value of the field with every new inserted record. | ||
*/ | ||
increment?: boolean; | ||
}; | ||
type ModelFieldNormal = ModelFieldBasics & { | ||
type?: 'string' | 'number' | 'boolean' | 'date' | 'json'; | ||
}; | ||
type ModelFieldReferenceAction = 'CASCADE' | 'RESTRICT' | 'SET NULL' | 'SET DEFAULT' | 'NO ACTION'; | ||
type ModelFieldReference = ModelFieldBasics & { | ||
} | { | ||
/** The kind of value that should be stored inside the field. */ | ||
type: 'link'; | ||
/** The target model of the relationship that is being established. */ | ||
target: string; | ||
/** Whether the field should be related to one record, or many records. */ | ||
kind?: 'one' | 'many'; | ||
/** | ||
* If the target record is updated or deleted, the defined actions maybe executed. | ||
*/ | ||
actions?: { | ||
onDelete?: ModelFieldReferenceAction; | ||
onUpdate?: ModelFieldReferenceAction; | ||
onDelete?: ModelFieldLinkAction; | ||
onUpdate?: ModelFieldLinkAction; | ||
}; | ||
}; | ||
type ModelField = ModelFieldNormal | ModelFieldReference; | ||
}); | ||
type ModelIndexField<T extends Array<ModelField> = Array<ModelField>> = { | ||
@@ -274,3 +293,9 @@ /** The collating sequence used for text placed inside the field. */ | ||
interface RoninFields { | ||
/** | ||
* The unique identifier for this record. | ||
*/ | ||
id: string; | ||
/** | ||
* Contains metadata about the record like creation date, last update, etc. | ||
*/ | ||
ronin: string; | ||
@@ -283,7 +308,19 @@ } | ||
interface Model<Fields> extends Omit<PublicModel, 'fields' | 'indexes' | 'triggers' | 'presets'> { | ||
/** | ||
* The fields that make up this model. | ||
*/ | ||
fields?: Fields; | ||
/** | ||
* Predefined query instructions that can be reused across multiple different queries. | ||
*/ | ||
presets?: Record<string, GetInstructions | WithInstruction>; | ||
/** | ||
* Database indexes to optimize query performance. | ||
*/ | ||
indexes?: Array<ModelIndex<Array<ModelField & { | ||
slug: keyof Fields; | ||
}>>>; | ||
/** | ||
* Queries that run automatically in response to other queries. | ||
*/ | ||
triggers?: Array<ModelTrigger<Array<ModelField & { | ||
@@ -293,38 +330,5 @@ slug: keyof Fields; | ||
} | ||
interface FieldGeneric { | ||
/** | ||
* The visual display name of the field. | ||
*/ | ||
name?: string; | ||
} | ||
interface SerializedField<T> { | ||
/** | ||
* Whether the field is required. | ||
*/ | ||
required: boolean; | ||
/** | ||
* The value that the field should provide in the case of a missing value. | ||
*/ | ||
defaultValue?: T | null; | ||
/** | ||
* Whether the field's value should be unique across all records of the model. | ||
*/ | ||
unique: boolean; | ||
/** | ||
* Whether the field's value should be incremented automatically with each new record. | ||
* Only available for number fields. | ||
*/ | ||
increment: T extends number ? boolean : never; | ||
} | ||
interface LinkField { | ||
actions?: { | ||
onDelete?: 'cascade' | 'restrict' | 'set null' | 'no action'; | ||
onUpdate?: 'cascade' | 'restrict' | 'set null' | 'no action'; | ||
}; | ||
model: RoninFields | { | ||
slug: string; | ||
}; | ||
} | ||
interface SerializedLinkField extends Omit<Partial<SerializedField<string>>, 'increment'>, LinkField { | ||
} | ||
type SerializedField<Type> = Partial<Omit<Extract<ModelField, { | ||
type: Type; | ||
}>, 'slug' | 'type'>>; | ||
type FieldsToTypes<F> = F extends Record<string, Primitives> ? { | ||
@@ -372,6 +376,16 @@ [K in keyof F]: F[K] extends Record<string, Primitives> ? FieldsToTypes<F[K]> : F[K]['type'] extends 'string' ? string : F[K]['type'] extends 'number' ? number : F[K]['type'] extends 'boolean' ? boolean : F[K]['type'] extends 'link' ? string : F[K]['type'] extends 'json' ? object : F[K]['type'] extends 'blob' ? Blob : F[K]['type'] extends 'date' ? Date : never; | ||
*/ | ||
declare const blob: (attributes?: Omit<Partial<SerializedField<string>>, "increment"> & FieldGeneric) => { | ||
declare const blob: (attributes?: SerializedField<"blob">) => { | ||
displayAs?: string | undefined; | ||
unique?: boolean | undefined; | ||
required?: boolean | undefined; | ||
defaultValue?: string | null | undefined; | ||
unique?: boolean | undefined; | ||
defaultValue?: unknown; | ||
computedAs?: { | ||
kind: "VIRTUAL" | "STORED"; | ||
value: { | ||
__RONIN_EXPRESSION: string; | ||
}; | ||
} | undefined; | ||
check?: { | ||
__RONIN_EXPRESSION: string; | ||
} | undefined; | ||
name: string | undefined; | ||
@@ -389,6 +403,16 @@ type: "blob"; | ||
*/ | ||
declare const boolean: (attributes?: Omit<Partial<SerializedField<boolean>>, "increment"> & FieldGeneric) => { | ||
declare const boolean: (attributes?: SerializedField<"boolean">) => { | ||
displayAs?: string | undefined; | ||
unique?: boolean | undefined; | ||
required?: boolean | undefined; | ||
defaultValue?: boolean | null | undefined; | ||
unique?: boolean | undefined; | ||
defaultValue?: unknown; | ||
computedAs?: { | ||
kind: "VIRTUAL" | "STORED"; | ||
value: { | ||
__RONIN_EXPRESSION: string; | ||
}; | ||
} | undefined; | ||
check?: { | ||
__RONIN_EXPRESSION: string; | ||
} | undefined; | ||
name: string | undefined; | ||
@@ -406,6 +430,16 @@ type: "boolean"; | ||
*/ | ||
declare const date: (attributes?: Omit<Partial<SerializedField<string | Date>>, "increment"> & FieldGeneric) => { | ||
declare const date: (attributes?: SerializedField<"date">) => { | ||
displayAs?: string | undefined; | ||
unique?: boolean | undefined; | ||
required?: boolean | undefined; | ||
defaultValue?: string | Date | null | undefined; | ||
unique?: boolean | undefined; | ||
defaultValue?: unknown; | ||
computedAs?: { | ||
kind: "VIRTUAL" | "STORED"; | ||
value: { | ||
__RONIN_EXPRESSION: string; | ||
}; | ||
} | undefined; | ||
check?: { | ||
__RONIN_EXPRESSION: string; | ||
} | undefined; | ||
name: string | undefined; | ||
@@ -415,5 +449,2 @@ type: "date"; | ||
interface JsonAttributes { | ||
displayAs?: 'rich-text'; | ||
} | ||
/** | ||
@@ -427,8 +458,17 @@ * Creates a JSON field definition returning an object that includes the field type | ||
*/ | ||
declare const json: (attributes?: Omit<Partial<SerializedField<string | object>>, "increment"> & FieldGeneric & JsonAttributes) => { | ||
declare const json: (attributes?: SerializedField<"json">) => { | ||
unique?: boolean | undefined; | ||
required?: boolean | undefined; | ||
defaultValue?: string | object | null | undefined; | ||
unique?: boolean | undefined; | ||
defaultValue?: unknown; | ||
computedAs?: { | ||
kind: "VIRTUAL" | "STORED"; | ||
value: { | ||
__RONIN_EXPRESSION: string; | ||
}; | ||
} | undefined; | ||
check?: { | ||
__RONIN_EXPRESSION: string; | ||
} | undefined; | ||
name: string | undefined; | ||
displayAs: "rich-text" | undefined; | ||
displayAs: string | undefined; | ||
type: "json"; | ||
@@ -445,6 +485,16 @@ }; | ||
*/ | ||
declare const number: (attributes?: Partial<SerializedField<number>> & FieldGeneric) => { | ||
declare const number: (attributes?: SerializedField<"number">) => { | ||
displayAs?: string | undefined; | ||
unique?: boolean | undefined; | ||
required?: boolean | undefined; | ||
defaultValue?: number | null | undefined; | ||
unique?: boolean | undefined; | ||
defaultValue?: unknown; | ||
computedAs?: { | ||
kind: "VIRTUAL" | "STORED"; | ||
value: { | ||
__RONIN_EXPRESSION: string; | ||
}; | ||
} | undefined; | ||
check?: { | ||
__RONIN_EXPRESSION: string; | ||
} | undefined; | ||
increment?: boolean | undefined; | ||
@@ -463,13 +513,22 @@ name: string | undefined; | ||
*/ | ||
declare const link: (attributes: SerializedLinkField & FieldGeneric) => { | ||
declare const link: (attributes?: SerializedField<"link">) => { | ||
displayAs?: string | undefined; | ||
unique?: boolean | undefined; | ||
required?: boolean | undefined; | ||
defaultValue?: string | null | undefined; | ||
unique?: boolean | undefined; | ||
defaultValue?: unknown; | ||
computedAs?: { | ||
kind: "VIRTUAL" | "STORED"; | ||
value: { | ||
__RONIN_EXPRESSION: string; | ||
}; | ||
} | undefined; | ||
check?: { | ||
__RONIN_EXPRESSION: string; | ||
} | undefined; | ||
kind?: ("one" | "many") | undefined; | ||
name: string | undefined; | ||
model: RoninFields | { | ||
slug: string; | ||
}; | ||
target: string; | ||
actions: { | ||
onDelete?: "cascade" | "restrict" | "set null" | "no action"; | ||
onUpdate?: "cascade" | "restrict" | "set null" | "no action"; | ||
onDelete?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION"; | ||
onUpdate?: "CASCADE" | "RESTRICT" | "SET NULL" | "SET DEFAULT" | "NO ACTION"; | ||
} | undefined; | ||
@@ -479,5 +538,2 @@ type: "link"; | ||
interface StringAttributes { | ||
displayAs?: 'single-line' | 'multi-line' | 'secret'; | ||
} | ||
/** | ||
@@ -491,8 +547,18 @@ * Creates a string field definition returning an object that includes the field type | ||
*/ | ||
declare const string: (attributes?: Omit<Partial<SerializedField<string>>, "increment"> & FieldGeneric & StringAttributes) => { | ||
declare const string: (attributes?: SerializedField<"string">) => { | ||
unique?: boolean | undefined; | ||
required?: boolean | undefined; | ||
defaultValue?: string | null | undefined; | ||
unique?: boolean | undefined; | ||
defaultValue?: unknown; | ||
computedAs?: { | ||
kind: "VIRTUAL" | "STORED"; | ||
value: { | ||
__RONIN_EXPRESSION: string; | ||
}; | ||
} | undefined; | ||
check?: { | ||
__RONIN_EXPRESSION: string; | ||
} | undefined; | ||
collation?: ("BINARY" | "NOCASE" | "RTRIM") | undefined; | ||
name: string | undefined; | ||
displayAs: "single-line" | "multi-line" | "secret"; | ||
displayAs: string; | ||
type: "string"; | ||
@@ -499,0 +565,0 @@ }; |
@@ -919,8 +919,8 @@ var __create = Object.create; | ||
// src/model/primitives/link.ts | ||
var link = (attributes) => { | ||
const { name, model: model2, actions, ...rest } = attributes; | ||
if (!model2) throw new Error("A model is required for a link field"); | ||
var link = (attributes = {}) => { | ||
const { name, target, actions, ...rest } = attributes; | ||
if (!target) throw new Error("A model is required for a link field"); | ||
return { | ||
name, | ||
model: model2, | ||
target, | ||
actions, | ||
@@ -943,2 +943,54 @@ type: "link", | ||
// src/model/utils/errors.ts | ||
var RoninError = class extends Error { | ||
code; | ||
model; | ||
field; | ||
fields; | ||
issues; | ||
constructor(details) { | ||
super(details.message); | ||
this.name = "RoninError"; | ||
this.code = details.code; | ||
this.model = details.model; | ||
this.field = details.field; | ||
this.fields = details.fields; | ||
this.issues = details.issues; | ||
} | ||
}; | ||
var throwForbiddenModelDefinition = (model2) => { | ||
const { fields, indexes, slug } = model2; | ||
for (const [fieldSlug, _field] of Object.entries(fields ?? {})) { | ||
if (fieldSlug === "id") { | ||
throw new RoninError({ | ||
message: 'The field "id" is reserved and cannot be used.', | ||
code: "FIELD_RESERVED", | ||
model: slug, | ||
field: "id" | ||
}); | ||
} | ||
} | ||
if (indexes && indexes.length > 0) { | ||
for (const index of indexes) { | ||
if (index.fields.length === 0) { | ||
throw new RoninError({ | ||
message: "An index must have at least one field.", | ||
code: "INDEX_NO_FIELDS", | ||
model: slug | ||
}); | ||
} | ||
for (const field of index.fields) { | ||
if ("slug" in field && fields && fields[field.slug] === void 0) { | ||
throw new RoninError({ | ||
message: `The field ${field.slug} does not exist in this model.`, | ||
code: "FIELD_NOT_DEFINED", | ||
field: field.slug, | ||
model: slug | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
// node_modules/ronin/node_modules/@ronin/compiler/dist/index.js | ||
@@ -1038,3 +1090,3 @@ var import_cuid2 = __toESM(require_cuid2(), 1); | ||
const { type, unique, defaultValue, required, name } = value; | ||
const { actions, model: model2 } = value; | ||
const { actions, target } = value; | ||
return { | ||
@@ -1046,6 +1098,4 @@ slug: key, | ||
defaultValue, | ||
// @ts-expect-error: The `type` property exists in the model. | ||
type, | ||
// @ts-expect-error: The `target` property exists in the model. | ||
target: model2?.slug, | ||
target, | ||
actions | ||
@@ -1101,14 +1151,3 @@ }; | ||
} = model2; | ||
if (indexes && indexes.length > 0) { | ||
for (const index of indexes) { | ||
if (index.fields.length === 0) { | ||
throw new Error("An index must have at least one field."); | ||
} | ||
for (const field of index.fields) { | ||
if ("slug" in field && fields && fields[field.slug] === void 0) { | ||
throw new Error(`The field ${field.slug} does not exist in this model.`); | ||
} | ||
} | ||
} | ||
} | ||
throwForbiddenModelDefinition(model2); | ||
return { | ||
@@ -1115,0 +1154,0 @@ slug, |
{ | ||
"name": "@ronin/schema", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"type": "module", | ||
@@ -32,3 +32,3 @@ "description": "Allows for defining the schema of a RONIN database in code.", | ||
"@biomejs/biome": "1.9.4", | ||
"@ronin/compiler": "0.13.1", | ||
"@ronin/compiler": "0.13.8", | ||
"@types/bun": "1.1.14", | ||
@@ -35,0 +35,0 @@ "ronin": "5.3.5", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
70873
1697
0