tsondb 0.21.11 → 0.23.0
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/config.d.ts +3 -6
- package/dist/src/node/index.d.ts +12 -132
- package/dist/src/node/index.js +66 -521
- package/dist/src/node/readonly.d.ts +158 -0
- package/dist/src/node/readonly.js +321 -0
- package/dist/src/node/server/index.d.ts +6 -5
- package/dist/src/node/server/index.js +7 -3
- package/dist/src/node/shared.d.ts +46 -0
- package/dist/src/node/shared.js +267 -0
- package/dist/src/node/utils/databaseInMemory.d.ts +2 -0
- package/dist/src/node/utils/databaseInMemory.js +10 -0
- package/package.json +1 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { InstanceContainer, InstanceContainerOverview, SingletonInstanceContainer } from "../shared/utils/instances.ts";
|
|
2
|
+
import type { FormatterOptions } from "./config.ts";
|
|
3
|
+
import { type ChildEntity, type ChildEntityConfig, type ChildEntityName, type DefaultTSONDBTypes, type Entity, type EntityName, type SingletonEntityName, type TSONDBOptions, type ValidationOptions } from "./index.ts";
|
|
4
|
+
import type { Output } from "./output.ts";
|
|
5
|
+
import type { EntityDecl } from "./schema/dsl/index.ts";
|
|
6
|
+
import type { IdArgsVariant } from "./schema/generatedTypeHelpers.ts";
|
|
7
|
+
import type { Schema } from "./schema/index.ts";
|
|
8
|
+
import { DatabaseInMemory } from "./utils/databaseInMemory.ts";
|
|
9
|
+
import { type ReferencesToInstances } from "./utils/references.ts";
|
|
10
|
+
/**
|
|
11
|
+
* The main class for managing a typed JSON database that is read-only.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ReadonlyTSONDB<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> {
|
|
14
|
+
#private;
|
|
15
|
+
protected _data: DatabaseInMemory<T["entityMap"], T["singletonEntityMap"]>;
|
|
16
|
+
protected _referencesToInstances: ReferencesToInstances;
|
|
17
|
+
protected _validationOptions: ValidationOptions;
|
|
18
|
+
protected _formatterOptions: Partial<FormatterOptions> | undefined;
|
|
19
|
+
protected constructor(options: {
|
|
20
|
+
dataRootPath: string;
|
|
21
|
+
data: DatabaseInMemory<T["entityMap"], T["singletonEntityMap"]>;
|
|
22
|
+
schema: Schema<T>;
|
|
23
|
+
locales?: string[];
|
|
24
|
+
referencesToInstances: ReferencesToInstances;
|
|
25
|
+
validationOptions: ValidationOptions;
|
|
26
|
+
formatterOptions?: Partial<FormatterOptions> | undefined;
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new TSONDB instance with the specified options.
|
|
30
|
+
*
|
|
31
|
+
* @param options The options for creating the TSONDB instance.
|
|
32
|
+
* @param skipReferenceCache If set to `true`, the reference cache will not be created during initialization. This can speed up the initialization process, but referential integrity checks will not work correctly until the cache is created. This should only be set to `true` if you plan to call `sync()` immediately after initialization.
|
|
33
|
+
*/
|
|
34
|
+
static create<Types extends DefaultTSONDBTypes = DefaultTSONDBTypes>(options: TSONDBOptions<Types>, skipReferenceCache?: boolean): Promise<ReadonlyTSONDB<Types>>;
|
|
35
|
+
/**
|
|
36
|
+
* Generates outputs based on the provided schema and output configurations.
|
|
37
|
+
*/
|
|
38
|
+
static generateOutputs(options: {
|
|
39
|
+
schema: Schema;
|
|
40
|
+
outputs: Output[];
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Generates outputs based on the current schema and the provided output configurations.
|
|
44
|
+
*/
|
|
45
|
+
generateOutputs(outputs: Output[]): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Validates the data in the database.
|
|
48
|
+
*
|
|
49
|
+
* Returns `true` if the data is valid, `false` otherwise.
|
|
50
|
+
*/
|
|
51
|
+
validate(): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Checks if the data on disk is formatted according to the current in-memory representation.
|
|
54
|
+
*/
|
|
55
|
+
checkFormat(): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Reloads the data from disk into memory.
|
|
58
|
+
*/
|
|
59
|
+
sync(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Retrieves an instance of the specified entity by its identifier.
|
|
62
|
+
*/
|
|
63
|
+
getInstanceOfEntityById<E extends EntityName<T>>(...args: IdArgsVariant<T["entityMap"], E>): T["entityMap"][E] | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves the instance container of the specified entity by its identifier.
|
|
66
|
+
*/
|
|
67
|
+
getInstanceContainerOfEntityById<E extends EntityName<T>>(...args: IdArgsVariant<T["entityMap"], E>): InstanceContainer<T["entityMap"][E]> | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Retrieves the overview of an instance of the specified entity by its identifier.
|
|
70
|
+
*/
|
|
71
|
+
getInstanceOverviewOfEntityById(...args: IdArgsVariant<T["entityMap"]>): InstanceContainerOverview | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* Retrieves all instances of the specified entity.
|
|
74
|
+
*/
|
|
75
|
+
getAllInstancesOfEntity<E extends EntityName<T>>(entityName: E): Entity<T, E>[];
|
|
76
|
+
/**
|
|
77
|
+
* Retrieves all instance containers of the specified entity.
|
|
78
|
+
*/
|
|
79
|
+
getAllInstanceContainersOfEntity<E extends EntityName<T>>(entityName: E): InstanceContainer<Entity<T, E>>[];
|
|
80
|
+
/**
|
|
81
|
+
* Retrieves all instance overviews of the specified entity.
|
|
82
|
+
*/
|
|
83
|
+
getAllInstanceOverviewsOfEntity(entity: EntityDecl<EntityName<T>> | EntityName<T>): InstanceContainerOverview[];
|
|
84
|
+
/**
|
|
85
|
+
* Retrieves an instance of the specified entity by its identifier.
|
|
86
|
+
*/
|
|
87
|
+
getSingletonInstanceOfEntity<E extends SingletonEntityName<T>>(entityName: E): T["singletonEntityMap"][E] | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Retrieves the instance container of the specified entity by its identifier.
|
|
90
|
+
*/
|
|
91
|
+
getSingletonInstanceContainerOfEntity<E extends SingletonEntityName<T>>(entityName: E): SingletonInstanceContainer<T["singletonEntityMap"][E]> | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Counts the number of instances registered for the specified entity.
|
|
94
|
+
*/
|
|
95
|
+
countInstancesOfEntity(entityName: EntityName<T>): number;
|
|
96
|
+
/**
|
|
97
|
+
* Returns whether the singleton entity has an instance.
|
|
98
|
+
*/
|
|
99
|
+
hasInstanceOfSingletonEntity(entityName: SingletonEntityName<T>): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Retrieves overviews for all instances in the database, grouped by entity name.
|
|
102
|
+
*/
|
|
103
|
+
getAllInstanceOverviews(): Record<EntityName<T>, InstanceContainerOverview[]>;
|
|
104
|
+
/**
|
|
105
|
+
* Retrieves all instance containers of the specified child entity for a given parent instance.
|
|
106
|
+
*/
|
|
107
|
+
getAllChildInstanceContainersForParent<U extends ChildEntityName<T>>(childEntityName: U, parentId: ChildEntityConfig<T, U>[2]): InstanceContainer<ChildEntity<T, U>>[];
|
|
108
|
+
/**
|
|
109
|
+
* Displays the name of an entity instance including its ID. If no display name is found, `undefined` is returned.
|
|
110
|
+
*/
|
|
111
|
+
getDisplayName(...args: IdArgsVariant<T["entityMap"]>): string | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* Displays the name of an entity instance including its ID. If no display name is found, only the ID is returned.
|
|
114
|
+
*/
|
|
115
|
+
getDisplayNameWithId(...args: IdArgsVariant<T["entityMap"]>): string;
|
|
116
|
+
/**
|
|
117
|
+
* Checks if a given instance is referenced by other instances in the database.
|
|
118
|
+
*
|
|
119
|
+
* If `otherInstancesToDelete` is provided, those instances will be excluded from the check. This can be useful when planning to delete multiple instances at once and wanting to ignore references from those instances.
|
|
120
|
+
*/
|
|
121
|
+
isReferencedByOtherInstances(instance: string, otherInstancesToDelete?: string[]): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* The configured locales for this TSONDB instance.
|
|
124
|
+
*/
|
|
125
|
+
get locales(): string[];
|
|
126
|
+
/**
|
|
127
|
+
* Sets the locales for this TSONDB instance.
|
|
128
|
+
*
|
|
129
|
+
* The provided locales must exist in the database if a locale entity is defined in the schema.
|
|
130
|
+
*/
|
|
131
|
+
setLocales(locales: string[]): void;
|
|
132
|
+
/**
|
|
133
|
+
* The loaded schema for this TSONDB instance.
|
|
134
|
+
*/
|
|
135
|
+
get schema(): Schema<T>;
|
|
136
|
+
get dataRootPath(): string;
|
|
137
|
+
/**
|
|
138
|
+
* Searches for instances matching the provided query in their ID or display name.
|
|
139
|
+
*
|
|
140
|
+
* The results are sorted by relevance. A custom relevance score function can be provided.
|
|
141
|
+
*/
|
|
142
|
+
search(query: string, options?: Partial<{
|
|
143
|
+
getRelevanceScore: <E extends EntityName<T>>(args: {
|
|
144
|
+
entityName: E;
|
|
145
|
+
id: string;
|
|
146
|
+
content: Entity<T, E>;
|
|
147
|
+
displayName: string;
|
|
148
|
+
displayNameLocaleId: string | undefined;
|
|
149
|
+
query: string;
|
|
150
|
+
queryLowerCase: string;
|
|
151
|
+
}) => number;
|
|
152
|
+
}>): [string, InstanceContainerOverview][];
|
|
153
|
+
/**
|
|
154
|
+
* Retrieves the name of the entity for a given instance ID. If no instance with the provided ID is found, `undefined` is returned.
|
|
155
|
+
*/
|
|
156
|
+
getEntityNameOfInstanceId(id: string): string | undefined;
|
|
157
|
+
}
|
|
158
|
+
export type { Config } from "./config.ts";
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { on } from "@elyukai/utils/function";
|
|
2
|
+
import { compareNumber, reduceCompare } from "@elyukai/utils/ordering";
|
|
3
|
+
import Debug from "debug";
|
|
4
|
+
import { stderr } from "node:process";
|
|
5
|
+
import { styleText } from "node:util";
|
|
6
|
+
import { TSONDB, } from "./index.js";
|
|
7
|
+
import { getDisplayName, getDisplayNameWithId } from "./schema/detached.js";
|
|
8
|
+
import { isEntityDeclWithParentReference } from "./schema/guards.js";
|
|
9
|
+
import { checkLocales, generateOutputs, initData, prepareFolders, validate } from "./shared.js";
|
|
10
|
+
import { DatabaseInMemory } from "./utils/databaseInMemory.js";
|
|
11
|
+
import { getAllInstanceOverviewsByEntityName, getDisplayNameFromEntityInstance, getInstanceOverview, getInstanceOverviewsByEntityName, } from "./utils/displayName.js";
|
|
12
|
+
import { formatInstance, readInstance, readSingletonInstance } from "./utils/files.js";
|
|
13
|
+
import { isReferencedByOtherInstances } from "./utils/references.js";
|
|
14
|
+
const debug = Debug("tsondb:readonly");
|
|
15
|
+
/**
|
|
16
|
+
* The main class for managing a typed JSON database that is read-only.
|
|
17
|
+
*/
|
|
18
|
+
export class ReadonlyTSONDB {
|
|
19
|
+
_data;
|
|
20
|
+
#dataRootPath;
|
|
21
|
+
#schema;
|
|
22
|
+
#locales;
|
|
23
|
+
_referencesToInstances;
|
|
24
|
+
_validationOptions;
|
|
25
|
+
_formatterOptions;
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.#dataRootPath = options.dataRootPath;
|
|
28
|
+
this._data = options.data;
|
|
29
|
+
this.#schema = options.schema;
|
|
30
|
+
this.#locales = options.locales ?? [];
|
|
31
|
+
this._referencesToInstances = options.referencesToInstances;
|
|
32
|
+
this._validationOptions = options.validationOptions;
|
|
33
|
+
this._formatterOptions = options.formatterOptions;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new TSONDB instance with the specified options.
|
|
37
|
+
*
|
|
38
|
+
* @param options The options for creating the TSONDB instance.
|
|
39
|
+
* @param skipReferenceCache If set to `true`, the reference cache will not be created during initialization. This can speed up the initialization process, but referential integrity checks will not work correctly until the cache is created. This should only be set to `true` if you plan to call `sync()` immediately after initialization.
|
|
40
|
+
*/
|
|
41
|
+
static async create(options, skipReferenceCache = false) {
|
|
42
|
+
const { entities } = options.schema;
|
|
43
|
+
debug("using data root path: %s", options.dataRootPath);
|
|
44
|
+
await prepareFolders(options.schema.version, options.dataRootPath, entities);
|
|
45
|
+
debug("prepared folders");
|
|
46
|
+
const { localeEntity } = options.schema;
|
|
47
|
+
if (!localeEntity && options.locales) {
|
|
48
|
+
throw new Error("Schema does not contain a locale entity, locales cannot be provided.");
|
|
49
|
+
}
|
|
50
|
+
const { data, referencesToInstances } = await initData(options.dataRootPath, options.schema, options.locales ?? [], undefined, undefined, skipReferenceCache, true);
|
|
51
|
+
const db = new ReadonlyTSONDB({
|
|
52
|
+
...options,
|
|
53
|
+
data,
|
|
54
|
+
referencesToInstances,
|
|
55
|
+
validationOptions: {
|
|
56
|
+
checkReferentialIntegrity: true,
|
|
57
|
+
checkOnlyEntities: [],
|
|
58
|
+
...options.validationOptions,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
return db;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Generates outputs based on the provided schema and output configurations.
|
|
65
|
+
*/
|
|
66
|
+
static async generateOutputs(options) {
|
|
67
|
+
await generateOutputs(options.schema, options.outputs);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Generates outputs based on the current schema and the provided output configurations.
|
|
71
|
+
*/
|
|
72
|
+
async generateOutputs(outputs) {
|
|
73
|
+
await generateOutputs(this.#schema, outputs);
|
|
74
|
+
}
|
|
75
|
+
#validate(data, options = {}) {
|
|
76
|
+
return validate({
|
|
77
|
+
data,
|
|
78
|
+
dataRootPath: this.#dataRootPath,
|
|
79
|
+
locales: this.#locales,
|
|
80
|
+
schema: this.#schema,
|
|
81
|
+
validationOptions: this._validationOptions,
|
|
82
|
+
}, options);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validates the data in the database.
|
|
86
|
+
*
|
|
87
|
+
* Returns `true` if the data is valid, `false` otherwise.
|
|
88
|
+
*/
|
|
89
|
+
validate() {
|
|
90
|
+
debug("Validating database ...");
|
|
91
|
+
return this.#validate(this._data).length === 0;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Checks if the data on disk is formatted according to the current in-memory representation.
|
|
95
|
+
*/
|
|
96
|
+
async checkFormat() {
|
|
97
|
+
debug("Check database formatting ...");
|
|
98
|
+
let counter = 0;
|
|
99
|
+
let total = 0;
|
|
100
|
+
await this._data.forEachInstance(async (entityName, instance) => {
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
102
|
+
const entity = this.#schema.getEntity(entityName);
|
|
103
|
+
const instanceOnDisk = await readInstance(this.#dataRootPath, entity, instance.id);
|
|
104
|
+
const currentFormatted = formatInstance(entity, instance.content, this._formatterOptions);
|
|
105
|
+
if (instanceOnDisk !== currentFormatted) {
|
|
106
|
+
console.log(`Instance ${styleText("yellow", instance.id)} of entity ${styleText("cyan", entityName)} is not formatted correctly.`);
|
|
107
|
+
counter++;
|
|
108
|
+
}
|
|
109
|
+
total++;
|
|
110
|
+
}, true);
|
|
111
|
+
await this._data.forEachSingletonInstance(async (entityName, instance) => {
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
113
|
+
const entity = this.#schema.getSingletonEntity(entityName);
|
|
114
|
+
const instanceOnDisk = await readSingletonInstance(this.#dataRootPath, entity);
|
|
115
|
+
const currentFormatted = formatInstance(entity, instance.content, this._formatterOptions);
|
|
116
|
+
if (instanceOnDisk !== currentFormatted) {
|
|
117
|
+
console.log(`Singleton instance of entity ${styleText("cyan", entityName)} is not formatted correctly.`);
|
|
118
|
+
counter++;
|
|
119
|
+
}
|
|
120
|
+
total++;
|
|
121
|
+
}, true);
|
|
122
|
+
if (counter === 0) {
|
|
123
|
+
console.log(styleText("green", `All ${total.toFixed()} instance${total === 1 ? "" : "s"} are formatted correctly`));
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.error(styleText("red", `${counter.toFixed()} instance${counter === 1 ? "" : "s"} of ${total.toFixed()} instance${total === 1 ? "" : "s"} are not formatted correctly`, {
|
|
128
|
+
stream: stderr,
|
|
129
|
+
}));
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Reloads the data from disk into memory.
|
|
135
|
+
*/
|
|
136
|
+
async sync() {
|
|
137
|
+
debug("Syncing with disk ...");
|
|
138
|
+
const { data, referencesToInstances } = await initData(this.#dataRootPath, this.#schema, this.#locales, undefined, undefined, false, !(this instanceof TSONDB));
|
|
139
|
+
this._data = data;
|
|
140
|
+
this._referencesToInstances = referencesToInstances;
|
|
141
|
+
debug("Synced with disk");
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Retrieves an instance of the specified entity by its identifier.
|
|
145
|
+
*/
|
|
146
|
+
getInstanceOfEntityById(...args) {
|
|
147
|
+
return this._data.getInstanceContainerOfEntityById(...args)?.content;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Retrieves the instance container of the specified entity by its identifier.
|
|
151
|
+
*/
|
|
152
|
+
getInstanceContainerOfEntityById(...args) {
|
|
153
|
+
return this._data.getInstanceContainerOfEntityById(...args);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Retrieves the overview of an instance of the specified entity by its identifier.
|
|
157
|
+
*/
|
|
158
|
+
getInstanceOverviewOfEntityById(...args) {
|
|
159
|
+
return getInstanceOverview(this.getInstanceContainerOfEntityById.bind(this), this.getAllChildInstanceContainersForParent.bind(this), this.#schema.getEntity.bind(this.#schema), this.#locales, args);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Retrieves all instances of the specified entity.
|
|
163
|
+
*/
|
|
164
|
+
getAllInstancesOfEntity(entityName) {
|
|
165
|
+
return this._data.getAllInstanceContainersOfEntity(entityName).map(ic => ic.content);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Retrieves all instance containers of the specified entity.
|
|
169
|
+
*/
|
|
170
|
+
getAllInstanceContainersOfEntity(entityName) {
|
|
171
|
+
return this._data.getAllInstanceContainersOfEntity(entityName);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Retrieves all instance overviews of the specified entity.
|
|
175
|
+
*/
|
|
176
|
+
getAllInstanceOverviewsOfEntity(entity) {
|
|
177
|
+
const normEntity = typeof entity === "object" ? entity : this.#schema.getEntity(entity);
|
|
178
|
+
if (!normEntity) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
return getInstanceOverviewsByEntityName(this.getInstanceContainerOfEntityById.bind(this), this.getAllChildInstanceContainersForParent.bind(this), this.#schema.getEntity.bind(this.#schema), normEntity, this.getAllInstanceContainersOfEntity(normEntity.name), this.#locales);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Retrieves an instance of the specified entity by its identifier.
|
|
185
|
+
*/
|
|
186
|
+
getSingletonInstanceOfEntity(entityName) {
|
|
187
|
+
return this._data.getSingletonInstanceOfEntity(entityName);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Retrieves the instance container of the specified entity by its identifier.
|
|
191
|
+
*/
|
|
192
|
+
getSingletonInstanceContainerOfEntity(entityName) {
|
|
193
|
+
return this._data.getSingletonInstanceContainerOfEntity(entityName);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Counts the number of instances registered for the specified entity.
|
|
197
|
+
*/
|
|
198
|
+
countInstancesOfEntity(entityName) {
|
|
199
|
+
return this._data.countInstancesOfEntity(entityName);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Returns whether the singleton entity has an instance.
|
|
203
|
+
*/
|
|
204
|
+
hasInstanceOfSingletonEntity(entityName) {
|
|
205
|
+
return this._data.hasInstanceOfSingletonEntity(entityName);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Retrieves overviews for all instances in the database, grouped by entity name.
|
|
209
|
+
*/
|
|
210
|
+
getAllInstanceOverviews() {
|
|
211
|
+
return getAllInstanceOverviewsByEntityName(this.#schema.getEntity.bind(this.#schema), this._data, this.#locales);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Retrieves all instance containers of the specified child entity for a given parent instance.
|
|
215
|
+
*/
|
|
216
|
+
getAllChildInstanceContainersForParent(childEntityName, parentId) {
|
|
217
|
+
return this._data.getAllChildInstanceContainersForParent(this.#schema.getEntity.bind(this.#schema), childEntityName, parentId);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Displays the name of an entity instance including its ID. If no display name is found, `undefined` is returned.
|
|
221
|
+
*/
|
|
222
|
+
getDisplayName(...args) {
|
|
223
|
+
return getDisplayName(this.#schema.getEntity.bind(this.#schema), this.#locales, this._data, ...args);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Displays the name of an entity instance including its ID. If no display name is found, only the ID is returned.
|
|
227
|
+
*/
|
|
228
|
+
getDisplayNameWithId(...args) {
|
|
229
|
+
return getDisplayNameWithId(this.#schema.getEntity.bind(this.#schema), this.#locales, this._data, ...args);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Checks if a given instance is referenced by other instances in the database.
|
|
233
|
+
*
|
|
234
|
+
* If `otherInstancesToDelete` is provided, those instances will be excluded from the check. This can be useful when planning to delete multiple instances at once and wanting to ignore references from those instances.
|
|
235
|
+
*/
|
|
236
|
+
isReferencedByOtherInstances(instance, otherInstancesToDelete) {
|
|
237
|
+
return isReferencedByOtherInstances(this._referencesToInstances, instance, otherInstancesToDelete);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The configured locales for this TSONDB instance.
|
|
241
|
+
*/
|
|
242
|
+
get locales() {
|
|
243
|
+
return this.#locales;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Sets the locales for this TSONDB instance.
|
|
247
|
+
*
|
|
248
|
+
* The provided locales must exist in the database if a locale entity is defined in the schema.
|
|
249
|
+
*/
|
|
250
|
+
setLocales(locales) {
|
|
251
|
+
checkLocales(this.#schema, this._data, locales);
|
|
252
|
+
this.#locales = locales;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* The loaded schema for this TSONDB instance.
|
|
256
|
+
*/
|
|
257
|
+
get schema() {
|
|
258
|
+
return this.#schema;
|
|
259
|
+
}
|
|
260
|
+
get dataRootPath() {
|
|
261
|
+
return this.#dataRootPath;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Searches for instances matching the provided query in their ID or display name.
|
|
265
|
+
*
|
|
266
|
+
* The results are sorted by relevance. A custom relevance score function can be provided.
|
|
267
|
+
*/
|
|
268
|
+
search(query, options = {}) {
|
|
269
|
+
if (!query || query.trim().length === 0) {
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
const { getRelevanceScore = ({ id, displayName }) => {
|
|
273
|
+
const searchableName = displayName.toLowerCase();
|
|
274
|
+
return id.startsWith(query) || searchableName.startsWith(lowerCaseQuery)
|
|
275
|
+
? 1
|
|
276
|
+
: id.includes(query) || searchableName.includes(lowerCaseQuery)
|
|
277
|
+
? 0.5
|
|
278
|
+
: 0;
|
|
279
|
+
}, } = options;
|
|
280
|
+
const lowerCaseQuery = query.toLowerCase();
|
|
281
|
+
return this._data
|
|
282
|
+
.getAllInstances()
|
|
283
|
+
.flatMap(([entityName, instances]) => {
|
|
284
|
+
const entity = this.#schema.getEntity(entityName);
|
|
285
|
+
if (entity && isEntityDeclWithParentReference(entity)) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
return instances
|
|
289
|
+
.map((instance) => {
|
|
290
|
+
const { name, localeId } = getDisplayNameFromEntityInstance(
|
|
291
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
292
|
+
entity, instance, this.#schema.getEntity.bind(this.#schema), this.getInstanceContainerOfEntityById.bind(this), this.getAllChildInstanceContainersForParent.bind(this), this.#locales);
|
|
293
|
+
return [
|
|
294
|
+
entityName,
|
|
295
|
+
{
|
|
296
|
+
id: instance.id,
|
|
297
|
+
displayName: name,
|
|
298
|
+
displayNameLocaleId: localeId,
|
|
299
|
+
relevance: getRelevanceScore({
|
|
300
|
+
entityName,
|
|
301
|
+
id: instance.id,
|
|
302
|
+
content: instance.content,
|
|
303
|
+
displayName: name,
|
|
304
|
+
displayNameLocaleId: localeId,
|
|
305
|
+
query,
|
|
306
|
+
queryLowerCase: lowerCaseQuery,
|
|
307
|
+
}),
|
|
308
|
+
},
|
|
309
|
+
];
|
|
310
|
+
})
|
|
311
|
+
.filter(instance => instance[1].relevance > 0);
|
|
312
|
+
})
|
|
313
|
+
.toSorted(reduceCompare(on(item => item[1].relevance, compareNumber), (a, b) => a[1].displayName.localeCompare(b[1].displayName, a[1].displayNameLocaleId)));
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Retrieves the name of the entity for a given instance ID. If no instance with the provided ID is found, `undefined` is returned.
|
|
317
|
+
*/
|
|
318
|
+
getEntityNameOfInstanceId(id) {
|
|
319
|
+
return this._data.getEntityNameOfInstanceId(id);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import type { HomeLayoutSection,
|
|
1
|
+
import type { HomeLayoutSection, ServerOptions } from "../config.ts";
|
|
2
2
|
import type { DefaultTSONDBTypes, TSONDB, ValidationOptions } from "../index.ts";
|
|
3
|
-
export type ServerOptions
|
|
4
|
-
port: number;
|
|
5
|
-
};
|
|
3
|
+
export type { ServerOptions };
|
|
6
4
|
export interface TSONDBRequestLocals {
|
|
7
5
|
db: TSONDB;
|
|
8
6
|
defaultLocales: string[];
|
|
@@ -16,4 +14,7 @@ declare global {
|
|
|
16
14
|
}
|
|
17
15
|
}
|
|
18
16
|
}
|
|
19
|
-
export declare const createServer: <T extends DefaultTSONDBTypes = DefaultTSONDBTypes>(db: TSONDB<T>, config:
|
|
17
|
+
export declare const createServer: <T extends DefaultTSONDBTypes = DefaultTSONDBTypes>(db: TSONDB<T>, config: {
|
|
18
|
+
serverOptions?: ServerOptions;
|
|
19
|
+
validationOptions?: Partial<ValidationOptions>;
|
|
20
|
+
}, callback?: (error?: NodeJS.ErrnoException) => void) => void;
|
|
@@ -15,9 +15,12 @@ const staticNodeModule = (moduleName) => {
|
|
|
15
15
|
}
|
|
16
16
|
return express.static(dirname(pathToPackageJson));
|
|
17
17
|
};
|
|
18
|
-
export const createServer = (db, config) => {
|
|
19
|
-
const { serverOptions: options,
|
|
20
|
-
const { port } = {
|
|
18
|
+
export const createServer = (db, config, callback) => {
|
|
19
|
+
const { serverOptions: options, validationOptions } = config;
|
|
20
|
+
const { port, homeLayoutSections, customAssetsPath, customStylesheetPath } = {
|
|
21
|
+
...defaultOptions,
|
|
22
|
+
...options,
|
|
23
|
+
};
|
|
21
24
|
const app = express();
|
|
22
25
|
app.use(express.static(join(import.meta.dirname, "../../../../public")));
|
|
23
26
|
app.use("/js/node_modules/preact", staticNodeModule("preact"));
|
|
@@ -84,5 +87,6 @@ export const createServer = (db, config) => {
|
|
|
84
87
|
else {
|
|
85
88
|
debug(`server listening on http://localhost:${port.toString()}`);
|
|
86
89
|
}
|
|
90
|
+
callback?.(error);
|
|
87
91
|
});
|
|
88
92
|
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SimpleGit, StatusResult } from "simple-git";
|
|
2
|
+
import type { InstanceContent } from "../shared/utils/instances.ts";
|
|
3
|
+
import type { DefaultTSONDBTypes, ValidationOptions } from "./index.ts";
|
|
4
|
+
import type { Output } from "./output.ts";
|
|
5
|
+
import type { EntityDecl } from "./schema/dsl/index.ts";
|
|
6
|
+
import type { AnyEntityMap, AnySingletonEntityMap, RegisteredEntityMap, RegisteredSingletonEntityMap } from "./schema/generatedTypeHelpers.ts";
|
|
7
|
+
import type { Schema } from "./schema/index.ts";
|
|
8
|
+
import { DatabaseInMemory } from "./utils/databaseInMemory.ts";
|
|
9
|
+
import { type ReferencesToInstances } from "./utils/references.ts";
|
|
10
|
+
interface MetaFile {
|
|
11
|
+
version: string;
|
|
12
|
+
schemaVersion: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const getTSONDBVersion: () => Promise<string>;
|
|
15
|
+
export declare const readMetaFile: (dataRootPath: string) => Promise<MetaFile>;
|
|
16
|
+
export declare const writeMetaFile: (dataRootPath: string, meta: MetaFile) => Promise<void>;
|
|
17
|
+
export declare const prepareFolders: (schemaVersion: string, dataRootPath: string, entities: EntityDecl[]) => Promise<void>;
|
|
18
|
+
export declare const generateOutputs: <T extends DefaultTSONDBTypes>(schema: Schema<T>, outputs: Output[]) => Promise<void>;
|
|
19
|
+
export declare const checkLocales: <T extends DefaultTSONDBTypes>(schema: Schema<T>, data: DatabaseInMemory<T["entityMap"]>, locales: string[]) => void;
|
|
20
|
+
export type TSONDBGit = {
|
|
21
|
+
client: SimpleGit;
|
|
22
|
+
root: string;
|
|
23
|
+
};
|
|
24
|
+
export declare const initData: <T extends DefaultTSONDBTypes>(dataRootPath: string, schema: Schema<T>, locales: string[], git: TSONDBGit | undefined, gitStatus: StatusResult | undefined, skipReferenceCache: boolean, allowCompressedFile: boolean) => Promise<{
|
|
25
|
+
data: DatabaseInMemory<T["entityMap"], T["singletonEntityMap"]>;
|
|
26
|
+
referencesToInstances: ReferencesToInstances;
|
|
27
|
+
meta: MetaFile;
|
|
28
|
+
}>;
|
|
29
|
+
export declare const validate: <T extends DefaultTSONDBTypes>(properties: {
|
|
30
|
+
data: DatabaseInMemory<T["entityMap"], T["singletonEntityMap"]>;
|
|
31
|
+
schema: Schema<T>;
|
|
32
|
+
validationOptions: ValidationOptions;
|
|
33
|
+
dataRootPath: string;
|
|
34
|
+
locales: string[];
|
|
35
|
+
}, options?: {
|
|
36
|
+
logToConsole?: boolean;
|
|
37
|
+
skipStructuralValidation?: boolean;
|
|
38
|
+
changedEntities?: Extract<keyof (T["entityMap"] & T["singletonEntityMap"]), string>[];
|
|
39
|
+
}) => Error[];
|
|
40
|
+
export type CompressedDatabaseFile<EM extends AnyEntityMap = RegisteredEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap> = {
|
|
41
|
+
meta: MetaFile;
|
|
42
|
+
data: Record<keyof EM, Record<string, InstanceContent>> & Record<keyof SEM, InstanceContent>;
|
|
43
|
+
};
|
|
44
|
+
export declare const createCompressedDatabaseFile: <EM extends AnyEntityMap = RegisteredEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap>(data: DatabaseInMemory<EM, SEM>, schemaVersion: string) => CompressedDatabaseFile<EM, SEM>;
|
|
45
|
+
export declare const isEntityPair: <EM extends AnyEntityMap = RegisteredEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap>(pair: [Extract<keyof EM, string>, Record<string, InstanceContent>] | [Extract<keyof SEM, string>, InstanceContent], isEntityName: (entityName: Extract<keyof EM | keyof SEM, string>) => entityName is Extract<keyof EM, string>) => pair is [Extract<keyof EM, string>, Record<string, InstanceContent>];
|
|
46
|
+
export {};
|