tsondb 0.2.0 → 0.3.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/lib/ModelContainer.js +9 -7
- package/lib/client/api.d.ts +14 -1
- package/lib/client/api.js +119 -0
- package/lib/client/components/Git.d.ts +2 -0
- package/lib/client/components/Git.js +116 -0
- package/lib/client/components/Layout.js +2 -1
- package/lib/client/components/typeInputs/ArrayTypeInput.js +2 -1
- package/lib/client/components/typeInputs/EnumTypeInput.d.ts +13 -0
- package/lib/client/components/typeInputs/{utils/EnumDeclField.js → EnumTypeInput.js} +10 -10
- package/lib/client/components/typeInputs/IncludeIdentifierTypeInput.js +1 -10
- package/lib/client/components/typeInputs/TypeInput.js +3 -0
- package/lib/client/components/typeInputs/utils/Markdown.js +1 -32
- package/lib/client/hooks/useAPIResource.d.ts +1 -0
- package/lib/client/hooks/useAPIResource.js +2 -0
- package/lib/client/hooks/useMappedAPIResource.d.ts +1 -0
- package/lib/client/hooks/useMappedAPIResource.js +19 -0
- package/lib/client/routes/Entity.js +18 -24
- package/lib/client/routes/Home.js +3 -12
- package/lib/client/utils/typeSkeleton.js +10 -16
- package/lib/renderers/jsonschema/index.d.ts +1 -1
- package/lib/renderers/jsonschema/index.js +30 -7
- package/lib/renderers/jsonschema/render.d.ts +5 -1
- package/lib/renderers/jsonschema/render.js +35 -16
- package/lib/renderers/ts/index.d.ts +1 -1
- package/lib/renderers/ts/index.js +37 -6
- package/lib/renderers/ts/render.d.ts +2 -0
- package/lib/renderers/ts/render.js +55 -32
- package/lib/schema/Node.d.ts +1 -0
- package/lib/schema/Node.js +6 -1
- package/lib/schema/declarations/Declaration.d.ts +3 -1
- package/lib/schema/declarations/Declaration.js +4 -0
- package/lib/schema/declarations/EntityDecl.d.ts +3 -0
- package/lib/schema/declarations/EnumDecl.d.ts +4 -21
- package/lib/schema/declarations/EnumDecl.js +16 -79
- package/lib/schema/index.d.ts +1 -0
- package/lib/schema/index.js +1 -0
- package/lib/schema/types/Type.d.ts +8 -2
- package/lib/schema/types/Type.js +57 -11
- package/lib/schema/types/generic/ArrayType.d.ts +2 -1
- package/lib/schema/types/generic/ArrayType.js +2 -1
- package/lib/schema/types/generic/EnumType.d.ts +38 -0
- package/lib/schema/types/generic/EnumType.js +96 -0
- package/lib/schema/types/generic/ObjectType.d.ts +2 -1
- package/lib/schema/types/generic/ObjectType.js +4 -0
- package/lib/schema/types/primitives/BooleanType.d.ts +2 -1
- package/lib/schema/types/primitives/BooleanType.js +1 -0
- package/lib/schema/types/primitives/DateType.d.ts +2 -1
- package/lib/schema/types/primitives/DateType.js +1 -0
- package/lib/schema/types/primitives/FloatType.d.ts +2 -1
- package/lib/schema/types/primitives/FloatType.js +1 -0
- package/lib/schema/types/primitives/IntegerType.d.ts +2 -1
- package/lib/schema/types/primitives/IntegerType.js +1 -0
- package/lib/schema/types/primitives/StringType.d.ts +2 -1
- package/lib/schema/types/primitives/StringType.js +1 -0
- package/lib/schema/types/references/GenericArgumentIdentifierType.d.ts +2 -1
- package/lib/schema/types/references/GenericArgumentIdentifierType.js +1 -0
- package/lib/schema/types/references/IncludeIdentifierType.d.ts +5 -3
- package/lib/schema/types/references/IncludeIdentifierType.js +15 -2
- package/lib/schema/types/references/NestedEntityMapType.d.ts +2 -1
- package/lib/schema/types/references/NestedEntityMapType.js +5 -1
- package/lib/schema/types/references/ReferenceIdentifierType.d.ts +2 -1
- package/lib/schema/types/references/ReferenceIdentifierType.js +2 -1
- package/lib/server/api/declarations.d.ts +1 -0
- package/lib/server/api/declarations.js +154 -0
- package/lib/server/api/git.d.ts +1 -0
- package/lib/server/api/git.js +174 -0
- package/lib/server/api/index.d.ts +1 -0
- package/lib/server/api/index.js +8 -0
- package/lib/server/api/instanceOperations.d.ts +6 -0
- package/lib/server/api/instanceOperations.js +82 -0
- package/lib/server/api/instances.d.ts +1 -0
- package/lib/server/api/instances.js +23 -0
- package/lib/server/index.d.ts +22 -1
- package/lib/server/index.js +11 -165
- package/lib/server/init.d.ts +5 -0
- package/lib/server/init.js +56 -0
- package/lib/shared/api.d.ts +12 -1
- package/lib/shared/utils/array.d.ts +19 -0
- package/lib/shared/utils/array.js +27 -0
- 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/shared/utils/markdown.d.ts +14 -0
- package/lib/shared/utils/markdown.js +42 -0
- package/lib/shared/utils/object.d.ts +1 -0
- package/lib/shared/utils/object.js +4 -0
- package/lib/shared/utils/string.d.ts +1 -0
- package/lib/shared/utils/string.js +9 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/git.d.ts +3 -0
- package/lib/utils/git.js +12 -0
- package/lib/utils/instances.d.ts +3 -2
- package/lib/utils/instances.js +9 -2
- package/lib/utils/path.d.ts +1 -0
- package/lib/utils/path.js +2 -0
- package/lib/utils/references.d.ts +7 -0
- package/lib/utils/references.js +40 -0
- package/lib/utils/render.d.ts +6 -1
- package/lib/utils/render.js +27 -1
- package/package.json +8 -1
- package/public/css/styles.css +200 -1
- package/lib/client/components/typeInputs/utils/EnumDeclField.d.ts +0 -13
- package/lib/server/instanceOperations.d.ts +0 -7
- package/lib/server/instanceOperations.js +0 -67
package/lib/server/index.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
+
import Debug from "debug";
|
|
1
2
|
import express from "express";
|
|
2
3
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import { isTypeAliasDecl } from "../schema/declarations/TypeAliasDecl.js";
|
|
7
|
-
import { getDisplayNameFromEntityInstance } from "../shared/utils/displayName.js";
|
|
8
|
-
import { isOk } from "../utils/result.js";
|
|
9
|
-
import { createInstance, deleteInstance, updateInstance } from "./instanceOperations.js";
|
|
4
|
+
import { api } from "./api/index.js";
|
|
5
|
+
import { init } from "./init.js";
|
|
6
|
+
const debug = Debug("tsondb:server");
|
|
10
7
|
const defaultOptions = {
|
|
11
8
|
name: "tsondb server",
|
|
12
9
|
port: 3000,
|
|
13
10
|
};
|
|
14
|
-
export const createServer = (modelContainer, instancesByEntityName, options) => {
|
|
11
|
+
export const createServer = async (modelContainer, instancesByEntityName, options) => {
|
|
15
12
|
const { name, port } = { ...defaultOptions, ...options };
|
|
16
13
|
const app = express();
|
|
17
14
|
app.use(express.static(join(import.meta.dirname, "../../public")));
|
|
@@ -19,163 +16,12 @@ export const createServer = (modelContainer, instancesByEntityName, options) =>
|
|
|
19
16
|
app.use("/js/client", express.static(join(import.meta.dirname, "../../lib/client")));
|
|
20
17
|
app.use("/js/shared", express.static(join(import.meta.dirname, "../../lib/shared")));
|
|
21
18
|
app.use(express.json());
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
app.get("/api/declarations", (req, res) => {
|
|
27
|
-
let filteredEntities;
|
|
28
|
-
switch (req.query["kind"]) {
|
|
29
|
-
case "Entity":
|
|
30
|
-
filteredEntities = entities.filter(isEntityDecl);
|
|
31
|
-
break;
|
|
32
|
-
case "TypeAlias":
|
|
33
|
-
filteredEntities = entities.filter(isTypeAliasDecl);
|
|
34
|
-
break;
|
|
35
|
-
case "Enum":
|
|
36
|
-
filteredEntities = entities.filter(isEnumDecl);
|
|
37
|
-
break;
|
|
38
|
-
default:
|
|
39
|
-
filteredEntities = declarations;
|
|
40
|
-
}
|
|
41
|
-
const body = {
|
|
42
|
-
declarations: filteredEntities.map(decl => ({
|
|
43
|
-
declaration: serializeDecl(decl),
|
|
44
|
-
instanceCount: instancesByEntityNameInMemory[decl.name]?.length ?? 0,
|
|
45
|
-
})),
|
|
46
|
-
localeEntity: modelContainer.schema.localeEntity?.name,
|
|
47
|
-
};
|
|
48
|
-
res.json(body);
|
|
49
|
-
});
|
|
50
|
-
app.get("/api/declarations/:name", (req, res) => {
|
|
51
|
-
const decl = declarations.find(decl => decl.name === req.params.name);
|
|
52
|
-
if (decl === undefined) {
|
|
53
|
-
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const body = {
|
|
57
|
-
declaration: serializeDecl(decl),
|
|
58
|
-
instanceCount: instancesByEntityNameInMemory[decl.name]?.length ?? 0,
|
|
59
|
-
isLocaleEntity: decl === modelContainer.schema.localeEntity,
|
|
60
|
-
};
|
|
61
|
-
res.json(body);
|
|
62
|
-
});
|
|
63
|
-
app.get("/api/declarations/:name/instances", (req, res) => {
|
|
64
|
-
const decl = declarations.find(decl => decl.name === req.params.name);
|
|
65
|
-
if (decl === undefined) {
|
|
66
|
-
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (!isEntityDecl(decl)) {
|
|
70
|
-
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
const body = {
|
|
74
|
-
instances: instancesByEntityNameInMemory[req.params.name] ?? [],
|
|
75
|
-
isLocaleEntity: decl === modelContainer.schema.localeEntity,
|
|
76
|
-
};
|
|
77
|
-
res.json(body);
|
|
78
|
-
});
|
|
79
|
-
app.post("/api/declarations/:name/instances", async (req, res) => {
|
|
80
|
-
const decl = declarations.find(decl => decl.name === req.params.name);
|
|
81
|
-
if (decl === undefined) {
|
|
82
|
-
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (!isEntityDecl(decl)) {
|
|
86
|
-
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const result = await createInstance(modelContainer, entitiesByName, instancesByEntityNameInMemory, decl.name, req.body, req.query["id"]);
|
|
90
|
-
if (isOk(result)) {
|
|
91
|
-
const body = {
|
|
92
|
-
instance: result.value,
|
|
93
|
-
isLocaleEntity: decl === modelContainer.schema.localeEntity,
|
|
94
|
-
};
|
|
95
|
-
res.json(body);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
res.status(result.error[0]).send(result.error[1]);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
app.get("/api/declarations/:name/instances/:id", (req, res) => {
|
|
102
|
-
const decl = declarations.find(decl => decl.name === req.params.name);
|
|
103
|
-
if (decl === undefined) {
|
|
104
|
-
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (!isEntityDecl(decl)) {
|
|
108
|
-
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const instance = instancesByEntityNameInMemory[decl.name]?.find(instance => instance.id === req.params.id);
|
|
112
|
-
if (instance === undefined) {
|
|
113
|
-
res.status(404).send(`Instance "${req.params.id}" not found`);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
const body = {
|
|
117
|
-
instance: instance,
|
|
118
|
-
isLocaleEntity: decl === modelContainer.schema.localeEntity,
|
|
119
|
-
};
|
|
120
|
-
res.json(body);
|
|
121
|
-
});
|
|
122
|
-
app.put("/api/declarations/:name/instances/:id", async (req, res) => {
|
|
123
|
-
const decl = declarations.find(decl => decl.name === req.params.name);
|
|
124
|
-
if (decl === undefined) {
|
|
125
|
-
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (!isEntityDecl(decl)) {
|
|
129
|
-
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const result = await updateInstance(modelContainer, entitiesByName, instancesByEntityNameInMemory, decl.name, req.params.id, req.body);
|
|
133
|
-
if (isOk(result)) {
|
|
134
|
-
const body = {
|
|
135
|
-
instance: result.value,
|
|
136
|
-
isLocaleEntity: decl === modelContainer.schema.localeEntity,
|
|
137
|
-
};
|
|
138
|
-
res.json(body);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
res.status(result.error[0]).send(result.error[1]);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
app.delete("/api/declarations/:name/instances/:id", async (req, res) => {
|
|
145
|
-
const decl = declarations.find(decl => decl.name === req.params.name);
|
|
146
|
-
if (decl === undefined) {
|
|
147
|
-
res.status(404).send(`Declaration "${req.params.name}" not found`);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
if (!isEntityDecl(decl)) {
|
|
151
|
-
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const result = await deleteInstance(modelContainer, instancesByEntityNameInMemory, decl.name, req.params.id);
|
|
155
|
-
if (isOk(result)) {
|
|
156
|
-
const body = {
|
|
157
|
-
instance: result.value,
|
|
158
|
-
isLocaleEntity: decl === modelContainer.schema.localeEntity,
|
|
159
|
-
};
|
|
160
|
-
res.json(body);
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
res.status(result.error[0]).send(result.error[1]);
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
app.get("/api/instances", (req, res) => {
|
|
167
|
-
const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
|
|
168
|
-
const body = {
|
|
169
|
-
instances: Object.fromEntries(Object.entries(instancesByEntityNameInMemory).map(([entityName, instances]) => [
|
|
170
|
-
entityName,
|
|
171
|
-
instances.map(instance => ({
|
|
172
|
-
id: instance.id,
|
|
173
|
-
name: getDisplayNameFromEntityInstance(serializeEntityDecl(entitiesByName[entityName]), instance.content, instance.id, locales),
|
|
174
|
-
})),
|
|
175
|
-
])),
|
|
176
|
-
};
|
|
177
|
-
res.json(body);
|
|
19
|
+
const requestLocals = await init(modelContainer, Object.assign({}, instancesByEntityName));
|
|
20
|
+
app.use((req, _res, next) => {
|
|
21
|
+
Object.assign(req, requestLocals);
|
|
22
|
+
next();
|
|
178
23
|
});
|
|
24
|
+
app.use("/api", api);
|
|
179
25
|
app.get(/^\/.*/, (_req, res) => {
|
|
180
26
|
res.send(`<!DOCTYPE html>
|
|
181
27
|
<html lang="en">
|
|
@@ -202,6 +48,6 @@ export const createServer = (modelContainer, instancesByEntityName, options) =>
|
|
|
202
48
|
</html>`);
|
|
203
49
|
});
|
|
204
50
|
app.listen(port, () => {
|
|
205
|
-
|
|
51
|
+
debug(`${name} listening on http://localhost:${port}`);
|
|
206
52
|
});
|
|
207
53
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ModelContainer } from "../ModelContainer.js";
|
|
2
|
+
import { InstancesByEntityName } from "../shared/utils/instances.js";
|
|
3
|
+
import { TSONDBRequestLocals } from "./index.js";
|
|
4
|
+
export declare const init: (modelContainer: ModelContainer, 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 (modelContainer) => {
|
|
7
|
+
const git = simpleGit({ baseDir: modelContainer.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 (modelContainer, instancesByEntityName) => {
|
|
23
|
+
const { git, root: gitRoot, status: gitStatus } = await getGit(modelContainer);
|
|
24
|
+
const declarations = resolveTypeArgumentsInDecls(modelContainer.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, modelContainer.dataRootPath, gitRoot, gitStatus);
|
|
31
|
+
}
|
|
32
|
+
const requestLocals = {
|
|
33
|
+
git: git,
|
|
34
|
+
gitRoot: gitRoot,
|
|
35
|
+
dataRoot: modelContainer.dataRootPath,
|
|
36
|
+
declarations: declarations,
|
|
37
|
+
entities: entities,
|
|
38
|
+
instancesByEntityName: instancesByEntityNameInMemory,
|
|
39
|
+
entitiesByName: entitiesByName,
|
|
40
|
+
localeEntity: modelContainer.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
|
+
};
|
package/lib/shared/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SerializedDecl } from "../schema/declarations/Declaration.js";
|
|
2
|
-
import { InstanceContainer } from "./utils/instances.js";
|
|
2
|
+
import { InstanceContainer, InstanceContainerOverview } from "./utils/instances.js";
|
|
3
3
|
export interface GetAllDeclarationsResponseBody<D extends SerializedDecl = SerializedDecl> {
|
|
4
4
|
declarations: {
|
|
5
5
|
declaration: D;
|
|
@@ -40,3 +40,14 @@ export interface GetAllInstancesResponseBody {
|
|
|
40
40
|
}[];
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
export interface GitStatusResponseBody {
|
|
44
|
+
commitsAhead: number;
|
|
45
|
+
commitsBehind: number;
|
|
46
|
+
instances: {
|
|
47
|
+
[entity: string]: InstanceContainerOverview[];
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface GetAllGitBranchesResponseBody {
|
|
51
|
+
allBranches: string[];
|
|
52
|
+
currentBranch: string;
|
|
53
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const removeAt: <T>(arr: T[], index: number) => T[];
|
|
2
|
+
export declare const insertAt: <T>(arr: T[], index: number, item: T) => T[];
|
|
3
|
+
/**
|
|
4
|
+
* Calculates the difference between two arrays, including duplicated values.
|
|
5
|
+
* @param oldArr - The original array.
|
|
6
|
+
* @param newArr - The new array to compare against.
|
|
7
|
+
* @returns An object containing the added and removed elements.
|
|
8
|
+
*/
|
|
9
|
+
export declare const difference: <T>(oldArr: T[], newArr: T[]) => ArrayDiffResult<T>;
|
|
10
|
+
export interface ArrayDiffResult<T> {
|
|
11
|
+
/**
|
|
12
|
+
* Elements in `newArr` that are not in `oldArr`.
|
|
13
|
+
*/
|
|
14
|
+
added: T[];
|
|
15
|
+
/**
|
|
16
|
+
* Elements in `oldArr` that are not in `newArr`.
|
|
17
|
+
*/
|
|
18
|
+
removed: T[];
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const removeAt = (arr, index) => [
|
|
2
|
+
...arr.slice(0, index),
|
|
3
|
+
...arr.slice(index + 1),
|
|
4
|
+
];
|
|
5
|
+
export const insertAt = (arr, index, item) => [
|
|
6
|
+
...arr.slice(0, index),
|
|
7
|
+
item,
|
|
8
|
+
...arr.slice(index),
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Calculates the difference between two arrays, including duplicated values.
|
|
12
|
+
* @param oldArr - The original array.
|
|
13
|
+
* @param newArr - The new array to compare against.
|
|
14
|
+
* @returns An object containing the added and removed elements.
|
|
15
|
+
*/
|
|
16
|
+
export const difference = (oldArr, newArr) => newArr.reduce((acc, item) => {
|
|
17
|
+
const oldIndex = acc.removed.indexOf(item);
|
|
18
|
+
const newIndex = acc.added.indexOf(item);
|
|
19
|
+
if (oldIndex > -1) {
|
|
20
|
+
return {
|
|
21
|
+
...acc,
|
|
22
|
+
removed: removeAt(acc.removed, oldIndex),
|
|
23
|
+
added: removeAt(acc.added, newIndex),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return acc;
|
|
27
|
+
}, { removed: oldArr, added: newArr });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type GitFileStatus = {
|
|
2
|
+
index: GitFileStatusCode;
|
|
3
|
+
workingDir: GitFileStatusCode;
|
|
4
|
+
};
|
|
5
|
+
export declare const isChangedInIndex: (fileStatus: GitFileStatus) => boolean;
|
|
6
|
+
export declare const isChangedInWorkingDir: (fileStatus: GitFileStatus) => boolean;
|
|
7
|
+
export type GitFileStatusCode = " " | "M" | "T" | "A" | "D" | "R" | "C" | "U" | "m" | "?" | "!";
|
|
8
|
+
export declare const isUnchangedStatus: (code: GitFileStatusCode) => boolean;
|
|
9
|
+
export type GitFileStatusForDisplay = "U" | "M" | "A" | "D" | "R" | undefined;
|
|
10
|
+
export declare const hasFileChanges: (fileStatus: GitFileStatus | undefined) => boolean;
|
|
11
|
+
export declare const getGitStatusForDisplay: (fileStatus: GitFileStatus | undefined) => GitFileStatusForDisplay;
|
|
12
|
+
export declare const getLabelForGitStatus: (status: GitFileStatusForDisplay) => string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const isChangedInIndex = (fileStatus) => {
|
|
2
|
+
const { index } = fileStatus;
|
|
3
|
+
switch (index) {
|
|
4
|
+
case "M":
|
|
5
|
+
case "T":
|
|
6
|
+
case "A":
|
|
7
|
+
case "D":
|
|
8
|
+
case "R":
|
|
9
|
+
case "C":
|
|
10
|
+
case "U":
|
|
11
|
+
return true;
|
|
12
|
+
case " ":
|
|
13
|
+
case "m":
|
|
14
|
+
case "?":
|
|
15
|
+
case "!":
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const isChangedInWorkingDir = (fileStatus) => {
|
|
20
|
+
const { workingDir } = fileStatus;
|
|
21
|
+
switch (workingDir) {
|
|
22
|
+
case "M":
|
|
23
|
+
case "T":
|
|
24
|
+
case "A":
|
|
25
|
+
case "D":
|
|
26
|
+
case "R":
|
|
27
|
+
case "C":
|
|
28
|
+
case "U":
|
|
29
|
+
case "?":
|
|
30
|
+
return true;
|
|
31
|
+
case " ":
|
|
32
|
+
case "m":
|
|
33
|
+
case "!":
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const unchangedStatusCodes = new Set([" ", "!", "C", "T", "m", "U"]);
|
|
38
|
+
export const isUnchangedStatus = (code) => unchangedStatusCodes.has(code);
|
|
39
|
+
export const hasFileChanges = (fileStatus) => {
|
|
40
|
+
if (fileStatus === undefined) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const { index, workingDir } = fileStatus;
|
|
44
|
+
return !isUnchangedStatus(index) || !isUnchangedStatus(workingDir);
|
|
45
|
+
};
|
|
46
|
+
export const getGitStatusForDisplay = (fileStatus) => {
|
|
47
|
+
if (fileStatus === undefined) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const { index, workingDir } = fileStatus;
|
|
51
|
+
switch (workingDir) {
|
|
52
|
+
case " ":
|
|
53
|
+
switch (index) {
|
|
54
|
+
case " ":
|
|
55
|
+
return undefined;
|
|
56
|
+
case "M":
|
|
57
|
+
return "M";
|
|
58
|
+
case "A":
|
|
59
|
+
return "A";
|
|
60
|
+
case "D":
|
|
61
|
+
return "D";
|
|
62
|
+
case "R":
|
|
63
|
+
return "R";
|
|
64
|
+
case "?":
|
|
65
|
+
return "U";
|
|
66
|
+
default:
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
case "M":
|
|
70
|
+
return "M";
|
|
71
|
+
case "A":
|
|
72
|
+
return "A";
|
|
73
|
+
case "D":
|
|
74
|
+
return "D";
|
|
75
|
+
case "R":
|
|
76
|
+
return "R";
|
|
77
|
+
case "?":
|
|
78
|
+
return "U";
|
|
79
|
+
default:
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
export const getLabelForGitStatus = (status) => {
|
|
84
|
+
switch (status) {
|
|
85
|
+
case "U":
|
|
86
|
+
return "untracked";
|
|
87
|
+
case "M":
|
|
88
|
+
return "modified";
|
|
89
|
+
case "A":
|
|
90
|
+
return "sdded";
|
|
91
|
+
case "D":
|
|
92
|
+
return "deleted";
|
|
93
|
+
case "R":
|
|
94
|
+
return "renamed";
|
|
95
|
+
default:
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
import { SerializedEntityDecl } from "../../schema/index.js";
|
|
2
|
+
import { GitFileStatus } from "./git.js";
|
|
1
3
|
export interface InstanceContainer {
|
|
2
4
|
fileName: string;
|
|
3
5
|
id: string;
|
|
4
6
|
content: unknown;
|
|
7
|
+
gitStatus?: GitFileStatus;
|
|
5
8
|
}
|
|
9
|
+
export interface InstanceContainerOverview {
|
|
10
|
+
fileName: string;
|
|
11
|
+
id: string;
|
|
12
|
+
gitStatus?: GitFileStatus;
|
|
13
|
+
displayName: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const getInstanceContainerOverview: (entity: SerializedEntityDecl, instanceContainer: InstanceContainer, locales?: string[]) => InstanceContainerOverview;
|
|
6
16
|
export type InstancesByEntityName = Record<string, InstanceContainer[]>;
|
|
@@ -1 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { getDisplayNameFromEntityInstance } from "./displayName.js";
|
|
2
|
+
export const getInstanceContainerOverview = (entity, instanceContainer, locales) => {
|
|
3
|
+
const { content: _, ...rest } = instanceContainer;
|
|
4
|
+
return {
|
|
5
|
+
...rest,
|
|
6
|
+
displayName: getDisplayNameFromEntityInstance(entity, instanceContainer.content, instanceContainer.id, locales),
|
|
7
|
+
};
|
|
8
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type TextNode = {
|
|
2
|
+
kind: "text";
|
|
3
|
+
content: string;
|
|
4
|
+
};
|
|
5
|
+
export type InlineMarkdownNode = {
|
|
6
|
+
kind: "bold" | "italic";
|
|
7
|
+
content: InlineMarkdownNode[];
|
|
8
|
+
} | TextNode;
|
|
9
|
+
export type BlockMarkdownNode = {
|
|
10
|
+
kind: "paragraph";
|
|
11
|
+
content: InlineMarkdownNode[];
|
|
12
|
+
} | TextNode;
|
|
13
|
+
export declare const parseBlockMarkdown: (text: string) => BlockMarkdownNode[];
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const boldWithItalicRule = {
|
|
2
|
+
pattern: /\*\*(.*?\*.+?\*.*?)\*\*/,
|
|
3
|
+
map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1]) }),
|
|
4
|
+
};
|
|
5
|
+
const italicWithBoldRule = {
|
|
6
|
+
pattern: /\*(.*?\*\*.+?\*\*.*?)\*/,
|
|
7
|
+
map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1]) }),
|
|
8
|
+
};
|
|
9
|
+
const boldRule = {
|
|
10
|
+
pattern: /\*\*(.+?)\*\*/,
|
|
11
|
+
map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1]) }),
|
|
12
|
+
};
|
|
13
|
+
const italicRule = {
|
|
14
|
+
pattern: /\*(.+?)\*/,
|
|
15
|
+
map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1]) }),
|
|
16
|
+
};
|
|
17
|
+
const rules = [boldWithItalicRule, italicWithBoldRule, boldRule, italicRule];
|
|
18
|
+
export const parseBlockMarkdown = (text) => text.split(/\n{2,}/).map(par => ({ kind: "paragraph", content: parseInlineMarkdown(par) }));
|
|
19
|
+
const parseForRules = (rules, text) => {
|
|
20
|
+
if (text.length === 0) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
if (rules.length === 0) {
|
|
24
|
+
return [{ kind: "text", content: text }];
|
|
25
|
+
}
|
|
26
|
+
const activeRule = rules[0];
|
|
27
|
+
const res = activeRule.pattern.exec(text);
|
|
28
|
+
if (res && (activeRule.predicate?.(res) ?? true)) {
|
|
29
|
+
const { index } = res;
|
|
30
|
+
const before = text.slice(0, index);
|
|
31
|
+
const after = text.slice(index + res[0].length);
|
|
32
|
+
return [
|
|
33
|
+
...(before.length > 0 ? parseForRules(rules.slice(1), before) : []),
|
|
34
|
+
activeRule.map(res, text => parseForRules(rules.slice(1), text)),
|
|
35
|
+
...(after.length > 0 ? parseForRules(rules, after) : []),
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return parseForRules(rules.slice(1), text);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const parseInlineMarkdown = (text) => parseForRules(rules, text);
|
|
@@ -1,2 +1,3 @@
|
|
|
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
|
+
export declare const mergeObjects: <T>(obj1: Record<string, T>, obj2: Record<string, T>, solveConflict: (a: T, b: T) => T) => Record<string, T>;
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export const sortObjectKeys = (obj, keys) => Object.fromEntries(keys.flatMap(key => (obj[key] === undefined ? [] : [[key, obj[key]]])));
|
|
2
2
|
export const sortObjectKeysAlphabetically = (obj) => Object.fromEntries(Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)));
|
|
3
|
+
export const mergeObjects = (obj1, obj2, solveConflict) => Object.entries(obj2).reduce((acc, [key, value]) => ({
|
|
4
|
+
...acc,
|
|
5
|
+
[key]: Object.hasOwn(acc, key) ? solveConflict(acc[key], value) : value,
|
|
6
|
+
}), obj1);
|
|
@@ -4,3 +4,4 @@ export declare const toCamelCase: (str: string) => string;
|
|
|
4
4
|
export declare const toKebabCase: (str: string) => string;
|
|
5
5
|
export declare const toSnakeCase: (str: string) => string;
|
|
6
6
|
export declare const toTitleCase: (str: string) => string;
|
|
7
|
+
export declare const commonPrefix: (...strs: string[]) => string;
|
|
@@ -50,3 +50,12 @@ export const toSnakeCase = (str) => splitStringParts(str)
|
|
|
50
50
|
export const toTitleCase = (str) => splitStringParts(str)
|
|
51
51
|
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
52
52
|
.join(" ");
|
|
53
|
+
export const commonPrefix = (...strs) => {
|
|
54
|
+
if (strs.length === 0) {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
return strs.reduce((accPrefix, str) => {
|
|
58
|
+
const indexOfDifference = Array.from(accPrefix).findIndex((char, i) => str[i] !== char);
|
|
59
|
+
return indexOfDifference === -1 ? accPrefix : accPrefix.slice(0, indexOfDifference);
|
|
60
|
+
});
|
|
61
|
+
};
|