tsondb 0.12.3 → 0.12.5
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/index.js +17 -20
- package/dist/src/node/schema/Node.d.ts +2 -2
- package/dist/src/node/schema/Node.js +3 -5
- package/dist/src/node/schema/declarations/EntityDecl.d.ts +4 -4
- package/dist/src/node/schema/types/references/IncludeIdentifierType.js +0 -3
- package/dist/src/node/server/api/declarations.js +27 -10
- package/dist/src/node/server/api/git.js +2 -5
- package/dist/src/node/server/api/instances.js +2 -3
- package/dist/src/node/server/index.d.ts +4 -3
- package/dist/src/node/server/index.js +2 -2
- package/dist/src/node/server/init.d.ts +2 -2
- package/dist/src/node/server/init.js +14 -11
- package/dist/src/node/server/utils/childInstances.d.ts +0 -3
- package/dist/src/node/server/utils/childInstances.js +2 -43
- package/dist/src/node/server/utils/instanceOperations.d.ts +3 -4
- package/dist/src/node/server/utils/instanceOperations.js +44 -84
- package/dist/src/node/utils/childInstances.d.ts +11 -10
- package/dist/src/node/utils/childInstances.js +60 -130
- package/dist/src/node/utils/databaseInMemory.d.ts +18 -0
- package/dist/src/node/utils/databaseInMemory.js +86 -0
- package/dist/src/node/utils/databaseOnDisk.d.ts +2 -0
- package/dist/src/node/utils/databaseOnDisk.js +62 -0
- package/dist/src/node/utils/databaseTransactions.d.ts +46 -0
- package/dist/src/node/utils/databaseTransactions.js +105 -0
- package/dist/src/node/utils/displayName.d.ts +3 -3
- package/dist/src/node/utils/error.d.ts +4 -0
- package/dist/src/node/utils/error.js +8 -0
- package/dist/src/node/utils/files.d.ts +4 -2
- package/dist/src/node/utils/files.js +2 -1
- package/dist/src/node/utils/git.d.ts +3 -1
- package/dist/src/node/utils/git.js +8 -2
- package/dist/src/node/utils/instanceTransactionSteps.d.ts +9 -0
- package/dist/src/node/utils/instanceTransactionSteps.js +40 -0
- package/dist/src/node/utils/references.d.ts +4 -3
- package/dist/src/node/utils/references.js +3 -2
- package/dist/src/shared/schema/declarations/EntityDecl.d.ts +2 -2
- package/dist/src/shared/utils/dictionary.d.ts +17 -0
- package/dist/src/shared/utils/dictionary.js +76 -0
- package/dist/src/shared/utils/instances.d.ts +2 -2
- package/dist/src/shared/utils/result.d.ts +4 -0
- package/dist/src/shared/utils/result.js +4 -0
- package/dist/src/web/components/InstanceRouteSkeleton.d.ts +9 -8
- package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.js +1 -1
- package/dist/src/web/hooks/useGitClient.js +0 -1
- package/dist/src/web/hooks/useMappedAPIResource.js +2 -4
- package/dist/src/web/utils/typeSkeleton.d.ts +2 -2
- package/dist/src/web/utils/typeSkeleton.js +1 -1
- package/package.json +3 -3
- package/dist/src/node/utils/instanceOperations.d.ts +0 -14
- package/dist/src/node/utils/instanceOperations.js +0 -88
- package/dist/src/node/utils/instances.d.ts +0 -6
- package/dist/src/node/utils/instances.js +0 -29
package/dist/src/node/index.js
CHANGED
|
@@ -7,9 +7,9 @@ import { validateEntityDecl } from "./schema/declarations/EntityDecl.js";
|
|
|
7
7
|
import { createValidators } from "./schema/Node.js";
|
|
8
8
|
import { getEntities } from "./schema/Schema.js";
|
|
9
9
|
import { createServer } from "./server/index.js";
|
|
10
|
+
import { asyncForEachInstanceInDatabaseInMemory, createDatabaseInMemory, getInstancesOfEntityFromDatabaseInMemory, } from "./utils/databaseInMemory.js";
|
|
10
11
|
import { countErrors, getErrorMessageForDisplay, wrapErrorsIfAny } from "./utils/error.js";
|
|
11
12
|
import { getFileNameForId, writeInstance } from "./utils/files.js";
|
|
12
|
-
import { getInstancesByEntityName } from "./utils/instances.js";
|
|
13
13
|
const debug = Debug("tsondb:jsapi");
|
|
14
14
|
const prepareFolders = async (dataRootPath, entities) => {
|
|
15
15
|
await mkdir(dataRootPath, { recursive: true });
|
|
@@ -23,18 +23,18 @@ export const generateOutputs = async (schema, outputs) => {
|
|
|
23
23
|
await output.run(schema);
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
|
-
const _validate = (dataRootPath, entities,
|
|
26
|
+
const _validate = (dataRootPath, entities, databaseInMemory, options = {}) => {
|
|
27
27
|
const { checkReferentialIntegrity = true, checkOnlyEntities = [] } = options;
|
|
28
28
|
for (const onlyEntity of checkOnlyEntities) {
|
|
29
29
|
if (!entities.find(entity => entity.name === onlyEntity)) {
|
|
30
30
|
throw new Error(`Entity "${onlyEntity}" not found in schema`);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
const validationHelpers = createValidators(
|
|
33
|
+
const validationHelpers = createValidators(databaseInMemory, true, checkReferentialIntegrity);
|
|
34
34
|
const errors = (checkOnlyEntities.length > 0
|
|
35
35
|
? entities.filter(entity => checkOnlyEntities.includes(entity.name))
|
|
36
36
|
: entities)
|
|
37
|
-
.flatMap(entity => parallelizeErrors(
|
|
37
|
+
.flatMap(entity => parallelizeErrors(getInstancesOfEntityFromDatabaseInMemory(databaseInMemory, entity.name).map(instance => wrapErrorsIfAny(`in file ${styleText("white", `"${dataRootPath}${sep}${styleText("bold", join(entity.name, getFileNameForId(instance.id)))}"`)}`, validateEntityDecl(validationHelpers, [], entity, instance.content)))))
|
|
38
38
|
.toSorted((a, b) => a.message.localeCompare(b.message));
|
|
39
39
|
if (errors.length === 0) {
|
|
40
40
|
debug("All entities are valid");
|
|
@@ -47,15 +47,15 @@ const _validate = (dataRootPath, entities, instancesByEntityName, options = {})
|
|
|
47
47
|
export const validate = async (schema, dataRootPath, options) => {
|
|
48
48
|
const entities = getEntities(schema);
|
|
49
49
|
await prepareFolders(dataRootPath, entities);
|
|
50
|
-
const
|
|
51
|
-
_validate(dataRootPath, entities,
|
|
50
|
+
const databaseInMemory = await createDatabaseInMemory(dataRootPath, entities);
|
|
51
|
+
_validate(dataRootPath, entities, databaseInMemory, options);
|
|
52
52
|
};
|
|
53
53
|
export const generateAndValidate = async (schema, outputs, dataRootPath, validationOptions) => {
|
|
54
54
|
await generateOutputs(schema, outputs);
|
|
55
55
|
const entities = getEntities(schema);
|
|
56
56
|
await prepareFolders(dataRootPath, entities);
|
|
57
|
-
const
|
|
58
|
-
_validate(dataRootPath, entities,
|
|
57
|
+
const databaseInMemory = await createDatabaseInMemory(dataRootPath, entities);
|
|
58
|
+
_validate(dataRootPath, entities, databaseInMemory, validationOptions);
|
|
59
59
|
};
|
|
60
60
|
export const serve = async (schema, dataRootPath, defaultLocales, homeLayoutSections, serverOptions, customStylesheetPath) => {
|
|
61
61
|
if (defaultLocales.length === 0) {
|
|
@@ -64,28 +64,25 @@ export const serve = async (schema, dataRootPath, defaultLocales, homeLayoutSect
|
|
|
64
64
|
const entities = getEntities(schema);
|
|
65
65
|
await prepareFolders(dataRootPath, entities);
|
|
66
66
|
debug("prepared folders");
|
|
67
|
-
const
|
|
67
|
+
const databaseInMemory = await createDatabaseInMemory(dataRootPath, entities);
|
|
68
68
|
debug("loaded instances");
|
|
69
|
-
await createServer(schema, dataRootPath,
|
|
69
|
+
await createServer(schema, dataRootPath, databaseInMemory, defaultLocales, homeLayoutSections, serverOptions, customStylesheetPath);
|
|
70
70
|
};
|
|
71
71
|
export const generateValidateAndServe = async (schema, outputs, dataRootPath, defaultLocales, homeLayoutSections, serverOptions, validationOptions) => {
|
|
72
72
|
await generateOutputs(schema, outputs);
|
|
73
73
|
const entities = getEntities(schema);
|
|
74
74
|
await prepareFolders(dataRootPath, entities);
|
|
75
|
-
const
|
|
76
|
-
_validate(dataRootPath, entities,
|
|
77
|
-
await createServer(schema, dataRootPath,
|
|
75
|
+
const databaseInMemory = await createDatabaseInMemory(dataRootPath, entities);
|
|
76
|
+
_validate(dataRootPath, entities, databaseInMemory, validationOptions);
|
|
77
|
+
await createServer(schema, dataRootPath, databaseInMemory, defaultLocales, homeLayoutSections, serverOptions);
|
|
78
78
|
};
|
|
79
79
|
export const format = async (schema, dataRootPath) => {
|
|
80
80
|
const entities = getEntities(schema);
|
|
81
81
|
await prepareFolders(dataRootPath, entities);
|
|
82
|
-
const
|
|
83
|
-
|
|
82
|
+
const databaseInMemory = await createDatabaseInMemory(dataRootPath, entities);
|
|
83
|
+
await asyncForEachInstanceInDatabaseInMemory(databaseInMemory, async (entityName, instance) => {
|
|
84
84
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
85
85
|
const entity = entities.find(entity => entity.name === entityName);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
await writeInstance(dataRootPath, entity, instance.id, instance.content);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
86
|
+
await writeInstance(dataRootPath, entity, instance.id, instance.content);
|
|
87
|
+
});
|
|
91
88
|
};
|
|
@@ -17,7 +17,7 @@ import type { SerializedReferenceIdentifierType } from "../../shared/schema/type
|
|
|
17
17
|
import type { SerializedStringType } from "../../shared/schema/types/StringType.ts";
|
|
18
18
|
import type { SerializedTranslationObjectType } from "../../shared/schema/types/TranslationObjectType.ts";
|
|
19
19
|
import type { SerializedTypeArgumentType } from "../../shared/schema/types/TypeArgumentType.ts";
|
|
20
|
-
import type
|
|
20
|
+
import { type DatabaseInMemory } from "../utils/databaseInMemory.ts";
|
|
21
21
|
import { type Decl, type IncludableDeclP } from "./declarations/Declaration.ts";
|
|
22
22
|
import { type EntityDecl } from "./declarations/EntityDecl.ts";
|
|
23
23
|
import { type EnumDecl } from "./declarations/EnumDecl.ts";
|
|
@@ -62,7 +62,7 @@ export interface Validators {
|
|
|
62
62
|
useStyling: boolean;
|
|
63
63
|
checkReferentialIntegrity: (identifier: IdentifierToCheck) => Error[];
|
|
64
64
|
}
|
|
65
|
-
export declare const createValidators: (
|
|
65
|
+
export declare const createValidators: (databaseInMemory: DatabaseInMemory, useStyling: boolean, checkReferentialIntegrity?: boolean) => Validators;
|
|
66
66
|
export type Predicate<T extends Node> = (node: Node) => node is T;
|
|
67
67
|
export type GetNestedDeclarations<T extends Node = Node> = (addedDecls: NestedDecl[], node: T, parentDecl: Decl | undefined) => NestedDecl[];
|
|
68
68
|
export declare const getNestedDeclarations: GetNestedDeclarations;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NodeKind } from "../../shared/schema/Node.js";
|
|
2
2
|
import { assertExhaustive } from "../../shared/utils/typeSafety.js";
|
|
3
|
+
import { getInstancesOfEntityFromDatabaseInMemory, } from "../utils/databaseInMemory.js";
|
|
3
4
|
import { entity, json } from "../utils/errorFormatting.js";
|
|
4
5
|
import { isDecl } from "./declarations/Declaration.js";
|
|
5
6
|
import { getNestedDeclarationsInEntityDecl, getReferencesForEntityDecl, resolveTypeArgumentsInEntityDecl, serializeEntityDecl, validateEntityDecl, } from "./declarations/EntityDecl.js";
|
|
@@ -80,13 +81,10 @@ export const flatMapAuxiliaryDecls = (callbackFn, declarations) => reduceNodes((
|
|
|
80
81
|
});
|
|
81
82
|
return declsWithCurrentDecl.concat(normalizedResult);
|
|
82
83
|
}, declarations);
|
|
83
|
-
export const createValidators = (
|
|
84
|
+
export const createValidators = (databaseInMemory, useStyling, checkReferentialIntegrity = true) => ({
|
|
84
85
|
useStyling,
|
|
85
86
|
checkReferentialIntegrity: checkReferentialIntegrity
|
|
86
|
-
? ({ name, value }) =>
|
|
87
|
-
instance.content !== null &&
|
|
88
|
-
!Array.isArray(instance.content) &&
|
|
89
|
-
instance.id === value)
|
|
87
|
+
? ({ name, value }) => getInstancesOfEntityFromDatabaseInMemory(databaseInMemory, name).some(instance => instance.id === value)
|
|
90
88
|
? []
|
|
91
89
|
: [
|
|
92
90
|
ReferenceError(`Invalid reference to instance of entity ${entity(`"${name}"`, useStyling)} with identifier ${json(value, useStyling)}`),
|
|
@@ -23,7 +23,7 @@ export type EntityDisplayName<T extends TConstraint> = Leaves<AsType<ObjectType<
|
|
|
23
23
|
pathInLocaleMap?: string;
|
|
24
24
|
} | null;
|
|
25
25
|
type TConstraint = Record<string, MemberDecl>;
|
|
26
|
-
export interface EntityDecl<Name extends string = string, T extends TConstraint = TConstraint, FK extends
|
|
26
|
+
export interface EntityDecl<Name extends string = string, T extends TConstraint = TConstraint, FK extends Extract<keyof T, string> | undefined = Extract<keyof T, string> | undefined> extends BaseDecl<Name, []> {
|
|
27
27
|
kind: NodeKind["EntityDecl"];
|
|
28
28
|
namePlural: string;
|
|
29
29
|
type: Lazy<ObjectType<T>>;
|
|
@@ -35,7 +35,7 @@ export interface EntityDecl<Name extends string = string, T extends TConstraint
|
|
|
35
35
|
displayNameCustomizer?: DisplayNameCustomizer<ObjectType<T>>;
|
|
36
36
|
isDeprecated?: boolean;
|
|
37
37
|
}
|
|
38
|
-
export interface EntityDeclWithParentReference<Name extends string = string, T extends TConstraint = TConstraint, FK extends keyof T
|
|
38
|
+
export interface EntityDeclWithParentReference<Name extends string = string, T extends TConstraint = TConstraint, FK extends Extract<keyof T, string> = Extract<keyof T, string>> extends EntityDecl<Name, T, FK> {
|
|
39
39
|
}
|
|
40
40
|
export declare const EntityDecl: {
|
|
41
41
|
<Name extends string, T extends TConstraint>(sourceUrl: string, options: {
|
|
@@ -50,7 +50,7 @@ export declare const EntityDecl: {
|
|
|
50
50
|
displayNameCustomizer?: DisplayNameCustomizer<ObjectType<T>>;
|
|
51
51
|
isDeprecated?: boolean;
|
|
52
52
|
}): EntityDecl<Name, T, undefined>;
|
|
53
|
-
<Name extends string, T extends TConstraint, FK extends keyof T
|
|
53
|
+
<Name extends string, T extends TConstraint, FK extends Extract<keyof T, string>>(sourceUrl: string, options: {
|
|
54
54
|
name: Name;
|
|
55
55
|
namePlural: string;
|
|
56
56
|
comment?: string;
|
|
@@ -66,7 +66,7 @@ export declare const EntityDecl: {
|
|
|
66
66
|
};
|
|
67
67
|
export { EntityDecl as Entity };
|
|
68
68
|
export declare const isEntityDecl: Predicate<EntityDecl>;
|
|
69
|
-
export declare const isEntityDeclWithParentReference: <Name extends string, T extends TConstraint, FK extends
|
|
69
|
+
export declare const isEntityDeclWithParentReference: <Name extends string, T extends TConstraint, FK extends Extract<keyof T, string> | undefined>(decl: EntityDecl<Name, T, FK>) => decl is EntityDecl<Name, T, NonNullable<FK>>;
|
|
70
70
|
export declare const getNestedDeclarationsInEntityDecl: GetNestedDeclarations<EntityDecl>;
|
|
71
71
|
export declare const validateEntityDecl: Validator<EntityDecl>;
|
|
72
72
|
export declare const resolveTypeArgumentsInEntityDecl: TypeArgumentsResolver<EntityDecl>;
|
|
@@ -29,9 +29,6 @@ export const resolveTypeArgumentsInIncludeIdentifierType = ((args, type, inDecl)
|
|
|
29
29
|
else {
|
|
30
30
|
const parentDecl = inDecl[inDecl.length - 1];
|
|
31
31
|
const grandParentDecl = inDecl[inDecl.length - 2];
|
|
32
|
-
if (grandParentDecl?.name === "AdventurePointsDependingOnActiveInstancesMultiplier") {
|
|
33
|
-
console.log(type.args.every((arg, argIndex) => isTypeArgumentType(arg) && parentDecl?.parameters[argIndex] === arg.argument));
|
|
34
|
-
}
|
|
35
32
|
if (type.reference === parentDecl &&
|
|
36
33
|
parentDecl.parameters.length > 0 &&
|
|
37
34
|
grandParentDecl &&
|
|
@@ -7,6 +7,8 @@ import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
|
|
|
7
7
|
import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
|
|
8
8
|
import { serializeNode } from "../../schema/index.js";
|
|
9
9
|
import { getChildInstances } from "../../utils/childInstances.js";
|
|
10
|
+
import { countInstancesOfEntityInDatabaseInMemory, getInstanceOfEntityFromDatabaseInMemory, getInstancesOfEntityFromDatabaseInMemory, } from "../../utils/databaseInMemory.js";
|
|
11
|
+
import { HTTPError } from "../../utils/error.js";
|
|
10
12
|
import { createChildInstancesForInstanceIdGetter } from "../utils/childInstances.js";
|
|
11
13
|
import { createInstance, deleteInstance, updateInstance } from "../utils/instanceOperations.js";
|
|
12
14
|
const debug = Debug("tsondb:server:api:declarations");
|
|
@@ -33,7 +35,7 @@ declarationsApi.get("/", (req, res) => {
|
|
|
33
35
|
const body = {
|
|
34
36
|
declarations: filteredEntities.map(decl => ({
|
|
35
37
|
declaration: serializeNode(decl),
|
|
36
|
-
instanceCount: req.
|
|
38
|
+
instanceCount: countInstancesOfEntityInDatabaseInMemory(req.databaseInMemory, decl.name),
|
|
37
39
|
})),
|
|
38
40
|
localeEntity: req.localeEntity?.name,
|
|
39
41
|
};
|
|
@@ -47,7 +49,7 @@ declarationsApi.get("/:name", (req, res) => {
|
|
|
47
49
|
}
|
|
48
50
|
const body = {
|
|
49
51
|
declaration: serializeNode(decl),
|
|
50
|
-
instanceCount: req.
|
|
52
|
+
instanceCount: countInstancesOfEntityInDatabaseInMemory(req.databaseInMemory, decl.name),
|
|
51
53
|
isLocaleEntity: decl === req.localeEntity,
|
|
52
54
|
};
|
|
53
55
|
res.json(body);
|
|
@@ -64,9 +66,9 @@ declarationsApi.get("/:name/instances", (req, res) => {
|
|
|
64
66
|
}
|
|
65
67
|
const getChildInstancesForInstanceId = createChildInstancesForInstanceIdGetter(req);
|
|
66
68
|
const body = {
|
|
67
|
-
instances: req.
|
|
68
|
-
|
|
69
|
-
.toSorted((a, b) => a.displayName.localeCompare(b.displayName, undefined, { numeric: true }))
|
|
69
|
+
instances: getInstancesOfEntityFromDatabaseInMemory(req.databaseInMemory, decl.name)
|
|
70
|
+
.map(instanceContainer => getInstanceContainerOverview(decl, instanceContainer, req.getInstanceById, getChildInstancesForInstanceId, req.locales))
|
|
71
|
+
.toSorted((a, b) => a.displayName.localeCompare(b.displayName, undefined, { numeric: true })),
|
|
70
72
|
isLocaleEntity: decl === req.localeEntity,
|
|
71
73
|
};
|
|
72
74
|
res.json(body);
|
|
@@ -91,7 +93,12 @@ declarationsApi.post("/:name/instances", async (req, res) => {
|
|
|
91
93
|
res.json(body);
|
|
92
94
|
}
|
|
93
95
|
else {
|
|
94
|
-
|
|
96
|
+
if (result.error instanceof HTTPError) {
|
|
97
|
+
res.status(result.error.code).send(result.error.message);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
res.status(500).send(result.error.message);
|
|
101
|
+
}
|
|
95
102
|
}
|
|
96
103
|
});
|
|
97
104
|
declarationsApi.get("/:name/instances/:id", (req, res) => {
|
|
@@ -104,7 +111,7 @@ declarationsApi.get("/:name/instances/:id", (req, res) => {
|
|
|
104
111
|
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
105
112
|
return;
|
|
106
113
|
}
|
|
107
|
-
const instance = req.
|
|
114
|
+
const instance = getInstanceOfEntityFromDatabaseInMemory(req.databaseInMemory, decl.name, req.params.id);
|
|
108
115
|
if (instance === undefined) {
|
|
109
116
|
res.status(404).send(`Instance "${req.params.id}" not found`);
|
|
110
117
|
return;
|
|
@@ -135,7 +142,12 @@ declarationsApi.put("/:name/instances/:id", async (req, res) => {
|
|
|
135
142
|
res.json(body);
|
|
136
143
|
}
|
|
137
144
|
else {
|
|
138
|
-
|
|
145
|
+
if (result.error instanceof HTTPError) {
|
|
146
|
+
res.status(result.error.code).send(result.error.message);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
res.status(500).send(result.error.message);
|
|
150
|
+
}
|
|
139
151
|
}
|
|
140
152
|
});
|
|
141
153
|
declarationsApi.delete("/:name/instances/:id", async (req, res) => {
|
|
@@ -157,7 +169,12 @@ declarationsApi.delete("/:name/instances/:id", async (req, res) => {
|
|
|
157
169
|
res.json(body);
|
|
158
170
|
}
|
|
159
171
|
else {
|
|
160
|
-
|
|
172
|
+
if (result.error instanceof HTTPError) {
|
|
173
|
+
res.status(result.error.code).send(result.error.message);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
res.status(500).send(result.error.message);
|
|
177
|
+
}
|
|
161
178
|
}
|
|
162
179
|
});
|
|
163
180
|
declarationsApi.get("/:name/instances/:id/children", (req, res) => {
|
|
@@ -171,7 +188,7 @@ declarationsApi.get("/:name/instances/:id/children", (req, res) => {
|
|
|
171
188
|
return;
|
|
172
189
|
}
|
|
173
190
|
const body = {
|
|
174
|
-
instances: getChildInstances(req.
|
|
191
|
+
instances: getChildInstances(req.databaseInMemory, decl, req.params.id),
|
|
175
192
|
};
|
|
176
193
|
res.json(body);
|
|
177
194
|
});
|
|
@@ -3,7 +3,7 @@ import express, {} from "express";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { hasFileChanges, splitBranchName } from "../../../shared/utils/git.js";
|
|
5
5
|
import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getGroupedInstancesFromDatabaseInMemory } from "../../utils/databaseInMemory.js";
|
|
7
7
|
import { reinit } from "../init.js";
|
|
8
8
|
import { createChildInstancesForInstanceIdGetter } from "../utils/childInstances.js";
|
|
9
9
|
const debug = Debug("tsondb:server:api:git");
|
|
@@ -24,16 +24,13 @@ gitApi.get("/", (req, res) => {
|
|
|
24
24
|
});
|
|
25
25
|
gitApi.get("/status", async (req, res) => {
|
|
26
26
|
const status = await req.git.status();
|
|
27
|
-
attachGitStatusToInstancesByEntityName(req.instancesByEntityName, req.dataRoot,
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
29
|
-
req.gitRoot, status);
|
|
30
27
|
const getChildInstancesForInstanceId = createChildInstancesForInstanceIdGetter(req);
|
|
31
28
|
const body = {
|
|
32
29
|
currentBranch: status.current,
|
|
33
30
|
trackingBranch: status.tracking,
|
|
34
31
|
commitsAhead: status.ahead,
|
|
35
32
|
commitsBehind: status.behind,
|
|
36
|
-
instances: Object.fromEntries(
|
|
33
|
+
instances: Object.fromEntries(getGroupedInstancesFromDatabaseInMemory(req.databaseInMemory).map(([entityName, instances]) => [
|
|
37
34
|
entityName,
|
|
38
35
|
instances
|
|
39
36
|
.filter(instance => hasFileChanges(instance.gitStatus))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
2
|
import express from "express";
|
|
3
|
+
import { getGroupedInstancesFromDatabaseInMemory } from "../../utils/databaseInMemory.js";
|
|
3
4
|
import { getDisplayNameFromEntityInstance } from "../../utils/displayName.js";
|
|
4
5
|
import { createChildInstancesForInstanceIdGetter } from "../utils/childInstances.js";
|
|
5
6
|
const debug = Debug("tsondb:server:api:instances");
|
|
@@ -11,9 +12,7 @@ instancesApi.use((req, _res, next) => {
|
|
|
11
12
|
instancesApi.get("/", (req, res) => {
|
|
12
13
|
const getChildInstancesForInstanceId = createChildInstancesForInstanceIdGetter(req);
|
|
13
14
|
const body = {
|
|
14
|
-
instances: Object.fromEntries(
|
|
15
|
-
.filter(([entityName]) => Object.hasOwn(req.entitiesByName, entityName))
|
|
16
|
-
.map(([entityName, instances]) => [
|
|
15
|
+
instances: Object.fromEntries(getGroupedInstancesFromDatabaseInMemory(req.databaseInMemory).map(([entityName, instances]) => [
|
|
17
16
|
entityName,
|
|
18
17
|
instances
|
|
19
18
|
.map(instance => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { SimpleGit } from "simple-git";
|
|
2
2
|
import type { SerializedDecl } from "../../shared/schema/declarations/Declaration.ts";
|
|
3
|
-
import type { InstanceContainer
|
|
3
|
+
import type { InstanceContainer } from "../../shared/utils/instances.ts";
|
|
4
4
|
import type { HomeLayoutSection } from "../config.ts";
|
|
5
5
|
import type { Decl } from "../schema/declarations/Declaration.ts";
|
|
6
6
|
import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
|
|
7
7
|
import type { Schema } from "../schema/Schema.ts";
|
|
8
|
+
import type { DatabaseInMemory } from "../utils/databaseInMemory.ts";
|
|
8
9
|
import type { ReferencesToInstances } from "../utils/references.ts";
|
|
9
10
|
export type ServerOptions = {
|
|
10
11
|
port: number;
|
|
@@ -15,7 +16,7 @@ export interface TSONDBRequestLocals {
|
|
|
15
16
|
dataRoot: string;
|
|
16
17
|
declarations: readonly Decl[];
|
|
17
18
|
entities: readonly EntityDecl[];
|
|
18
|
-
|
|
19
|
+
databaseInMemory: DatabaseInMemory;
|
|
19
20
|
entitiesByName: Record<string, EntityDecl>;
|
|
20
21
|
serializedDeclarationsByName: Record<string, SerializedDecl>;
|
|
21
22
|
localeEntity?: EntityDecl;
|
|
@@ -36,4 +37,4 @@ declare global {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
export declare const createServer: (schema: Schema, dataRootPath: string,
|
|
40
|
+
export declare const createServer: (schema: Schema, dataRootPath: string, databaseInMemory: DatabaseInMemory, defaultLocales: string[], homeLayoutSections?: HomeLayoutSection[], options?: Partial<ServerOptions>, customStylesheetPath?: string) => Promise<void>;
|
|
@@ -16,7 +16,7 @@ const staticNodeModule = (moduleName) => {
|
|
|
16
16
|
}
|
|
17
17
|
return express.static(dirname(pathToPackageJson));
|
|
18
18
|
};
|
|
19
|
-
export const createServer = async (schema, dataRootPath,
|
|
19
|
+
export const createServer = async (schema, dataRootPath, databaseInMemory, defaultLocales, homeLayoutSections, options, customStylesheetPath) => {
|
|
20
20
|
const { port } = { ...defaultOptions, ...options };
|
|
21
21
|
const app = express();
|
|
22
22
|
app.use(express.static(join(import.meta.dirname, "../../../../public")));
|
|
@@ -27,7 +27,7 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
|
|
|
27
27
|
app.use("/js/client", express.static(join(import.meta.dirname, "../../../../dist/src/web")));
|
|
28
28
|
app.use("/js/shared", express.static(join(import.meta.dirname, "../../../../dist/src/shared")));
|
|
29
29
|
app.use(express.json());
|
|
30
|
-
const requestLocals = await init(schema, dataRootPath,
|
|
30
|
+
const requestLocals = await init(schema, dataRootPath, databaseInMemory, defaultLocales, homeLayoutSections);
|
|
31
31
|
app.use((req, _res, next) => {
|
|
32
32
|
debug("%s %s", req.method, req.originalUrl);
|
|
33
33
|
requestLocals.setLocal = (key, value) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
2
1
|
import type { HomeLayoutSection } from "../config.ts";
|
|
3
2
|
import type { Schema } from "../schema/Schema.ts";
|
|
3
|
+
import { type DatabaseInMemory } from "../utils/databaseInMemory.ts";
|
|
4
4
|
import type { TSONDBRequestLocals } from "./index.ts";
|
|
5
|
-
export declare const init: (schema: Schema, dataRootPath: string,
|
|
5
|
+
export declare const init: (schema: Schema, dataRootPath: string, databaseInMemory: DatabaseInMemory, defaultLocales: string[], homeLayoutSections?: HomeLayoutSection[]) => Promise<Omit<TSONDBRequestLocals, "setLocal">>;
|
|
6
6
|
export declare const reinit: (locals: TSONDBRequestLocals) => Promise<void>;
|
|
@@ -2,7 +2,8 @@ import Debug from "debug";
|
|
|
2
2
|
import { simpleGit } from "simple-git";
|
|
3
3
|
import { isEntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
4
4
|
import { resolveTypeArgumentsInDecls, serializeNode } from "../schema/index.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createDatabaseInMemory, getInstanceFromDatabaseInMemory, } from "../utils/databaseInMemory.js";
|
|
6
|
+
import { attachGitStatusToDatabaseInMemory } from "../utils/git.js";
|
|
6
7
|
import { getReferencesToInstances } from "../utils/references.js";
|
|
7
8
|
const debug = Debug("tsondb:server:init");
|
|
8
9
|
const getGit = async (dataRootPath) => {
|
|
@@ -21,7 +22,7 @@ const getGit = async (dataRootPath) => {
|
|
|
21
22
|
return { git };
|
|
22
23
|
}
|
|
23
24
|
};
|
|
24
|
-
export const init = async (schema, dataRootPath,
|
|
25
|
+
export const init = async (schema, dataRootPath, databaseInMemory, defaultLocales, homeLayoutSections) => {
|
|
25
26
|
const { git, root: gitRoot, status: gitStatus } = await getGit(dataRootPath);
|
|
26
27
|
const declarations = resolveTypeArgumentsInDecls(schema.declarations);
|
|
27
28
|
debug("resolved type arguments in declarations");
|
|
@@ -29,16 +30,17 @@ export const init = async (schema, dataRootPath, instancesByEntityName, defaultL
|
|
|
29
30
|
debug("found %d entities in declarations", entities.length);
|
|
30
31
|
const entitiesByName = Object.fromEntries(entities.map(entity => [entity.name, entity]));
|
|
31
32
|
const serializedDeclarationsByName = Object.fromEntries(declarations.map(decl => [decl.name, serializeNode(decl)]));
|
|
32
|
-
const referencesToInstances = await getReferencesToInstances(
|
|
33
|
+
const referencesToInstances = await getReferencesToInstances(databaseInMemory, serializedDeclarationsByName);
|
|
33
34
|
debug("created references cache");
|
|
34
35
|
if (gitStatus) {
|
|
35
|
-
|
|
36
|
+
databaseInMemory = attachGitStatusToDatabaseInMemory(databaseInMemory, dataRootPath, gitRoot, gitStatus);
|
|
36
37
|
debug("retrieved git status to instances");
|
|
37
38
|
}
|
|
38
39
|
const getInstanceById = id => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const res = getInstanceFromDatabaseInMemory(requestLocals.databaseInMemory, id);
|
|
41
|
+
if (res) {
|
|
42
|
+
const [entityName, instance] = res;
|
|
43
|
+
if (requestLocals.entitiesByName[entityName]) {
|
|
42
44
|
return { entity: requestLocals.entitiesByName[entityName], instance };
|
|
43
45
|
}
|
|
44
46
|
}
|
|
@@ -50,7 +52,7 @@ export const init = async (schema, dataRootPath, instancesByEntityName, defaultL
|
|
|
50
52
|
dataRoot: dataRootPath,
|
|
51
53
|
declarations: declarations,
|
|
52
54
|
entities: entities,
|
|
53
|
-
|
|
55
|
+
databaseInMemory,
|
|
54
56
|
entitiesByName: entitiesByName,
|
|
55
57
|
serializedDeclarationsByName,
|
|
56
58
|
localeEntity: schema.localeEntity,
|
|
@@ -63,10 +65,11 @@ export const init = async (schema, dataRootPath, instancesByEntityName, defaultL
|
|
|
63
65
|
return requestLocals;
|
|
64
66
|
};
|
|
65
67
|
export const reinit = async (locals) => {
|
|
66
|
-
|
|
67
|
-
locals.setLocal("referencesToInstances", await getReferencesToInstances(locals.instancesByEntityName, locals.serializedDeclarationsByName));
|
|
68
|
+
let databaseInMemory = await createDatabaseInMemory(locals.dataRoot, locals.entities);
|
|
68
69
|
const gitStatus = (await locals.git.checkIsRepo()) ? await locals.git.status() : undefined;
|
|
69
70
|
if (locals.gitRoot && gitStatus) {
|
|
70
|
-
|
|
71
|
+
databaseInMemory = attachGitStatusToDatabaseInMemory(databaseInMemory, locals.dataRoot, locals.gitRoot, gitStatus);
|
|
71
72
|
}
|
|
73
|
+
locals.setLocal("databaseInMemory", databaseInMemory);
|
|
74
|
+
locals.setLocal("referencesToInstances", await getReferencesToInstances(databaseInMemory, locals.serializedDeclarationsByName));
|
|
72
75
|
};
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { type Result } from "../../../shared/utils/result.ts";
|
|
2
|
-
import { type EntityTaggedInstanceContainerWithChildInstances, type UnsafeEntityTaggedInstanceContainerWithChildInstances } from "../../utils/childInstances.ts";
|
|
3
1
|
import type { GetChildInstancesForInstanceId } from "../../utils/displayName.ts";
|
|
4
2
|
import type { TSONDBRequestLocals } from "../index.ts";
|
|
5
|
-
export declare const updateLocalsAfterInstanceTreeChangeToReflectDiskState: (locals: TSONDBRequestLocals, parentId: string | undefined, parentEntityName: string, oldChildInstances: EntityTaggedInstanceContainerWithChildInstances[], childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[]) => Promise<Result<void, [code: number, message: string]>>;
|
|
6
3
|
export declare const createChildInstancesForInstanceIdGetter: (locals: TSONDBRequestLocals) => GetChildInstancesForInstanceId;
|
|
@@ -1,46 +1,5 @@
|
|
|
1
|
-
import { error, ok } from "../../../shared/utils/result.js";
|
|
2
1
|
import { isEntityDeclWithParentReference } from "../../schema/index.js";
|
|
3
|
-
import { getChildInstancesFromEntity
|
|
4
|
-
import { updateLocalsAfterInstanceChangeToReflectDiskState } from "./instanceOperations.js";
|
|
5
|
-
export const updateLocalsAfterInstanceTreeChangeToReflectDiskState = async (locals, parentId, parentEntityName, oldChildInstances, childInstances) => {
|
|
6
|
-
const parentEntity = locals.entitiesByName[parentEntityName];
|
|
7
|
-
if (parentEntity === undefined) {
|
|
8
|
-
return error([400, `Unknown entity "${parentEntityName}"`]);
|
|
9
|
-
}
|
|
10
|
-
if (parentId !== undefined) {
|
|
11
|
-
// existing parent, some child instances may already exist
|
|
12
|
-
for (const oldChildInstance of oldChildInstances) {
|
|
13
|
-
const newChildInstance = childInstances.find(ci => ci.id === oldChildInstance.id);
|
|
14
|
-
if (newChildInstance === undefined) {
|
|
15
|
-
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, oldChildInstance.entityName, oldChildInstance.id, undefined);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, oldChildInstance.entityName, oldChildInstance.id, newChildInstance.content);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
for (const newChildInstance of childInstances.filter(predicate => predicate.id === undefined)) {
|
|
22
|
-
if (newChildInstance.id !== undefined) {
|
|
23
|
-
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, newChildInstance.entityName, newChildInstance.id, newChildInstance.content);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
// new parent, all child instances are new
|
|
29
|
-
for (const newChildInstance of childInstances) {
|
|
30
|
-
if (newChildInstance.id !== undefined) {
|
|
31
|
-
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, newChildInstance.entityName, newChildInstance.id, newChildInstance.content);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
// check recursively for child instances of child instances
|
|
36
|
-
for (const childInstance of childInstances) {
|
|
37
|
-
const oldChildInstance = childInstance.id
|
|
38
|
-
? oldChildInstances.find(ci => ci.id === childInstance.id)
|
|
39
|
-
: undefined;
|
|
40
|
-
await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, childInstance.id, childInstance.entityName, oldChildInstance ? oldChildInstance.childInstances : [], childInstance.childInstances);
|
|
41
|
-
}
|
|
42
|
-
return ok();
|
|
43
|
-
};
|
|
2
|
+
import { getChildInstancesFromEntity } from "../../utils/childInstances.js";
|
|
44
3
|
export const createChildInstancesForInstanceIdGetter = (locals) => (parentEntityName, parentId, childEntityName) => {
|
|
45
4
|
const parentEntity = locals.entitiesByName[parentEntityName];
|
|
46
5
|
const childEntity = locals.entitiesByName[childEntityName];
|
|
@@ -49,5 +8,5 @@ export const createChildInstancesForInstanceIdGetter = (locals) => (parentEntity
|
|
|
49
8
|
!isEntityDeclWithParentReference(childEntity)) {
|
|
50
9
|
return [];
|
|
51
10
|
}
|
|
52
|
-
return getChildInstancesFromEntity(locals.
|
|
11
|
+
return getChildInstancesFromEntity(locals.databaseInMemory, parentEntity, parentId, childEntity);
|
|
53
12
|
};
|
|
@@ -2,7 +2,6 @@ import type { InstanceContainer } from "../../../shared/utils/instances.ts";
|
|
|
2
2
|
import { type Result } from "../../../shared/utils/result.ts";
|
|
3
3
|
import { type CreatedEntityTaggedInstanceContainerWithChildInstances, type UpdatedEntityTaggedInstanceContainerWithChildInstances } from "../../utils/childInstances.ts";
|
|
4
4
|
import type { TSONDBRequestLocals } from "../index.ts";
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
7
|
-
export declare const
|
|
8
|
-
export declare const deleteInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
5
|
+
export declare const createInstance: (locals: TSONDBRequestLocals, instance: CreatedEntityTaggedInstanceContainerWithChildInstances, idQueryParam: unknown) => Promise<Result<InstanceContainer, Error>>;
|
|
6
|
+
export declare const updateInstance: (locals: TSONDBRequestLocals, instance: UpdatedEntityTaggedInstanceContainerWithChildInstances) => Promise<Result<InstanceContainer, Error>>;
|
|
7
|
+
export declare const deleteInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string) => Promise<Result<InstanceContainer, Error>>;
|