tsondb 0.5.18 → 0.6.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/bin/tsondb.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/node/config.d.ts +20 -0
- package/dist/src/node/config.js +5 -0
- package/dist/src/node/index.d.ts +3 -2
- package/dist/src/node/index.js +13 -8
- package/dist/src/node/renderers/jsonschema/render.d.ts +1 -1
- package/dist/src/node/renderers/jsonschema/render.js +12 -7
- package/dist/src/node/renderers/ts/render.d.ts +1 -1
- package/dist/src/node/renderers/ts/render.js +12 -6
- package/dist/src/node/schema/Node.d.ts +100 -29
- package/dist/src/node/schema/Node.js +268 -61
- package/dist/src/node/schema/Schema.js +96 -1
- package/dist/src/node/schema/TypeParameter.d.ts +7 -9
- package/dist/src/node/schema/TypeParameter.js +7 -5
- package/dist/src/node/schema/declarations/Declaration.d.ts +10 -28
- package/dist/src/node/schema/declarations/Declaration.js +8 -110
- package/dist/src/node/schema/declarations/EntityDecl.d.ts +48 -48
- package/dist/src/node/schema/declarations/EntityDecl.js +15 -12
- package/dist/src/node/schema/declarations/EnumDecl.d.ts +14 -17
- package/dist/src/node/schema/declarations/EnumDecl.js +12 -12
- package/dist/src/node/schema/declarations/TypeAliasDecl.d.ts +8 -14
- package/dist/src/node/schema/declarations/TypeAliasDecl.js +10 -11
- package/dist/src/node/schema/index.d.ts +0 -2
- package/dist/src/node/schema/index.js +0 -1
- package/dist/src/node/schema/types/Type.d.ts +19 -42
- package/dist/src/node/schema/types/Type.js +29 -167
- package/dist/src/node/schema/types/generic/ArrayType.d.ts +5 -14
- package/dist/src/node/schema/types/generic/ArrayType.js +15 -20
- package/dist/src/node/schema/types/generic/EnumType.d.ts +5 -17
- package/dist/src/node/schema/types/generic/EnumType.js +12 -20
- package/dist/src/node/schema/types/generic/ObjectType.d.ts +5 -19
- package/dist/src/node/schema/types/generic/ObjectType.js +11 -15
- package/dist/src/node/schema/types/primitives/BooleanType.d.ts +7 -8
- package/dist/src/node/schema/types/primitives/BooleanType.js +5 -4
- package/dist/src/node/schema/types/primitives/DateType.d.ts +6 -8
- package/dist/src/node/schema/types/primitives/DateType.js +5 -4
- package/dist/src/node/schema/types/primitives/FloatType.d.ts +9 -21
- package/dist/src/node/schema/types/primitives/FloatType.js +5 -4
- package/dist/src/node/schema/types/primitives/IntegerType.d.ts +9 -21
- package/dist/src/node/schema/types/primitives/IntegerType.js +5 -4
- package/dist/src/node/schema/types/primitives/StringType.d.ts +6 -10
- package/dist/src/node/schema/types/primitives/StringType.js +5 -4
- package/dist/src/node/schema/types/references/ChildEntitiesType.d.ts +18 -0
- package/dist/src/node/schema/types/references/ChildEntitiesType.js +18 -0
- package/dist/src/node/schema/types/references/IncludeIdentifierType.d.ts +7 -13
- package/dist/src/node/schema/types/references/IncludeIdentifierType.js +12 -12
- package/dist/src/node/schema/types/references/NestedEntityMapType.d.ts +7 -17
- package/dist/src/node/schema/types/references/NestedEntityMapType.js +11 -13
- package/dist/src/node/schema/types/references/ReferenceIdentifierType.d.ts +4 -10
- package/dist/src/node/schema/types/references/ReferenceIdentifierType.js +3 -5
- package/dist/src/node/schema/types/references/TypeArgumentType.d.ts +5 -10
- package/dist/src/node/schema/types/references/TypeArgumentType.js +10 -5
- package/dist/src/node/server/api/declarations.js +24 -6
- package/dist/src/node/server/api/index.js +11 -0
- package/dist/src/node/server/api/instances.js +9 -6
- package/dist/src/node/server/index.d.ts +6 -1
- package/dist/src/node/server/index.js +7 -3
- package/dist/src/node/server/init.d.ts +2 -1
- package/dist/src/node/server/init.js +11 -11
- package/dist/src/node/server/utils/childInstances.d.ts +4 -0
- package/dist/src/node/server/utils/childInstances.js +41 -0
- package/dist/src/node/server/utils/instanceOperations.d.ts +8 -0
- package/dist/src/node/server/utils/instanceOperations.js +107 -0
- package/dist/src/node/server/utils/locales.d.ts +2 -0
- package/dist/src/node/server/utils/locales.js +8 -0
- package/dist/src/node/utils/childInstances.d.ts +32 -0
- package/dist/src/node/utils/childInstances.js +164 -0
- package/dist/src/node/utils/displayName.d.ts +2 -1
- package/dist/src/node/utils/displayName.js +5 -4
- package/dist/src/node/utils/files.d.ts +5 -0
- package/dist/src/node/utils/files.js +9 -0
- package/dist/src/node/utils/instanceOperations.d.ts +14 -0
- package/dist/src/node/utils/instanceOperations.js +88 -0
- package/dist/src/node/utils/instances.d.ts +1 -1
- package/dist/src/node/utils/instances.js +12 -6
- package/dist/src/node/utils/references.d.ts +3 -1
- package/dist/src/node/utils/references.js +43 -18
- package/dist/src/node/utils/referencesWorker.d.ts +5 -0
- package/dist/src/node/utils/referencesWorker.js +22 -0
- package/dist/src/node/utils/workers.d.ts +15 -0
- package/dist/src/node/utils/workers.js +90 -0
- package/dist/src/shared/api.d.ts +14 -1
- package/dist/src/shared/schema/Node.d.ts +63 -0
- package/dist/src/shared/schema/Node.js +128 -0
- package/dist/src/shared/schema/TypeParameter.d.ts +9 -0
- package/dist/src/shared/schema/TypeParameter.js +2 -0
- package/dist/src/shared/schema/declarations/Declaration.d.ts +21 -0
- package/dist/src/shared/schema/declarations/Declaration.js +4 -0
- package/dist/src/shared/schema/declarations/EntityDecl.d.ts +32 -0
- package/dist/src/shared/schema/declarations/EntityDecl.js +8 -0
- package/dist/src/shared/schema/declarations/EnumDecl.d.ts +11 -0
- package/dist/src/shared/schema/declarations/EnumDecl.js +9 -0
- package/dist/src/shared/schema/declarations/TypeAliasDecl.d.ts +11 -0
- package/dist/src/shared/schema/declarations/TypeAliasDecl.js +7 -0
- package/dist/src/shared/schema/types/ArrayType.d.ts +11 -0
- package/dist/src/shared/schema/types/ArrayType.js +8 -0
- package/dist/src/shared/schema/types/BooleanType.d.ts +7 -0
- package/dist/src/shared/schema/types/BooleanType.js +2 -0
- package/dist/src/shared/schema/types/ChildEntitiesType.d.ts +8 -0
- package/dist/src/shared/schema/types/ChildEntitiesType.js +2 -0
- package/dist/src/shared/schema/types/DateType.d.ts +8 -0
- package/dist/src/shared/schema/types/DateType.js +2 -0
- package/dist/src/shared/schema/types/EnumType.d.ts +14 -0
- package/dist/src/shared/schema/types/EnumType.js +28 -0
- package/dist/src/shared/schema/types/FloatType.d.ts +11 -0
- package/dist/src/shared/schema/types/FloatType.js +2 -0
- package/dist/src/shared/schema/types/IncludeIdentifierType.d.ts +11 -0
- package/dist/src/shared/schema/types/IncludeIdentifierType.js +13 -0
- package/dist/src/shared/schema/types/IntegerType.d.ts +11 -0
- package/dist/src/shared/schema/types/IntegerType.js +2 -0
- package/dist/src/shared/schema/types/NestedEntityMapType.d.ts +17 -0
- package/dist/src/shared/schema/types/NestedEntityMapType.js +14 -0
- package/dist/src/shared/schema/types/ObjectType.d.ts +19 -0
- package/dist/src/shared/schema/types/ObjectType.js +14 -0
- package/dist/src/shared/schema/types/ReferenceIdentifierType.d.ts +8 -0
- package/dist/src/shared/schema/types/ReferenceIdentifierType.js +2 -0
- package/dist/src/shared/schema/types/StringType.d.ts +10 -0
- package/dist/src/shared/schema/types/StringType.js +2 -0
- package/dist/src/shared/schema/types/Type.d.ts +20 -0
- package/dist/src/shared/schema/types/TypeArgumentType.d.ts +12 -0
- package/dist/src/shared/schema/types/TypeArgumentType.js +7 -0
- package/dist/src/shared/utils/array.d.ts +4 -0
- package/dist/src/shared/utils/array.js +30 -0
- package/dist/src/shared/utils/async.d.ts +8 -0
- package/dist/src/shared/utils/async.js +35 -0
- package/dist/src/shared/utils/compare.js +3 -3
- package/dist/src/shared/utils/displayName.d.ts +6 -2
- package/dist/src/shared/utils/displayName.js +21 -8
- package/dist/src/shared/utils/instances.d.ts +2 -3
- package/dist/src/shared/utils/instances.js +3 -1
- package/dist/src/shared/utils/markdown.d.ts +4 -0
- package/dist/src/shared/utils/markdown.js +173 -110
- package/dist/src/shared/utils/object.d.ts +2 -0
- package/dist/src/shared/utils/object.js +2 -0
- package/dist/src/shared/utils/result.d.ts +8 -2
- package/dist/src/shared/utils/result.js +1 -1
- package/dist/src/web/api/declarations.d.ts +26 -0
- package/dist/src/web/api/declarations.js +51 -0
- package/dist/src/web/api/git.d.ts +14 -0
- package/dist/src/web/api/git.js +20 -0
- package/dist/src/web/api/index.d.ts +1 -0
- package/dist/src/web/api/index.js +2 -0
- package/dist/src/web/api/instances.d.ts +2 -0
- package/dist/src/web/api/instances.js +2 -0
- package/dist/src/web/components/Git.js +19 -16
- package/dist/src/web/components/InstanceRouteSkeleton.d.ts +42 -0
- package/dist/src/web/components/InstanceRouteSkeleton.js +114 -0
- package/dist/src/web/components/Layout.js +3 -2
- package/dist/src/web/components/ModalDialog.d.ts +3 -0
- package/dist/src/web/components/ModalDialog.js +16 -0
- package/dist/src/web/components/Settings.d.ts +2 -0
- package/dist/src/web/components/Settings.js +47 -0
- package/dist/src/web/components/typeInputs/ArrayTypeInput.d.ts +3 -11
- package/dist/src/web/components/typeInputs/ArrayTypeInput.js +5 -4
- package/dist/src/web/components/typeInputs/BooleanTypeInput.d.ts +3 -6
- package/dist/src/web/components/typeInputs/BooleanTypeInput.js +2 -2
- package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.d.ts +6 -0
- package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.js +27 -0
- package/dist/src/web/components/typeInputs/DateTypeInput.d.ts +3 -6
- package/dist/src/web/components/typeInputs/DateTypeInput.js +2 -2
- package/dist/src/web/components/typeInputs/EnumTypeInput.d.ts +3 -11
- package/dist/src/web/components/typeInputs/EnumTypeInput.js +55 -22
- package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +3 -6
- package/dist/src/web/components/typeInputs/FloatTypeInput.js +2 -2
- package/dist/src/web/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.d.ts +3 -4
- package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.d.ts +3 -11
- package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.js +3 -2
- package/dist/src/web/components/typeInputs/IntegerTypeInput.d.ts +3 -6
- package/dist/src/web/components/typeInputs/IntegerTypeInput.js +2 -2
- package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.d.ts +3 -11
- package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.js +5 -4
- package/dist/src/web/components/typeInputs/ObjectTypeInput.d.ts +3 -11
- package/dist/src/web/components/typeInputs/ObjectTypeInput.js +8 -5
- package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.d.ts +3 -8
- package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.js +2 -2
- package/dist/src/web/components/typeInputs/StringTypeInput.d.ts +3 -6
- package/dist/src/web/components/typeInputs/StringTypeInput.js +3 -3
- package/dist/src/web/components/typeInputs/TypeInput.d.ts +12 -4
- package/dist/src/web/components/typeInputs/TypeInput.js +22 -17
- package/dist/src/web/components/typeInputs/utils/ValidationErrors.d.ts +1 -0
- package/dist/src/web/components/typeInputs/utils/ValidationErrors.js +1 -3
- package/dist/src/web/context/config.d.ts +11 -0
- package/dist/src/web/context/config.js +6 -0
- package/dist/src/web/context/entities.d.ts +8 -5
- package/dist/src/web/context/entities.js +1 -1
- package/dist/src/web/context/settings.d.ts +8 -0
- package/dist/src/web/context/settings.js +10 -0
- package/dist/src/web/hooks/useEntityFromRoute.d.ts +1 -1
- package/dist/src/web/hooks/useEntityFromRoute.js +2 -2
- package/dist/src/web/hooks/useInstanceNamesByEntity.d.ts +1 -1
- package/dist/src/web/hooks/useInstanceNamesByEntity.js +4 -2
- package/dist/src/web/hooks/useMappedAPIResource.js +2 -4
- package/dist/src/web/hooks/useSecondaryDeclarations.d.ts +1 -1
- package/dist/src/web/hooks/useSecondaryDeclarations.js +8 -4
- package/dist/src/web/hooks/useSettings.d.ts +10 -0
- package/dist/src/web/hooks/useSettings.js +51 -0
- package/dist/src/web/index.js +11 -5
- package/dist/src/web/routes/CreateInstance.js +40 -79
- package/dist/src/web/routes/Entity.js +42 -19
- package/dist/src/web/routes/Home.js +24 -5
- package/dist/src/web/routes/Instance.js +34 -85
- package/dist/src/web/utils/InlineMarkdown.d.ts +1 -1
- package/dist/src/web/utils/InlineMarkdown.js +13 -1
- package/dist/src/web/utils/api.d.ts +25 -0
- package/dist/src/web/utils/api.js +34 -0
- package/dist/src/web/utils/typeSkeleton.d.ts +1 -1
- package/dist/src/web/utils/typeSkeleton.js +2 -0
- package/package.json +2 -1
- package/public/css/styles.css +171 -12
- package/dist/src/node/schema/types/primitives/NumericType.d.ts +0 -6
- package/dist/src/node/schema/types/primitives/NumericType.js +0 -2
- package/dist/src/node/schema/types/primitives/PrimitiveType.d.ts +0 -6
- package/dist/src/node/schema/validation/type.d.ts +0 -4
- package/dist/src/node/schema/validation/type.js +0 -1
- package/dist/src/node/server/api/instanceOperations.d.ts +0 -6
- package/dist/src/node/server/api/instanceOperations.js +0 -93
- package/dist/src/shared/config.d.ts +0 -11
- package/dist/src/shared/config.js +0 -1
- package/dist/src/web/api.d.ts +0 -24
- package/dist/src/web/api.js +0 -201
- /package/dist/src/{node/schema/types/primitives/PrimitiveType.js → shared/schema/types/Type.js} +0 -0
|
@@ -2,11 +2,12 @@ import Debug from "debug";
|
|
|
2
2
|
import express from "express";
|
|
3
3
|
import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
|
|
4
4
|
import { isOk } from "../../../shared/utils/result.js";
|
|
5
|
-
import { serializeDecl } from "../../schema/declarations/Declaration.js";
|
|
6
5
|
import { isEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
7
6
|
import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
|
|
8
7
|
import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
|
|
9
|
-
import {
|
|
8
|
+
import { serializeNode } from "../../schema/index.js";
|
|
9
|
+
import { getChildInstances } from "../../utils/childInstances.js";
|
|
10
|
+
import { createInstance, deleteInstance, updateInstance } from "../utils/instanceOperations.js";
|
|
10
11
|
const debug = Debug("tsondb:server:api:declarations");
|
|
11
12
|
export const declarationsApi = express.Router();
|
|
12
13
|
declarationsApi.use((req, _res, next) => {
|
|
@@ -30,7 +31,7 @@ declarationsApi.get("/", (req, res) => {
|
|
|
30
31
|
}
|
|
31
32
|
const body = {
|
|
32
33
|
declarations: filteredEntities.map(decl => ({
|
|
33
|
-
declaration:
|
|
34
|
+
declaration: serializeNode(decl),
|
|
34
35
|
instanceCount: req.instancesByEntityName[decl.name]?.length ?? 0,
|
|
35
36
|
})),
|
|
36
37
|
localeEntity: req.localeEntity?.name,
|
|
@@ -44,7 +45,7 @@ declarationsApi.get("/:name", (req, res) => {
|
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
const body = {
|
|
47
|
-
declaration:
|
|
48
|
+
declaration: serializeNode(decl),
|
|
48
49
|
instanceCount: req.instancesByEntityName[decl.name]?.length ?? 0,
|
|
49
50
|
isLocaleEntity: decl === req.localeEntity,
|
|
50
51
|
};
|
|
@@ -78,7 +79,8 @@ declarationsApi.post("/:name/instances", async (req, res) => {
|
|
|
78
79
|
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
79
80
|
return;
|
|
80
81
|
}
|
|
81
|
-
const
|
|
82
|
+
const requestBody = req.body;
|
|
83
|
+
const result = await createInstance(req, requestBody.instance, req.query["id"]);
|
|
82
84
|
if (isOk(result)) {
|
|
83
85
|
const body = {
|
|
84
86
|
instance: result.value,
|
|
@@ -121,7 +123,8 @@ declarationsApi.put("/:name/instances/:id", async (req, res) => {
|
|
|
121
123
|
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
122
124
|
return;
|
|
123
125
|
}
|
|
124
|
-
const
|
|
126
|
+
const requestBody = req.body;
|
|
127
|
+
const result = await updateInstance(req, requestBody.instance);
|
|
125
128
|
if (isOk(result)) {
|
|
126
129
|
const body = {
|
|
127
130
|
instance: result.value,
|
|
@@ -155,3 +158,18 @@ declarationsApi.delete("/:name/instances/:id", async (req, res) => {
|
|
|
155
158
|
res.status(result.error[0]).send(result.error[1]);
|
|
156
159
|
}
|
|
157
160
|
});
|
|
161
|
+
declarationsApi.get("/:name/instances/:id/children", (req, res) => {
|
|
162
|
+
const decl = req.declarations.find(decl => decl.name === req.params.name);
|
|
163
|
+
if (decl === undefined) {
|
|
164
|
+
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!isEntityDecl(decl)) {
|
|
168
|
+
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const body = {
|
|
172
|
+
instances: getChildInstances(req.instancesByEntityName, decl, req.params.id),
|
|
173
|
+
};
|
|
174
|
+
res.json(body);
|
|
175
|
+
});
|
|
@@ -6,3 +6,14 @@ export const api = express.Router();
|
|
|
6
6
|
api.use("/declarations", declarationsApi);
|
|
7
7
|
api.use("/instances", instancesApi);
|
|
8
8
|
api.use("/git", gitApi);
|
|
9
|
+
api.get("/config", (req, res) => {
|
|
10
|
+
const body = {
|
|
11
|
+
localeEntityName: req.localeEntity?.name,
|
|
12
|
+
defaultLocales: req.defaultLocales,
|
|
13
|
+
homeLayoutSections: req.homeLayoutSections?.map(section => ({
|
|
14
|
+
...section,
|
|
15
|
+
entities: section.entities.map(entity => entity.name),
|
|
16
|
+
})),
|
|
17
|
+
};
|
|
18
|
+
res.json(body);
|
|
19
|
+
});
|
|
@@ -8,18 +8,21 @@ instancesApi.use((req, _res, next) => {
|
|
|
8
8
|
next();
|
|
9
9
|
});
|
|
10
10
|
instancesApi.get("/", (req, res) => {
|
|
11
|
-
const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
|
|
12
11
|
const body = {
|
|
13
12
|
instances: Object.fromEntries(Object.entries(req.instancesByEntityName)
|
|
14
13
|
.filter(([entityName]) => Object.hasOwn(req.entitiesByName, entityName))
|
|
15
14
|
.map(([entityName, instances]) => [
|
|
16
15
|
entityName,
|
|
17
|
-
instances.map(instance =>
|
|
18
|
-
|
|
19
|
-
name: getDisplayNameFromEntityInstance(
|
|
16
|
+
instances.map(instance => {
|
|
17
|
+
const { name, localeId } = getDisplayNameFromEntityInstance(
|
|
20
18
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
21
|
-
req.entitiesByName[entityName], instance.content, req.getInstanceById, locales)
|
|
22
|
-
|
|
19
|
+
req.entitiesByName[entityName], instance.content, req.getInstanceById, req.locales);
|
|
20
|
+
return {
|
|
21
|
+
id: instance.id,
|
|
22
|
+
name,
|
|
23
|
+
displayNameLocaleId: localeId,
|
|
24
|
+
};
|
|
25
|
+
}),
|
|
23
26
|
])),
|
|
24
27
|
};
|
|
25
28
|
res.json(body);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { SimpleGit } from "simple-git";
|
|
2
|
+
import type { SerializedDecl } from "../../shared/schema/declarations/Declaration.ts";
|
|
2
3
|
import type { InstanceContainer, InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
4
|
+
import type { HomeLayoutSection } from "../config.ts";
|
|
3
5
|
import type { Decl } from "../schema/declarations/Declaration.ts";
|
|
4
6
|
import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
|
|
5
7
|
import type { Schema } from "../schema/Schema.ts";
|
|
@@ -15,9 +17,12 @@ export interface TSONDBRequestLocals {
|
|
|
15
17
|
entities: readonly EntityDecl[];
|
|
16
18
|
instancesByEntityName: InstancesByEntityName;
|
|
17
19
|
entitiesByName: Record<string, EntityDecl>;
|
|
20
|
+
serializedDeclarationsByName: Record<string, SerializedDecl>;
|
|
18
21
|
localeEntity?: EntityDecl;
|
|
19
22
|
referencesToInstances: ReferencesToInstances;
|
|
23
|
+
defaultLocales: string[];
|
|
20
24
|
locales: string[];
|
|
25
|
+
homeLayoutSections?: HomeLayoutSection[];
|
|
21
26
|
getInstanceById: GetInstanceById;
|
|
22
27
|
}
|
|
23
28
|
export type GetInstanceById = (id: string) => {
|
|
@@ -30,4 +35,4 @@ declare global {
|
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
|
-
export declare const createServer: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, options?: Partial<ServerOptions>) => Promise<void>;
|
|
38
|
+
export declare const createServer: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, defaultLocales: string[], homeLayoutSections?: HomeLayoutSection[], options?: Partial<ServerOptions>) => Promise<void>;
|
|
@@ -4,6 +4,7 @@ import { findPackageJSON } from "node:module";
|
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { api } from "./api/index.js";
|
|
6
6
|
import { init } from "./init.js";
|
|
7
|
+
import { getLocalesFromRequest } from "./utils/locales.js";
|
|
7
8
|
const debug = Debug("tsondb:server");
|
|
8
9
|
const defaultOptions = {
|
|
9
10
|
port: 3000,
|
|
@@ -15,7 +16,7 @@ const staticNodeModule = (moduleName) => {
|
|
|
15
16
|
}
|
|
16
17
|
return express.static(dirname(pathToPackageJson));
|
|
17
18
|
};
|
|
18
|
-
export const createServer = async (schema, dataRootPath, instancesByEntityName, options) => {
|
|
19
|
+
export const createServer = async (schema, dataRootPath, instancesByEntityName, defaultLocales, homeLayoutSections, options) => {
|
|
19
20
|
const { port } = { ...defaultOptions, ...options };
|
|
20
21
|
const app = express();
|
|
21
22
|
app.use(express.static(join(import.meta.dirname, "../../../../public")));
|
|
@@ -24,18 +25,21 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
|
|
|
24
25
|
app.use("/js/client", express.static(join(import.meta.dirname, "../../../../dist/src/web")));
|
|
25
26
|
app.use("/js/shared", express.static(join(import.meta.dirname, "../../../../dist/src/shared")));
|
|
26
27
|
app.use(express.json());
|
|
27
|
-
const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName));
|
|
28
|
+
const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName), defaultLocales, homeLayoutSections);
|
|
28
29
|
app.use((req, _res, next) => {
|
|
29
30
|
debug("%s %s", req.method, req.originalUrl);
|
|
30
31
|
Object.assign(req, requestLocals);
|
|
32
|
+
req.locales = getLocalesFromRequest(req) ?? defaultLocales;
|
|
31
33
|
next();
|
|
32
34
|
});
|
|
33
35
|
app.use("/api", api);
|
|
34
36
|
const importMap = JSON.stringify({
|
|
35
37
|
imports: {
|
|
36
38
|
preact: "/js/node_modules/preact/dist/preact.module.js",
|
|
37
|
-
"preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
|
|
38
39
|
"preact/compat": "/js/node_modules/preact/compat/dist/compat.module.js",
|
|
40
|
+
"preact/debug": "/js/node_modules/preact/debug/dist/debug.module.js",
|
|
41
|
+
"preact/devtools": "/js/node_modules/preact/devtools/dist/devtools.module.js",
|
|
42
|
+
"preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
|
|
39
43
|
"preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
|
|
40
44
|
"preact-iso": "/js/node_modules/preact-iso/src/index.js",
|
|
41
45
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
2
|
+
import type { HomeLayoutSection } from "../config.ts";
|
|
2
3
|
import type { Schema } from "../schema/Schema.ts";
|
|
3
4
|
import type { TSONDBRequestLocals } from "./index.ts";
|
|
4
|
-
export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName) => Promise<TSONDBRequestLocals>;
|
|
5
|
+
export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, defaultLocales: string[], homeLayoutSections?: HomeLayoutSection[]) => Promise<TSONDBRequestLocals>;
|
|
5
6
|
export declare const reinit: (locals: TSONDBRequestLocals) => Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
2
|
import { simpleGit } from "simple-git";
|
|
3
3
|
import { isEntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
4
|
-
import { resolveTypeArgumentsInDecls } from "../schema/index.js";
|
|
4
|
+
import { resolveTypeArgumentsInDecls, serializeNode } from "../schema/index.js";
|
|
5
5
|
import { attachGitStatusToInstancesByEntityName, getInstancesByEntityName, } from "../utils/instances.js";
|
|
6
6
|
import { getReferencesToInstances } from "../utils/references.js";
|
|
7
7
|
const debug = Debug("tsondb:server:init");
|
|
@@ -21,15 +21,16 @@ const getGit = async (dataRootPath) => {
|
|
|
21
21
|
return { git };
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
|
-
export const init = async (schema, dataRootPath, instancesByEntityName) => {
|
|
24
|
+
export const init = async (schema, dataRootPath, instancesByEntityName, defaultLocales, homeLayoutSections) => {
|
|
25
25
|
const { git, root: gitRoot, status: gitStatus } = await getGit(dataRootPath);
|
|
26
26
|
const declarations = resolveTypeArgumentsInDecls(schema.declarations);
|
|
27
27
|
debug("resolved type arguments in declarations");
|
|
28
28
|
const entities = declarations.filter(isEntityDecl);
|
|
29
29
|
debug("found %d entities in declarations", entities.length);
|
|
30
30
|
const entitiesByName = Object.fromEntries(entities.map(entity => [entity.name, entity]));
|
|
31
|
+
const serializedDeclarationsByName = Object.fromEntries(declarations.map(decl => [decl.name, serializeNode(decl)]));
|
|
31
32
|
const instancesByEntityNameInMemory = Object.assign({}, instancesByEntityName);
|
|
32
|
-
const referencesToInstances = getReferencesToInstances(instancesByEntityName,
|
|
33
|
+
const referencesToInstances = await getReferencesToInstances(instancesByEntityName, serializedDeclarationsByName);
|
|
33
34
|
debug("created references cache");
|
|
34
35
|
if (gitStatus) {
|
|
35
36
|
attachGitStatusToInstancesByEntityName(instancesByEntityName, dataRootPath, gitRoot, gitStatus);
|
|
@@ -52,22 +53,21 @@ export const init = async (schema, dataRootPath, instancesByEntityName) => {
|
|
|
52
53
|
entities: entities,
|
|
53
54
|
instancesByEntityName: instancesByEntityNameInMemory,
|
|
54
55
|
entitiesByName: entitiesByName,
|
|
56
|
+
serializedDeclarationsByName,
|
|
55
57
|
localeEntity: schema.localeEntity,
|
|
56
58
|
getInstanceById,
|
|
57
59
|
referencesToInstances,
|
|
58
|
-
|
|
60
|
+
defaultLocales,
|
|
61
|
+
locales: defaultLocales,
|
|
62
|
+
homeLayoutSections,
|
|
59
63
|
};
|
|
60
64
|
return requestLocals;
|
|
61
65
|
};
|
|
62
66
|
export const reinit = async (locals) => {
|
|
67
|
+
locals.instancesByEntityName = await getInstancesByEntityName(locals.dataRoot, locals.entities);
|
|
68
|
+
locals.referencesToInstances = await getReferencesToInstances(locals.instancesByEntityName, locals.serializedDeclarationsByName);
|
|
63
69
|
const gitStatus = (await locals.git.checkIsRepo()) ? await locals.git.status() : undefined;
|
|
64
|
-
const instancesByEntityName = await getInstancesByEntityName(locals.dataRoot, locals.entities);
|
|
65
|
-
const referencesToInstances = getReferencesToInstances(instancesByEntityName, locals.entitiesByName);
|
|
66
70
|
if (locals.gitRoot && gitStatus) {
|
|
67
|
-
attachGitStatusToInstancesByEntityName(instancesByEntityName, locals.dataRoot, locals.gitRoot, gitStatus);
|
|
71
|
+
attachGitStatusToInstancesByEntityName(locals.instancesByEntityName, locals.dataRoot, locals.gitRoot, gitStatus);
|
|
68
72
|
}
|
|
69
|
-
Object.assign(locals, {
|
|
70
|
-
instancesByEntityName,
|
|
71
|
-
referencesToInstances,
|
|
72
|
-
});
|
|
73
73
|
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Result } from "../../../shared/utils/result.ts";
|
|
2
|
+
import type { EntityTaggedInstanceContainerWithChildInstances, UnsafeEntityTaggedInstanceContainerWithChildInstances } from "../../utils/childInstances.ts";
|
|
3
|
+
import type { TSONDBRequestLocals } from "../index.ts";
|
|
4
|
+
export declare const updateLocalsAfterInstanceTreeChangeToReflectDiskState: (locals: TSONDBRequestLocals, parentId: string | undefined, parentEntityName: string, oldChildInstances: EntityTaggedInstanceContainerWithChildInstances[], childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[]) => Promise<Result<void, [code: number, message: string]>>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { error, ok } from "../../../shared/utils/result.js";
|
|
2
|
+
import { updateLocalsAfterInstanceChangeToReflectDiskState } from "./instanceOperations.js";
|
|
3
|
+
export const updateLocalsAfterInstanceTreeChangeToReflectDiskState = async (locals, parentId, parentEntityName, oldChildInstances, childInstances) => {
|
|
4
|
+
const parentEntity = locals.entitiesByName[parentEntityName];
|
|
5
|
+
if (parentEntity === undefined) {
|
|
6
|
+
return error([400, `Unknown entity "${parentEntityName}"`]);
|
|
7
|
+
}
|
|
8
|
+
if (parentId !== undefined) {
|
|
9
|
+
// existing parent, some child instances may already exist
|
|
10
|
+
for (const oldChildInstance of oldChildInstances) {
|
|
11
|
+
const newChildInstance = childInstances.find(ci => ci.id === oldChildInstance.id);
|
|
12
|
+
if (newChildInstance === undefined) {
|
|
13
|
+
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, oldChildInstance.entityName, oldChildInstance.id, undefined);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, oldChildInstance.entityName, oldChildInstance.id, newChildInstance.content);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
for (const newChildInstance of childInstances.filter(predicate => predicate.id === undefined)) {
|
|
20
|
+
if (newChildInstance.id !== undefined) {
|
|
21
|
+
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, newChildInstance.entityName, newChildInstance.id, newChildInstance.content);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// new parent, all child instances are new
|
|
27
|
+
for (const newChildInstance of childInstances) {
|
|
28
|
+
if (newChildInstance.id !== undefined) {
|
|
29
|
+
await updateLocalsAfterInstanceChangeToReflectDiskState(locals, newChildInstance.entityName, newChildInstance.id, newChildInstance.content);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// check recursively for child instances of child instances
|
|
34
|
+
for (const childInstance of childInstances) {
|
|
35
|
+
const oldChildInstance = childInstance.id
|
|
36
|
+
? oldChildInstances.find(ci => ci.id === childInstance.id)
|
|
37
|
+
: undefined;
|
|
38
|
+
await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, childInstance.id, childInstance.entityName, oldChildInstance ? oldChildInstance.childInstances : [], childInstance.childInstances);
|
|
39
|
+
}
|
|
40
|
+
return ok();
|
|
41
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { InstanceContainer } from "../../../shared/utils/instances.ts";
|
|
2
|
+
import { type Result } from "../../../shared/utils/result.ts";
|
|
3
|
+
import { type CreatedEntityTaggedInstanceContainerWithChildInstances, type UpdatedEntityTaggedInstanceContainerWithChildInstances } from "../../utils/childInstances.ts";
|
|
4
|
+
import type { TSONDBRequestLocals } from "../index.ts";
|
|
5
|
+
export declare const updateLocalsAfterInstanceChangeToReflectDiskState: (locals: TSONDBRequestLocals, entityName: string, instanceId: string, newInstanceContent: unknown) => Promise<InstanceContainer>;
|
|
6
|
+
export declare const createInstance: (locals: TSONDBRequestLocals, instance: CreatedEntityTaggedInstanceContainerWithChildInstances, idQueryParam: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
7
|
+
export declare const updateInstance: (locals: TSONDBRequestLocals, instance: UpdatedEntityTaggedInstanceContainerWithChildInstances) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
8
|
+
export declare const deleteInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { removeAt } from "../../../shared/utils/array.js";
|
|
2
|
+
import { error, isError, ok } from "../../../shared/utils/result.js";
|
|
3
|
+
import { checkWriteInstanceTreePossible, getChildInstances, unsafeApplyInstanceTree, } from "../../utils/childInstances.js";
|
|
4
|
+
import { getFileNameForId } from "../../utils/files.js";
|
|
5
|
+
import { getGitFileStatusFromStatusResult } from "../../utils/git.js";
|
|
6
|
+
import * as Instances from "../../utils/instanceOperations.js";
|
|
7
|
+
import { updateReferencesToInstances } from "../../utils/references.js";
|
|
8
|
+
import { updateLocalsAfterInstanceTreeChangeToReflectDiskState } from "./childInstances.js";
|
|
9
|
+
export const updateLocalsAfterInstanceChangeToReflectDiskState = async (locals, entityName, instanceId, newInstanceContent) => {
|
|
10
|
+
const oldInstances = locals.instancesByEntityName[entityName] ?? [];
|
|
11
|
+
const oldInstanceContainerIndex = oldInstances.findIndex(instance => instance.id === instanceId);
|
|
12
|
+
const oldInstanceContainer = oldInstanceContainerIndex > -1 ? oldInstances[oldInstanceContainerIndex] : undefined;
|
|
13
|
+
const instanceContainer = oldInstanceContainer ?? {
|
|
14
|
+
id: instanceId,
|
|
15
|
+
content: undefined,
|
|
16
|
+
};
|
|
17
|
+
// old content as alternative if instance is deleted to restore old instance container
|
|
18
|
+
instanceContainer.content = newInstanceContent ?? instanceContainer.content;
|
|
19
|
+
instanceContainer.gitStatus =
|
|
20
|
+
locals.gitRoot === undefined
|
|
21
|
+
? undefined
|
|
22
|
+
: getGitFileStatusFromStatusResult(await locals.git.status(), locals.gitRoot, locals.dataRoot, entityName, getFileNameForId(instanceId));
|
|
23
|
+
if (oldInstanceContainer === undefined) {
|
|
24
|
+
locals.instancesByEntityName[entityName] = [...oldInstances, instanceContainer];
|
|
25
|
+
}
|
|
26
|
+
else if (newInstanceContent === undefined) {
|
|
27
|
+
locals.instancesByEntityName[entityName] = removeAt(oldInstances, oldInstanceContainerIndex);
|
|
28
|
+
}
|
|
29
|
+
Object.assign(locals.referencesToInstances, updateReferencesToInstances(locals.entitiesByName, locals.referencesToInstances, entityName, instanceId, oldInstanceContainer?.content, newInstanceContent));
|
|
30
|
+
return instanceContainer;
|
|
31
|
+
};
|
|
32
|
+
export const createInstance = async (locals, instance, idQueryParam) => {
|
|
33
|
+
const entity = locals.entitiesByName[instance.entityName];
|
|
34
|
+
if (entity === undefined) {
|
|
35
|
+
return error([400, "Entity not found"]);
|
|
36
|
+
}
|
|
37
|
+
const checkTreeResult = checkWriteInstanceTreePossible(locals.entitiesByName, locals.instancesByEntityName, locals.referencesToInstances, instance.id, entity.name, [], instance.childInstances);
|
|
38
|
+
if (isError(checkTreeResult)) {
|
|
39
|
+
return checkTreeResult;
|
|
40
|
+
}
|
|
41
|
+
const res = await Instances.createInstance(locals.dataRoot, locals.localeEntity, locals.instancesByEntityName, entity, instance.content, idQueryParam);
|
|
42
|
+
if (isError(res)) {
|
|
43
|
+
return res;
|
|
44
|
+
}
|
|
45
|
+
const treeRes = await unsafeApplyInstanceTree(locals.dataRoot, locals.entitiesByName, locals.instancesByEntityName, instance.id, instance.entityName, [], instance.childInstances);
|
|
46
|
+
if (isError(treeRes)) {
|
|
47
|
+
return treeRes;
|
|
48
|
+
}
|
|
49
|
+
const newInstanceId = res.value;
|
|
50
|
+
const instanceContainer = await updateLocalsAfterInstanceChangeToReflectDiskState(locals, entity.name, newInstanceId, instance.content);
|
|
51
|
+
await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, newInstanceId, entity.name, [], instance.childInstances);
|
|
52
|
+
return ok(instanceContainer);
|
|
53
|
+
};
|
|
54
|
+
export const updateInstance = async (locals, instance) => {
|
|
55
|
+
const instanceContainer = locals.instancesByEntityName[instance.entityName]?.find(instance => instance.id === instance.id);
|
|
56
|
+
if (instanceContainer === undefined) {
|
|
57
|
+
return error([404, "Instance not found"]);
|
|
58
|
+
}
|
|
59
|
+
const entity = locals.entitiesByName[instance.entityName];
|
|
60
|
+
if (entity === undefined) {
|
|
61
|
+
return error([400, "Entity not found"]);
|
|
62
|
+
}
|
|
63
|
+
const oldChildInstances = getChildInstances(locals.instancesByEntityName, entity, instance.id);
|
|
64
|
+
const checkTreeResult = checkWriteInstanceTreePossible(locals.entitiesByName, locals.instancesByEntityName, locals.referencesToInstances, instance.id, entity.name, oldChildInstances, instance.childInstances);
|
|
65
|
+
if (isError(checkTreeResult)) {
|
|
66
|
+
return checkTreeResult;
|
|
67
|
+
}
|
|
68
|
+
const res = await Instances.updateInstance(locals.dataRoot, locals.instancesByEntityName, entity, instance.id, instance.content);
|
|
69
|
+
if (isError(res)) {
|
|
70
|
+
return res;
|
|
71
|
+
}
|
|
72
|
+
const treeRes = await unsafeApplyInstanceTree(locals.dataRoot, locals.entitiesByName, locals.instancesByEntityName, instance.id, instance.entityName, oldChildInstances, instance.childInstances);
|
|
73
|
+
if (isError(treeRes)) {
|
|
74
|
+
return treeRes;
|
|
75
|
+
}
|
|
76
|
+
const newInstanceContainer = await updateLocalsAfterInstanceChangeToReflectDiskState(locals, entity.name, instance.id, instance.content);
|
|
77
|
+
await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, instance.id, instance.entityName, oldChildInstances, instance.childInstances);
|
|
78
|
+
return ok(newInstanceContainer);
|
|
79
|
+
};
|
|
80
|
+
export const deleteInstance = async (locals, entityName, instanceId) => {
|
|
81
|
+
const instances = locals.instancesByEntityName[entityName] ?? [];
|
|
82
|
+
const instanceContainerIndex = instances.findIndex(instance => instance.id === instanceId);
|
|
83
|
+
const instanceContainer = instances[instanceContainerIndex];
|
|
84
|
+
if (instanceContainer === undefined) {
|
|
85
|
+
return error([404, "Instance not found"]);
|
|
86
|
+
}
|
|
87
|
+
const entity = locals.entitiesByName[entityName];
|
|
88
|
+
if (entity === undefined) {
|
|
89
|
+
return error([400, "Entity not found"]);
|
|
90
|
+
}
|
|
91
|
+
const oldChildInstances = getChildInstances(locals.instancesByEntityName, entity, instanceContainer.id);
|
|
92
|
+
const checkTreeResult = checkWriteInstanceTreePossible(locals.entitiesByName, locals.instancesByEntityName, locals.referencesToInstances, instanceContainer.id, entityName, oldChildInstances, []);
|
|
93
|
+
if (isError(checkTreeResult)) {
|
|
94
|
+
return checkTreeResult;
|
|
95
|
+
}
|
|
96
|
+
const res = await Instances.deleteInstance(locals.dataRoot, locals.referencesToInstances, entityName, instanceId);
|
|
97
|
+
if (isError(res)) {
|
|
98
|
+
return res;
|
|
99
|
+
}
|
|
100
|
+
const treeRes = await unsafeApplyInstanceTree(locals.dataRoot, locals.entitiesByName, locals.instancesByEntityName, instanceContainer.id, entityName, oldChildInstances, []);
|
|
101
|
+
if (isError(treeRes)) {
|
|
102
|
+
return treeRes;
|
|
103
|
+
}
|
|
104
|
+
const oldInstanceContainer = await updateLocalsAfterInstanceChangeToReflectDiskState(locals, entity.name, instanceContainer.id, undefined);
|
|
105
|
+
await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, instanceContainer.id, entityName, oldChildInstances, []);
|
|
106
|
+
return ok(oldInstanceContainer);
|
|
107
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const getLocalesFromRequest = (req) => {
|
|
2
|
+
const requestLocales = req.query["locales"];
|
|
3
|
+
return Array.isArray(requestLocales) && requestLocales.every(locale => typeof locale === "string")
|
|
4
|
+
? requestLocales
|
|
5
|
+
: typeof requestLocales === "string"
|
|
6
|
+
? [requestLocales]
|
|
7
|
+
: undefined;
|
|
8
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { GitFileStatus } from "../../shared/utils/git.ts";
|
|
2
|
+
import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
3
|
+
import { type Result } from "../../shared/utils/result.ts";
|
|
4
|
+
import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
|
|
5
|
+
import { type ReferencesToInstances } from "./references.ts";
|
|
6
|
+
export interface ChildInstanceContainer {
|
|
7
|
+
id?: string;
|
|
8
|
+
content: unknown;
|
|
9
|
+
gitStatus?: GitFileStatus;
|
|
10
|
+
}
|
|
11
|
+
export interface EntityTaggedInstanceContainer {
|
|
12
|
+
entityName: string;
|
|
13
|
+
id: string;
|
|
14
|
+
content: unknown;
|
|
15
|
+
}
|
|
16
|
+
export interface CreatedEntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<undefined, UnsafeEntityTaggedInstanceContainerWithChildInstances> {
|
|
17
|
+
}
|
|
18
|
+
export interface UpdatedEntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<string, UnsafeEntityTaggedInstanceContainerWithChildInstances> {
|
|
19
|
+
}
|
|
20
|
+
export interface UnsafeEntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<string | undefined, UnsafeEntityTaggedInstanceContainerWithChildInstances> {
|
|
21
|
+
}
|
|
22
|
+
export interface EntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<string, EntityTaggedInstanceContainerWithChildInstances> {
|
|
23
|
+
}
|
|
24
|
+
export interface GenEntityTaggedInstanceContainerWithChildInstances<ID extends string | undefined, C> {
|
|
25
|
+
entityName: string;
|
|
26
|
+
id: ID;
|
|
27
|
+
content: unknown;
|
|
28
|
+
childInstances: C[];
|
|
29
|
+
}
|
|
30
|
+
export declare const getChildInstances: (instancesByEntityName: InstancesByEntityName, parentEntity: EntityDecl, parentId: string) => EntityTaggedInstanceContainerWithChildInstances[];
|
|
31
|
+
export declare const checkWriteInstanceTreePossible: (entitiesByName: Record<string, EntityDecl>, instancesByEntityName: InstancesByEntityName, referencesToInstances: ReferencesToInstances, parentId: string | undefined, parentEntityName: string, oldChildInstances: EntityTaggedInstanceContainerWithChildInstances[], childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[]) => Result<void, [code: number, message: string]>;
|
|
32
|
+
export declare const unsafeApplyInstanceTree: (dataRoot: string, entitiesByName: Record<string, EntityDecl>, instancesByEntityName: InstancesByEntityName, parentId: string | undefined, parentEntityName: string, oldChildInstances: EntityTaggedInstanceContainerWithChildInstances[], childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[]) => Promise<Result<void, [code: number, message: string]>>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { hasKey } from "../../shared/utils/object.js";
|
|
2
|
+
import { error, isError, ok } from "../../shared/utils/result.js";
|
|
3
|
+
import { reduceNodes } from "../schema/index.js";
|
|
4
|
+
import { isChildEntitiesType } from "../schema/types/references/ChildEntitiesType.js";
|
|
5
|
+
import { checkCreateNonLocaleInstancePossible, checkDeleteInstancePossible, checkUpdateInstancePossible, createNewId, unsafeDeleteInstance, unsafeWriteInstance, } from "./instanceOperations.js";
|
|
6
|
+
import {} from "./references.js";
|
|
7
|
+
const isParentReferenceReferencingParent = (value, parentEntityName, parentId) => {
|
|
8
|
+
if (typeof value === "object" && value !== null && hasKey(value, "kind")) {
|
|
9
|
+
return (value.kind === parentEntityName &&
|
|
10
|
+
hasKey(value, parentEntityName) &&
|
|
11
|
+
value[parentEntityName] === parentId);
|
|
12
|
+
}
|
|
13
|
+
else if (typeof value === "string") {
|
|
14
|
+
return value === parentId;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const getChildInstances = (instancesByEntityName, parentEntity, parentId) => {
|
|
21
|
+
const childEntities = reduceNodes((_parentNodes, node, collectedResults) => isChildEntitiesType(node) ? [...collectedResults, node.entity] : collectedResults, [parentEntity], { followIncludes: true });
|
|
22
|
+
return childEntities.flatMap(childEntity => instancesByEntityName[childEntity.name]
|
|
23
|
+
?.filter(instanceContainer => typeof instanceContainer.content === "object" &&
|
|
24
|
+
instanceContainer.content !== null &&
|
|
25
|
+
hasKey(instanceContainer.content, childEntity.parentReferenceKey) &&
|
|
26
|
+
isParentReferenceReferencingParent(instanceContainer.content[childEntity.parentReferenceKey], parentEntity.name, parentId))
|
|
27
|
+
.map(container => ({
|
|
28
|
+
...container,
|
|
29
|
+
entityName: childEntity.name,
|
|
30
|
+
childInstances: getChildInstances(instancesByEntityName, childEntity, container.id),
|
|
31
|
+
})) ?? []);
|
|
32
|
+
};
|
|
33
|
+
export const checkWriteInstanceTreePossible = (entitiesByName, instancesByEntityName, referencesToInstances, parentId, parentEntityName, oldChildInstances, childInstances) => {
|
|
34
|
+
const parentEntity = entitiesByName[parentEntityName];
|
|
35
|
+
if (parentEntity === undefined) {
|
|
36
|
+
return error([400, `Unknown entity "${parentEntityName}"`]);
|
|
37
|
+
}
|
|
38
|
+
if (parentId !== undefined) {
|
|
39
|
+
// existing parent, some child instances may already exist
|
|
40
|
+
for (const oldChildInstance of oldChildInstances) {
|
|
41
|
+
const newChildInstance = childInstances.find(ci => ci.id === oldChildInstance.id);
|
|
42
|
+
if (newChildInstance === undefined) {
|
|
43
|
+
const prerequisiteCheckResult = checkDeleteInstancePossible(referencesToInstances, oldChildInstance.id);
|
|
44
|
+
if (isError(prerequisiteCheckResult)) {
|
|
45
|
+
return prerequisiteCheckResult;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const entity = entitiesByName[newChildInstance.entityName];
|
|
50
|
+
if (entity === undefined) {
|
|
51
|
+
return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
|
|
52
|
+
}
|
|
53
|
+
const prerequisiteCheckResult = checkUpdateInstancePossible(instancesByEntityName, entity, newChildInstance.content);
|
|
54
|
+
if (isError(prerequisiteCheckResult)) {
|
|
55
|
+
return prerequisiteCheckResult;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const newChildInstance of childInstances.filter(predicate => predicate.id === undefined)) {
|
|
60
|
+
const entity = entitiesByName[newChildInstance.entityName];
|
|
61
|
+
if (entity === undefined) {
|
|
62
|
+
return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
|
|
63
|
+
}
|
|
64
|
+
const prerequisiteCheckResult = checkCreateNonLocaleInstancePossible(instancesByEntityName, entity, newChildInstance.content);
|
|
65
|
+
if (isError(prerequisiteCheckResult)) {
|
|
66
|
+
return prerequisiteCheckResult;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// new parent, all child instances are new
|
|
72
|
+
for (const newChildInstance of childInstances) {
|
|
73
|
+
const entity = entitiesByName[newChildInstance.entityName];
|
|
74
|
+
if (entity === undefined) {
|
|
75
|
+
return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
|
|
76
|
+
}
|
|
77
|
+
const prerequisiteCheckResult = checkCreateNonLocaleInstancePossible(instancesByEntityName, entity, newChildInstance.content);
|
|
78
|
+
if (isError(prerequisiteCheckResult)) {
|
|
79
|
+
return prerequisiteCheckResult;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// check recursively for child instances of child instances
|
|
84
|
+
for (const childInstance of childInstances) {
|
|
85
|
+
const oldChildInstance = childInstance.id
|
|
86
|
+
? oldChildInstances.find(ci => ci.id === childInstance.id)
|
|
87
|
+
: undefined;
|
|
88
|
+
const prerequisiteCheckResult = checkWriteInstanceTreePossible(entitiesByName, instancesByEntityName, referencesToInstances, childInstance.id, childInstance.entityName, oldChildInstance ? oldChildInstance.childInstances : [], childInstance.childInstances);
|
|
89
|
+
if (isError(prerequisiteCheckResult)) {
|
|
90
|
+
return prerequisiteCheckResult;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return ok();
|
|
94
|
+
};
|
|
95
|
+
export const unsafeApplyInstanceTree = async (dataRoot, entitiesByName, instancesByEntityName, parentId, parentEntityName, oldChildInstances, childInstances) => {
|
|
96
|
+
const parentEntity = entitiesByName[parentEntityName];
|
|
97
|
+
if (parentEntity === undefined) {
|
|
98
|
+
return error([400, `Unknown entity "${parentEntityName}"`]);
|
|
99
|
+
}
|
|
100
|
+
if (parentId !== undefined) {
|
|
101
|
+
// existing parent, some child instances may already exist
|
|
102
|
+
for (const oldChildInstance of oldChildInstances) {
|
|
103
|
+
const newChildInstance = childInstances.find(ci => ci.id === oldChildInstance.id);
|
|
104
|
+
if (newChildInstance === undefined) {
|
|
105
|
+
const oldGrandChildInstancesResult = await unsafeApplyInstanceTree(dataRoot, entitiesByName, instancesByEntityName, oldChildInstance.id, oldChildInstance.entityName, oldChildInstance.childInstances, []);
|
|
106
|
+
if (isError(oldGrandChildInstancesResult)) {
|
|
107
|
+
return oldGrandChildInstancesResult;
|
|
108
|
+
}
|
|
109
|
+
const prerequisiteCheckResult = await unsafeDeleteInstance(dataRoot, oldChildInstance.entityName, oldChildInstance.id);
|
|
110
|
+
if (isError(prerequisiteCheckResult)) {
|
|
111
|
+
return prerequisiteCheckResult;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const entity = entitiesByName[newChildInstance.entityName];
|
|
116
|
+
if (entity === undefined) {
|
|
117
|
+
return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
|
|
118
|
+
}
|
|
119
|
+
const prerequisiteCheckResult = await unsafeWriteInstance(dataRoot, entity, oldChildInstance.id, newChildInstance.content);
|
|
120
|
+
if (isError(prerequisiteCheckResult)) {
|
|
121
|
+
return prerequisiteCheckResult;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const newChildInstance of childInstances.filter(predicate => predicate.id === undefined)) {
|
|
126
|
+
const entity = entitiesByName[newChildInstance.entityName];
|
|
127
|
+
if (entity === undefined) {
|
|
128
|
+
return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
|
|
129
|
+
}
|
|
130
|
+
const newInstanceID = createNewId();
|
|
131
|
+
const prerequisiteCheckResult = await unsafeWriteInstance(dataRoot, entity, newInstanceID, newChildInstance.content);
|
|
132
|
+
if (isError(prerequisiteCheckResult)) {
|
|
133
|
+
return prerequisiteCheckResult;
|
|
134
|
+
}
|
|
135
|
+
newChildInstance.id = newInstanceID;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// new parent, all child instances are new
|
|
140
|
+
for (const newChildInstance of childInstances) {
|
|
141
|
+
const entity = entitiesByName[newChildInstance.entityName];
|
|
142
|
+
if (entity === undefined) {
|
|
143
|
+
return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
|
|
144
|
+
}
|
|
145
|
+
const newInstanceID = createNewId();
|
|
146
|
+
const prerequisiteCheckResult = await unsafeWriteInstance(dataRoot, entity, newInstanceID, newChildInstance.content);
|
|
147
|
+
if (isError(prerequisiteCheckResult)) {
|
|
148
|
+
return prerequisiteCheckResult;
|
|
149
|
+
}
|
|
150
|
+
newChildInstance.id = newInstanceID;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// check recursively for child instances of child instances
|
|
154
|
+
for (const childInstance of childInstances) {
|
|
155
|
+
const oldChildInstance = childInstance.id
|
|
156
|
+
? oldChildInstances.find(ci => ci.id === childInstance.id)
|
|
157
|
+
: undefined;
|
|
158
|
+
const prerequisiteCheckResult = await unsafeApplyInstanceTree(dataRoot, entitiesByName, instancesByEntityName, childInstance.id, childInstance.entityName, oldChildInstance ? oldChildInstance.childInstances : [], childInstance.childInstances);
|
|
159
|
+
if (isError(prerequisiteCheckResult)) {
|
|
160
|
+
return prerequisiteCheckResult;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return ok();
|
|
164
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type EntityDecl } from "../../node/schema/index.ts";
|
|
2
2
|
import type { GetInstanceById } from "../../node/server/index.ts";
|
|
3
|
-
|
|
3
|
+
import { type DisplayNameResult } from "../../shared/utils/displayName.ts";
|
|
4
|
+
export declare const getDisplayNameFromEntityInstance: (entity: EntityDecl, instance: unknown, getInstanceById: GetInstanceById, locales: string[], defaultName?: string, useCustomizer?: boolean) => DisplayNameResult;
|