tsondb 0.21.1 → 0.21.2
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 +2 -2
- package/dist/src/node/git.d.ts +4 -4
- package/dist/src/node/index.d.ts +39 -4
- package/dist/src/node/index.js +112 -11
- package/dist/src/node/renderers/jsonschema/render.js +7 -0
- package/dist/src/node/renderers/ts/render.d.ts +4 -0
- package/dist/src/node/renderers/ts/render.js +6 -0
- package/dist/src/node/schema/dsl/declarations/Decl.d.ts +3 -2
- package/dist/src/node/schema/dsl/declarations/Decl.js +2 -1
- package/dist/src/node/schema/dsl/declarations/SingletonEntityDecl.d.ts +35 -0
- package/dist/src/node/schema/dsl/declarations/SingletonEntityDecl.js +23 -0
- package/dist/src/node/schema/dsl/index.d.ts +1 -0
- package/dist/src/node/schema/dsl/index.js +1 -0
- package/dist/src/node/schema/generatedTypeHelpers.d.ts +18 -0
- package/dist/src/node/schema/guards.d.ts +1 -0
- package/dist/src/node/schema/guards.js +1 -0
- package/dist/src/node/schema/helpers.js +1 -0
- package/dist/src/node/schema/index.d.ts +12 -1
- package/dist/src/node/schema/index.js +44 -0
- package/dist/src/node/schema/treeOperations/customConstraints.d.ts +2 -1
- package/dist/src/node/schema/treeOperations/customConstraints.js +10 -10
- package/dist/src/node/schema/treeOperations/implicitSchema.js +1 -0
- package/dist/src/node/schema/treeOperations/references.js +1 -0
- package/dist/src/node/schema/treeOperations/serialization.d.ts +4 -1
- package/dist/src/node/schema/treeOperations/serialization.js +8 -0
- package/dist/src/node/schema/treeOperations/typeResolution.d.ts +4 -1
- package/dist/src/node/schema/treeOperations/typeResolution.js +6 -0
- package/dist/src/node/schema/treeOperations/validation.js +2 -0
- package/dist/src/node/server/api/declarations.js +165 -23
- package/dist/src/node/transaction.d.ts +28 -11
- package/dist/src/node/transaction.js +93 -0
- package/dist/src/node/utils/customConstraints.d.ts +6 -5
- package/dist/src/node/utils/customConstraints.js +34 -15
- package/dist/src/node/utils/databaseInMemory.d.ts +20 -8
- package/dist/src/node/utils/databaseInMemory.js +67 -9
- package/dist/src/node/utils/databaseOnDisk.js +27 -30
- package/dist/src/node/utils/files.d.ts +7 -2
- package/dist/src/node/utils/files.js +9 -0
- package/dist/src/node/utils/references.d.ts +2 -0
- package/dist/src/node/utils/references.js +6 -1
- package/dist/src/shared/api.d.ts +41 -2
- package/dist/src/shared/schema/Node.d.ts +5 -1
- package/dist/src/shared/schema/Node.js +6 -0
- package/dist/src/shared/schema/declarations/Declaration.d.ts +3 -2
- package/dist/src/shared/schema/declarations/SingletonEntityDecl.d.ts +18 -0
- package/dist/src/shared/schema/declarations/SingletonEntityDecl.js +8 -0
- package/dist/src/shared/utils/childInstances.d.ts +4 -0
- package/dist/src/shared/utils/instances.d.ts +4 -0
- package/dist/src/web/web.js +573 -130
- package/package.json +1 -1
- package/public/css/styles.css +1 -0
|
@@ -33,6 +33,27 @@ const checkDeleteInstancePossible = (referencesToInstances, instanceId) => {
|
|
|
33
33
|
throw new HTTPError(400, "Cannot delete instance that is referenced by other instances");
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* @throws {HTTPError}
|
|
38
|
+
*/
|
|
39
|
+
const checkCreateSingletonInstancePossible = (validate, databaseInMemory, entity, instanceContent) => {
|
|
40
|
+
if (databaseInMemory.hasInstanceOfSingletonEntity(entity.name)) {
|
|
41
|
+
throw new HTTPError(400, `Duplicate instance for singleton entity`);
|
|
42
|
+
}
|
|
43
|
+
checkUpdateSingletonInstancePossible(validate, entity, instanceContent);
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* @throws {HTTPError}
|
|
47
|
+
*/
|
|
48
|
+
const checkUpdateSingletonInstancePossible = (validate, entity, instanceContent) => {
|
|
49
|
+
const validationErrors = validate(entity, instanceContent);
|
|
50
|
+
if (validationErrors.length > 0) {
|
|
51
|
+
throw new HTTPError(400, validationErrors.map(getErrorMessageForDisplay).join("\n\n"));
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const checkDeleteSingletonInstancePossible = () => {
|
|
55
|
+
// cannot be referenced and does not have any integration into other areas, so no checks needed
|
|
56
|
+
};
|
|
36
57
|
export class Transaction {
|
|
37
58
|
#values;
|
|
38
59
|
constructor(values) {
|
|
@@ -115,6 +136,78 @@ export class Transaction {
|
|
|
115
136
|
{ id: instanceId, content: oldInstance },
|
|
116
137
|
];
|
|
117
138
|
}
|
|
139
|
+
createSingletonInstance(entity, instanceContent) {
|
|
140
|
+
const { data, steps, referencesToInstances, getEntity, validate } = this.#values;
|
|
141
|
+
checkCreateSingletonInstancePossible(validate, data, entity, instanceContent);
|
|
142
|
+
const [updatedDb] = data.setInstanceContainerOfSingletonEntity(entity.name, {
|
|
143
|
+
content: instanceContent,
|
|
144
|
+
});
|
|
145
|
+
const updatedRefs = updateReferencesToInstances(getEntity, referencesToInstances, entity.name, entity.name, undefined, instanceContent);
|
|
146
|
+
const step = {
|
|
147
|
+
kind: "createSingleton",
|
|
148
|
+
entity,
|
|
149
|
+
instanceContent,
|
|
150
|
+
};
|
|
151
|
+
return [
|
|
152
|
+
new Transaction({
|
|
153
|
+
...this.#values,
|
|
154
|
+
data: updatedDb,
|
|
155
|
+
steps: [...steps, step],
|
|
156
|
+
referencesToInstances: updatedRefs,
|
|
157
|
+
}),
|
|
158
|
+
{ content: instanceContent },
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
updateSingletonInstance(entity, instanceContent) {
|
|
162
|
+
const { data, steps, referencesToInstances, getEntity, validate } = this.#values;
|
|
163
|
+
checkUpdateSingletonInstancePossible(validate, entity, instanceContent);
|
|
164
|
+
const [updatedDb, oldInstance] = data.setInstanceContainerOfSingletonEntity(entity.name, {
|
|
165
|
+
content: instanceContent,
|
|
166
|
+
});
|
|
167
|
+
if (oldInstance === undefined) {
|
|
168
|
+
throw new HTTPError(400, `Singleton instance of entity "${entity.name}" did not yet exist`);
|
|
169
|
+
}
|
|
170
|
+
const updatedRefs = updateReferencesToInstances(getEntity, referencesToInstances, entity.name, entity.name, oldInstance, instanceContent);
|
|
171
|
+
const step = {
|
|
172
|
+
kind: "updateSingleton",
|
|
173
|
+
entity,
|
|
174
|
+
instanceContent,
|
|
175
|
+
oldInstance,
|
|
176
|
+
};
|
|
177
|
+
return [
|
|
178
|
+
new Transaction({
|
|
179
|
+
...this.#values,
|
|
180
|
+
data: updatedDb,
|
|
181
|
+
steps: [...steps, step],
|
|
182
|
+
referencesToInstances: updatedRefs,
|
|
183
|
+
}),
|
|
184
|
+
{ content: instanceContent },
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
deleteSingletonInstance(entity) {
|
|
188
|
+
const { data, steps, referencesToInstances, getEntity } = this.#values;
|
|
189
|
+
checkDeleteSingletonInstancePossible();
|
|
190
|
+
const [updatedDb, oldInstance] = data.deleteInstanceContainerOfSingletonEntity(entity.name);
|
|
191
|
+
if (oldInstance === undefined) {
|
|
192
|
+
// instance did not exist
|
|
193
|
+
throw new Error("Instance did not exist");
|
|
194
|
+
}
|
|
195
|
+
const updatedRefs = updateReferencesToInstances(getEntity, referencesToInstances, entity.name, entity.name, oldInstance, undefined);
|
|
196
|
+
const step = {
|
|
197
|
+
kind: "deleteSingleton",
|
|
198
|
+
entity,
|
|
199
|
+
oldInstance,
|
|
200
|
+
};
|
|
201
|
+
return [
|
|
202
|
+
new Transaction({
|
|
203
|
+
...this.#values,
|
|
204
|
+
data: updatedDb,
|
|
205
|
+
steps: [...steps, step],
|
|
206
|
+
referencesToInstances: updatedRefs,
|
|
207
|
+
}),
|
|
208
|
+
{ content: oldInstance },
|
|
209
|
+
];
|
|
210
|
+
}
|
|
118
211
|
/**
|
|
119
212
|
* @package
|
|
120
213
|
*/
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { type Result } from "@elyukai/utils/result";
|
|
2
2
|
import type { InstanceContent } from "../../shared/utils/instances.ts";
|
|
3
|
-
import type { DefaultTSONDBTypes, EntityName } from "../index.ts";
|
|
4
|
-
import { type EntityDecl } from "../schema/dsl/index.ts";
|
|
5
|
-
import type { AnyChildEntityMap, AnyEntityMap, GetAllChildInstanceContainersForParent, GetAllInstanceContainers, GetAllInstances, GetDisplayName, GetDisplayNameAndId, GetEntityByName, GetInstanceById, GetInstanceOverviewOfEntityById, RegisteredChildEntityMap, RegisteredEntity, RegisteredEntityMap, RegisteredEnumOrTypeAlias } from "../schema/generatedTypeHelpers.ts";
|
|
3
|
+
import type { DefaultTSONDBTypes, EntityName, SingletonEntityName } from "../index.ts";
|
|
4
|
+
import { type EntityDecl, type SingletonEntityDecl } from "../schema/dsl/index.ts";
|
|
5
|
+
import type { AnyChildEntityMap, AnyEntityMap, AnySingletonEntityMap, GetAllChildInstanceContainersForParent, GetAllInstanceContainers, GetAllInstances, GetDisplayName, GetDisplayNameAndId, GetEntityByName, GetInstanceById, GetInstanceOverviewOfEntityById, GetSingletonInstance, RegisteredChildEntityMap, RegisteredEntity, RegisteredEntityMap, RegisteredEnumOrTypeAlias, RegisteredSingletonEntityMap } from "../schema/generatedTypeHelpers.ts";
|
|
6
6
|
import type { DatabaseInMemory } from "./databaseInMemory.ts";
|
|
7
|
-
export type CustomConstraintHelpers<EM extends AnyEntityMap = RegisteredEntityMap, CEM extends AnyChildEntityMap = RegisteredChildEntityMap> = {
|
|
7
|
+
export type CustomConstraintHelpers<EM extends AnyEntityMap = RegisteredEntityMap, CEM extends AnyChildEntityMap = RegisteredChildEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap> = {
|
|
8
8
|
getInstanceById: GetInstanceById<EM>;
|
|
9
9
|
getAllInstances: GetAllInstances<EM>;
|
|
10
10
|
getAllInstanceContainers: GetAllInstanceContainers<EM>;
|
|
11
11
|
getAllChildInstancesForParent: GetAllChildInstanceContainersForParent<CEM>;
|
|
12
12
|
getDisplayName: GetDisplayName<EM>;
|
|
13
13
|
getDisplayNameAndId: GetDisplayNameAndId<EM>;
|
|
14
|
+
getSingletonInstance: GetSingletonInstance<SEM>;
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
16
17
|
* A constraint that can be defined on an entity to enforce custom validation logic.
|
|
@@ -71,4 +72,4 @@ export type TypedNestedCustomConstraint<Name extends string, EM extends AnyEntit
|
|
|
71
72
|
* `AggregateError`s for each entity if there are any violations of any custom
|
|
72
73
|
* constraint.
|
|
73
74
|
*/
|
|
74
|
-
export declare const checkCustomConstraintsForAllEntities: <T extends DefaultTSONDBTypes>(getDisplayName: GetDisplayName<T["entityMap"]>, getDisplayNameAndId: GetDisplayNameAndId<T["entityMap"]>, getInstanceOverviewOfEntityById: GetInstanceOverviewOfEntityById<T["entityMap"]>, getEntityByName: GetEntityByName<T["entityMap"]>, data: DatabaseInMemory<T["entityMap"]>, entities: EntityDecl<EntityName<T>>[]) => Result<void, AggregateError>;
|
|
75
|
+
export declare const checkCustomConstraintsForAllEntities: <T extends DefaultTSONDBTypes>(getDisplayName: GetDisplayName<T["entityMap"]>, getDisplayNameAndId: GetDisplayNameAndId<T["entityMap"]>, getInstanceOverviewOfEntityById: GetInstanceOverviewOfEntityById<T["entityMap"]>, getEntityByName: GetEntityByName<T["entityMap"]>, data: DatabaseInMemory<T["entityMap"], T["singletonEntityMap"]>, entities: (EntityDecl<EntityName<T>> | SingletonEntityDecl<SingletonEntityName<T>>)[]) => Result<void, AggregateError>;
|
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
import { error, isError, mapError, ok } from "@elyukai/utils/result";
|
|
2
|
+
import { NodeKind } from "../../shared/schema/Node.js";
|
|
2
3
|
import {} from "../schema/dsl/index.js";
|
|
3
|
-
import {
|
|
4
|
+
import { checkCustomConstraintsInEntityDecl, checkCustomConstraintsInSingletonEntityDecl, } from "../schema/treeOperations/customConstraints.js";
|
|
5
|
+
const collectErrorsForEntity = (data, entity, getInstanceOverviewOfEntityById, helpers) => {
|
|
6
|
+
switch (entity.kind) {
|
|
7
|
+
case NodeKind.EntityDecl:
|
|
8
|
+
return data
|
|
9
|
+
.getAllInstanceContainersOfEntity(entity.name)
|
|
10
|
+
.map((instance) => [
|
|
11
|
+
instance,
|
|
12
|
+
checkCustomConstraintsInEntityDecl(entity, [instance.id, instance.content], helpers),
|
|
13
|
+
])
|
|
14
|
+
.filter(([, violations]) => violations.length > 0)
|
|
15
|
+
.map(([instance, violations]) => {
|
|
16
|
+
const instanceOverview = getInstanceOverviewOfEntityById(entity.name, instance.id);
|
|
17
|
+
const name = instanceOverview
|
|
18
|
+
? `"${instanceOverview.displayName}" (${instance.id})`
|
|
19
|
+
: instance.id;
|
|
20
|
+
return new AggregateError(violations.map(violation => new Error(violation)), `in instance ${name}`);
|
|
21
|
+
});
|
|
22
|
+
case NodeKind.SingletonEntityDecl: {
|
|
23
|
+
const instance = data.getSingletonInstanceContainerOfEntity(entity.name);
|
|
24
|
+
if (instance) {
|
|
25
|
+
return checkCustomConstraintsInSingletonEntityDecl(entity, instance.content, helpers);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
default:
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
};
|
|
4
35
|
/**
|
|
5
36
|
* Checks all custom constraints for all provided entities and their instances.
|
|
6
37
|
*
|
|
@@ -16,22 +47,10 @@ export const checkCustomConstraintsForAllEntities = (getDisplayName, getDisplayN
|
|
|
16
47
|
getAllChildInstancesForParent: data.getAllChildInstanceContainersForParent.bind(data, getEntityByName),
|
|
17
48
|
getDisplayName,
|
|
18
49
|
getDisplayNameAndId,
|
|
50
|
+
getSingletonInstance: data.getSingletonInstanceOfEntity.bind(data),
|
|
19
51
|
};
|
|
20
52
|
return mapError(entities.reduce((acc, entity) => {
|
|
21
|
-
const errors = data
|
|
22
|
-
.getAllInstanceContainersOfEntity(entity.name)
|
|
23
|
-
.map((instance) => [
|
|
24
|
-
instance,
|
|
25
|
-
checkCustomConstraints(entity, [instance.id, instance.content], helpers),
|
|
26
|
-
])
|
|
27
|
-
.filter(([, violations]) => violations.length > 0)
|
|
28
|
-
.map(([instance, violations]) => {
|
|
29
|
-
const instanceOverview = getInstanceOverviewOfEntityById(entity.name, instance.id);
|
|
30
|
-
const name = instanceOverview
|
|
31
|
-
? `"${instanceOverview.displayName}" (${instance.id})`
|
|
32
|
-
: instance.id;
|
|
33
|
-
return new AggregateError(violations.map(violation => new Error(violation)), `in instance ${name}`);
|
|
34
|
-
});
|
|
53
|
+
const errors = collectErrorsForEntity(data, entity, getInstanceOverviewOfEntityById, helpers);
|
|
35
54
|
const aggregate = errors.length > 0 ? new AggregateError(errors, `in entity "${entity.name}"`) : undefined;
|
|
36
55
|
if (isError(acc)) {
|
|
37
56
|
if (aggregate) {
|
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import { Dictionary } from "@elyukai/utils/dictionary";
|
|
2
|
-
import type { InstanceContainer, InstanceContent } from "../../shared/utils/instances.ts";
|
|
3
|
-
import type { EntityDecl } from "../schema/dsl/index.ts";
|
|
4
|
-
import { type AnyChildEntityMap, type AnyEntityMap, type GetEntityByName, type IdArgsVariant, type RegisteredEntityMap } from "../schema/generatedTypeHelpers.ts";
|
|
5
|
-
export declare class DatabaseInMemory<EM extends AnyEntityMap = RegisteredEntityMap> {
|
|
2
|
+
import type { InstanceContainer, InstanceContent, SingletonInstanceContainer } from "../../shared/utils/instances.ts";
|
|
3
|
+
import type { EntityDecl, SingletonEntityDecl } from "../schema/dsl/index.ts";
|
|
4
|
+
import { type AnyChildEntityMap, type AnyEntityMap, type AnySingletonEntityMap, type GetEntityByName, type IdArgsVariant, type RegisteredEntityMap, type RegisteredSingletonEntityMap } from "../schema/generatedTypeHelpers.ts";
|
|
5
|
+
export declare class DatabaseInMemory<EM extends AnyEntityMap = RegisteredEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap> {
|
|
6
6
|
#private;
|
|
7
7
|
private constructor();
|
|
8
|
-
static load<EM extends AnyEntityMap = RegisteredEntityMap>(dataRoot: string, entities: readonly EntityDecl<Extract<keyof EM, string>>[]): Promise<DatabaseInMemory<EM>>;
|
|
8
|
+
static load<EM extends AnyEntityMap = RegisteredEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap>(dataRoot: string, entities: readonly EntityDecl<Extract<keyof EM, string>>[], singletonEntities: readonly SingletonEntityDecl<Extract<keyof SEM, string>>[]): Promise<DatabaseInMemory<EM, SEM>>;
|
|
9
9
|
getAllInstanceContainersOfEntity<E extends Extract<keyof EM, string>>(entityName: E): InstanceContainer<EM[E]>[];
|
|
10
10
|
getAllInstancesOfEntity<E extends Extract<keyof EM, string>>(entityName: E): EM[E][];
|
|
11
11
|
getAllInstances(): [entityName: Extract<keyof EM, string>, instances: InstanceContainer[]][];
|
|
12
|
+
getAllSingletonInstances(): [
|
|
13
|
+
entityName: Extract<keyof SEM, string>,
|
|
14
|
+
instance: SingletonInstanceContainer
|
|
15
|
+
][];
|
|
12
16
|
getInstanceContainerOfEntityById<E extends Extract<keyof EM, string>>(...args: IdArgsVariant<EM, E>): InstanceContainer<EM[E]> | undefined;
|
|
13
17
|
getInstanceOfEntityById<E extends Extract<keyof EM, string>>(...args: IdArgsVariant<EM, E>): EM[E] | undefined;
|
|
18
|
+
getSingletonInstanceContainerOfEntity<E extends Extract<keyof SEM, string>>(entityName: E): SingletonInstanceContainer<SEM[E]> | undefined;
|
|
19
|
+
getSingletonInstanceOfEntity<E extends Extract<keyof SEM, string>>(entityName: E): SEM[E] | undefined;
|
|
14
20
|
hasInstanceOfEntityById<E extends Extract<keyof EM, string>>(...args: IdArgsVariant<EM, E>): boolean;
|
|
21
|
+
hasInstanceOfSingletonEntity(entityName: Extract<keyof SEM, string>): boolean;
|
|
15
22
|
forEachInstance(fn: (entityName: Extract<keyof EM, string>, instance: InstanceContainer) => Promise<void>, async: true): Promise<void>;
|
|
16
23
|
forEachInstance(fn: (entityName: Extract<keyof EM, string>, instance: InstanceContainer) => void, async?: false): void;
|
|
24
|
+
forEachSingletonInstance(fn: (entityName: Extract<keyof SEM, string>, instance: SingletonInstanceContainer) => Promise<void>, async: true): Promise<void>;
|
|
25
|
+
forEachSingletonInstance(fn: (entityName: Extract<keyof SEM, string>, instance: SingletonInstanceContainer) => void, async?: false): void;
|
|
17
26
|
countInstancesOfEntity(entityName: Extract<keyof EM, string>): number;
|
|
18
27
|
get totalSize(): number;
|
|
19
28
|
getAllChildInstanceContainersForParent: <CEM extends AnyChildEntityMap, CE extends Extract<keyof CEM, string>>(getEntityByName: GetEntityByName<EM>, childEntityName: CE, parentId: CEM[CE][2]) => InstanceContainer<CEM[CE][0]>[];
|
|
20
29
|
getAllChildInstancesForParent: <CEM extends AnyChildEntityMap, CE extends Extract<keyof CEM, string>>(getEntityByName: GetEntityByName<EM>, childEntityName: CE, parentId: CEM[CE][2]) => CEM[CE][0][];
|
|
21
|
-
setInstanceContainerOfEntityById(entityName: Extract<keyof EM, string>, instance: InstanceContainer): [DatabaseInMemory<EM>, oldInstance: InstanceContent | undefined];
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
setInstanceContainerOfEntityById(entityName: Extract<keyof EM, string>, instance: InstanceContainer): [DatabaseInMemory<EM, SEM>, oldInstance: InstanceContent | undefined];
|
|
31
|
+
setInstanceContainerOfSingletonEntity(entityName: Extract<keyof SEM, string>, instance: SingletonInstanceContainer): [DatabaseInMemory<EM, SEM>, oldInstance: InstanceContent | undefined];
|
|
32
|
+
deleteInstanceContainerOfEntityById(entityName: Extract<keyof EM, string>, instanceId: string): [DatabaseInMemory<EM, SEM>, oldInstance: InstanceContent | undefined];
|
|
33
|
+
deleteInstanceContainerOfSingletonEntity(entityName: Extract<keyof SEM, string>): [DatabaseInMemory<EM, SEM>, oldInstance: InstanceContent | undefined];
|
|
34
|
+
map(fn: (instances: Dictionary<InstanceContainer>, entityName: Extract<keyof EM, string>) => Dictionary<InstanceContainer>): DatabaseInMemory<EM, SEM>;
|
|
35
|
+
mapSingletons(fn: (instance: SingletonInstanceContainer, entityName: Extract<keyof SEM, string>) => SingletonInstanceContainer): DatabaseInMemory<EM, SEM>;
|
|
24
36
|
getEntityNameOfInstanceId(id: string): Extract<keyof EM, string> | undefined;
|
|
25
37
|
}
|
|
@@ -3,19 +3,22 @@ import { Dictionary } from "@elyukai/utils/dictionary";
|
|
|
3
3
|
import { deepEqual } from "@elyukai/utils/equality";
|
|
4
4
|
import { Lazy } from "@elyukai/utils/lazy";
|
|
5
5
|
import child_process from "node:child_process";
|
|
6
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
6
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
7
7
|
import { basename, extname, join } from "node:path";
|
|
8
8
|
import { platform } from "node:process";
|
|
9
9
|
import { promisify } from "node:util";
|
|
10
10
|
import { normalizedIdArgs, } from "../schema/generatedTypeHelpers.js";
|
|
11
|
+
import { getPathToSingletonInstance, readSingletonInstance } from "./files.js";
|
|
11
12
|
const exec = promisify(child_process.exec);
|
|
12
13
|
const ulimit = platform === "win32" ? 2048 : Number.parseInt((await exec("ulimit -n")).stdout);
|
|
13
14
|
export class DatabaseInMemory {
|
|
14
15
|
#data;
|
|
15
|
-
|
|
16
|
+
#singletonData;
|
|
17
|
+
constructor(data, singletonData) {
|
|
16
18
|
this.#data = data;
|
|
19
|
+
this.#singletonData = singletonData;
|
|
17
20
|
}
|
|
18
|
-
static async load(dataRoot, entities) {
|
|
21
|
+
static async load(dataRoot, entities, singletonEntities) {
|
|
19
22
|
return new DatabaseInMemory(Dictionary.fromEntries(await mapAsync(entities, async (entity) => {
|
|
20
23
|
const entityDir = join(dataRoot, entity.name);
|
|
21
24
|
const instanceFileNames = await readdir(entityDir);
|
|
@@ -38,7 +41,21 @@ export class DatabaseInMemory {
|
|
|
38
41
|
}, ulimit);
|
|
39
42
|
const instancesById = Dictionary.fromEntries(instances);
|
|
40
43
|
return [entity.name, instancesById];
|
|
41
|
-
}, 1)))
|
|
44
|
+
}, 1)), Dictionary.fromEntries((await mapAsync(singletonEntities, async (singletonEntity) => {
|
|
45
|
+
const pathToSingletonFile = getPathToSingletonInstance(dataRoot, singletonEntity.name);
|
|
46
|
+
try {
|
|
47
|
+
await stat(pathToSingletonFile);
|
|
48
|
+
return [
|
|
49
|
+
singletonEntity.name,
|
|
50
|
+
{
|
|
51
|
+
content: JSON.parse(await readSingletonInstance(dataRoot, singletonEntity)),
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [singletonEntity.name, undefined];
|
|
57
|
+
}
|
|
58
|
+
}, 1)).filter((pair) => pair[1] !== undefined)));
|
|
42
59
|
}
|
|
43
60
|
getAllInstanceContainersOfEntity(entityName) {
|
|
44
61
|
return (this.#data.getMap(entityName, instances => instances.values()) ?? []);
|
|
@@ -49,6 +66,9 @@ export class DatabaseInMemory {
|
|
|
49
66
|
getAllInstances() {
|
|
50
67
|
return this.#data.entries().map(([entityName, instances]) => [entityName, instances.values()]);
|
|
51
68
|
}
|
|
69
|
+
getAllSingletonInstances() {
|
|
70
|
+
return this.#singletonData.entries();
|
|
71
|
+
}
|
|
52
72
|
getInstanceContainerOfEntityById(...args) {
|
|
53
73
|
const { entityName, id } = normalizedIdArgs(args);
|
|
54
74
|
return this.#data.getMap(entityName, instances => instances.get(id));
|
|
@@ -56,10 +76,19 @@ export class DatabaseInMemory {
|
|
|
56
76
|
getInstanceOfEntityById(...args) {
|
|
57
77
|
return this.getInstanceContainerOfEntityById(...args)?.content;
|
|
58
78
|
}
|
|
79
|
+
getSingletonInstanceContainerOfEntity(entityName) {
|
|
80
|
+
return this.#singletonData.get(entityName);
|
|
81
|
+
}
|
|
82
|
+
getSingletonInstanceOfEntity(entityName) {
|
|
83
|
+
return this.getSingletonInstanceContainerOfEntity(entityName)?.content;
|
|
84
|
+
}
|
|
59
85
|
hasInstanceOfEntityById(...args) {
|
|
60
86
|
const { entityName, id } = normalizedIdArgs(args);
|
|
61
87
|
return this.#data.getMap(entityName, instances => instances.has(id)) ?? false;
|
|
62
88
|
}
|
|
89
|
+
hasInstanceOfSingletonEntity(entityName) {
|
|
90
|
+
return this.#singletonData.get(entityName) !== undefined;
|
|
91
|
+
}
|
|
63
92
|
forEachInstance(...args) {
|
|
64
93
|
const [fn, async] = args;
|
|
65
94
|
if (async) {
|
|
@@ -73,10 +102,21 @@ export class DatabaseInMemory {
|
|
|
73
102
|
});
|
|
74
103
|
}
|
|
75
104
|
}
|
|
105
|
+
forEachSingletonInstance(...args) {
|
|
106
|
+
const [fn, async] = args;
|
|
107
|
+
if (async) {
|
|
108
|
+
return this.#singletonData.forEach((instance, entityName) => fn(entityName, instance), true);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.#singletonData.forEach((instance, entityName) => {
|
|
112
|
+
fn(entityName, instance);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
76
116
|
countInstancesOfEntity(entityName) {
|
|
77
117
|
return this.#data.getMap(entityName, instances => instances.size) ?? 0;
|
|
78
118
|
}
|
|
79
|
-
#totalSize = Lazy.of(() => this.#data.reduce((total, instances) => total + instances.size, 0));
|
|
119
|
+
#totalSize = Lazy.of(() => this.#data.reduce((total, instances) => total + instances.size, 0) + this.#singletonData.size);
|
|
80
120
|
get totalSize() {
|
|
81
121
|
return this.#totalSize.value;
|
|
82
122
|
}
|
|
@@ -94,8 +134,14 @@ export class DatabaseInMemory {
|
|
|
94
134
|
setInstanceContainerOfEntityById(entityName, instance) {
|
|
95
135
|
const instances = this.#data.get(entityName) ?? Dictionary.empty;
|
|
96
136
|
return [
|
|
97
|
-
new DatabaseInMemory(this.#data.set(entityName, instances.set(instance.id, instance))),
|
|
98
|
-
instances.get(instance.id),
|
|
137
|
+
new DatabaseInMemory(this.#data.set(entityName, instances.set(instance.id, instance)), this.#singletonData),
|
|
138
|
+
instances.get(instance.id)?.content,
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
setInstanceContainerOfSingletonEntity(entityName, instance) {
|
|
142
|
+
return [
|
|
143
|
+
new DatabaseInMemory(this.#data, this.#singletonData.set(entityName, instance)),
|
|
144
|
+
this.#singletonData.get(entityName)?.content,
|
|
99
145
|
];
|
|
100
146
|
}
|
|
101
147
|
deleteInstanceContainerOfEntityById(entityName, instanceId) {
|
|
@@ -109,13 +155,25 @@ export class DatabaseInMemory {
|
|
|
109
155
|
}
|
|
110
156
|
const remainingInstances = instances.remove(instanceId);
|
|
111
157
|
return remainingInstances.size === 0 ? undefined : remainingInstances;
|
|
112
|
-
})),
|
|
158
|
+
}), this.#singletonData),
|
|
159
|
+
oldInstance.content,
|
|
160
|
+
]
|
|
161
|
+
: [this, undefined];
|
|
162
|
+
}
|
|
163
|
+
deleteInstanceContainerOfSingletonEntity(entityName) {
|
|
164
|
+
const oldInstance = this.#singletonData.get(entityName);
|
|
165
|
+
return oldInstance
|
|
166
|
+
? [
|
|
167
|
+
new DatabaseInMemory(this.#data, this.#singletonData.remove(entityName)),
|
|
113
168
|
oldInstance.content,
|
|
114
169
|
]
|
|
115
170
|
: [this, undefined];
|
|
116
171
|
}
|
|
117
172
|
map(fn) {
|
|
118
|
-
return new DatabaseInMemory(this.#data.map(fn));
|
|
173
|
+
return new DatabaseInMemory(this.#data.map(fn), this.#singletonData);
|
|
174
|
+
}
|
|
175
|
+
mapSingletons(fn) {
|
|
176
|
+
return new DatabaseInMemory(this.#data, this.#singletonData.map(fn));
|
|
119
177
|
}
|
|
120
178
|
getEntityNameOfInstanceId(id) {
|
|
121
179
|
return this.#data.findKey(instances => instances.has(id));
|
|
@@ -1,28 +1,12 @@
|
|
|
1
|
-
import { error,
|
|
1
|
+
import { error, ok } from "@elyukai/utils/result";
|
|
2
2
|
import * as DatabaseFilesystem from "./files.js";
|
|
3
|
-
const setInstanceOnDisk = async (root, entity, instanceId, instanceContent, formatterOptions) => {
|
|
4
|
-
try {
|
|
5
|
-
await DatabaseFilesystem.writeInstance(root, entity, instanceId, instanceContent, formatterOptions);
|
|
6
|
-
return ok();
|
|
7
|
-
}
|
|
8
|
-
catch (e) {
|
|
9
|
-
return error(e);
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
const deleteInstanceOnDisk = async (root, entityName, instanceId) => {
|
|
13
|
-
try {
|
|
14
|
-
await DatabaseFilesystem.deleteInstance(root, entityName, instanceId);
|
|
15
|
-
return ok();
|
|
16
|
-
}
|
|
17
|
-
catch (e) {
|
|
18
|
-
return error(e);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
3
|
const rollbackChanges = async (root, steps, formatterOptions) => {
|
|
22
4
|
for (const step of steps) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
5
|
+
try {
|
|
6
|
+
await runReverseStepAction(root, step, formatterOptions);
|
|
7
|
+
}
|
|
8
|
+
catch (e) {
|
|
9
|
+
return error(e);
|
|
26
10
|
}
|
|
27
11
|
}
|
|
28
12
|
return ok();
|
|
@@ -31,29 +15,42 @@ const runStepAction = (root, step, formatterOptions) => {
|
|
|
31
15
|
switch (step.kind) {
|
|
32
16
|
case "create":
|
|
33
17
|
case "update":
|
|
34
|
-
return
|
|
18
|
+
return DatabaseFilesystem.writeInstance(root, step.entity, step.instanceId, step.instanceContent, formatterOptions);
|
|
35
19
|
case "delete":
|
|
36
|
-
return
|
|
20
|
+
return DatabaseFilesystem.deleteInstance(root, step.entity.name, step.instanceId);
|
|
21
|
+
case "createSingleton":
|
|
22
|
+
case "updateSingleton":
|
|
23
|
+
return DatabaseFilesystem.writeSingletonInstance(root, step.entity, step.instanceContent, formatterOptions);
|
|
24
|
+
case "deleteSingleton":
|
|
25
|
+
return DatabaseFilesystem.deleteSingletonInstance(root, step.entity.name);
|
|
37
26
|
}
|
|
38
27
|
};
|
|
39
28
|
const runReverseStepAction = (root, step, formatterOptions) => {
|
|
40
29
|
switch (step.kind) {
|
|
41
30
|
case "create":
|
|
42
|
-
return
|
|
31
|
+
return DatabaseFilesystem.deleteInstance(root, step.entity.name, step.instanceId);
|
|
43
32
|
case "update":
|
|
44
|
-
return
|
|
33
|
+
return DatabaseFilesystem.writeInstance(root, step.entity, step.instanceId, step.oldInstance, formatterOptions);
|
|
45
34
|
case "delete":
|
|
46
|
-
return
|
|
35
|
+
return DatabaseFilesystem.writeInstance(root, step.entity, step.instanceId, step.oldInstance, formatterOptions);
|
|
36
|
+
case "createSingleton":
|
|
37
|
+
return DatabaseFilesystem.deleteSingletonInstance(root, step.entity.name);
|
|
38
|
+
case "updateSingleton":
|
|
39
|
+
return DatabaseFilesystem.writeSingletonInstance(root, step.entity, step.oldInstance, formatterOptions);
|
|
40
|
+
case "deleteSingleton":
|
|
41
|
+
return DatabaseFilesystem.writeSingletonInstance(root, step.entity, step.oldInstance, formatterOptions);
|
|
47
42
|
}
|
|
48
43
|
};
|
|
49
44
|
export const applyStepsToDisk = async (root, steps, formatterOptions) => {
|
|
50
45
|
for (let i = 0; i < steps.length; i++) {
|
|
51
46
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
52
47
|
const step = steps[i];
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
try {
|
|
49
|
+
await runStepAction(root, step, formatterOptions);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
55
52
|
await rollbackChanges(root, steps.slice(0, i), formatterOptions);
|
|
56
|
-
return
|
|
53
|
+
return error(e);
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
56
|
return ok();
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import type { InstanceContent } from "../../shared/utils/instances.ts";
|
|
2
2
|
import type { FormatterOptions } from "../config.ts";
|
|
3
|
-
import { type EntityDecl } from "../schema/dsl/index.ts";
|
|
3
|
+
import { type EntityDecl, type SingletonEntityDecl } from "../schema/dsl/index.ts";
|
|
4
4
|
export declare const getFileNameForId: (id: string) => string;
|
|
5
|
+
export declare const getSingletonFileName: (entityName: string) => string;
|
|
5
6
|
export declare const getPathToInstance: (dataRoot: string, entityName: string, id: string) => string;
|
|
7
|
+
export declare const getPathToSingletonInstance: (dataRoot: string, entityName: string) => string;
|
|
6
8
|
export declare const readInstance: (dataRoot: string, entity: EntityDecl, id: string) => Promise<string>;
|
|
9
|
+
export declare const readSingletonInstance: (dataRoot: string, entity: SingletonEntityDecl) => Promise<string>;
|
|
7
10
|
export declare const writeInstance: (dataRoot: string, entity: EntityDecl, id: string, instance: InstanceContent, formatterOptions: Partial<FormatterOptions> | undefined) => Promise<void>;
|
|
11
|
+
export declare const writeSingletonInstance: (dataRoot: string, entity: SingletonEntityDecl, instance: InstanceContent, formatterOptions: Partial<FormatterOptions> | undefined) => Promise<void>;
|
|
8
12
|
export declare const deleteInstance: (dataRoot: string, entityName: string, id: string) => Promise<void>;
|
|
9
|
-
export declare const
|
|
13
|
+
export declare const deleteSingletonInstance: (dataRoot: string, entityName: string) => Promise<void>;
|
|
14
|
+
export declare const formatInstance: (entity: EntityDecl | SingletonEntityDecl, instanceContent: InstanceContent, options: Partial<FormatterOptions> | undefined) => string;
|
|
@@ -3,12 +3,21 @@ import { join } from "node:path";
|
|
|
3
3
|
import {} from "../schema/dsl/index.js";
|
|
4
4
|
import { formatValue } from "../schema/treeOperations/format.js";
|
|
5
5
|
export const getFileNameForId = (id) => `${id}.json`;
|
|
6
|
+
export const getSingletonFileName = (entityName) => `${entityName}.json`;
|
|
6
7
|
export const getPathToInstance = (dataRoot, entityName, id) => join(dataRoot, entityName, getFileNameForId(id));
|
|
8
|
+
export const getPathToSingletonInstance = (dataRoot, entityName) => join(dataRoot, getSingletonFileName(entityName));
|
|
7
9
|
export const readInstance = (dataRoot, entity, id) => readFile(getPathToInstance(dataRoot, entity.name, id), {
|
|
8
10
|
encoding: "utf-8",
|
|
9
11
|
});
|
|
12
|
+
export const readSingletonInstance = (dataRoot, entity) => readFile(getPathToSingletonInstance(dataRoot, entity.name), {
|
|
13
|
+
encoding: "utf-8",
|
|
14
|
+
});
|
|
10
15
|
export const writeInstance = (dataRoot, entity, id, instance, formatterOptions) => writeFile(getPathToInstance(dataRoot, entity.name, id), formatInstance(entity, instance, formatterOptions), {
|
|
11
16
|
encoding: "utf-8",
|
|
12
17
|
});
|
|
18
|
+
export const writeSingletonInstance = (dataRoot, entity, instance, formatterOptions) => writeFile(getPathToSingletonInstance(dataRoot, entity.name), formatInstance(entity, instance, formatterOptions), {
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
});
|
|
13
21
|
export const deleteInstance = (dataRoot, entityName, id) => rm(getPathToInstance(dataRoot, entityName, id));
|
|
22
|
+
export const deleteSingletonInstance = (dataRoot, entityName) => rm(getPathToSingletonInstance(dataRoot, entityName));
|
|
14
23
|
export const formatInstance = (entity, instanceContent, options) => JSON.stringify(formatValue(entity.type.value, instanceContent, {}, options), undefined, 2) + "\n";
|
|
@@ -4,6 +4,8 @@ import type { AnyEntityMap, GetEntityByName, RegisteredEntityMap } from "../sche
|
|
|
4
4
|
import { type DatabaseInMemory } from "./databaseInMemory.ts";
|
|
5
5
|
/**
|
|
6
6
|
* A mapping from instance IDs to the list of instance IDs that reference them.
|
|
7
|
+
*
|
|
8
|
+
* For single instance entities, the instance ID is the entity name.
|
|
7
9
|
*/
|
|
8
10
|
export type ReferencesToInstances = {
|
|
9
11
|
[instanceId: string]: string[];
|
|
@@ -2,6 +2,7 @@ import { removeAt } from "@elyukai/utils/array/modify";
|
|
|
2
2
|
import { difference } from "@elyukai/utils/array/sets";
|
|
3
3
|
import { isOk } from "@elyukai/utils/result";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
|
+
import { getReferencesForSerializedSingletonEntityDecl, } from "../../shared/schema/declarations/SingletonEntityDecl.js";
|
|
5
6
|
import { getReferences } from "../schema/treeOperations/references.js";
|
|
6
7
|
import {} from "./databaseInMemory.js";
|
|
7
8
|
import { WorkerPool } from "./workers.js";
|
|
@@ -52,7 +53,11 @@ export const getReferencesToInstances = async (databaseInMemory, serializedDecla
|
|
|
52
53
|
}
|
|
53
54
|
})));
|
|
54
55
|
await pool.close();
|
|
55
|
-
const results = separateResults
|
|
56
|
+
const results = separateResults
|
|
57
|
+
.concat(databaseInMemory
|
|
58
|
+
.getAllSingletonInstances()
|
|
59
|
+
.reduce((acc, [entityName, instance]) => addReferences(acc, getReferencesForSerializedSingletonEntityDecl(serializedDeclarationsByName, serializedDeclarationsByName[entityName], instance.content), entityName), {}))
|
|
60
|
+
.reduce(mergeReferences, {});
|
|
56
61
|
return results;
|
|
57
62
|
};
|
|
58
63
|
export const updateReferencesToInstances = (getEntity, referencesToInstances, entityName, instanceId, oldInstance, newInstance) => {
|
package/dist/src/shared/api.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { SerializedDecl } from "./schema/declarations/Declaration.ts";
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
2
|
+
import type { SerializedEntityDecl } from "./schema/declarations/EntityDecl.ts";
|
|
3
|
+
import type { SerializedSingletonEntityDecl } from "./schema/declarations/SingletonEntityDecl.ts";
|
|
4
|
+
import type { CreatedEntityTaggedInstanceContainerWithChildInstances, EntityTaggedInstanceContainerWithChildInstances, EntityTaggedSingletonInstanceContainer, UpdatedEntityTaggedInstanceContainerWithChildInstances } from "./utils/childInstances.ts";
|
|
5
|
+
import type { InstanceContainer, InstanceContainerOverview, SingletonInstanceContainer } from "./utils/instances.ts";
|
|
4
6
|
export type WebConfig = {
|
|
5
7
|
localeEntityName: string | undefined;
|
|
6
8
|
defaultLocales: string[];
|
|
@@ -12,17 +14,36 @@ export type WebConfig = {
|
|
|
12
14
|
};
|
|
13
15
|
export type GetWebConfigResponseBody = WebConfig;
|
|
14
16
|
export interface GetAllDeclarationsResponseBody<D extends SerializedDecl = SerializedDecl> {
|
|
17
|
+
declarations: {
|
|
18
|
+
declaration: D;
|
|
19
|
+
}[];
|
|
20
|
+
localeEntity?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface GetAllEntityDeclarationsResponseBody<D extends SerializedEntityDecl = SerializedEntityDecl> {
|
|
15
23
|
declarations: {
|
|
16
24
|
declaration: D;
|
|
17
25
|
instanceCount: number;
|
|
18
26
|
}[];
|
|
19
27
|
localeEntity?: string;
|
|
20
28
|
}
|
|
29
|
+
export interface GetAllSingletonEntityDeclarationsResponseBody<D extends SerializedSingletonEntityDecl = SerializedSingletonEntityDecl> {
|
|
30
|
+
declarations: {
|
|
31
|
+
declaration: D;
|
|
32
|
+
hasInstance: boolean;
|
|
33
|
+
}[];
|
|
34
|
+
}
|
|
21
35
|
export interface GetDeclarationResponseBody<D extends SerializedDecl = SerializedDecl> {
|
|
22
36
|
declaration: D;
|
|
37
|
+
}
|
|
38
|
+
export interface GetEntityDeclarationResponseBody<D extends SerializedEntityDecl = SerializedEntityDecl> {
|
|
39
|
+
declaration: D;
|
|
23
40
|
instanceCount: number;
|
|
24
41
|
isLocaleEntity: boolean;
|
|
25
42
|
}
|
|
43
|
+
export interface GetSingletonEntityDeclarationResponseBody<D extends SerializedSingletonEntityDecl = SerializedSingletonEntityDecl> {
|
|
44
|
+
declaration: D;
|
|
45
|
+
hasInstance: boolean;
|
|
46
|
+
}
|
|
26
47
|
export interface GetAllInstancesOfEntityResponseBody {
|
|
27
48
|
instances: InstanceContainerOverview[];
|
|
28
49
|
isLocaleEntity: boolean;
|
|
@@ -52,6 +73,24 @@ export interface DeleteInstanceOfEntityResponseBody {
|
|
|
52
73
|
instance: InstanceContainer;
|
|
53
74
|
isLocaleEntity: boolean;
|
|
54
75
|
}
|
|
76
|
+
export interface CreateSingletonInstanceOfEntityRequestBody {
|
|
77
|
+
instance: EntityTaggedSingletonInstanceContainer;
|
|
78
|
+
}
|
|
79
|
+
export interface CreateSingletonInstanceOfEntityResponseBody {
|
|
80
|
+
instance: SingletonInstanceContainer;
|
|
81
|
+
}
|
|
82
|
+
export interface GetSingletonInstanceOfEntityResponseBody {
|
|
83
|
+
instance: SingletonInstanceContainer;
|
|
84
|
+
}
|
|
85
|
+
export interface UpdateSingletonInstanceOfEntityRequestBody {
|
|
86
|
+
instance: EntityTaggedSingletonInstanceContainer;
|
|
87
|
+
}
|
|
88
|
+
export interface UpdateSingletonInstanceOfEntityResponseBody {
|
|
89
|
+
instance: SingletonInstanceContainer;
|
|
90
|
+
}
|
|
91
|
+
export interface DeleteSingletonInstanceOfEntityResponseBody {
|
|
92
|
+
instance: SingletonInstanceContainer;
|
|
93
|
+
}
|
|
55
94
|
export interface GetAllInstancesResponseBody {
|
|
56
95
|
instances: {
|
|
57
96
|
[entity: string]: {
|