tsondb 0.19.0 → 0.19.1

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.
@@ -35,7 +35,7 @@ export class Git {
35
35
  trackingBranch: status.tracking,
36
36
  commitsAhead: status.ahead,
37
37
  commitsBehind: status.behind,
38
- instances: getAllInstanceOverviewsByEntityName(this.#db.getInstanceContainerOfEntityById.bind(this.#db), this.#db.getAllChildInstanceContainersForParent.bind(this.#db), this.#db.schema.getEntity.bind(this.#db.schema), data, this.#db.locales),
38
+ instances: getAllInstanceOverviewsByEntityName(this.#db.schema.getEntity.bind(this.#db.schema), data, this.#db.locales),
39
39
  latestCommit: await this.#git.revparse(["HEAD"]),
40
40
  };
41
41
  }
@@ -14,6 +14,9 @@ export interface DefaultTSONDBTypes {
14
14
  }
15
15
  export type Entity<T extends DefaultTSONDBTypes, E extends EntityName<T>> = T["entityMap"][E];
16
16
  export type EntityName<T extends DefaultTSONDBTypes> = Extract<keyof T["entityMap"], string>;
17
+ export type EnumName<T extends DefaultTSONDBTypes> = Extract<keyof T["enumMap"], string>;
18
+ export type TypeAliasName<T extends DefaultTSONDBTypes> = Extract<keyof T["typeAliasMap"], string>;
19
+ export type DeclarationName<T extends DefaultTSONDBTypes> = EntityName<T> | EnumName<T> | TypeAliasName<T>;
17
20
  export type ChildEntity<T extends DefaultTSONDBTypes, E extends ChildEntityName<T>> = T["childEntityMap"][E][0];
18
21
  export type ChildEntityConfig<T extends DefaultTSONDBTypes, E extends ChildEntityName<T>> = T["childEntityMap"][E];
19
22
  export type ChildEntityName<T extends DefaultTSONDBTypes> = Extract<keyof T["childEntityMap"], string>;
@@ -1,4 +1,3 @@
1
- import { deepEqual } from "@elyukai/utils/equality";
2
1
  import { Lazy } from "@elyukai/utils/lazy";
3
2
  import { isError } from "@elyukai/utils/result";
4
3
  import Debug from "debug";
@@ -9,14 +8,15 @@ import { styleText } from "node:util";
9
8
  import { simpleGit } from "simple-git";
10
9
  import { parallelizeErrors } from "../shared/utils/validation.js";
11
10
  import { Git } from "./git.js";
11
+ import { getDisplayName, getDisplayNameWithId } from "./schema/detached.js";
12
12
  import {} from "./schema/dsl/index.js";
13
- import { normalizedIdArgs } from "./schema/generatedTypeHelpers.js";
13
+ import {} from "./schema/generatedTypeHelpers.js";
14
14
  import { isEntityDeclWithParentReference } from "./schema/guards.js";
15
15
  import { serializeNode } from "./schema/treeOperations/serialization.js";
16
- import { createValidationContext, validateDecl } from "./schema/treeOperations/validation.js";
16
+ import { createReferenceValidator, validateDeclReferentialIntegrity, validateDeclStructuralIntegrity, } from "./schema/treeOperations/validation.js";
17
17
  import { Transaction } from "./transaction.js";
18
18
  import { checkCustomConstraintsForAllEntities } from "./utils/customConstraints.js";
19
- import { asyncForEachInstanceInDatabaseInMemory, countInstancesInDatabaseInMemory, countInstancesOfEntityInDatabaseInMemory, createDatabaseInMemory, getGroupedInstancesFromDatabaseInMemory, getInstanceOfEntityFromDatabaseInMemory, getInstancesOfEntityFromDatabaseInMemory, hasInstanceOfEntityFromDatabaseInMemory, } from "./utils/databaseInMemory.js";
19
+ import { DatabaseInMemory } from "./utils/databaseInMemory.js";
20
20
  import { applyStepsToDisk } from "./utils/databaseOnDisk.js";
21
21
  import { getAllInstanceOverviewsByEntityName, getDisplayNameFromEntityInstance, getInstanceOverview, getInstanceOverviewsByEntityName, } from "./utils/displayName.js";
22
22
  import { countError, countErrors, getErrorMessageForDisplay, wrapErrorsIfAny, } from "./utils/error.js";
@@ -41,7 +41,7 @@ const getGit = async (dataRootPath) => {
41
41
  const git = simpleGit({ baseDir: dataRootPath });
42
42
  if (await git.checkIsRepo()) {
43
43
  try {
44
- const root = await git.revparse({ "--show-toplevel": null });
44
+ const root = await git.revparse(["--show-toplevel"]);
45
45
  const status = await git.status();
46
46
  return { git, root, status };
47
47
  }
@@ -55,11 +55,11 @@ const getGit = async (dataRootPath) => {
55
55
  };
56
56
  const initData = async (dataRootPath, schema, locales, git, gitStatus) => {
57
57
  debug("loading database into memory ...");
58
- let data = await createDatabaseInMemory(dataRootPath, schema.entities);
58
+ let data = await DatabaseInMemory.load(dataRootPath, schema.entities);
59
59
  debug("done");
60
60
  const localeEntity = schema.localeEntity;
61
61
  if (localeEntity &&
62
- !locales.every(locale => hasInstanceOfEntityFromDatabaseInMemory(data, localeEntity.name, locale))) {
62
+ !locales.every(locale => data.hasInstanceOfEntityById(localeEntity.name, locale))) {
63
63
  throw new Error("All provided locales must exist in the database.");
64
64
  }
65
65
  const serializedDeclarationsByName = Object.fromEntries(schema.resolvedDeclarations.map(decl => [decl.name, serializeNode(decl)]));
@@ -144,25 +144,34 @@ export class TSONDB {
144
144
  async generateOutputs(outputs) {
145
145
  await generateOutputs(this.#schema, outputs);
146
146
  }
147
- /**
148
- * Validates the data in the database.
149
- *
150
- * Returns `true` if the data is valid, `false` otherwise.
151
- */
152
- validate() {
147
+ #strucurallyValidateInstance(entity, instanceContent) {
148
+ const validationContext = {
149
+ validationOptions: this.#validationOptions,
150
+ useStyling: true,
151
+ };
152
+ return validateDeclStructuralIntegrity(validationContext, [], entity, [], instanceContent);
153
+ }
154
+ #validate(data) {
153
155
  const { checkReferentialIntegrity, checkOnlyEntities } = this.#validationOptions;
154
156
  const entities = this.#schema.entities;
157
+ const getEntity = this.#schema.getEntity.bind(this.#schema);
155
158
  for (const onlyEntity of checkOnlyEntities) {
156
159
  if (!entities.find(entity => entity.name === onlyEntity)) {
157
160
  throw new Error(`Entity "${onlyEntity}" not found in schema`);
158
161
  }
159
162
  }
160
- const validationContext = createValidationContext(this.#validationOptions, this.#data, true, checkReferentialIntegrity);
161
- debug("Checking structural integrity ...");
162
- const errors = (checkOnlyEntities.length > 0
163
+ const onlyEntities = checkOnlyEntities.length > 0
163
164
  ? entities.filter(entity => checkOnlyEntities.includes(entity.name))
164
- : entities)
165
- .flatMap(entity => parallelizeErrors(getInstancesOfEntityFromDatabaseInMemory(this.#data, entity.name).map(instance => wrapErrorsIfAny(`in file ${styleText("white", `"${this.#dataRootPath}${sep}${styleText("bold", join(entity.name, getFileNameForId(instance.id)))}"`)}`, validateDecl(validationContext, [], entity, [], instance.content)))))
165
+ : entities;
166
+ const validationContext = {
167
+ validationOptions: this.#validationOptions,
168
+ useStyling: true,
169
+ };
170
+ debug("Checking structural integrity ...");
171
+ const errors = onlyEntities
172
+ .flatMap(entity => parallelizeErrors(data
173
+ .getAllInstanceContainersOfEntity(entity.name)
174
+ .map(instance => wrapErrorsIfAny(`in file ${styleText("white", `"${this.#dataRootPath}${sep}${styleText("bold", join(entity.name, getFileNameForId(instance.id)))}"`)}`, validateDeclStructuralIntegrity(validationContext, [], entity, [], instance.content)))))
166
175
  .toSorted((a, b) => a.message.localeCompare(b.message));
167
176
  if (errors.length > 0) {
168
177
  const errorCount = countErrors(errors);
@@ -172,9 +181,29 @@ export class TSONDB {
172
181
  debug("No structural integrity violations found");
173
182
  }
174
183
  if (errors.length === 0) {
184
+ if (checkReferentialIntegrity) {
185
+ debug("Checking referential integrity ...");
186
+ const referenceValidator = createReferenceValidator(this.#schema.isEntityName.bind(this.#schema), data, true);
187
+ const referenceErrors = onlyEntities
188
+ .flatMap(entity => parallelizeErrors(data
189
+ .getAllInstanceContainersOfEntity(entity.name)
190
+ .map(instance => wrapErrorsIfAny(`in file ${styleText("white", `"${this.#dataRootPath}${sep}${styleText("bold", join(entity.name, getFileNameForId(instance.id)))}"`)}`, validateDeclReferentialIntegrity(validationContext, referenceValidator, [], entity, [], instance.content)))))
191
+ .toSorted((a, b) => a.message.localeCompare(b.message));
192
+ if (referenceErrors.length > 0) {
193
+ const errorCount = countErrors(referenceErrors);
194
+ debug(`${errorCount.toString()} referential integrity violation${errorCount === 1 ? "" : "s"} found`);
195
+ errors.push(...referenceErrors);
196
+ }
197
+ else {
198
+ debug("No referential integrity violations found");
199
+ }
200
+ }
201
+ else {
202
+ debug("Disabled referential integrity checks, skipping them");
203
+ }
175
204
  debug("Checking unique constraints ...");
176
- const instanceOverviewsByEntityName = this.getAllInstanceOverviews();
177
- const uniqueConstraintResult = checkUniqueConstraintsForAllEntities(this.#data, this.#schema.entities, instanceOverviewsByEntityName);
205
+ const instanceOverviewsByEntityName = getAllInstanceOverviewsByEntityName(getEntity, data, this.#locales);
206
+ const uniqueConstraintResult = checkUniqueConstraintsForAllEntities(data, onlyEntities, instanceOverviewsByEntityName);
178
207
  if (isError(uniqueConstraintResult)) {
179
208
  const errorCount = countError(uniqueConstraintResult.error);
180
209
  debug(`${errorCount.toString()} unique constraint violation${errorCount === 1 ? "" : "s"} found`);
@@ -184,7 +213,7 @@ export class TSONDB {
184
213
  debug("No unique constraint violations found");
185
214
  }
186
215
  debug("Checking custom constraints ...");
187
- const customConstraintResult = checkCustomConstraintsForAllEntities(this);
216
+ const customConstraintResult = checkCustomConstraintsForAllEntities((...args) => getDisplayName(getEntity, this.#locales, data, ...args), (...args) => getDisplayNameWithId(getEntity, this.#locales, data, ...args), (...args) => getInstanceOverview(data.getInstanceContainerOfEntityById.bind(data), data.getAllChildInstanceContainersForParent.bind(data, getEntity), getEntity, this.#locales, args), getEntity, data, entities);
188
217
  if (isError(customConstraintResult)) {
189
218
  const errorCount = countError(customConstraintResult.error);
190
219
  debug(`${errorCount.toString()} custom constraint violation${errorCount === 1 ? "" : "s"} found`);
@@ -195,29 +224,35 @@ export class TSONDB {
195
224
  }
196
225
  }
197
226
  else {
198
- debug("Skipping unique constraint checks due to previous structural integrity errors");
227
+ debug("Skipping further checks due to previous structural integrity errors");
199
228
  }
200
- const totalInstanceCount = countInstancesInDatabaseInMemory(this.#data);
201
- console.log(`${totalInstanceCount.toString()} instance${totalInstanceCount === 1 ? "" : "s"} checked`);
229
+ console.log(`${data.totalSize.toString()} instance${data.totalSize === 1 ? "" : "s"} checked`);
202
230
  if (errors.length === 0) {
203
231
  console.log(styleText("green", "All instances are valid"));
204
- return true;
205
232
  }
206
233
  else {
207
234
  const errorCount = countErrors(errors);
208
235
  console.error(styleText("red", `${errorCount.toString()} validation error${errorCount === 1 ? "" : "s"} found\n\n${errors.map(err => getErrorMessageForDisplay(err)).join("\n\n")}`, { stream: stderr }));
209
- return false;
210
236
  }
237
+ return errors;
238
+ }
239
+ /**
240
+ * Validates the data in the database.
241
+ *
242
+ * Returns `true` if the data is valid, `false` otherwise.
243
+ */
244
+ validate() {
245
+ return this.#validate(this.#data).length === 0;
211
246
  }
212
247
  /**
213
248
  * Formats the data on disk according to the current in-memory representation.
214
249
  */
215
250
  async format() {
216
- await asyncForEachInstanceInDatabaseInMemory(this.#data, async (entityName, instance) => {
251
+ await this.#data.forEachInstance(async (entityName, instance) => {
217
252
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
218
253
  const entity = this.#schema.getEntity(entityName);
219
254
  await writeInstance(this.#dataRootPath, entity, instance.id, instance.content);
220
- });
255
+ }, true);
221
256
  }
222
257
  /**
223
258
  * Reloads the data from disk into memory.
@@ -239,20 +274,15 @@ export class TSONDB {
239
274
  data: this.#data,
240
275
  getEntity,
241
276
  referencesToInstances: this.#referencesToInstances,
242
- validationOptions: this.#validationOptions,
277
+ validate: this.#strucurallyValidateInstance.bind(this),
243
278
  localeEntity: this.#schema.localeEntity,
244
279
  steps: [],
245
280
  }));
246
281
  const txtResult = txn.getResult();
247
282
  const { data: newData, referencesToInstances: newRefs, steps } = txtResult;
248
- const instanceOverviewsByEntityName = getAllInstanceOverviewsByEntityName(this.getInstanceContainerOfEntityById.bind(this), this.getAllChildInstanceContainersForParent.bind(this), getEntity, newData, this.#locales);
249
- const uniqueConstraintResult = checkUniqueConstraintsForAllEntities(newData, this.#schema.entities, instanceOverviewsByEntityName);
250
- if (isError(uniqueConstraintResult)) {
251
- throw uniqueConstraintResult.error;
252
- }
253
- const customConstraintResult = checkCustomConstraintsForAllEntities(this);
254
- if (isError(customConstraintResult)) {
255
- throw customConstraintResult.error;
283
+ const errors = this.#validate(newData);
284
+ if (errors.length > 0) {
285
+ throw new AggregateError(errors, "Validation errors occurred");
256
286
  }
257
287
  const diskResult = await applyStepsToDisk(this.#dataRootPath, steps);
258
288
  if (isError(diskResult)) {
@@ -260,8 +290,7 @@ export class TSONDB {
260
290
  }
261
291
  if (this.#git) {
262
292
  const status = await this.#git.client.status();
263
- const repoRoot = await this.#git.client.revparse(["--show-toplevel"]);
264
- const newDbWithUpdatedGit = attachGitStatusToDatabaseInMemory(newData, this.#dataRootPath, repoRoot, status);
293
+ const newDbWithUpdatedGit = attachGitStatusToDatabaseInMemory(newData, this.#dataRootPath, this.#git.root, status);
265
294
  this.#data = newDbWithUpdatedGit;
266
295
  }
267
296
  else {
@@ -308,14 +337,13 @@ export class TSONDB {
308
337
  * Retrieves an instance of the specified entity by its identifier.
309
338
  */
310
339
  getInstanceOfEntityById(...args) {
311
- return this.getInstanceContainerOfEntityById(...args)?.content;
340
+ return this.#data.getInstanceContainerOfEntityById(...args)?.content;
312
341
  }
313
342
  /**
314
343
  * Retrieves the instance container of the specified entity by its identifier.
315
344
  */
316
345
  getInstanceContainerOfEntityById(...args) {
317
- const { entityName, id } = normalizedIdArgs(args);
318
- return getInstanceOfEntityFromDatabaseInMemory(this.#data, entityName, id);
346
+ return this.#data.getInstanceContainerOfEntityById(...args);
319
347
  }
320
348
  /**
321
349
  * Retrieves the overview of an instance of the specified entity by its identifier.
@@ -327,13 +355,13 @@ export class TSONDB {
327
355
  * Retrieves all instances of the specified entity.
328
356
  */
329
357
  getAllInstancesOfEntity(entityName) {
330
- return this.getAllInstanceContainersOfEntity(entityName).map(ic => ic.content);
358
+ return this.#data.getAllInstanceContainersOfEntity(entityName).map(ic => ic.content);
331
359
  }
332
360
  /**
333
361
  * Retrieves all instance containers of the specified entity.
334
362
  */
335
363
  getAllInstanceContainersOfEntity(entityName) {
336
- return getInstancesOfEntityFromDatabaseInMemory(this.#data, entityName);
364
+ return this.#data.getAllInstanceContainersOfEntity(entityName);
337
365
  }
338
366
  /**
339
367
  * Retrieves all instance overviews of the specified entity.
@@ -349,48 +377,31 @@ export class TSONDB {
349
377
  * Counts the number of instances registered for the specified entity.
350
378
  */
351
379
  countInstancesOfEntity(entityName) {
352
- return countInstancesOfEntityInDatabaseInMemory(this.#data, entityName);
380
+ return this.#data.countInstancesOfEntity(entityName);
353
381
  }
354
382
  /**
355
383
  * Retrieves overviews for all instances in the database, grouped by entity name.
356
384
  */
357
385
  getAllInstanceOverviews() {
358
- return getAllInstanceOverviewsByEntityName(this.getInstanceContainerOfEntityById.bind(this), this.getAllChildInstanceContainersForParent.bind(this), this.#schema.getEntity.bind(this.#schema), this.#data, this.#locales);
386
+ return getAllInstanceOverviewsByEntityName(this.#schema.getEntity.bind(this.#schema), this.#data, this.#locales);
359
387
  }
360
388
  /**
361
389
  * Retrieves all instance containers of the specified child entity for a given parent instance.
362
390
  */
363
391
  getAllChildInstanceContainersForParent(childEntityName, parentId) {
364
- const entity = this.#schema.getEntity(childEntityName);
365
- if (!entity || !entity.parentReferenceKey) {
366
- return [];
367
- }
368
- const parentKey = entity.parentReferenceKey;
369
- return getInstancesOfEntityFromDatabaseInMemory(this.#data, childEntityName).filter(instance => deepEqual(instance.content[parentKey], parentId));
392
+ return this.#data.getAllChildInstanceContainersForParent(this.#schema.getEntity.bind(this.#schema), childEntityName, parentId);
370
393
  }
371
394
  /**
372
395
  * Displays the name of an entity instance including its ID. If no display name is found, `undefined` is returned.
373
396
  */
374
397
  getDisplayName(...args) {
375
- const { entityName, id } = normalizedIdArgs(args);
376
- // return instanceOverviewsByEntityName[entityName]?.find(o => o.id === id)?.displayName
377
- const entity = this.#schema.getEntity(entityName);
378
- if (!entity) {
379
- return undefined;
380
- }
381
- const instance = getInstanceOfEntityFromDatabaseInMemory(this.#data, entity.name, id);
382
- if (!instance) {
383
- return undefined;
384
- }
385
- return getDisplayNameFromEntityInstance(entity, instance, this.#schema.getEntity.bind(this.#schema), this.getInstanceContainerOfEntityById.bind(this), this.getAllChildInstanceContainersForParent.bind(this), this.#locales).name;
398
+ return getDisplayName(this.#schema.getEntity.bind(this.#schema), this.#locales, this.#data, ...args);
386
399
  }
387
400
  /**
388
401
  * Displays the name of an entity instance including its ID. If no display name is found, only the ID is returned.
389
402
  */
390
403
  getDisplayNameWithId(...args) {
391
- const { id } = normalizedIdArgs(args);
392
- const displayName = this.getDisplayName(...args);
393
- return displayName ? `"${displayName}" (${id})` : id;
404
+ return getDisplayNameWithId(this.#schema.getEntity.bind(this.#schema), this.#locales, this.#data, ...args);
394
405
  }
395
406
  /**
396
407
  * Checks if a given instance is referenced by other instances in the database.
@@ -442,7 +453,8 @@ export class TSONDB {
442
453
  : 0;
443
454
  }, } = options;
444
455
  const lowerCaseQuery = query.toLowerCase();
445
- return getGroupedInstancesFromDatabaseInMemory(this.#data)
456
+ return this.#data
457
+ .getAllInstances()
446
458
  .flatMap(([entityName, instances]) => {
447
459
  const entity = this.#schema.getEntity(entityName);
448
460
  if (entity && isEntityDeclWithParentReference(entity)) {
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Standalone functions for database operations without needing a full TSONDB instance.
3
+ */
4
+ import { type DatabaseInMemory } from "../utils/databaseInMemory.ts";
5
+ import { type AnyEntityMap, type GetEntityByName, type IdArgsVariant } from "./generatedTypeHelpers.ts";
6
+ export declare const getDisplayName: <EM extends AnyEntityMap>(getEntity: GetEntityByName<EM>, locales: string[], data: DatabaseInMemory<EM>, ...args: IdArgsVariant<EM>) => string | undefined;
7
+ export declare const getDisplayNameWithId: <EM extends AnyEntityMap>(getEntity: GetEntityByName<EM>, locales: string[], data: DatabaseInMemory<EM>, ...args: IdArgsVariant<EM>) => string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Standalone functions for database operations without needing a full TSONDB instance.
3
+ */
4
+ import {} from "../utils/databaseInMemory.js";
5
+ import { getDisplayNameFromEntityInstance } from "../utils/displayName.js";
6
+ import { normalizedIdArgs, } from "./generatedTypeHelpers.js";
7
+ export const getDisplayName = (getEntity, locales, data, ...args) => {
8
+ const { entityName, id } = normalizedIdArgs(args);
9
+ // return instanceOverviewsByEntityName[entityName]?.find(o => o.id === id)?.displayName
10
+ const entity = getEntity(entityName);
11
+ if (!entity) {
12
+ return undefined;
13
+ }
14
+ const instance = data.getInstanceContainerOfEntityById(entity.name, id);
15
+ if (!instance) {
16
+ return undefined;
17
+ }
18
+ return getDisplayNameFromEntityInstance(entity, instance, getEntity, data.getInstanceContainerOfEntityById.bind(data), data.getAllChildInstanceContainersForParent.bind(data, getEntity), locales).name;
19
+ };
20
+ export const getDisplayNameWithId = (getEntity, locales, data, ...args) => {
21
+ const { id } = normalizedIdArgs(args);
22
+ const displayName = getDisplayName(getEntity, locales, data, ...args);
23
+ return displayName ? `"${displayName}" (${id})` : id;
24
+ };
@@ -11,7 +11,7 @@ export type TypeArguments<Params extends TypeParameter[]> = {
11
11
  };
12
12
  export declare const getParameterNames: (decl: Decl) => string[];
13
13
  export declare const getTypeArgumentsRecord: <Params extends TypeParameter[]>(decl: DeclP<Params>, args: TypeArguments<Params>) => Record<string, Type>;
14
- export type Decl = EntityDecl | EnumDecl | TypeAliasDecl;
14
+ export type Decl<Name extends string = string> = EntityDecl<Name> | EnumDecl<Name> | TypeAliasDecl<Name>;
15
15
  export type DeclP<Params extends TypeParameter[] = TypeParameter[]> = EntityDecl | EnumDecl<string, Record<string, EnumCaseDecl>, Params> | TypeAliasDecl<string, Type, Params>;
16
16
  export type IncludableDeclP<Params extends TypeParameter[] = TypeParameter[]> = EnumDecl<string, Record<string, EnumCaseDecl>, Params> | TypeAliasDecl<string, Type, Params>;
17
17
  export type SecondaryDecl = EnumDecl | TypeAliasDecl;
@@ -17,6 +17,8 @@ export interface NestedEntityMapType<Name extends string = string, T extends TCo
17
17
  comment?: string;
18
18
  secondaryEntity: EntityDecl;
19
19
  type: Lazy<PossibleType<T>>;
20
+ minProperties?: number;
21
+ maxProperties?: number;
20
22
  }
21
23
  export declare const NestedEntityMapType: <Name extends string, T extends TConstraint>(options: {
22
24
  name: Name;
@@ -24,7 +26,8 @@ export declare const NestedEntityMapType: <Name extends string, T extends TConst
24
26
  comment?: string;
25
27
  secondaryEntity: EntityDecl;
26
28
  type: PossibleType<T>;
27
- isDeprecated?: boolean;
29
+ minProperties?: number;
30
+ maxProperties?: number;
28
31
  }) => NestedEntityMapType<Name, T>;
29
32
  export { NestedEntityMapType as NestedEntityMap };
30
33
  export declare const _NestedEntityMapType: <Name extends string, T extends TConstraint>(options: {
@@ -33,6 +36,7 @@ export declare const _NestedEntityMapType: <Name extends string, T extends TCons
33
36
  comment?: string;
34
37
  secondaryEntity: EntityDecl;
35
38
  type: () => PossibleType<T>;
36
- isDeprecated?: boolean;
39
+ minProperties?: number;
40
+ maxProperties?: number;
37
41
  }) => NestedEntityMapType<Name, T>;
38
42
  export declare const isNestedEntityMapType: (node: Node) => node is NestedEntityMapType;
@@ -4,7 +4,8 @@
4
4
  */
5
5
  import { ENUM_DISCRIMINATOR_KEY } from "../../shared/schema/declarations/EnumDecl.ts";
6
6
  import type { DisplayNameResult } from "../../shared/utils/displayName.ts";
7
- import type { InstanceContainer, InstanceContent } from "../../shared/utils/instances.ts";
7
+ import type { InstanceContainer, InstanceContainerOverview, InstanceContent } from "../../shared/utils/instances.ts";
8
+ import type { DeclarationName, DefaultTSONDBTypes } from "../index.ts";
8
9
  import type { EntityDecl } from "./dsl/declarations/EntityDecl.ts";
9
10
  export interface Register {
10
11
  }
@@ -89,6 +90,10 @@ export type GetInstanceById<T extends AnyEntityMap = RegisteredEntityMap> = <E e
89
90
  * A function that retrieves an instance container by its entity and identifier.
90
91
  */
91
92
  export type GetInstanceContainerById<T extends AnyEntityMap = RegisteredEntityMap> = <E extends Extract<keyof T, string> = Extract<keyof T, string>>(...args: IdArgsVariant<T, E>) => InstanceContainer<T[E]> | undefined;
93
+ /**
94
+ * A function that retrieves an instance container by its entity and identifier.
95
+ */
96
+ export type GetInstanceOverviewOfEntityById<T extends AnyEntityMap = RegisteredEntityMap> = <E extends Extract<keyof T, string> = Extract<keyof T, string>>(...args: IdArgsVariant<T, E>) => InstanceContainerOverview | undefined;
92
97
  /**
93
98
  * A function that retrieves all instances of a given entity.
94
99
  */
@@ -121,4 +126,12 @@ export type GetDisplayNameAndId<T extends AnyEntityMap = RegisteredEntityMap> =
121
126
  * A function that retrieves an entity declaration by its name.
122
127
  */
123
128
  export type GetEntityByName<T extends AnyEntityMap = RegisteredEntityMap> = <E extends Extract<keyof T, string> = Extract<keyof T, string>>(name: E) => EntityDecl<E> | undefined;
129
+ /**
130
+ * A type guard function that checks if a given string is a valid declaration name.
131
+ */
132
+ export type DeclarationNameGuard<T extends DefaultTSONDBTypes> = (name: string) => name is DeclarationName<T>;
133
+ /**
134
+ * A type guard function that checks if a given string is a valid entity name.
135
+ */
136
+ export type EntityNameGuard<T extends AnyEntityMap = RegisteredEntityMap> = (name: string) => name is Extract<keyof T, string>;
124
137
  export {};
@@ -1,4 +1,4 @@
1
- import type { DefaultTSONDBTypes, EntityName } from "../index.ts";
1
+ import type { DeclarationName, DefaultTSONDBTypes, EntityName } from "../index.ts";
2
2
  import type { Decl } from "./dsl/declarations/Decl.ts";
3
3
  import type { EntityDecl } from "./dsl/declarations/EntityDecl.ts";
4
4
  export declare class Schema<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> {
@@ -10,7 +10,19 @@ export declare class Schema<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> {
10
10
  getResolvedEntity<E extends EntityName<T>>(name: E): EntityDecl<E> | undefined;
11
11
  get declarations(): Decl[];
12
12
  get resolvedDeclarations(): Decl[];
13
- getDeclaration(name: string): Decl | undefined;
14
- getResolvedDeclaration(name: string): Decl | undefined;
13
+ getDeclaration<E extends DeclarationName<T>>(name: E): Decl<E> | undefined;
14
+ getResolvedDeclaration<E extends DeclarationName<T>>(name: E): Decl<E> | undefined;
15
15
  get localeEntity(): EntityDecl<EntityName<T>> | undefined;
16
+ /**
17
+ * Checks if the given name is a valid entity name in the schema.
18
+ *
19
+ * This includes all entity declarations, but not nested entity declarations, as they cannot be referenced directly by their name.
20
+ */
21
+ isEntityName(name: string): name is EntityName<T>;
22
+ /**
23
+ * Checks if the given name is a valid declaration name in the schema. This includes entity declarations, enum declarations and type alias declarations.
24
+ *
25
+ * Note that this does not include nested entity declarations, as they cannot be referenced directly by their name.
26
+ */
27
+ isDeclarationName(name: string): name is DeclarationName<T>;
16
28
  }
@@ -91,6 +91,22 @@ export class Schema {
91
91
  get localeEntity() {
92
92
  return this.#localeEntity;
93
93
  }
94
+ /**
95
+ * Checks if the given name is a valid entity name in the schema.
96
+ *
97
+ * This includes all entity declarations, but not nested entity declarations, as they cannot be referenced directly by their name.
98
+ */
99
+ isEntityName(name) {
100
+ return this.#entities.some(entity => entity.name === name);
101
+ }
102
+ /**
103
+ * Checks if the given name is a valid declaration name in the schema. This includes entity declarations, enum declarations and type alias declarations.
104
+ *
105
+ * Note that this does not include nested entity declarations, as they cannot be referenced directly by their name.
106
+ */
107
+ isDeclarationName(name) {
108
+ return this.#declarationMap.has(name);
109
+ }
94
110
  }
95
111
  const checkDuplicateIdentifier = (existingDecls, decl) => {
96
112
  const existingDeclWithSameName = existingDecls
@@ -2,15 +2,18 @@ import type { ValidationOptions } from "../../index.ts";
2
2
  import { type DatabaseInMemory } from "../../utils/databaseInMemory.ts";
3
3
  import { type Decl } from "../dsl/declarations/Decl.ts";
4
4
  import type { Type } from "../dsl/index.ts";
5
+ import type { AnyEntityMap } from "../generatedTypeHelpers.ts";
5
6
  export type IdentifierToCheck = {
6
7
  name: string;
7
8
  value: unknown;
8
9
  };
10
+ export type ReferenceValidator = (entityName: string, instanceId: unknown) => ReferenceError[];
9
11
  export interface ValidationContext {
10
12
  useStyling: boolean;
11
- checkReferentialIntegrity: (identifier: IdentifierToCheck) => Error[];
12
- checkTranslations?: ValidationOptions["checkTranslations"];
13
+ validationOptions: ValidationOptions;
13
14
  }
14
- export declare const createValidationContext: (options: Partial<ValidationOptions>, databaseInMemory: DatabaseInMemory, useStyling: boolean, checkReferentialIntegrity?: boolean) => ValidationContext;
15
- export declare const validateDecl: (helpers: ValidationContext, inDecls: Decl[], decl: Decl, typeArgs: Type[], value: unknown) => Error[];
16
- export declare const validateType: (helpers: ValidationContext, inDecls: Decl[], type: Type, value: unknown) => Error[];
15
+ export declare const createReferenceValidator: <EM extends AnyEntityMap>(isEntityName: (name: string) => name is Extract<keyof EM, string>, databaseInMemory: DatabaseInMemory<EM>, useStyling: boolean) => ReferenceValidator;
16
+ export declare const validateDeclStructuralIntegrity: (helpers: ValidationContext, inDecls: Decl[], decl: Decl, typeArgs: Type[], value: unknown) => TypeError[];
17
+ export declare const validateTypeStructuralIntegrity: (helpers: ValidationContext, inDecls: Decl[], type: Type, value: unknown) => TypeError[];
18
+ export declare const validateDeclReferentialIntegrity: (helpers: ValidationContext, checkReferentialIntegrity: ReferenceValidator, inDecls: Decl[], decl: Decl, typeArgs: Type[], value: unknown) => ReferenceError[];
19
+ export declare const validateTypeReferentialIntegrity: (helpers: ValidationContext, checkReferentialIntegrity: ReferenceValidator, inDecls: Decl[], type: Type, value: unknown) => ReferenceError[];