tsondb 0.2.0 → 0.4.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/LICENSE +385 -0
- package/README.md +173 -1
- package/lib/bin/tsondb.d.ts +8 -0
- package/lib/bin/tsondb.js +83 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -1
- package/lib/{Schema.d.ts → node/Schema.d.ts} +2 -2
- package/lib/{Schema.js → node/Schema.js} +1 -1
- package/lib/node/index.d.ts +8 -0
- package/lib/node/index.js +62 -0
- package/lib/{renderers → node/renderers}/Output.d.ts +1 -1
- package/lib/node/renderers/jsonschema/index.d.ts +6 -0
- package/lib/node/renderers/jsonschema/index.js +35 -0
- package/lib/node/renderers/jsonschema/render.d.ts +9 -0
- package/lib/{renderers → node/renderers}/jsonschema/render.js +42 -23
- package/lib/node/renderers/ts/index.d.ts +6 -0
- package/lib/node/renderers/ts/index.js +42 -0
- package/lib/{renderers → node/renderers}/ts/render.d.ts +3 -1
- package/lib/node/renderers/ts/render.js +136 -0
- package/lib/{schema → node/schema}/Node.d.ts +5 -4
- package/lib/{schema → node/schema}/Node.js +11 -6
- package/lib/{schema/parameters → node/schema}/TypeParameter.d.ts +3 -2
- package/lib/{schema/parameters → node/schema}/TypeParameter.js +2 -2
- package/lib/{schema → node/schema}/declarations/Declaration.d.ts +11 -10
- package/lib/{schema → node/schema}/declarations/Declaration.js +10 -4
- package/lib/{schema → node/schema}/declarations/EntityDecl.d.ts +13 -9
- package/lib/{schema → node/schema}/declarations/EntityDecl.js +2 -2
- package/lib/{schema → node/schema}/declarations/EnumDecl.d.ts +11 -26
- package/lib/node/schema/declarations/EnumDecl.js +52 -0
- package/lib/{schema → node/schema}/declarations/TypeAliasDecl.d.ts +8 -7
- package/lib/{schema → node/schema}/declarations/TypeAliasDecl.js +3 -3
- package/lib/{schema → node/schema}/index.d.ts +4 -3
- package/lib/{schema → node/schema}/index.js +3 -3
- package/lib/node/schema/types/Type.d.ts +48 -0
- package/lib/{schema → node/schema}/types/Type.js +90 -28
- package/lib/{schema → node/schema}/types/generic/ArrayType.d.ts +6 -4
- package/lib/{schema → node/schema}/types/generic/ArrayType.js +5 -4
- package/lib/node/schema/types/generic/EnumType.d.ts +39 -0
- package/lib/node/schema/types/generic/EnumType.js +98 -0
- package/lib/{schema → node/schema}/types/generic/ObjectType.d.ts +9 -7
- package/lib/{schema → node/schema}/types/generic/ObjectType.js +11 -3
- package/lib/{schema → node/schema}/types/primitives/BooleanType.d.ts +5 -3
- package/lib/{schema → node/schema}/types/primitives/BooleanType.js +1 -0
- package/lib/{schema → node/schema}/types/primitives/DateType.d.ts +6 -4
- package/lib/{schema → node/schema}/types/primitives/DateType.js +2 -1
- package/lib/{schema → node/schema}/types/primitives/FloatType.d.ts +6 -4
- package/lib/{schema → node/schema}/types/primitives/FloatType.js +2 -1
- package/lib/{schema → node/schema}/types/primitives/IntegerType.d.ts +6 -4
- package/lib/{schema → node/schema}/types/primitives/IntegerType.js +2 -1
- package/lib/{schema → node/schema}/types/primitives/NumericType.d.ts +2 -2
- package/lib/node/schema/types/primitives/PrimitiveType.d.ts +6 -0
- package/lib/{schema → node/schema}/types/primitives/StringType.d.ts +6 -4
- package/lib/{schema → node/schema}/types/primitives/StringType.js +2 -1
- package/lib/{schema → node/schema}/types/references/IncludeIdentifierType.d.ts +11 -8
- package/lib/{schema → node/schema}/types/references/IncludeIdentifierType.js +15 -2
- package/lib/{schema → node/schema}/types/references/NestedEntityMapType.d.ts +11 -9
- package/lib/{schema → node/schema}/types/references/NestedEntityMapType.js +6 -2
- package/lib/{schema → node/schema}/types/references/ReferenceIdentifierType.d.ts +8 -6
- package/lib/{schema → node/schema}/types/references/ReferenceIdentifierType.js +1 -0
- package/lib/node/schema/types/references/TypeArgumentType.d.ts +23 -0
- package/lib/node/schema/types/references/TypeArgumentType.js +19 -0
- package/lib/node/schema/validation/type.d.ts +4 -0
- package/lib/node/server/api/declarations.d.ts +1 -0
- package/lib/node/server/api/declarations.js +154 -0
- package/lib/node/server/api/git.d.ts +1 -0
- package/lib/node/server/api/git.js +178 -0
- package/lib/node/server/api/index.d.ts +1 -0
- package/lib/node/server/api/index.js +8 -0
- package/lib/node/server/api/instanceOperations.d.ts +6 -0
- package/lib/node/server/api/instanceOperations.js +91 -0
- package/lib/node/server/api/instances.d.ts +1 -0
- package/lib/node/server/api/instances.js +27 -0
- package/lib/node/server/index.d.ts +28 -0
- package/lib/node/server/index.js +53 -0
- package/lib/node/server/init.d.ts +5 -0
- package/lib/node/server/init.js +56 -0
- package/lib/{utils → node/utils}/error.js +4 -1
- package/lib/node/utils/git.d.ts +3 -0
- package/lib/node/utils/git.js +12 -0
- package/lib/node/utils/instances.d.ts +5 -0
- package/lib/node/utils/instances.js +21 -0
- package/lib/node/utils/path.d.ts +1 -0
- package/lib/node/utils/path.js +2 -0
- package/lib/node/utils/references.d.ts +7 -0
- package/lib/node/utils/references.js +40 -0
- package/lib/node/utils/render.d.ts +9 -0
- package/lib/node/utils/render.js +34 -0
- package/lib/shared/api.d.ts +19 -2
- package/lib/shared/utils/array.d.ts +19 -0
- package/lib/shared/utils/array.js +30 -0
- package/lib/shared/utils/displayName.d.ts +1 -1
- package/lib/shared/utils/displayName.js +4 -2
- package/lib/shared/utils/git.d.ts +12 -0
- package/lib/shared/utils/git.js +98 -0
- package/lib/shared/utils/instances.d.ts +10 -0
- package/lib/shared/utils/instances.js +8 -1
- package/lib/{utils → shared/utils}/lazy.js +1 -1
- package/lib/shared/utils/markdown.d.ts +14 -0
- package/lib/shared/utils/markdown.js +42 -0
- package/lib/shared/utils/object.d.ts +4 -0
- package/lib/shared/utils/object.js +5 -0
- package/lib/{utils → shared/utils}/result.js +2 -2
- package/lib/shared/utils/string.d.ts +1 -0
- package/lib/shared/utils/string.js +16 -4
- package/lib/shared/utils/validation.js +3 -2
- package/lib/shared/validation/number.js +3 -3
- package/lib/shared/validation/object.js +4 -3
- package/lib/shared/validation/string.js +12 -7
- package/lib/web/api.d.ts +24 -0
- package/lib/web/api.js +204 -0
- package/lib/web/components/Git.d.ts +2 -0
- package/lib/web/components/Git.js +160 -0
- package/lib/{client → web}/components/Layout.d.ts +1 -1
- package/lib/{client → web}/components/Layout.js +2 -1
- package/lib/web/components/Select.d.ts +3 -0
- package/lib/web/components/Select.js +5 -0
- package/lib/web/components/typeInputs/ArrayTypeInput.d.ts +13 -0
- package/lib/{client → web}/components/typeInputs/ArrayTypeInput.js +8 -1
- package/lib/{client → web}/components/typeInputs/BooleanTypeInput.d.ts +2 -2
- package/lib/{client → web}/components/typeInputs/DateTypeInput.d.ts +2 -2
- package/lib/web/components/typeInputs/EnumTypeInput.d.ts +13 -0
- package/lib/{client/components/typeInputs/utils/EnumDeclField.js → web/components/typeInputs/EnumTypeInput.js} +11 -11
- package/lib/{client → web}/components/typeInputs/FloatTypeInput.d.ts +2 -2
- package/lib/web/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.d.ts +7 -0
- package/lib/{client → web}/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.js +1 -1
- package/lib/web/components/typeInputs/IncludeIdentifierTypeInput.d.ts +13 -0
- package/lib/web/components/typeInputs/IncludeIdentifierTypeInput.js +9 -0
- package/lib/{client → web}/components/typeInputs/IntegerTypeInput.d.ts +2 -2
- package/lib/web/components/typeInputs/NestedEntityMapTypeInput.d.ts +13 -0
- package/lib/{client → web}/components/typeInputs/NestedEntityMapTypeInput.js +7 -2
- package/lib/{client → web}/components/typeInputs/ObjectTypeInput.d.ts +4 -4
- package/lib/{client → web}/components/typeInputs/ObjectTypeInput.js +4 -1
- package/lib/{client → web}/components/typeInputs/ReferenceIdentifierTypeInput.d.ts +3 -3
- package/lib/web/components/typeInputs/ReferenceIdentifierTypeInput.js +11 -0
- package/lib/{client → web}/components/typeInputs/StringTypeInput.d.ts +2 -2
- package/lib/web/components/typeInputs/TypeInput.d.ts +13 -0
- package/lib/{client → web}/components/typeInputs/TypeInput.js +6 -3
- package/lib/{client → web}/components/typeInputs/utils/Markdown.d.ts +1 -1
- package/lib/web/components/typeInputs/utils/Markdown.js +26 -0
- package/lib/{client → web}/components/typeInputs/utils/MismatchingTypeError.d.ts +1 -1
- package/lib/{client → web}/components/typeInputs/utils/ValidationErrors.d.ts +1 -1
- package/lib/web/hooks/useAPIResource.d.ts +1 -0
- package/lib/web/hooks/useAPIResource.js +2 -0
- package/lib/{client → web}/hooks/useEntityFromRoute.d.ts +1 -1
- package/lib/{client → web}/hooks/useEntityFromRoute.js +1 -1
- package/lib/{client → web}/hooks/useInstanceNamesByEntity.d.ts +1 -1
- package/lib/{client → web}/hooks/useInstanceNamesByEntity.js +8 -6
- package/lib/web/hooks/useMappedAPIResource.d.ts +1 -0
- package/lib/web/hooks/useMappedAPIResource.js +17 -0
- package/lib/{client → web}/hooks/useSecondaryDeclarations.d.ts +1 -1
- package/lib/{client → web}/hooks/useSecondaryDeclarations.js +4 -2
- package/lib/{client → web}/routes/CreateInstance.d.ts +1 -1
- package/lib/{client → web}/routes/CreateInstance.js +4 -2
- package/lib/web/routes/Entity.d.ts +2 -0
- package/lib/web/routes/Entity.js +43 -0
- package/lib/web/routes/Home.d.ts +2 -0
- package/lib/web/routes/Home.js +9 -0
- package/lib/{client → web}/routes/Instance.d.ts +1 -1
- package/lib/{client → web}/routes/Instance.js +14 -8
- package/lib/{client → web}/routes/NotFound.d.ts +1 -1
- package/lib/web/utils/typeSkeleton.d.ts +3 -0
- package/lib/{client → web}/utils/typeSkeleton.js +14 -17
- package/package.json +35 -15
- package/public/css/styles.css +200 -1
- package/lib/ModelContainer.d.ts +0 -17
- package/lib/ModelContainer.js +0 -63
- package/lib/client/api.d.ts +0 -11
- package/lib/client/api.js +0 -83
- package/lib/client/components/Select.d.ts +0 -3
- package/lib/client/components/Select.js +0 -2
- package/lib/client/components/typeInputs/ArrayTypeInput.d.ts +0 -13
- package/lib/client/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.d.ts +0 -7
- package/lib/client/components/typeInputs/IncludeIdentifierTypeInput.d.ts +0 -13
- package/lib/client/components/typeInputs/IncludeIdentifierTypeInput.js +0 -18
- package/lib/client/components/typeInputs/NestedEntityMapTypeInput.d.ts +0 -13
- package/lib/client/components/typeInputs/ReferenceIdentifierTypeInput.js +0 -9
- package/lib/client/components/typeInputs/TypeInput.d.ts +0 -13
- package/lib/client/components/typeInputs/utils/EnumDeclField.d.ts +0 -13
- package/lib/client/components/typeInputs/utils/Markdown.js +0 -57
- package/lib/client/routes/Entity.d.ts +0 -2
- package/lib/client/routes/Entity.js +0 -47
- package/lib/client/routes/Home.d.ts +0 -2
- package/lib/client/routes/Home.js +0 -18
- package/lib/client/utils/typeSkeleton.d.ts +0 -3
- package/lib/renderers/jsonschema/index.d.ts +0 -6
- package/lib/renderers/jsonschema/index.js +0 -12
- package/lib/renderers/jsonschema/render.d.ts +0 -5
- package/lib/renderers/ts/index.d.ts +0 -6
- package/lib/renderers/ts/index.js +0 -11
- package/lib/renderers/ts/render.js +0 -112
- package/lib/schema/declarations/EnumDecl.js +0 -115
- package/lib/schema/types/Type.d.ts +0 -42
- package/lib/schema/types/primitives/PrimitiveType.d.ts +0 -6
- package/lib/schema/types/references/GenericArgumentIdentifierType.d.ts +0 -21
- package/lib/schema/types/references/GenericArgumentIdentifierType.js +0 -18
- package/lib/schema/validation/type.d.ts +0 -4
- package/lib/server/index.d.ts +0 -8
- package/lib/server/index.js +0 -207
- package/lib/server/instanceOperations.d.ts +0 -7
- package/lib/server/instanceOperations.js +0 -67
- package/lib/tsconfig.tsbuildinfo +0 -1
- package/lib/utils/instances.d.ts +0 -4
- package/lib/utils/instances.js +0 -12
- package/lib/utils/object.d.ts +0 -3
- package/lib/utils/object.js +0 -1
- package/lib/utils/render.d.ts +0 -4
- package/lib/utils/render.js +0 -8
- /package/lib/{renderers → node/renderers}/Output.js +0 -0
- /package/lib/{schema → node/schema}/types/primitives/NumericType.js +0 -0
- /package/lib/{schema → node/schema}/types/primitives/PrimitiveType.js +0 -0
- /package/lib/{schema → node/schema}/validation/options.d.ts +0 -0
- /package/lib/{schema → node/schema}/validation/options.js +0 -0
- /package/lib/{schema → node/schema}/validation/type.js +0 -0
- /package/lib/{utils → node/utils}/error.d.ts +0 -0
- /package/lib/{utils → shared/utils}/enum.d.ts +0 -0
- /package/lib/{utils → shared/utils}/enum.js +0 -0
- /package/lib/{utils → shared/utils}/lazy.d.ts +0 -0
- /package/lib/{utils → shared/utils}/result.d.ts +0 -0
- /package/lib/{client → web}/components/typeInputs/BooleanTypeInput.js +0 -0
- /package/lib/{client → web}/components/typeInputs/DateTypeInput.js +0 -0
- /package/lib/{client → web}/components/typeInputs/FloatTypeInput.js +0 -0
- /package/lib/{client → web}/components/typeInputs/IntegerTypeInput.js +0 -0
- /package/lib/{client → web}/components/typeInputs/StringTypeInput.js +0 -0
- /package/lib/{client → web}/components/typeInputs/utils/MismatchingTypeError.js +0 -0
- /package/lib/{client → web}/components/typeInputs/utils/ValidationErrors.js +0 -0
- /package/lib/{client → web}/index.d.ts +0 -0
- /package/lib/{client → web}/index.js +0 -0
- /package/lib/{client → web}/routes/NotFound.js +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import Debug from "debug";
|
|
2
|
+
import express from "express";
|
|
3
|
+
import { isOk } from "../../../shared/utils/result.js";
|
|
4
|
+
import { serializeDecl } from "../../schema/declarations/Declaration.js";
|
|
5
|
+
import { isEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
6
|
+
import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
|
|
7
|
+
import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
|
|
8
|
+
import { createInstance, deleteInstance, updateInstance } from "./instanceOperations.js";
|
|
9
|
+
const debug = Debug("tsondb:server:api:declarations");
|
|
10
|
+
export const declarationsApi = express.Router();
|
|
11
|
+
declarationsApi.use((req, _res, next) => {
|
|
12
|
+
debug(req.path);
|
|
13
|
+
next();
|
|
14
|
+
});
|
|
15
|
+
declarationsApi.get("/", (req, res) => {
|
|
16
|
+
let filteredEntities;
|
|
17
|
+
switch (req.query["kind"]) {
|
|
18
|
+
case "Entity":
|
|
19
|
+
filteredEntities = req.entities;
|
|
20
|
+
break;
|
|
21
|
+
case "TypeAlias":
|
|
22
|
+
filteredEntities = req.declarations.filter(isTypeAliasDecl);
|
|
23
|
+
break;
|
|
24
|
+
case "Enum":
|
|
25
|
+
filteredEntities = req.declarations.filter(isEnumDecl);
|
|
26
|
+
break;
|
|
27
|
+
default:
|
|
28
|
+
filteredEntities = req.declarations;
|
|
29
|
+
}
|
|
30
|
+
const body = {
|
|
31
|
+
declarations: filteredEntities.map(decl => ({
|
|
32
|
+
declaration: serializeDecl(decl),
|
|
33
|
+
instanceCount: req.instancesByEntityName[decl.name]?.length ?? 0,
|
|
34
|
+
})),
|
|
35
|
+
localeEntity: req.localeEntity?.name,
|
|
36
|
+
};
|
|
37
|
+
res.json(body);
|
|
38
|
+
});
|
|
39
|
+
declarationsApi.get("/:name", (req, res) => {
|
|
40
|
+
const decl = req.declarations.find(decl => decl.name === req.params.name);
|
|
41
|
+
if (decl === undefined) {
|
|
42
|
+
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const body = {
|
|
46
|
+
declaration: serializeDecl(decl),
|
|
47
|
+
instanceCount: req.instancesByEntityName[decl.name]?.length ?? 0,
|
|
48
|
+
isLocaleEntity: decl === req.localeEntity,
|
|
49
|
+
};
|
|
50
|
+
res.json(body);
|
|
51
|
+
});
|
|
52
|
+
declarationsApi.get("/:name/instances", (req, res) => {
|
|
53
|
+
const decl = req.declarations.find(decl => decl.name === req.params.name);
|
|
54
|
+
if (decl === undefined) {
|
|
55
|
+
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!isEntityDecl(decl)) {
|
|
59
|
+
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const body = {
|
|
63
|
+
instances: req.instancesByEntityName[req.params.name] ?? [],
|
|
64
|
+
isLocaleEntity: decl === req.localeEntity,
|
|
65
|
+
};
|
|
66
|
+
res.json(body);
|
|
67
|
+
});
|
|
68
|
+
declarationsApi.post("/:name/instances", async (req, res) => {
|
|
69
|
+
const decl = req.declarations.find(decl => decl.name === req.params.name);
|
|
70
|
+
if (decl === undefined) {
|
|
71
|
+
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!isEntityDecl(decl)) {
|
|
75
|
+
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const result = await createInstance(req, req.params.name, req.body, req.query["id"]);
|
|
79
|
+
if (isOk(result)) {
|
|
80
|
+
const body = {
|
|
81
|
+
instance: result.value,
|
|
82
|
+
isLocaleEntity: decl === req.localeEntity,
|
|
83
|
+
};
|
|
84
|
+
res.json(body);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
res.status(result.error[0]).send(result.error[1]);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
declarationsApi.get("/:name/instances/:id", (req, res) => {
|
|
91
|
+
const decl = req.declarations.find(decl => decl.name === req.params.name);
|
|
92
|
+
if (decl === undefined) {
|
|
93
|
+
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!isEntityDecl(decl)) {
|
|
97
|
+
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const instance = req.instancesByEntityName[decl.name]?.find(instance => instance.id === req.params.id);
|
|
101
|
+
if (instance === undefined) {
|
|
102
|
+
res.status(404).send(`Instance "${req.params.id}" not found`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const body = {
|
|
106
|
+
instance: instance,
|
|
107
|
+
isLocaleEntity: decl === req.localeEntity,
|
|
108
|
+
};
|
|
109
|
+
res.json(body);
|
|
110
|
+
});
|
|
111
|
+
declarationsApi.put("/:name/instances/:id", async (req, res) => {
|
|
112
|
+
const decl = req.declarations.find(decl => decl.name === req.params.name);
|
|
113
|
+
if (decl === undefined) {
|
|
114
|
+
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (!isEntityDecl(decl)) {
|
|
118
|
+
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const result = await updateInstance(req, req.params.name, req.params.id, req.body);
|
|
122
|
+
if (isOk(result)) {
|
|
123
|
+
const body = {
|
|
124
|
+
instance: result.value,
|
|
125
|
+
isLocaleEntity: decl === req.localeEntity,
|
|
126
|
+
};
|
|
127
|
+
res.json(body);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
res.status(result.error[0]).send(result.error[1]);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
declarationsApi.delete("/:name/instances/:id", async (req, res) => {
|
|
134
|
+
const decl = req.declarations.find(decl => decl.name === req.params.name);
|
|
135
|
+
if (decl === undefined) {
|
|
136
|
+
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (!isEntityDecl(decl)) {
|
|
140
|
+
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const result = await deleteInstance(req, req.params.name, req.params.id);
|
|
144
|
+
if (isOk(result)) {
|
|
145
|
+
const body = {
|
|
146
|
+
instance: result.value,
|
|
147
|
+
isLocaleEntity: decl === req.localeEntity,
|
|
148
|
+
};
|
|
149
|
+
res.json(body);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
res.status(result.error[0]).send(result.error[1]);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const gitApi: import("express-serve-static-core").Router;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import Debug from "debug";
|
|
2
|
+
import express, {} from "express";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { hasFileChanges } from "../../../shared/utils/git.js";
|
|
5
|
+
import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
|
|
6
|
+
import { serializeEntityDecl } from "../../schema/index.js";
|
|
7
|
+
import { attachGitStatusToInstancesByEntityName } from "../../utils/instances.js";
|
|
8
|
+
import { reinit } from "../init.js";
|
|
9
|
+
const debug = Debug("tsondb:server:api:git");
|
|
10
|
+
export const gitApi = express.Router();
|
|
11
|
+
gitApi.use((req, res, next) => {
|
|
12
|
+
debug(req.path);
|
|
13
|
+
if (req.gitRoot === undefined) {
|
|
14
|
+
res.status(500).send("Git repository not found");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
next();
|
|
18
|
+
});
|
|
19
|
+
gitApi.get("/status", async (req, res) => {
|
|
20
|
+
const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
|
|
21
|
+
const status = await req.git.status();
|
|
22
|
+
attachGitStatusToInstancesByEntityName(req.instancesByEntityName, req.dataRoot,
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
24
|
+
req.gitRoot, status);
|
|
25
|
+
const body = {
|
|
26
|
+
commitsAhead: status.ahead,
|
|
27
|
+
commitsBehind: status.behind,
|
|
28
|
+
instances: Object.fromEntries(Object.entries(req.instancesByEntityName).map(([entityName, instances]) => [
|
|
29
|
+
entityName,
|
|
30
|
+
instances
|
|
31
|
+
.filter(instance => hasFileChanges(instance.gitStatus))
|
|
32
|
+
.map(instance => getInstanceContainerOverview(
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
34
|
+
serializeEntityDecl(req.entitiesByName[entityName]), instance, locales)),
|
|
35
|
+
])),
|
|
36
|
+
};
|
|
37
|
+
res.json(body);
|
|
38
|
+
});
|
|
39
|
+
gitApi.post("/stage", async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
await req.git.add(req.dataRoot);
|
|
42
|
+
res.status(200).send("Added all database files to index");
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
debug(`${req.path}: ${error.message}`);
|
|
46
|
+
res.status(500).send("Adding all database files to index failed");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
gitApi.post("/stage/:entityName", async (req, res) => {
|
|
50
|
+
try {
|
|
51
|
+
await req.git.add(join(req.dataRoot, req.params.entityName));
|
|
52
|
+
res.status(200).send(`Added all database files for entity ${req.params.entityName} to index`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
debug(`${req.path}: ${error.message}`);
|
|
56
|
+
res
|
|
57
|
+
.status(500)
|
|
58
|
+
.send(`Adding all database files for entity ${req.params.entityName} to index failed`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
gitApi.post("/stage/:entityName/:instanceId", async (req, res) => {
|
|
62
|
+
try {
|
|
63
|
+
await req.git.add(join(req.dataRoot, req.params.entityName, `${req.params.instanceId}.json`));
|
|
64
|
+
res
|
|
65
|
+
.status(200)
|
|
66
|
+
.send(`Added database file ${req.params.instanceId} for entity ${req.params.entityName} to index`);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
debug(`${req.path}: ${error.message}`);
|
|
70
|
+
res
|
|
71
|
+
.status(500)
|
|
72
|
+
.send(`Adding database file ${req.params.instanceId} for entity ${req.params.entityName} to index failed`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
gitApi.post("/unstage", async (req, res) => {
|
|
76
|
+
try {
|
|
77
|
+
await req.git.reset(["HEAD", "--", req.dataRoot]);
|
|
78
|
+
res.status(200).send("Removed all database files to index");
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
debug(`${req.path}: ${error.message}`);
|
|
82
|
+
res.status(500).send("Removing all database files to index failed");
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
gitApi.post("/unstage/:entityName", async (req, res) => {
|
|
86
|
+
try {
|
|
87
|
+
await req.git.reset(["HEAD", "--", join(req.dataRoot, req.params.entityName)]);
|
|
88
|
+
res.status(200).send(`Removed all database files for entity ${req.params.entityName} to index`);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
debug(`${req.path}: ${error.message}`);
|
|
92
|
+
res
|
|
93
|
+
.status(500)
|
|
94
|
+
.send(`Removing all database files for entity ${req.params.entityName} to index failed`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
gitApi.post("/unstage/:entityName/:instanceId", async (req, res) => {
|
|
98
|
+
try {
|
|
99
|
+
await req.git.reset([
|
|
100
|
+
"HEAD",
|
|
101
|
+
"--",
|
|
102
|
+
join(req.dataRoot, req.params.entityName, `${req.params.instanceId}.json`),
|
|
103
|
+
]);
|
|
104
|
+
res
|
|
105
|
+
.status(200)
|
|
106
|
+
.send(`Removed database file ${req.params.instanceId} for entity ${req.params.entityName} to index`);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
debug(`${req.path}: ${error.message}`);
|
|
110
|
+
res
|
|
111
|
+
.status(500)
|
|
112
|
+
.send(`Removing database file ${req.params.instanceId} for entity ${req.params.entityName} to index failed`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
gitApi.post("/commit", async (req, res) => {
|
|
116
|
+
const message = req.body.message;
|
|
117
|
+
if (typeof message !== "string" || message.length === 0) {
|
|
118
|
+
res.status(400).send("Invalid commit message");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await req.git.commit(message);
|
|
123
|
+
res.status(200).send("Commit successful");
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
res.status(500).send("Commit failed");
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
gitApi.post("/push", async (req, res) => {
|
|
130
|
+
try {
|
|
131
|
+
await req.git.push();
|
|
132
|
+
res.status(200).send("Push successful");
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
res.status(500).send("Push failed");
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
gitApi.post("/pull", async (req, res) => {
|
|
139
|
+
try {
|
|
140
|
+
await req.git.pull();
|
|
141
|
+
res.status(200).send("Pull successful");
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
res.status(500).send("Pull failed");
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
gitApi.get("/branch", async (req, res) => {
|
|
148
|
+
const branchSummary = await req.git.branchLocal();
|
|
149
|
+
const body = {
|
|
150
|
+
allBranches: branchSummary.all,
|
|
151
|
+
currentBranch: branchSummary.current,
|
|
152
|
+
};
|
|
153
|
+
res.json(body);
|
|
154
|
+
});
|
|
155
|
+
gitApi.post("/branch", async (req, res) => {
|
|
156
|
+
const branchName = req.body.branchName;
|
|
157
|
+
if (typeof branchName !== "string" || branchName.length === 0) {
|
|
158
|
+
res.status(400).send("Invalid branch name");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
await req.git.checkoutLocalBranch(branchName);
|
|
163
|
+
await reinit(req);
|
|
164
|
+
res.status(200).send(`Creation of branch "${branchName}" successful`);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
res.status(500).send(`Creation of branch "${branchName}" failed`);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
gitApi.post("/branch/:branchName", async (req, res) => {
|
|
171
|
+
try {
|
|
172
|
+
await req.git.checkout(req.params.branchName);
|
|
173
|
+
res.status(200).send(`Switch to branch "${req.params.branchName}" successful`);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
res.status(500).send(`Switch to branch "${req.params.branchName}" failed`);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const api: import("express-serve-static-core").Router;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { declarationsApi } from "./declarations.js";
|
|
3
|
+
import { gitApi } from "./git.js";
|
|
4
|
+
import { instancesApi } from "./instances.js";
|
|
5
|
+
export const api = express.Router();
|
|
6
|
+
api.use("/declarations", declarationsApi);
|
|
7
|
+
api.use("/instances", instancesApi);
|
|
8
|
+
api.use("/git", gitApi);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InstanceContainer } from "../../../shared/utils/instances.js";
|
|
2
|
+
import type { Result } from "../../../shared/utils/result.js";
|
|
3
|
+
import type { TSONDBRequestLocals } from "../index.js";
|
|
4
|
+
export declare const createInstance: (locals: TSONDBRequestLocals, entityName: string, instance: unknown, idQueryParam: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
5
|
+
export declare const updateInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string, instance: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
6
|
+
export declare const deleteInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import { removeAt } from "../../../shared/utils/array.js";
|
|
5
|
+
import { error, ok } from "../../../shared/utils/result.js";
|
|
6
|
+
import { validateEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
7
|
+
import { formatValue } from "../../schema/index.js";
|
|
8
|
+
import { createValidators } from "../../schema/Node.js";
|
|
9
|
+
import { getErrorMessageForDisplay } from "../../utils/error.js";
|
|
10
|
+
import { getGitFileStatusFromStatusResult } from "../../utils/git.js";
|
|
11
|
+
import { updateReferencesToInstances } from "../../utils/references.js";
|
|
12
|
+
export const createInstance = async (locals, entityName, instance, idQueryParam) => {
|
|
13
|
+
const entity = locals.entitiesByName[entityName];
|
|
14
|
+
if (entity === undefined) {
|
|
15
|
+
return error([400, "Entity not found"]);
|
|
16
|
+
}
|
|
17
|
+
const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
|
|
18
|
+
if (validationErrors.length > 0) {
|
|
19
|
+
return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
|
|
20
|
+
}
|
|
21
|
+
if (locals.localeEntity === entity && typeof idQueryParam !== "string") {
|
|
22
|
+
return error([400, "Missing id for locale entity"]);
|
|
23
|
+
}
|
|
24
|
+
const id = locals.localeEntity === entity ? idQueryParam : uuidv4();
|
|
25
|
+
if (locals.localeEntity === entity &&
|
|
26
|
+
locals.instancesByEntityName[entity.name]?.some(instance => instance.id === id)) {
|
|
27
|
+
return error([400, `Duplicate id "${id}" for locale entity`]);
|
|
28
|
+
}
|
|
29
|
+
const fileName = `${id}.json`;
|
|
30
|
+
await writeFile(join(locals.dataRoot, entity.name, fileName), JSON.stringify(formatValue(entity.type.value, instance), undefined, 2), { encoding: "utf-8" });
|
|
31
|
+
const instanceContainer = {
|
|
32
|
+
fileName,
|
|
33
|
+
id,
|
|
34
|
+
content: instance,
|
|
35
|
+
gitStatus: locals.gitRoot === undefined
|
|
36
|
+
? undefined
|
|
37
|
+
: getGitFileStatusFromStatusResult(await locals.git.status(), locals.gitRoot, locals.dataRoot, entity.name, fileName),
|
|
38
|
+
};
|
|
39
|
+
locals.instancesByEntityName[entity.name] = [
|
|
40
|
+
...(locals.instancesByEntityName[entity.name] ?? []),
|
|
41
|
+
instanceContainer,
|
|
42
|
+
];
|
|
43
|
+
Object.assign(locals.referencesToInstances, updateReferencesToInstances(locals.entitiesByName, locals.referencesToInstances, entity.name, id, undefined, instance));
|
|
44
|
+
return ok(instanceContainer);
|
|
45
|
+
};
|
|
46
|
+
export const updateInstance = async (locals, entityName, instanceId, instance) => {
|
|
47
|
+
const instanceContainer = locals.instancesByEntityName[entityName]?.find(instance => instance.id === instanceId);
|
|
48
|
+
if (instanceContainer === undefined) {
|
|
49
|
+
return error([404, "Instance not found"]);
|
|
50
|
+
}
|
|
51
|
+
const entity = locals.entitiesByName[entityName];
|
|
52
|
+
if (entity === undefined) {
|
|
53
|
+
return error([400, "Entity not found"]);
|
|
54
|
+
}
|
|
55
|
+
const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
|
|
56
|
+
if (validationErrors.length > 0) {
|
|
57
|
+
return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
|
|
58
|
+
}
|
|
59
|
+
await writeFile(join(locals.dataRoot, entity.name, instanceContainer.fileName), JSON.stringify(formatValue(entity.type.value, instance), undefined, 2), { encoding: "utf-8" });
|
|
60
|
+
const oldInstance = instanceContainer.content;
|
|
61
|
+
instanceContainer.content = instance;
|
|
62
|
+
instanceContainer.gitStatus =
|
|
63
|
+
locals.gitRoot === undefined
|
|
64
|
+
? undefined
|
|
65
|
+
: getGitFileStatusFromStatusResult(await locals.git.status(), locals.gitRoot, locals.dataRoot, entity.name, instanceContainer.fileName);
|
|
66
|
+
Object.assign(locals.referencesToInstances, updateReferencesToInstances(locals.entitiesByName, locals.referencesToInstances, entity.name, instanceId, oldInstance, instance));
|
|
67
|
+
return ok(instanceContainer);
|
|
68
|
+
};
|
|
69
|
+
export const deleteInstance = async (locals, entityName, instanceId) => {
|
|
70
|
+
const instances = locals.instancesByEntityName[entityName] ?? [];
|
|
71
|
+
const instanceContainerIndex = instances.findIndex(instance => instance.id === instanceId);
|
|
72
|
+
const instanceContainer = instances[instanceContainerIndex];
|
|
73
|
+
if (instanceContainer === undefined) {
|
|
74
|
+
return error([404, "Instance not found"]);
|
|
75
|
+
}
|
|
76
|
+
if (locals.referencesToInstances[instanceId]?.some(ref => ref !== instanceId)) {
|
|
77
|
+
return error([400, "Cannot delete instance that is referenced by other instances"]);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
await rm(join(locals.dataRoot, entityName, instanceContainer.fileName));
|
|
81
|
+
locals.instancesByEntityName[entityName] = removeAt(instances, instanceContainerIndex);
|
|
82
|
+
Object.assign(locals.referencesToInstances, updateReferencesToInstances(locals.entitiesByName, locals.referencesToInstances, entityName, instanceId, instanceContainer.content, undefined));
|
|
83
|
+
return ok(instanceContainer);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
return error([
|
|
87
|
+
500,
|
|
88
|
+
`Failed to delete instance: ${err instanceof Error ? err.toString() : String(err)}`,
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const instancesApi: import("express-serve-static-core").Router;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Debug from "debug";
|
|
2
|
+
import express from "express";
|
|
3
|
+
import { getDisplayNameFromEntityInstance } from "../../../shared/utils/displayName.js";
|
|
4
|
+
import { serializeEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
5
|
+
const debug = Debug("tsondb:server:api:instances");
|
|
6
|
+
export const instancesApi = express.Router();
|
|
7
|
+
instancesApi.use((req, _res, next) => {
|
|
8
|
+
debug(req.path);
|
|
9
|
+
next();
|
|
10
|
+
});
|
|
11
|
+
instancesApi.get("/", (req, res) => {
|
|
12
|
+
const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
|
|
13
|
+
const body = {
|
|
14
|
+
instances: Object.fromEntries(Object.entries(req.instancesByEntityName)
|
|
15
|
+
.filter(([entityName]) => Object.hasOwn(req.entitiesByName, entityName))
|
|
16
|
+
.map(([entityName, instances]) => [
|
|
17
|
+
entityName,
|
|
18
|
+
instances.map(instance => ({
|
|
19
|
+
id: instance.id,
|
|
20
|
+
name: getDisplayNameFromEntityInstance(
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22
|
+
serializeEntityDecl(req.entitiesByName[entityName]), instance.content, instance.id, locales),
|
|
23
|
+
})),
|
|
24
|
+
])),
|
|
25
|
+
};
|
|
26
|
+
res.json(body);
|
|
27
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SimpleGit } from "simple-git";
|
|
2
|
+
import type { InstancesByEntityName } from "../../shared/utils/instances.js";
|
|
3
|
+
import type { Schema } from "../Schema.ts";
|
|
4
|
+
import type { Decl } from "../schema/declarations/Declaration.js";
|
|
5
|
+
import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
6
|
+
import type { ReferencesToInstances } from "../utils/references.js";
|
|
7
|
+
export type ServerOptions = {
|
|
8
|
+
name: string;
|
|
9
|
+
port: number;
|
|
10
|
+
};
|
|
11
|
+
export interface TSONDBRequestLocals {
|
|
12
|
+
git: SimpleGit;
|
|
13
|
+
gitRoot: string | undefined;
|
|
14
|
+
dataRoot: string;
|
|
15
|
+
declarations: readonly Decl[];
|
|
16
|
+
entities: readonly EntityDecl[];
|
|
17
|
+
instancesByEntityName: InstancesByEntityName;
|
|
18
|
+
entitiesByName: Record<string, EntityDecl>;
|
|
19
|
+
localeEntity?: EntityDecl;
|
|
20
|
+
referencesToInstances: ReferencesToInstances;
|
|
21
|
+
}
|
|
22
|
+
declare global {
|
|
23
|
+
namespace Express {
|
|
24
|
+
interface Request extends TSONDBRequestLocals {
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export declare const createServer: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, options?: Partial<ServerOptions>) => Promise<void>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import Debug from "debug";
|
|
2
|
+
import express from "express";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { api } from "./api/index.js";
|
|
5
|
+
import { init } from "./init.js";
|
|
6
|
+
const debug = Debug("tsondb:server");
|
|
7
|
+
const defaultOptions = {
|
|
8
|
+
name: "tsondb server",
|
|
9
|
+
port: 3000,
|
|
10
|
+
};
|
|
11
|
+
export const createServer = async (schema, dataRootPath, instancesByEntityName, options) => {
|
|
12
|
+
const { name, port } = { ...defaultOptions, ...options };
|
|
13
|
+
const app = express();
|
|
14
|
+
app.use(express.static(join(import.meta.dirname, "../../public")));
|
|
15
|
+
app.use("/js/node_modules", express.static(join(import.meta.dirname, "../../node_modules")));
|
|
16
|
+
app.use("/js/client", express.static(join(import.meta.dirname, "../../lib/client")));
|
|
17
|
+
app.use("/js/shared", express.static(join(import.meta.dirname, "../../lib/shared")));
|
|
18
|
+
app.use(express.json());
|
|
19
|
+
const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName));
|
|
20
|
+
app.use((req, _res, next) => {
|
|
21
|
+
Object.assign(req, requestLocals);
|
|
22
|
+
next();
|
|
23
|
+
});
|
|
24
|
+
app.use("/api", api);
|
|
25
|
+
app.get(/^\/.*/, (_req, res) => {
|
|
26
|
+
res.send(`<!DOCTYPE html>
|
|
27
|
+
<html lang="en">
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="UTF-8">
|
|
30
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31
|
+
<title>TSONDB</title>
|
|
32
|
+
<link rel="stylesheet" href="/css/styles.css">
|
|
33
|
+
<script type="importmap">
|
|
34
|
+
{
|
|
35
|
+
"imports": {
|
|
36
|
+
"preact": "/js/node_modules/preact/dist/preact.module.js",
|
|
37
|
+
"preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
|
|
38
|
+
"preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
|
|
39
|
+
"preact-iso": "/js/node_modules/preact-iso/src/index.js"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<div id="app"></div>
|
|
46
|
+
<script type="module" src="/js/client/index.js"></script>
|
|
47
|
+
</body>
|
|
48
|
+
</html>`);
|
|
49
|
+
});
|
|
50
|
+
app.listen(port, () => {
|
|
51
|
+
debug(`${name} listening on http://localhost:${port.toString()}`);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { InstancesByEntityName } from "../../shared/utils/instances.js";
|
|
2
|
+
import type { Schema } from "../Schema.ts";
|
|
3
|
+
import type { TSONDBRequestLocals } from "./index.js";
|
|
4
|
+
export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName) => Promise<TSONDBRequestLocals>;
|
|
5
|
+
export declare const reinit: (locals: TSONDBRequestLocals) => Promise<void>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { simpleGit } from "simple-git";
|
|
2
|
+
import { isEntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
3
|
+
import { resolveTypeArgumentsInDecls } from "../schema/index.js";
|
|
4
|
+
import { attachGitStatusToInstancesByEntityName, getInstancesByEntityName, } from "../utils/instances.js";
|
|
5
|
+
import { getReferencesToInstances } from "../utils/references.js";
|
|
6
|
+
const getGit = async (dataRootPath) => {
|
|
7
|
+
const git = simpleGit({ baseDir: dataRootPath });
|
|
8
|
+
if (await git.checkIsRepo()) {
|
|
9
|
+
try {
|
|
10
|
+
const root = await git.revparse({ "--show-toplevel": null });
|
|
11
|
+
const status = await git.status();
|
|
12
|
+
return { git, root, status };
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return { git };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
return { git };
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export const init = async (schema, dataRootPath, instancesByEntityName) => {
|
|
23
|
+
const { git, root: gitRoot, status: gitStatus } = await getGit(dataRootPath);
|
|
24
|
+
const declarations = resolveTypeArgumentsInDecls(schema.declarations);
|
|
25
|
+
const entities = declarations.filter(isEntityDecl);
|
|
26
|
+
const entitiesByName = Object.fromEntries(entities.map(entity => [entity.name, entity]));
|
|
27
|
+
const instancesByEntityNameInMemory = Object.assign({}, instancesByEntityName);
|
|
28
|
+
const referencesToInstances = getReferencesToInstances(instancesByEntityName, entitiesByName);
|
|
29
|
+
if (gitStatus) {
|
|
30
|
+
attachGitStatusToInstancesByEntityName(instancesByEntityName, dataRootPath, gitRoot, gitStatus);
|
|
31
|
+
}
|
|
32
|
+
const requestLocals = {
|
|
33
|
+
git: git,
|
|
34
|
+
gitRoot: gitRoot,
|
|
35
|
+
dataRoot: dataRootPath,
|
|
36
|
+
declarations: declarations,
|
|
37
|
+
entities: entities,
|
|
38
|
+
instancesByEntityName: instancesByEntityNameInMemory,
|
|
39
|
+
entitiesByName: entitiesByName,
|
|
40
|
+
localeEntity: schema.localeEntity,
|
|
41
|
+
referencesToInstances,
|
|
42
|
+
};
|
|
43
|
+
return requestLocals;
|
|
44
|
+
};
|
|
45
|
+
export const reinit = async (locals) => {
|
|
46
|
+
const gitStatus = (await locals.git.checkIsRepo()) ? await locals.git.status() : undefined;
|
|
47
|
+
const instancesByEntityName = await getInstancesByEntityName(locals.dataRoot, locals.entities);
|
|
48
|
+
const referencesToInstances = getReferencesToInstances(instancesByEntityName, locals.entitiesByName);
|
|
49
|
+
if (locals.gitRoot && gitStatus) {
|
|
50
|
+
attachGitStatusToInstancesByEntityName(instancesByEntityName, locals.dataRoot, locals.gitRoot, gitStatus);
|
|
51
|
+
}
|
|
52
|
+
Object.assign(locals, {
|
|
53
|
+
instancesByEntityName,
|
|
54
|
+
referencesToInstances,
|
|
55
|
+
});
|
|
56
|
+
};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { applyIndentation } from "./render.js";
|
|
2
2
|
export const getErrorMessageForDisplay = (error) => {
|
|
3
3
|
if (error instanceof AggregateError) {
|
|
4
|
-
return `${error.message}\n${applyIndentation(1, error.errors
|
|
4
|
+
return `${error.message}\n${applyIndentation(1, error.errors
|
|
5
|
+
.filter(subError => subError instanceof Error)
|
|
6
|
+
.map(subError => getErrorMessageForDisplay(subError))
|
|
7
|
+
.join("\n"), 2)}`;
|
|
5
8
|
}
|
|
6
9
|
else if (error.cause instanceof Error) {
|
|
7
10
|
return `${error.message}\n${applyIndentation(1, getErrorMessageForDisplay(error.cause), 2)}`;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { StatusResult } from "simple-git";
|
|
2
|
+
import type { GitFileStatus } from "../../shared/utils/git.js";
|
|
3
|
+
export declare const getGitFileStatusFromStatusResult: (statusResult: StatusResult, repoRoot: string, dataRoot: string, entityName: string, fileName: string) => GitFileStatus | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
export const getGitFileStatusFromStatusResult = (statusResult, repoRoot, dataRoot, entityName, fileName) => {
|
|
3
|
+
const filePath = join(dataRoot, entityName, fileName);
|
|
4
|
+
const gitFile = statusResult.files.find(file => join(repoRoot, file.path) === filePath);
|
|
5
|
+
if (gitFile === undefined) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
index: gitFile.index,
|
|
10
|
+
workingDir: gitFile.working_dir,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { StatusResult } from "simple-git";
|
|
2
|
+
import type { InstancesByEntityName } from "../../shared/utils/instances.js";
|
|
3
|
+
import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
4
|
+
export declare const getInstancesByEntityName: (dataRoot: string, entities: readonly EntityDecl[]) => Promise<InstancesByEntityName>;
|
|
5
|
+
export declare const attachGitStatusToInstancesByEntityName: (instancesByEntityName: InstancesByEntityName, dataRoot: string, gitRoot: string, gitStatus: StatusResult) => void;
|