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.
- package/dist/src/node/git.js +1 -1
- package/dist/src/node/index.d.ts +3 -0
- package/dist/src/node/index.js +79 -67
- package/dist/src/node/schema/detached.d.ts +7 -0
- package/dist/src/node/schema/detached.js +24 -0
- package/dist/src/node/schema/dsl/declarations/Decl.d.ts +1 -1
- package/dist/src/node/schema/dsl/types/NestedEntityMapType.d.ts +6 -2
- package/dist/src/node/schema/generatedTypeHelpers.d.ts +14 -1
- package/dist/src/node/schema/index.d.ts +15 -3
- package/dist/src/node/schema/index.js +16 -0
- package/dist/src/node/schema/treeOperations/validation.d.ts +8 -5
- package/dist/src/node/schema/treeOperations/validation.js +108 -34
- package/dist/src/node/transaction.d.ts +1 -2
- package/dist/src/node/transaction.js +13 -15
- package/dist/src/node/utils/childInstances.js +4 -2
- package/dist/src/node/utils/customConstraints.d.ts +5 -3
- package/dist/src/node/utils/customConstraints.js +8 -9
- package/dist/src/node/utils/databaseInMemory.d.ts +21 -21
- package/dist/src/node/utils/databaseInMemory.js +99 -68
- package/dist/src/node/utils/displayName.d.ts +2 -2
- package/dist/src/node/utils/displayName.js +4 -4
- package/dist/src/node/utils/references.js +2 -2
- package/dist/src/node/utils/unique.js +2 -2
- package/dist/src/shared/validation/object.d.ts +1 -0
- package/dist/src/shared/validation/object.js +9 -3
- package/package.json +6 -6
package/dist/src/node/git.js
CHANGED
|
@@ -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.
|
|
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
|
}
|
package/dist/src/node/index.d.ts
CHANGED
|
@@ -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>;
|
package/dist/src/node/index.js
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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 {
|
|
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(
|
|
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
|
|
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 =>
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
177
|
-
const uniqueConstraintResult = checkUniqueConstraintsForAllEntities(
|
|
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
|
|
227
|
+
debug("Skipping further checks due to previous structural integrity errors");
|
|
199
228
|
}
|
|
200
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
14
|
-
getResolvedDeclaration(name:
|
|
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
|
-
|
|
12
|
-
checkTranslations?: ValidationOptions["checkTranslations"];
|
|
13
|
+
validationOptions: ValidationOptions;
|
|
13
14
|
}
|
|
14
|
-
export declare const
|
|
15
|
-
export declare const
|
|
16
|
-
export declare const
|
|
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[];
|