tsondb 0.3.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/{renderers → node/renderers}/jsonschema/index.d.ts +2 -2
- package/lib/{renderers → node/renderers}/jsonschema/index.js +2 -2
- package/lib/{renderers → node/renderers}/jsonschema/render.d.ts +1 -1
- package/lib/{renderers → node/renderers}/jsonschema/render.js +9 -9
- package/lib/{renderers → node/renderers}/ts/index.d.ts +2 -2
- package/lib/{renderers → node/renderers}/ts/index.js +2 -2
- package/lib/{renderers → node/renderers}/ts/render.d.ts +1 -1
- package/lib/{renderers → node/renderers}/ts/render.js +9 -8
- package/lib/{schema → node/schema}/Node.d.ts +4 -4
- package/lib/{schema → node/schema}/Node.js +5 -5
- 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 +10 -11
- package/lib/{schema → node/schema}/declarations/Declaration.js +7 -5
- package/lib/{schema → node/schema}/declarations/EntityDecl.d.ts +10 -9
- package/lib/{schema → node/schema}/declarations/EntityDecl.js +2 -2
- package/lib/{schema → node/schema}/declarations/EnumDecl.d.ts +9 -7
- package/lib/{schema → node/schema}/declarations/EnumDecl.js +3 -3
- 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 -4
- package/lib/{schema → node/schema}/index.js +3 -4
- package/lib/node/schema/types/Type.d.ts +48 -0
- package/lib/{schema → node/schema}/types/Type.js +41 -25
- package/lib/{schema → node/schema}/types/generic/ArrayType.d.ts +5 -4
- package/lib/{schema → node/schema}/types/generic/ArrayType.js +3 -3
- package/lib/{schema → node/schema}/types/generic/EnumType.d.ts +5 -4
- package/lib/{schema → node/schema}/types/generic/EnumType.js +4 -2
- package/lib/{schema → node/schema}/types/generic/ObjectType.d.ts +8 -7
- package/lib/{schema → node/schema}/types/generic/ObjectType.js +8 -4
- package/lib/{schema → node/schema}/types/primitives/BooleanType.d.ts +4 -3
- package/lib/{schema → node/schema}/types/primitives/DateType.d.ts +5 -4
- package/lib/{schema → node/schema}/types/primitives/DateType.js +1 -1
- package/lib/{schema → node/schema}/types/primitives/FloatType.d.ts +5 -4
- package/lib/{schema → node/schema}/types/primitives/FloatType.js +1 -1
- package/lib/{schema → node/schema}/types/primitives/IntegerType.d.ts +5 -4
- package/lib/{schema → node/schema}/types/primitives/IntegerType.js +1 -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 +5 -4
- package/lib/{schema → node/schema}/types/primitives/StringType.js +1 -1
- package/lib/{schema → node/schema}/types/references/IncludeIdentifierType.d.ts +9 -8
- package/lib/{schema → node/schema}/types/references/IncludeIdentifierType.js +1 -1
- package/lib/{schema → node/schema}/types/references/NestedEntityMapType.d.ts +10 -9
- package/lib/{schema → node/schema}/types/references/NestedEntityMapType.js +4 -4
- package/lib/{schema → node/schema}/types/references/ReferenceIdentifierType.d.ts +7 -6
- package/lib/{schema → node/schema}/types/references/ReferenceIdentifierType.js +1 -1
- 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/{server → node/server}/api/declarations.js +1 -1
- package/lib/{server → node/server}/api/git.js +14 -10
- package/lib/{server → node/server}/api/instanceOperations.d.ts +3 -3
- package/lib/{server → node/server}/api/instanceOperations.js +13 -4
- package/lib/{server → node/server}/api/instances.js +7 -3
- package/lib/node/server/index.d.ts +28 -0
- package/lib/{server → node/server}/index.js +6 -6
- package/lib/node/server/init.d.ts +5 -0
- package/lib/{server → node/server}/init.js +8 -8
- package/lib/{utils → node/utils}/error.js +4 -1
- package/lib/{utils → node/utils}/git.d.ts +2 -2
- package/lib/{utils → node/utils}/instances.d.ts +3 -3
- package/lib/{utils → node/utils}/instances.js +8 -6
- package/lib/{utils → node/utils}/references.d.ts +2 -2
- package/lib/{utils → node/utils}/references.js +3 -3
- package/lib/{utils → node/utils}/render.js +1 -1
- package/lib/shared/api.d.ts +8 -2
- package/lib/shared/utils/array.js +12 -9
- package/lib/shared/utils/displayName.d.ts +1 -1
- package/lib/shared/utils/displayName.js +4 -2
- package/lib/shared/utils/instances.d.ts +2 -2
- package/lib/{utils → shared/utils}/lazy.js +1 -1
- package/lib/shared/utils/markdown.js +6 -6
- package/lib/shared/utils/object.d.ts +3 -0
- package/lib/shared/utils/object.js +1 -0
- package/lib/{utils → shared/utils}/result.js +2 -2
- package/lib/shared/utils/string.js +7 -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/{client → web}/api.d.ts +2 -2
- package/lib/{client → web}/api.js +4 -2
- package/lib/web/components/Git.d.ts +2 -0
- package/lib/{client → web}/components/Git.js +58 -14
- package/lib/{client → web}/components/Layout.d.ts +1 -1
- package/lib/{client → web}/components/Layout.js +1 -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 +7 -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 → web}/components/typeInputs/EnumTypeInput.js +1 -1
- 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/{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 +3 -3
- package/lib/{client → web}/components/typeInputs/utils/Markdown.d.ts +1 -1
- package/lib/{client → web}/components/typeInputs/utils/Markdown.js +2 -2
- 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/{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/{client → web}/hooks/useMappedAPIResource.js +7 -9
- 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/{client → web}/routes/Entity.js +5 -3
- package/lib/web/routes/Home.d.ts +2 -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 +4 -1
- package/package.json +31 -18
- package/lib/ModelContainer.d.ts +0 -17
- package/lib/ModelContainer.js +0 -65
- package/lib/client/components/Git.d.ts +0 -2
- 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/EnumTypeInput.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/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/hooks/useAPIResource.d.ts +0 -1
- package/lib/client/hooks/useMappedAPIResource.d.ts +0 -1
- package/lib/client/routes/Entity.d.ts +0 -2
- package/lib/client/routes/Home.d.ts +0 -2
- package/lib/client/utils/typeSkeleton.d.ts +0 -3
- package/lib/schema/types/Type.d.ts +0 -48
- package/lib/schema/types/primitives/PrimitiveType.d.ts +0 -6
- package/lib/schema/types/references/GenericArgumentIdentifierType.d.ts +0 -22
- package/lib/schema/types/references/GenericArgumentIdentifierType.js +0 -19
- package/lib/schema/validation/type.d.ts +0 -4
- package/lib/server/index.d.ts +0 -29
- package/lib/server/init.d.ts +0 -5
- package/lib/tsconfig.tsbuildinfo +0 -1
- package/lib/utils/object.d.ts +0 -3
- package/lib/utils/object.js +0 -1
- /package/lib/{renderers → node/renderers}/Output.js +0 -0
- /package/lib/{schema → node/schema}/types/primitives/BooleanType.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/{server → node/server}/api/declarations.d.ts +0 -0
- /package/lib/{server → node/server}/api/git.d.ts +0 -0
- /package/lib/{server → node/server}/api/index.d.ts +0 -0
- /package/lib/{server → node/server}/api/index.js +0 -0
- /package/lib/{server → node/server}/api/instances.d.ts +0 -0
- /package/lib/{utils → node/utils}/error.d.ts +0 -0
- /package/lib/{utils → node/utils}/git.js +0 -0
- /package/lib/{utils → node/utils}/path.d.ts +0 -0
- /package/lib/{utils → node/utils}/path.js +0 -0
- /package/lib/{utils → node/utils}/render.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/IncludeIdentifierTypeInput.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}/hooks/useAPIResource.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/Home.js +0 -0
- /package/lib/{client → web}/routes/NotFound.js +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
2
|
import express from "express";
|
|
3
|
+
import { isOk } from "../../../shared/utils/result.js";
|
|
3
4
|
import { serializeDecl } from "../../schema/declarations/Declaration.js";
|
|
4
5
|
import { isEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
5
6
|
import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
|
|
6
7
|
import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
|
|
7
|
-
import { isOk } from "../../utils/result.js";
|
|
8
8
|
import { createInstance, deleteInstance, updateInstance } from "./instanceOperations.js";
|
|
9
9
|
const debug = Debug("tsondb:server:api:declarations");
|
|
10
10
|
export const declarationsApi = express.Router();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
|
-
import express from "express";
|
|
2
|
+
import express, {} from "express";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { hasFileChanges } from "../../../shared/utils/git.js";
|
|
5
|
+
import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
|
|
4
6
|
import { serializeEntityDecl } from "../../schema/index.js";
|
|
5
|
-
import { hasFileChanges } from "../../shared/utils/git.js";
|
|
6
|
-
import { getInstanceContainerOverview } from "../../shared/utils/instances.js";
|
|
7
7
|
import { attachGitStatusToInstancesByEntityName } from "../../utils/instances.js";
|
|
8
8
|
import { reinit } from "../init.js";
|
|
9
9
|
const debug = Debug("tsondb:server:api:git");
|
|
@@ -19,7 +19,9 @@ gitApi.use((req, res, next) => {
|
|
|
19
19
|
gitApi.get("/status", async (req, res) => {
|
|
20
20
|
const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
|
|
21
21
|
const status = await req.git.status();
|
|
22
|
-
attachGitStatusToInstancesByEntityName(req.instancesByEntityName, req.dataRoot,
|
|
22
|
+
attachGitStatusToInstancesByEntityName(req.instancesByEntityName, req.dataRoot,
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
24
|
+
req.gitRoot, status);
|
|
23
25
|
const body = {
|
|
24
26
|
commitsAhead: status.ahead,
|
|
25
27
|
commitsBehind: status.behind,
|
|
@@ -27,7 +29,9 @@ gitApi.get("/status", async (req, res) => {
|
|
|
27
29
|
entityName,
|
|
28
30
|
instances
|
|
29
31
|
.filter(instance => hasFileChanges(instance.gitStatus))
|
|
30
|
-
.map(instance => getInstanceContainerOverview(
|
|
32
|
+
.map(instance => getInstanceContainerOverview(
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
34
|
+
serializeEntityDecl(req.entitiesByName[entityName]), instance, locales)),
|
|
31
35
|
])),
|
|
32
36
|
};
|
|
33
37
|
res.json(body);
|
|
@@ -118,7 +122,7 @@ gitApi.post("/commit", async (req, res) => {
|
|
|
118
122
|
await req.git.commit(message);
|
|
119
123
|
res.status(200).send("Commit successful");
|
|
120
124
|
}
|
|
121
|
-
catch
|
|
125
|
+
catch {
|
|
122
126
|
res.status(500).send("Commit failed");
|
|
123
127
|
}
|
|
124
128
|
});
|
|
@@ -127,7 +131,7 @@ gitApi.post("/push", async (req, res) => {
|
|
|
127
131
|
await req.git.push();
|
|
128
132
|
res.status(200).send("Push successful");
|
|
129
133
|
}
|
|
130
|
-
catch
|
|
134
|
+
catch {
|
|
131
135
|
res.status(500).send("Push failed");
|
|
132
136
|
}
|
|
133
137
|
});
|
|
@@ -136,7 +140,7 @@ gitApi.post("/pull", async (req, res) => {
|
|
|
136
140
|
await req.git.pull();
|
|
137
141
|
res.status(200).send("Pull successful");
|
|
138
142
|
}
|
|
139
|
-
catch
|
|
143
|
+
catch {
|
|
140
144
|
res.status(500).send("Pull failed");
|
|
141
145
|
}
|
|
142
146
|
});
|
|
@@ -159,7 +163,7 @@ gitApi.post("/branch", async (req, res) => {
|
|
|
159
163
|
await reinit(req);
|
|
160
164
|
res.status(200).send(`Creation of branch "${branchName}" successful`);
|
|
161
165
|
}
|
|
162
|
-
catch
|
|
166
|
+
catch {
|
|
163
167
|
res.status(500).send(`Creation of branch "${branchName}" failed`);
|
|
164
168
|
}
|
|
165
169
|
});
|
|
@@ -168,7 +172,7 @@ gitApi.post("/branch/:branchName", async (req, res) => {
|
|
|
168
172
|
await req.git.checkout(req.params.branchName);
|
|
169
173
|
res.status(200).send(`Switch to branch "${req.params.branchName}" successful`);
|
|
170
174
|
}
|
|
171
|
-
catch
|
|
175
|
+
catch {
|
|
172
176
|
res.status(500).send(`Switch to branch "${req.params.branchName}" failed`);
|
|
173
177
|
}
|
|
174
178
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { InstanceContainer } from "
|
|
2
|
-
import { Result } from "
|
|
3
|
-
import { TSONDBRequestLocals } from "../index.js";
|
|
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
4
|
export declare const createInstance: (locals: TSONDBRequestLocals, entityName: string, instance: unknown, idQueryParam: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
5
5
|
export declare const updateInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string, instance: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
6
6
|
export declare const deleteInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import { removeAt } from "../../../shared/utils/array.js";
|
|
5
|
+
import { error, ok } from "../../../shared/utils/result.js";
|
|
4
6
|
import { validateEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
5
7
|
import { formatValue } from "../../schema/index.js";
|
|
6
8
|
import { createValidators } from "../../schema/Node.js";
|
|
7
|
-
import { removeAt } from "../../shared/utils/array.js";
|
|
8
9
|
import { getErrorMessageForDisplay } from "../../utils/error.js";
|
|
9
10
|
import { getGitFileStatusFromStatusResult } from "../../utils/git.js";
|
|
10
11
|
import { updateReferencesToInstances } from "../../utils/references.js";
|
|
11
|
-
import { error, ok } from "../../utils/result.js";
|
|
12
12
|
export const createInstance = async (locals, entityName, instance, idQueryParam) => {
|
|
13
13
|
const entity = locals.entitiesByName[entityName];
|
|
14
|
+
if (entity === undefined) {
|
|
15
|
+
return error([400, "Entity not found"]);
|
|
16
|
+
}
|
|
14
17
|
const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
|
|
15
18
|
if (validationErrors.length > 0) {
|
|
16
19
|
return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
|
|
@@ -20,7 +23,7 @@ export const createInstance = async (locals, entityName, instance, idQueryParam)
|
|
|
20
23
|
}
|
|
21
24
|
const id = locals.localeEntity === entity ? idQueryParam : uuidv4();
|
|
22
25
|
if (locals.localeEntity === entity &&
|
|
23
|
-
locals.instancesByEntityName[entity.name]
|
|
26
|
+
locals.instancesByEntityName[entity.name]?.some(instance => instance.id === id)) {
|
|
24
27
|
return error([400, `Duplicate id "${id}" for locale entity`]);
|
|
25
28
|
}
|
|
26
29
|
const fileName = `${id}.json`;
|
|
@@ -46,6 +49,9 @@ export const updateInstance = async (locals, entityName, instanceId, instance) =
|
|
|
46
49
|
return error([404, "Instance not found"]);
|
|
47
50
|
}
|
|
48
51
|
const entity = locals.entitiesByName[entityName];
|
|
52
|
+
if (entity === undefined) {
|
|
53
|
+
return error([400, "Entity not found"]);
|
|
54
|
+
}
|
|
49
55
|
const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
|
|
50
56
|
if (validationErrors.length > 0) {
|
|
51
57
|
return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
|
|
@@ -77,6 +83,9 @@ export const deleteInstance = async (locals, entityName, instanceId) => {
|
|
|
77
83
|
return ok(instanceContainer);
|
|
78
84
|
}
|
|
79
85
|
catch (err) {
|
|
80
|
-
return error([
|
|
86
|
+
return error([
|
|
87
|
+
500,
|
|
88
|
+
`Failed to delete instance: ${err instanceof Error ? err.toString() : String(err)}`,
|
|
89
|
+
]);
|
|
81
90
|
}
|
|
82
91
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
2
|
import express from "express";
|
|
3
|
+
import { getDisplayNameFromEntityInstance } from "../../../shared/utils/displayName.js";
|
|
3
4
|
import { serializeEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
4
|
-
import { getDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
|
|
5
5
|
const debug = Debug("tsondb:server:api:instances");
|
|
6
6
|
export const instancesApi = express.Router();
|
|
7
7
|
instancesApi.use((req, _res, next) => {
|
|
@@ -11,11 +11,15 @@ instancesApi.use((req, _res, next) => {
|
|
|
11
11
|
instancesApi.get("/", (req, res) => {
|
|
12
12
|
const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
|
|
13
13
|
const body = {
|
|
14
|
-
instances: Object.fromEntries(Object.entries(req.instancesByEntityName)
|
|
14
|
+
instances: Object.fromEntries(Object.entries(req.instancesByEntityName)
|
|
15
|
+
.filter(([entityName]) => Object.hasOwn(req.entitiesByName, entityName))
|
|
16
|
+
.map(([entityName, instances]) => [
|
|
15
17
|
entityName,
|
|
16
18
|
instances.map(instance => ({
|
|
17
19
|
id: instance.id,
|
|
18
|
-
name: getDisplayNameFromEntityInstance(
|
|
20
|
+
name: getDisplayNameFromEntityInstance(
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22
|
+
serializeEntityDecl(req.entitiesByName[entityName]), instance.content, instance.id, locales),
|
|
19
23
|
})),
|
|
20
24
|
])),
|
|
21
25
|
};
|
|
@@ -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>;
|
|
@@ -8,7 +8,7 @@ const defaultOptions = {
|
|
|
8
8
|
name: "tsondb server",
|
|
9
9
|
port: 3000,
|
|
10
10
|
};
|
|
11
|
-
export const createServer = async (
|
|
11
|
+
export const createServer = async (schema, dataRootPath, instancesByEntityName, options) => {
|
|
12
12
|
const { name, port } = { ...defaultOptions, ...options };
|
|
13
13
|
const app = express();
|
|
14
14
|
app.use(express.static(join(import.meta.dirname, "../../public")));
|
|
@@ -16,7 +16,7 @@ export const createServer = async (modelContainer, instancesByEntityName, option
|
|
|
16
16
|
app.use("/js/client", express.static(join(import.meta.dirname, "../../lib/client")));
|
|
17
17
|
app.use("/js/shared", express.static(join(import.meta.dirname, "../../lib/shared")));
|
|
18
18
|
app.use(express.json());
|
|
19
|
-
const requestLocals = await init(
|
|
19
|
+
const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName));
|
|
20
20
|
app.use((req, _res, next) => {
|
|
21
21
|
Object.assign(req, requestLocals);
|
|
22
22
|
next();
|
|
@@ -30,9 +30,6 @@ export const createServer = async (modelContainer, instancesByEntityName, option
|
|
|
30
30
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31
31
|
<title>TSONDB</title>
|
|
32
32
|
<link rel="stylesheet" href="/css/styles.css">
|
|
33
|
-
</head>
|
|
34
|
-
<body>
|
|
35
|
-
<div id="app"></div>
|
|
36
33
|
<script type="importmap">
|
|
37
34
|
{
|
|
38
35
|
"imports": {
|
|
@@ -43,11 +40,14 @@ export const createServer = async (modelContainer, instancesByEntityName, option
|
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
42
|
</script>
|
|
43
|
+
</head>
|
|
44
|
+
<body>
|
|
45
|
+
<div id="app"></div>
|
|
46
46
|
<script type="module" src="/js/client/index.js"></script>
|
|
47
47
|
</body>
|
|
48
48
|
</html>`);
|
|
49
49
|
});
|
|
50
50
|
app.listen(port, () => {
|
|
51
|
-
debug(`${name} listening on http://localhost:${port}`);
|
|
51
|
+
debug(`${name} listening on http://localhost:${port.toString()}`);
|
|
52
52
|
});
|
|
53
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>;
|
|
@@ -3,8 +3,8 @@ import { isEntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
|
3
3
|
import { resolveTypeArgumentsInDecls } from "../schema/index.js";
|
|
4
4
|
import { attachGitStatusToInstancesByEntityName, getInstancesByEntityName, } from "../utils/instances.js";
|
|
5
5
|
import { getReferencesToInstances } from "../utils/references.js";
|
|
6
|
-
const getGit = async (
|
|
7
|
-
const git = simpleGit({ baseDir:
|
|
6
|
+
const getGit = async (dataRootPath) => {
|
|
7
|
+
const git = simpleGit({ baseDir: dataRootPath });
|
|
8
8
|
if (await git.checkIsRepo()) {
|
|
9
9
|
try {
|
|
10
10
|
const root = await git.revparse({ "--show-toplevel": null });
|
|
@@ -19,25 +19,25 @@ const getGit = async (modelContainer) => {
|
|
|
19
19
|
return { git };
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
export const init = async (
|
|
23
|
-
const { git, root: gitRoot, status: gitStatus } = await getGit(
|
|
24
|
-
const declarations = resolveTypeArgumentsInDecls(
|
|
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
25
|
const entities = declarations.filter(isEntityDecl);
|
|
26
26
|
const entitiesByName = Object.fromEntries(entities.map(entity => [entity.name, entity]));
|
|
27
27
|
const instancesByEntityNameInMemory = Object.assign({}, instancesByEntityName);
|
|
28
28
|
const referencesToInstances = getReferencesToInstances(instancesByEntityName, entitiesByName);
|
|
29
29
|
if (gitStatus) {
|
|
30
|
-
attachGitStatusToInstancesByEntityName(instancesByEntityName,
|
|
30
|
+
attachGitStatusToInstancesByEntityName(instancesByEntityName, dataRootPath, gitRoot, gitStatus);
|
|
31
31
|
}
|
|
32
32
|
const requestLocals = {
|
|
33
33
|
git: git,
|
|
34
34
|
gitRoot: gitRoot,
|
|
35
|
-
dataRoot:
|
|
35
|
+
dataRoot: dataRootPath,
|
|
36
36
|
declarations: declarations,
|
|
37
37
|
entities: entities,
|
|
38
38
|
instancesByEntityName: instancesByEntityNameInMemory,
|
|
39
39
|
entitiesByName: entitiesByName,
|
|
40
|
-
localeEntity:
|
|
40
|
+
localeEntity: schema.localeEntity,
|
|
41
41
|
referencesToInstances,
|
|
42
42
|
};
|
|
43
43
|
return requestLocals;
|
|
@@ -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)}`;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { StatusResult } from "simple-git";
|
|
2
|
-
import { GitFileStatus } from "
|
|
1
|
+
import type { StatusResult } from "simple-git";
|
|
2
|
+
import type { GitFileStatus } from "../../shared/utils/git.js";
|
|
3
3
|
export declare const getGitFileStatusFromStatusResult: (statusResult: StatusResult, repoRoot: string, dataRoot: string, entityName: string, fileName: string) => GitFileStatus | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { StatusResult } from "simple-git";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
4
|
export declare const getInstancesByEntityName: (dataRoot: string, entities: readonly EntityDecl[]) => Promise<InstancesByEntityName>;
|
|
5
5
|
export declare const attachGitStatusToInstancesByEntityName: (instancesByEntityName: InstancesByEntityName, dataRoot: string, gitRoot: string, gitStatus: StatusResult) => void;
|
|
@@ -11,9 +11,11 @@ export const getInstancesByEntityName = async (dataRoot, entities) => Object.fro
|
|
|
11
11
|
})));
|
|
12
12
|
return [entity.name, instances];
|
|
13
13
|
})));
|
|
14
|
-
export const attachGitStatusToInstancesByEntityName = (instancesByEntityName, dataRoot, gitRoot, gitStatus) =>
|
|
15
|
-
instancesByEntityName[entityName
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
});
|
|
14
|
+
export const attachGitStatusToInstancesByEntityName = (instancesByEntityName, dataRoot, gitRoot, gitStatus) => {
|
|
15
|
+
Object.entries(instancesByEntityName).forEach(([entityName, instances]) => {
|
|
16
|
+
instancesByEntityName[entityName] = instances.map(instanceContainer => ({
|
|
17
|
+
...instanceContainer,
|
|
18
|
+
gitStatus: getGitFileStatusFromStatusResult(gitStatus, gitRoot, dataRoot, entityName, instanceContainer.fileName),
|
|
19
|
+
}));
|
|
20
|
+
});
|
|
21
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { InstanceContainer } from "../../shared/utils/instances.js";
|
|
2
|
+
import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
3
3
|
export type ReferencesToInstances = {
|
|
4
4
|
[instanceId: string]: string[];
|
|
5
5
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { difference, removeAt } from "../../shared/utils/array.js";
|
|
1
2
|
import { getReferencesForEntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
2
|
-
import { difference, removeAt } from "../shared/utils/array.js";
|
|
3
3
|
const addReference = (acc, reference, instanceId) => ({
|
|
4
4
|
...acc,
|
|
5
5
|
[reference]: [...(acc[reference] ?? []), instanceId],
|
|
@@ -31,8 +31,8 @@ export const updateReferencesToInstances = (entitiesByName, referencesToInstance
|
|
|
31
31
|
if (newInstance === undefined) {
|
|
32
32
|
return removeReferences(referencesToInstances, getReferencesForEntityDecl(entity, oldInstance), instanceId);
|
|
33
33
|
}
|
|
34
|
-
const oldReferences =
|
|
35
|
-
const newReferences =
|
|
34
|
+
const oldReferences = getReferencesForEntityDecl(entity, oldInstance);
|
|
35
|
+
const newReferences = getReferencesForEntityDecl(entity, newInstance);
|
|
36
36
|
const { added, removed } = difference(oldReferences, newReferences);
|
|
37
37
|
return removeReferences(addReferences(referencesToInstances, added, instanceId), removed, instanceId);
|
|
38
38
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EOL } from "node:os";
|
|
2
|
-
import { mergeObjects } from "
|
|
2
|
+
import { mergeObjects } from "../../shared/utils/object.js";
|
|
3
3
|
export const prefixLines = (prefix, text, includeEmptyLines = false) => text
|
|
4
4
|
.split(EOL)
|
|
5
5
|
.map(line => (includeEmptyLines || line.length > 0 ? prefix + line : line))
|
package/lib/shared/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SerializedDecl } from "../schema/declarations/Declaration.js";
|
|
2
|
-
import { InstanceContainer, InstanceContainerOverview } from "./utils/instances.js";
|
|
1
|
+
import type { SerializedDecl } from "../node/schema/declarations/Declaration.js";
|
|
2
|
+
import type { InstanceContainer, InstanceContainerOverview } from "./utils/instances.js";
|
|
3
3
|
export interface GetAllDeclarationsResponseBody<D extends SerializedDecl = SerializedDecl> {
|
|
4
4
|
declarations: {
|
|
5
5
|
declaration: D;
|
|
@@ -51,3 +51,9 @@ export interface GetAllGitBranchesResponseBody {
|
|
|
51
51
|
allBranches: string[];
|
|
52
52
|
currentBranch: string;
|
|
53
53
|
}
|
|
54
|
+
export interface CreateCommitRequestBody {
|
|
55
|
+
message: string;
|
|
56
|
+
}
|
|
57
|
+
export interface CreateBranchRequestBody {
|
|
58
|
+
branchName: string;
|
|
59
|
+
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
export const removeAt = (arr, index) =>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
export const removeAt = (arr, index) => {
|
|
2
|
+
if (index < 0 || index >= arr.length) {
|
|
3
|
+
throw new RangeError(`index ${index.toString()} is out of bounds for array of length ${arr.length.toString()}`);
|
|
4
|
+
}
|
|
5
|
+
return [...arr.slice(0, index), ...arr.slice(index + 1)];
|
|
6
|
+
};
|
|
7
|
+
export const insertAt = (arr, index, item) => {
|
|
8
|
+
if (index < 0 || index > arr.length) {
|
|
9
|
+
throw new RangeError(`index ${index.toString()} is out of bounds for array of length ${arr.length.toString()}`);
|
|
10
|
+
}
|
|
11
|
+
return [...arr.slice(0, index), item, ...arr.slice(index)];
|
|
12
|
+
};
|
|
10
13
|
/**
|
|
11
14
|
* Calculates the difference between two arrays, including duplicated values.
|
|
12
15
|
* @param oldArr - The original array.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { SerializedEntityDecl } from "../../schema/index.js";
|
|
1
|
+
import type { SerializedEntityDecl } from "../../node/schema/index.js";
|
|
2
2
|
export declare const getDisplayNameFromEntityInstance: (entity: SerializedEntityDecl, instance: unknown, defaultName: string, locales?: string[]) => string;
|
|
@@ -23,9 +23,11 @@ export const getDisplayNameFromEntityInstance = (entity, instance, defaultName,
|
|
|
23
23
|
const availableLocales = Object.keys(localeMap ?? {});
|
|
24
24
|
return availableLocales.length === 0
|
|
25
25
|
? defaultName
|
|
26
|
-
: locales.reduce((name, locale) => name ??
|
|
26
|
+
: (locales.reduce((name, locale) => name ??
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
27
28
|
getValueAtPath(localeMap[locale], pathInLocaleMap), undefined) ??
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
28
30
|
getValueAtPath(localeMap[availableLocales[0]], pathInLocaleMap) ??
|
|
29
|
-
defaultName;
|
|
31
|
+
defaultName);
|
|
30
32
|
}
|
|
31
33
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SerializedEntityDecl } from "../../schema/index.js";
|
|
2
|
-
import { GitFileStatus } from "./git.js";
|
|
1
|
+
import type { SerializedEntityDecl } from "../../node/schema/index.js";
|
|
2
|
+
import type { GitFileStatus } from "./git.js";
|
|
3
3
|
export interface InstanceContainer {
|
|
4
4
|
fileName: string;
|
|
5
5
|
id: string;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
const boldWithItalicRule = {
|
|
2
2
|
pattern: /\*\*(.*?\*.+?\*.*?)\*\*/,
|
|
3
|
-
map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1]) }),
|
|
3
|
+
map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1] ?? "") }),
|
|
4
4
|
};
|
|
5
5
|
const italicWithBoldRule = {
|
|
6
6
|
pattern: /\*(.*?\*\*.+?\*\*.*?)\*/,
|
|
7
|
-
map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1]) }),
|
|
7
|
+
map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1] ?? "") }),
|
|
8
8
|
};
|
|
9
9
|
const boldRule = {
|
|
10
10
|
pattern: /\*\*(.+?)\*\*/,
|
|
11
|
-
map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1]) }),
|
|
11
|
+
map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1] ?? "") }),
|
|
12
12
|
};
|
|
13
13
|
const italicRule = {
|
|
14
14
|
pattern: /\*(.+?)\*/,
|
|
15
|
-
map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1]) }),
|
|
15
|
+
map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1] ?? "") }),
|
|
16
16
|
};
|
|
17
17
|
const rules = [boldWithItalicRule, italicWithBoldRule, boldRule, italicRule];
|
|
18
18
|
export const parseBlockMarkdown = (text) => text.split(/\n{2,}/).map(par => ({ kind: "paragraph", content: parseInlineMarkdown(par) }));
|
|
@@ -20,10 +20,10 @@ const parseForRules = (rules, text) => {
|
|
|
20
20
|
if (text.length === 0) {
|
|
21
21
|
return [];
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
const activeRule = rules[0];
|
|
24
|
+
if (activeRule === undefined) {
|
|
24
25
|
return [{ kind: "text", content: text }];
|
|
25
26
|
}
|
|
26
|
-
const activeRule = rules[0];
|
|
27
27
|
const res = activeRule.pattern.exec(text);
|
|
28
28
|
if (res && (activeRule.predicate?.(res) ?? true)) {
|
|
29
29
|
const { index } = res;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export declare const sortObjectKeys: (obj: Record<string, unknown>, keys: string[]) => Record<string, unknown>;
|
|
2
2
|
export declare const sortObjectKeysAlphabetically: (obj: Record<string, unknown>) => Record<string, unknown>;
|
|
3
3
|
export declare const mergeObjects: <T>(obj1: Record<string, T>, obj2: Record<string, T>, solveConflict: (a: T, b: T) => T) => Record<string, T>;
|
|
4
|
+
export type Leaves<T> = T extends object ? {
|
|
5
|
+
[K in keyof T]: T[K] extends unknown[] ? never : `${Exclude<K, symbol>}${Leaves<T[K]> extends never ? "" : `.${Leaves<T[K]>}`}`;
|
|
6
|
+
}[keyof T] : never;
|
|
@@ -2,5 +2,6 @@ export const sortObjectKeys = (obj, keys) => Object.fromEntries(keys.flatMap(key
|
|
|
2
2
|
export const sortObjectKeysAlphabetically = (obj) => Object.fromEntries(Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)));
|
|
3
3
|
export const mergeObjects = (obj1, obj2, solveConflict) => Object.entries(obj2).reduce((acc, [key, value]) => ({
|
|
4
4
|
...acc,
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
5
6
|
[key]: Object.hasOwn(acc, key) ? solveConflict(acc[key], value) : value,
|
|
6
7
|
}), obj1);
|
|
@@ -24,11 +24,11 @@ export const reduce = (result, fok, ferror) => (isOk(result) ? fok(result.value)
|
|
|
24
24
|
/**
|
|
25
25
|
* Maps the value of a result to a new value.
|
|
26
26
|
*/
|
|
27
|
-
export const map = (result, f) =>
|
|
27
|
+
export const map = (result, f) => isOk(result) ? ok(f(result.value)) : result;
|
|
28
28
|
/**
|
|
29
29
|
* Maps an error to a new error.
|
|
30
30
|
*/
|
|
31
|
-
export const mapError = (result, f) =>
|
|
31
|
+
export const mapError = (result, f) => isError(result) ? error(f(result.error)) : result;
|
|
32
32
|
export const combine = (result1, result2, fok, ferror) => isOk(result1)
|
|
33
33
|
? isOk(result2)
|
|
34
34
|
? ok(fok(result1.value, result2.value))
|
|
@@ -5,14 +5,17 @@ const separator = /[^a-z0-9]/;
|
|
|
5
5
|
const lastChar = (str) => str[str.length - 1];
|
|
6
6
|
const lastElement = (arr) => arr[arr.length - 1];
|
|
7
7
|
const isAllUppercase = (str) => str === str.toUpperCase();
|
|
8
|
-
export const splitStringParts = (str) => [...str].reduce((acc,
|
|
8
|
+
export const splitStringParts = (str) => [...new Intl.Segmenter().segment(str)].reduce((acc, segment, i, strArr) => {
|
|
9
|
+
const char = segment.segment;
|
|
9
10
|
if (acc.length === 0) {
|
|
10
11
|
return letterOrDigit.test(char) ? [char] : acc;
|
|
11
12
|
}
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
12
14
|
const lastPart = lastElement(acc);
|
|
13
15
|
if (uppercase.test(char)) {
|
|
14
16
|
const lastCharOfLastPart = lastChar(lastPart);
|
|
15
|
-
const
|
|
17
|
+
const nextSegment = strArr[i + 1];
|
|
18
|
+
const nextChar = nextSegment?.segment;
|
|
16
19
|
if (lastCharOfLastPart === undefined ||
|
|
17
20
|
(uppercase.test(lastCharOfLastPart) && (nextChar === undefined || separator.test(nextChar)))) {
|
|
18
21
|
return [...acc.slice(0, -1), lastPart + char];
|
|
@@ -47,8 +50,8 @@ export const toKebabCase = (str) => splitStringParts(str)
|
|
|
47
50
|
export const toSnakeCase = (str) => splitStringParts(str)
|
|
48
51
|
.map(part => part.toLowerCase())
|
|
49
52
|
.join("_");
|
|
50
|
-
export const toTitleCase = (str) => splitStringParts(str)
|
|
51
|
-
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
53
|
+
export const toTitleCase = (str) => splitStringParts(isAllUppercase(str) ? str.toLowerCase() : str)
|
|
54
|
+
.map(part => isAllUppercase(part) ? part : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
52
55
|
.join(" ");
|
|
53
56
|
export const commonPrefix = (...strs) => {
|
|
54
57
|
if (strs.length === 0) {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { gte, lte } from "./compare.js";
|
|
2
2
|
export const parallelizeErrors = (errors) => errors.filter(error => error !== undefined);
|
|
3
|
+
const normalizeLabel = (label) => Array.isArray(label) ? label : [label, label + "s"];
|
|
3
4
|
export const validateLengthRangeBound = (end, label, rangeBound, value) => {
|
|
4
5
|
if (rangeBound === undefined) {
|
|
5
6
|
return;
|
|
6
7
|
}
|
|
7
8
|
const [operator, description] = end === "lower" ? [gte, "least"] : [lte, "most"];
|
|
8
9
|
const length = value.length;
|
|
9
|
-
const normalizedLabel =
|
|
10
|
+
const normalizedLabel = normalizeLabel(label);
|
|
10
11
|
if (!operator(length, rangeBound)) {
|
|
11
|
-
return RangeError(`
|
|
12
|
+
return RangeError(`expected at ${description} ${rangeBound.toString()} ${normalizedLabel[rangeBound === 1 ? 0 : 1]}, but got ${length.toString()} ${normalizedLabel[length === 1 ? 0 : 1]}`);
|
|
12
13
|
}
|
|
13
14
|
return;
|
|
14
15
|
};
|
|
@@ -18,8 +18,8 @@ export const validateRangeBound = (end, rangeBound, value) => {
|
|
|
18
18
|
: normalizedRangeBound.isExclusive
|
|
19
19
|
? [lt, "less than"]
|
|
20
20
|
: [lte, "less than or equal"];
|
|
21
|
-
if (operator(value, normalizedRangeBound.value)) {
|
|
22
|
-
return RangeError(`expected a value ${description} ${normalizedRangeBound.value}, but got ${value}`);
|
|
21
|
+
if (!operator(value, normalizedRangeBound.value)) {
|
|
22
|
+
return RangeError(`expected a value ${description} ${normalizedRangeBound.value.toString()}, but got ${value.toString()}`);
|
|
23
23
|
}
|
|
24
24
|
return;
|
|
25
25
|
};
|
|
@@ -28,7 +28,7 @@ export const validateMultipleOf = (multipleOf, value) => {
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
if (value % multipleOf !== 0) {
|
|
31
|
-
return RangeError(`expected a value that is a multiple of ${multipleOf}, but got ${value}`);
|
|
31
|
+
return RangeError(`expected a value that is a multiple of ${multipleOf.toString()}, but got ${value.toString()}`);
|
|
32
32
|
}
|
|
33
33
|
return;
|
|
34
34
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { parallelizeErrors, validateLengthRangeBound } from "../utils/validation.js";
|
|
2
2
|
export const validateObjectConstraints = (constraints, expectedKeys, value) => {
|
|
3
3
|
const label = ["property", "properties"];
|
|
4
|
+
const actualKeys = Object.keys(value);
|
|
4
5
|
return parallelizeErrors([
|
|
5
|
-
validateLengthRangeBound("lower", label, constraints.minProperties,
|
|
6
|
-
validateLengthRangeBound("upper", label, constraints.maxProperties,
|
|
6
|
+
validateLengthRangeBound("lower", label, constraints.minProperties, actualKeys),
|
|
7
|
+
validateLengthRangeBound("upper", label, constraints.maxProperties, actualKeys),
|
|
7
8
|
...(constraints.additionalProperties !== true
|
|
8
|
-
?
|
|
9
|
+
? actualKeys.flatMap(valueKey => expectedKeys.includes(valueKey)
|
|
9
10
|
? []
|
|
10
11
|
: [TypeError(`object does not allow unknown keys and key "${valueKey}" is not known`)])
|
|
11
12
|
: []),
|