tsondb 0.5.7 → 0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/bin/tsondb.d.ts +1 -9
- package/dist/src/bin/tsondb.js +7 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/node/index.d.ts +2 -2
- package/dist/src/node/index.js +8 -2
- package/dist/src/node/renderers/jsonschema/index.d.ts +1 -1
- package/dist/src/node/renderers/ts/index.d.ts +1 -1
- package/dist/src/node/{Schema.d.ts → schema/Schema.d.ts} +2 -2
- package/dist/src/node/{Schema.js → schema/Schema.js} +10 -7
- package/dist/src/node/schema/declarations/EntityDecl.d.ts +31 -30
- package/dist/src/node/schema/declarations/EntityDecl.js +1 -0
- package/dist/src/node/server/api/declarations.js +3 -4
- package/dist/src/node/server/api/git.js +1 -2
- package/dist/src/node/server/api/instances.js +2 -3
- package/dist/src/node/server/index.d.ts +7 -2
- package/dist/src/node/server/index.js +13 -13
- package/dist/src/node/server/init.d.ts +1 -1
- package/dist/src/node/server/init.js +10 -0
- package/dist/src/node/utils/displayName.d.ts +3 -0
- package/dist/src/node/utils/displayName.js +19 -0
- package/dist/src/shared/config.d.ts +11 -0
- package/dist/src/{node/renderers/Output.d.ts → shared/output.d.ts} +1 -1
- package/dist/src/shared/output.js +1 -0
- package/dist/src/shared/utils/compare.js +6 -1
- package/dist/src/shared/utils/displayName.d.ts +1 -1
- package/dist/src/shared/utils/displayName.js +1 -1
- package/dist/src/shared/utils/instances.d.ts +3 -2
- package/dist/src/shared/utils/instances.js +3 -3
- package/dist/src/shared/utils/markdown.js +1 -1
- package/dist/src/web/components/Git.js +1 -1
- package/dist/src/web/components/typeInputs/ArrayTypeInput.js +4 -4
- package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.js +16 -11
- package/dist/src/web/components/typeInputs/ObjectTypeInput.js +3 -3
- package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.js +1 -1
- package/dist/src/web/components/typeInputs/StringTypeInput.js +7 -3
- package/dist/src/web/components/typeInputs/TypeInput.d.ts +2 -2
- package/dist/src/web/components/typeInputs/TypeInput.js +5 -1
- package/dist/src/web/hooks/useInstanceNamesByEntity.d.ts +1 -1
- package/dist/src/web/hooks/useInstanceNamesByEntity.js +1 -1
- package/dist/src/web/hooks/useSecondaryDeclarations.d.ts +1 -1
- package/dist/src/web/hooks/useSecondaryDeclarations.js +3 -3
- package/dist/src/web/routes/CreateInstance.js +26 -12
- package/dist/src/web/routes/Entity.js +27 -13
- package/dist/src/web/routes/Home.js +15 -1
- package/dist/src/web/routes/Instance.js +26 -12
- package/dist/src/web/routes/NotFound.js +4 -0
- package/package.json +6 -6
- package/public/css/styles.css +128 -34
- /package/dist/src/{node/renderers/Output.js → shared/config.js} +0 -0
package/dist/src/bin/tsondb.d.ts
CHANGED
|
@@ -1,10 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import type { Schema } from "../node/Schema.ts";
|
|
4
|
-
import type { ServerOptions } from "../node/server/index.ts";
|
|
5
|
-
export type Config = {
|
|
6
|
-
serverOptions?: ServerOptions;
|
|
7
|
-
schema: Schema;
|
|
8
|
-
outputs: Output[];
|
|
9
|
-
dataRootPath: string;
|
|
10
|
-
};
|
|
2
|
+
export {};
|
package/dist/src/bin/tsondb.js
CHANGED
|
@@ -88,6 +88,13 @@ switch (passedArguments.command.name) {
|
|
|
88
88
|
break;
|
|
89
89
|
case "validate":
|
|
90
90
|
debug(`running command: validate`);
|
|
91
|
+
if (passedArguments.command.options?.checkReferentialIntegrity !== undefined) {
|
|
92
|
+
debug(`check referential integrity: ${passedArguments.command.options.checkReferentialIntegrity ? "yes" : "no"}`);
|
|
93
|
+
}
|
|
94
|
+
if (passedArguments.command.options?.entities !== undefined) {
|
|
95
|
+
const entities = passedArguments.command.options.entities;
|
|
96
|
+
debug(`only check the following entities: ${entities.join(", ")}`);
|
|
97
|
+
}
|
|
91
98
|
await validate(config.schema, config.dataRootPath, {
|
|
92
99
|
checkReferentialIntegrity: passedArguments.command.options?.checkReferentialIntegrity,
|
|
93
100
|
checkOnlyEntities: passedArguments.command.options?.entities,
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/node/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Output } from "
|
|
2
|
-
import type
|
|
1
|
+
import type { Output } from "../shared/output.ts";
|
|
2
|
+
import { type Schema } from "./schema/Schema.ts";
|
|
3
3
|
import type { ServerOptions } from "./server/index.ts";
|
|
4
4
|
export declare const generateOutputs: (schema: Schema, outputs: Output[]) => Promise<void>;
|
|
5
5
|
type ValidationOptions = {
|
package/dist/src/node/index.js
CHANGED
|
@@ -3,8 +3,9 @@ import { mkdir, writeFile } from "fs/promises";
|
|
|
3
3
|
import { join, sep } from "path";
|
|
4
4
|
import { styleText } from "util";
|
|
5
5
|
import { parallelizeErrors } from "../shared/utils/validation.js";
|
|
6
|
-
import {
|
|
7
|
-
import { createValidators
|
|
6
|
+
import { validateEntityDecl } from "./schema/declarations/EntityDecl.js";
|
|
7
|
+
import { createValidators } from "./schema/Node.js";
|
|
8
|
+
import { getEntities } from "./schema/Schema.js";
|
|
8
9
|
import { createServer } from "./server/index.js";
|
|
9
10
|
import { countErrors, getErrorMessageForDisplay, wrapErrorsIfAny } from "./utils/error.js";
|
|
10
11
|
import { formatInstance, getInstancesByEntityName } from "./utils/instances.js";
|
|
@@ -23,6 +24,11 @@ export const generateOutputs = async (schema, outputs) => {
|
|
|
23
24
|
};
|
|
24
25
|
const _validate = (dataRootPath, entities, instancesByEntityName, options = {}) => {
|
|
25
26
|
const { checkReferentialIntegrity = false, checkOnlyEntities = [] } = options;
|
|
27
|
+
for (const onlyEntity of checkOnlyEntities) {
|
|
28
|
+
if (!entities.find(entity => entity.name === onlyEntity)) {
|
|
29
|
+
throw new Error(`Entity "${onlyEntity}" not found in schema`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
26
32
|
const errors = (checkOnlyEntities.length > 0
|
|
27
33
|
? entities.filter(entity => checkOnlyEntities.includes(entity.name))
|
|
28
34
|
: entities)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Decl } from "./
|
|
2
|
-
import type { EntityDecl } from "./
|
|
1
|
+
import type { Decl } from "./declarations/Declaration.ts";
|
|
2
|
+
import type { EntityDecl } from "./declarations/EntityDecl.ts";
|
|
3
3
|
export interface Schema {
|
|
4
4
|
declarations: readonly Decl[];
|
|
5
5
|
localeEntity?: EntityDecl;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { getNestedDeclarations, getParameterNames, walkNodeTree, } from "./
|
|
2
|
-
import { isEntityDecl } from "./
|
|
3
|
-
import { isStringType } from "./
|
|
4
|
-
import { isIncludeIdentifierType } from "./
|
|
5
|
-
import { isNestedEntityMapType } from "./
|
|
6
|
-
import { findTypeAtPath } from "./
|
|
1
|
+
import { getNestedDeclarations, getParameterNames, walkNodeTree, } from "./declarations/Declaration.js";
|
|
2
|
+
import { isEntityDecl } from "./declarations/EntityDecl.js";
|
|
3
|
+
import { isStringType } from "./types/primitives/StringType.js";
|
|
4
|
+
import { isIncludeIdentifierType } from "./types/references/IncludeIdentifierType.js";
|
|
5
|
+
import { isNestedEntityMapType } from "./types/references/NestedEntityMapType.js";
|
|
6
|
+
import { findTypeAtPath } from "./types/Type.js";
|
|
7
7
|
const checkDuplicateIdentifier = (existingDecls, decl) => {
|
|
8
8
|
const existingDeclWithSameName = existingDecls
|
|
9
9
|
.values()
|
|
@@ -27,7 +27,10 @@ const checkEntityDisplayNamePaths = (decls, localeEntity) => {
|
|
|
27
27
|
for (const decl of decls) {
|
|
28
28
|
if (isEntityDecl(decl) && decl.displayName !== null) {
|
|
29
29
|
const displayName = decl.displayName ?? "name";
|
|
30
|
-
if (typeof displayName === "
|
|
30
|
+
if (typeof displayName === "function") {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
else if (typeof displayName === "object") {
|
|
31
34
|
const pathToLocaleMap = displayName.pathToLocaleMap ?? "translations";
|
|
32
35
|
const pathInLocaleMap = displayName.pathInLocaleMap ?? "name";
|
|
33
36
|
if (localeEntity === undefined) {
|
|
@@ -8,6 +8,32 @@ import type { AsType, SerializedAsType } from "../types/Type.ts";
|
|
|
8
8
|
import type { ValidatorHelpers } from "../validation/type.ts";
|
|
9
9
|
import type { BaseDecl, GetNestedDeclarations, SerializedBaseDecl } from "./Declaration.ts";
|
|
10
10
|
import { TypeAliasDecl } from "./TypeAliasDecl.ts";
|
|
11
|
+
export type GenericDisplayNameFn = (instance: unknown, instanceDisplayName: string, getInstanceById: (id: string) => unknown, getDisplayNameForInstanceId: (id: string) => string | undefined, locales: string[] | undefined) => string;
|
|
12
|
+
export type GenericEntityDisplayName = string | {
|
|
13
|
+
pathToLocaleMap?: string;
|
|
14
|
+
pathInLocaleMap?: string;
|
|
15
|
+
} | null;
|
|
16
|
+
export type DisplayNameFn<T extends ObjectType = ObjectType> = (instance: AsType<T>, instanceDisplayName: string, getInstanceById: (id: string) => unknown, getDisplayNameForInstanceId: (id: string) => string | undefined, locales: string[] | undefined) => string;
|
|
17
|
+
export type EntityDisplayName<T extends ObjectType> = Leaves<AsType<T>> | {
|
|
18
|
+
/**
|
|
19
|
+
* @default "translations"
|
|
20
|
+
*/
|
|
21
|
+
pathToLocaleMap?: Leaves<AsType<T>>;
|
|
22
|
+
/**
|
|
23
|
+
* @default "name"
|
|
24
|
+
*/
|
|
25
|
+
pathInLocaleMap?: string;
|
|
26
|
+
} | null;
|
|
27
|
+
export type SerializedEntityDisplayName<T extends SerializedObjectType> = Leaves<SerializedAsType<T>> | {
|
|
28
|
+
/**
|
|
29
|
+
* @default "translations"
|
|
30
|
+
*/
|
|
31
|
+
pathToLocaleMap?: Leaves<SerializedAsType<T>>;
|
|
32
|
+
/**
|
|
33
|
+
* @default "name"
|
|
34
|
+
*/
|
|
35
|
+
pathInLocaleMap?: string;
|
|
36
|
+
} | null;
|
|
11
37
|
export interface EntityDecl<Name extends string = string, T extends ObjectType = ObjectType> extends BaseDecl<Name, []> {
|
|
12
38
|
kind: NodeKind["EntityDecl"];
|
|
13
39
|
namePlural: string;
|
|
@@ -15,16 +41,8 @@ export interface EntityDecl<Name extends string = string, T extends ObjectType =
|
|
|
15
41
|
/**
|
|
16
42
|
* @default "name"
|
|
17
43
|
*/
|
|
18
|
-
displayName?:
|
|
19
|
-
|
|
20
|
-
* @default "translations"
|
|
21
|
-
*/
|
|
22
|
-
pathToLocaleMap?: Leaves<AsType<T>>;
|
|
23
|
-
/**
|
|
24
|
-
* @default "name"
|
|
25
|
-
*/
|
|
26
|
-
pathInLocaleMap?: string;
|
|
27
|
-
} | null;
|
|
44
|
+
displayName?: EntityDisplayName<T>;
|
|
45
|
+
displayNameCustomizer?: DisplayNameFn<T>;
|
|
28
46
|
isDeprecated?: boolean;
|
|
29
47
|
}
|
|
30
48
|
export interface SerializedEntityDecl<Name extends string = string, T extends SerializedObjectType = SerializedObjectType> extends SerializedBaseDecl<Name, []> {
|
|
@@ -34,16 +52,7 @@ export interface SerializedEntityDecl<Name extends string = string, T extends Se
|
|
|
34
52
|
/**
|
|
35
53
|
* @default "name"
|
|
36
54
|
*/
|
|
37
|
-
displayName?:
|
|
38
|
-
/**
|
|
39
|
-
* @default "translations"
|
|
40
|
-
*/
|
|
41
|
-
pathToLocaleMap?: Leaves<SerializedAsType<T>>;
|
|
42
|
-
/**
|
|
43
|
-
* @default "name"
|
|
44
|
-
*/
|
|
45
|
-
pathInLocaleMap?: string;
|
|
46
|
-
} | null;
|
|
55
|
+
displayName?: SerializedEntityDisplayName<T>;
|
|
47
56
|
isDeprecated?: boolean;
|
|
48
57
|
}
|
|
49
58
|
export declare const EntityDecl: <Name extends string, T extends ObjectType>(sourceUrl: string, options: {
|
|
@@ -54,16 +63,8 @@ export declare const EntityDecl: <Name extends string, T extends ObjectType>(sou
|
|
|
54
63
|
/**
|
|
55
64
|
* @default "name"
|
|
56
65
|
*/
|
|
57
|
-
displayName?:
|
|
58
|
-
|
|
59
|
-
* @default "translations"
|
|
60
|
-
*/
|
|
61
|
-
pathToLocaleMap?: Leaves<AsType<T>>;
|
|
62
|
-
/**
|
|
63
|
-
* @default "name"
|
|
64
|
-
*/
|
|
65
|
-
pathInLocaleMap?: string;
|
|
66
|
-
} | null;
|
|
66
|
+
displayName?: EntityDisplayName<T>;
|
|
67
|
+
displayNameCustomizer?: DisplayNameFn<T>;
|
|
67
68
|
isDeprecated?: boolean;
|
|
68
69
|
}) => EntityDecl<Name, T>;
|
|
69
70
|
export { EntityDecl as Entity };
|
|
@@ -52,5 +52,6 @@ export const createEntityIdentifierTypeAsDecl = (decl) => TypeAliasDecl(decl.sou
|
|
|
52
52
|
export const serializeEntityDecl = type => ({
|
|
53
53
|
...type,
|
|
54
54
|
type: serializeObjectType(type.type.value),
|
|
55
|
+
displayName: typeof type.displayName === "function" ? null : type.displayName,
|
|
55
56
|
});
|
|
56
57
|
export const getReferencesForEntityDecl = (decl, value) => getReferencesForObjectType(decl.type.value, value);
|
|
@@ -3,7 +3,7 @@ import express from "express";
|
|
|
3
3
|
import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
|
|
4
4
|
import { isOk } from "../../../shared/utils/result.js";
|
|
5
5
|
import { serializeDecl } from "../../schema/declarations/Declaration.js";
|
|
6
|
-
import { isEntityDecl
|
|
6
|
+
import { isEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
7
7
|
import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
|
|
8
8
|
import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
|
|
9
9
|
import { createInstance, deleteInstance, updateInstance } from "./instanceOperations.js";
|
|
@@ -60,11 +60,10 @@ declarationsApi.get("/:name/instances", (req, res) => {
|
|
|
60
60
|
res.status(400).send(`Declaration "${decl.name}" is not an entity`);
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
const serializedEntityDecl = serializeEntityDecl(decl);
|
|
64
63
|
const body = {
|
|
65
64
|
instances: req.instancesByEntityName[req.params.name]
|
|
66
|
-
?.map(instanceContainer => getInstanceContainerOverview(
|
|
67
|
-
.toSorted((a, b) => a.displayName.localeCompare(b.displayName)) ?? [],
|
|
65
|
+
?.map(instanceContainer => getInstanceContainerOverview(decl, instanceContainer, req.getInstanceById, req.locales))
|
|
66
|
+
.toSorted((a, b) => a.displayName.localeCompare(b.displayName, undefined, { numeric: true })) ?? [],
|
|
68
67
|
isLocaleEntity: decl === req.localeEntity,
|
|
69
68
|
};
|
|
70
69
|
res.json(body);
|
|
@@ -3,7 +3,6 @@ import express, {} from "express";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { hasFileChanges } from "../../../shared/utils/git.js";
|
|
5
5
|
import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
|
|
6
|
-
import { serializeEntityDecl } from "../../schema/index.js";
|
|
7
6
|
import { attachGitStatusToInstancesByEntityName } from "../../utils/instances.js";
|
|
8
7
|
import { reinit } from "../init.js";
|
|
9
8
|
const debug = Debug("tsondb:server:api:git");
|
|
@@ -30,7 +29,7 @@ gitApi.get("/status", async (req, res) => {
|
|
|
30
29
|
.filter(instance => hasFileChanges(instance.gitStatus))
|
|
31
30
|
.map(instance => getInstanceContainerOverview(
|
|
32
31
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
33
|
-
|
|
32
|
+
req.entitiesByName[entityName], instance, req.getInstanceById, req.locales)),
|
|
34
33
|
])),
|
|
35
34
|
};
|
|
36
35
|
res.json(body);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
2
|
import express from "express";
|
|
3
|
-
import { getDisplayNameFromEntityInstance } from "
|
|
4
|
-
import { serializeEntityDecl } from "../../schema/declarations/EntityDecl.js";
|
|
3
|
+
import { getDisplayNameFromEntityInstance } from "../../utils/displayName.js";
|
|
5
4
|
const debug = Debug("tsondb:server:api:instances");
|
|
6
5
|
export const instancesApi = express.Router();
|
|
7
6
|
instancesApi.use((req, _res, next) => {
|
|
@@ -19,7 +18,7 @@ instancesApi.get("/", (req, res) => {
|
|
|
19
18
|
id: instance.id,
|
|
20
19
|
name: getDisplayNameFromEntityInstance(
|
|
21
20
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22
|
-
|
|
21
|
+
req.entitiesByName[entityName], instance.content, req.getInstanceById, locales),
|
|
23
22
|
})),
|
|
24
23
|
])),
|
|
25
24
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { SimpleGit } from "simple-git";
|
|
2
|
-
import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
3
|
-
import type { Schema } from "../Schema.ts";
|
|
2
|
+
import type { InstanceContainer, InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
4
3
|
import type { Decl } from "../schema/declarations/Declaration.ts";
|
|
5
4
|
import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
|
|
5
|
+
import type { Schema } from "../schema/Schema.ts";
|
|
6
6
|
import type { ReferencesToInstances } from "../utils/references.ts";
|
|
7
7
|
export type ServerOptions = {
|
|
8
8
|
port: number;
|
|
@@ -18,7 +18,12 @@ export interface TSONDBRequestLocals {
|
|
|
18
18
|
localeEntity?: EntityDecl;
|
|
19
19
|
referencesToInstances: ReferencesToInstances;
|
|
20
20
|
locales: string[];
|
|
21
|
+
getInstanceById: GetInstanceById;
|
|
21
22
|
}
|
|
23
|
+
export type GetInstanceById = (id: string) => {
|
|
24
|
+
entity: EntityDecl;
|
|
25
|
+
instance: InstanceContainer;
|
|
26
|
+
} | undefined;
|
|
22
27
|
declare global {
|
|
23
28
|
namespace Express {
|
|
24
29
|
interface Request extends TSONDBRequestLocals {
|
|
@@ -18,11 +18,11 @@ const staticNodeModule = (moduleName) => {
|
|
|
18
18
|
export const createServer = async (schema, dataRootPath, instancesByEntityName, options) => {
|
|
19
19
|
const { port } = { ...defaultOptions, ...options };
|
|
20
20
|
const app = express();
|
|
21
|
-
app.use(express.static(join(import.meta.dirname, "
|
|
21
|
+
app.use(express.static(join(import.meta.dirname, "../../../../public")));
|
|
22
22
|
app.use("/js/node_modules/preact", staticNodeModule("preact"));
|
|
23
23
|
app.use("/js/node_modules/preact-iso", staticNodeModule("preact-iso"));
|
|
24
|
-
app.use("/js/client", express.static(join(import.meta.dirname, "
|
|
25
|
-
app.use("/js/shared", express.static(join(import.meta.dirname, "
|
|
24
|
+
app.use("/js/client", express.static(join(import.meta.dirname, "../../../../dist/src/web")));
|
|
25
|
+
app.use("/js/shared", express.static(join(import.meta.dirname, "../../../../dist/src/shared")));
|
|
26
26
|
app.use(express.json());
|
|
27
27
|
const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName));
|
|
28
28
|
app.use((req, _res, next) => {
|
|
@@ -31,6 +31,15 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
|
|
|
31
31
|
next();
|
|
32
32
|
});
|
|
33
33
|
app.use("/api", api);
|
|
34
|
+
const importMap = JSON.stringify({
|
|
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/compat": "/js/node_modules/preact/compat/dist/compat.module.js",
|
|
39
|
+
"preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
|
|
40
|
+
"preact-iso": "/js/node_modules/preact-iso/src/index.js",
|
|
41
|
+
},
|
|
42
|
+
}, null, 2);
|
|
34
43
|
app.get(/^\/.*/, (_req, res) => {
|
|
35
44
|
res.send(`<!DOCTYPE html>
|
|
36
45
|
<html lang="en">
|
|
@@ -39,16 +48,7 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
|
|
|
39
48
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
40
49
|
<title>TSONDB</title>
|
|
41
50
|
<link rel="stylesheet" href="/css/styles.css">
|
|
42
|
-
<script type="importmap">
|
|
43
|
-
{
|
|
44
|
-
"imports": {
|
|
45
|
-
"preact": "/js/node_modules/preact/dist/preact.module.js",
|
|
46
|
-
"preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
|
|
47
|
-
"preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
|
|
48
|
-
"preact-iso": "/js/node_modules/preact-iso/src/index.js"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
</script>
|
|
51
|
+
<script type="importmap">${importMap}</script>
|
|
52
52
|
</head>
|
|
53
53
|
<body>
|
|
54
54
|
<div id="app"></div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
2
|
-
import type { Schema } from "../Schema.ts";
|
|
2
|
+
import type { Schema } from "../schema/Schema.ts";
|
|
3
3
|
import type { TSONDBRequestLocals } from "./index.ts";
|
|
4
4
|
export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName) => Promise<TSONDBRequestLocals>;
|
|
5
5
|
export declare const reinit: (locals: TSONDBRequestLocals) => Promise<void>;
|
|
@@ -29,6 +29,15 @@ export const init = async (schema, dataRootPath, instancesByEntityName) => {
|
|
|
29
29
|
if (gitStatus) {
|
|
30
30
|
attachGitStatusToInstancesByEntityName(instancesByEntityName, dataRootPath, gitRoot, gitStatus);
|
|
31
31
|
}
|
|
32
|
+
const getInstanceById = id => {
|
|
33
|
+
for (const entityName in instancesByEntityNameInMemory) {
|
|
34
|
+
const instance = instancesByEntityNameInMemory[entityName]?.find(i => i.id === id);
|
|
35
|
+
if (instance && entitiesByName[entityName]) {
|
|
36
|
+
return { entity: entitiesByName[entityName], instance };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
|
32
41
|
const requestLocals = {
|
|
33
42
|
git: git,
|
|
34
43
|
gitRoot: gitRoot,
|
|
@@ -38,6 +47,7 @@ export const init = async (schema, dataRootPath, instancesByEntityName) => {
|
|
|
38
47
|
instancesByEntityName: instancesByEntityNameInMemory,
|
|
39
48
|
entitiesByName: entitiesByName,
|
|
40
49
|
localeEntity: schema.localeEntity,
|
|
50
|
+
getInstanceById,
|
|
41
51
|
referencesToInstances,
|
|
42
52
|
locales: ["de-DE", "en-US"], // TODO: Make this configurable
|
|
43
53
|
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type EntityDecl } from "../../node/schema/index.ts";
|
|
2
|
+
import type { GetInstanceById } from "../../node/server/index.ts";
|
|
3
|
+
export declare const getDisplayNameFromEntityInstance: (entity: EntityDecl, instance: unknown, getInstanceById: GetInstanceById, locales?: string[], defaultName?: string, useCustomizer?: boolean) => string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { serializeEntityDecl } from "../../node/schema/index.js";
|
|
2
|
+
import { getSerializedDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
|
|
3
|
+
export const getDisplayNameFromEntityInstance = (entity, instance, getInstanceById, locales = [], defaultName = "", useCustomizer = true) => {
|
|
4
|
+
if (useCustomizer && entity.displayNameCustomizer) {
|
|
5
|
+
return entity.displayNameCustomizer(instance, getDisplayNameFromEntityInstance(entity, instance, getInstanceById, locales, defaultName, false), id => getInstanceById(id)?.instance.content, id => {
|
|
6
|
+
const result = getInstanceById(id);
|
|
7
|
+
if (result) {
|
|
8
|
+
const { entity, instance } = result;
|
|
9
|
+
return getDisplayNameFromEntityInstance(entity, instance.content, getInstanceById, locales, id);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}, locales);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
return getSerializedDisplayNameFromEntityInstance(serializeEntityDecl(entity), instance, defaultName, locales);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Schema } from "../node/schema/Schema.ts";
|
|
2
|
+
import type { Output } from "./output.ts";
|
|
3
|
+
export type Config = {
|
|
4
|
+
serverOptions?: ServerOptions;
|
|
5
|
+
schema: Schema;
|
|
6
|
+
outputs: Output[];
|
|
7
|
+
dataRootPath: string;
|
|
8
|
+
};
|
|
9
|
+
export type ServerOptions = {
|
|
10
|
+
port: number;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -18,7 +18,12 @@ export const deepEqual = (a, b) => {
|
|
|
18
18
|
if (keys.length !== Object.keys(b).length) {
|
|
19
19
|
return false;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
if (!(key in b) || !deepEqual(a[key], b[key])) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
22
27
|
}
|
|
23
28
|
return false;
|
|
24
29
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SerializedEntityDecl } from "../../node/schema/index.ts";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const getSerializedDisplayNameFromEntityInstance: (entity: SerializedEntityDecl, instance: unknown, defaultName: string, locales?: string[]) => string;
|
|
@@ -11,7 +11,7 @@ const getValueAtPath = (value, path) => {
|
|
|
11
11
|
}
|
|
12
12
|
return current;
|
|
13
13
|
};
|
|
14
|
-
export const
|
|
14
|
+
export const getSerializedDisplayNameFromEntityInstance = (entity, instance, defaultName, locales = []) => {
|
|
15
15
|
if (entity.displayName === null) {
|
|
16
16
|
return defaultName;
|
|
17
17
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EntityDecl } from "../../node/schema/index.ts";
|
|
2
|
+
import type { GetInstanceById } from "../../node/server/index.ts";
|
|
2
3
|
import type { GitFileStatus } from "./git.ts";
|
|
3
4
|
export interface InstanceContainer {
|
|
4
5
|
fileName: string;
|
|
@@ -12,5 +13,5 @@ export interface InstanceContainerOverview {
|
|
|
12
13
|
gitStatus?: GitFileStatus;
|
|
13
14
|
displayName: string;
|
|
14
15
|
}
|
|
15
|
-
export declare const getInstanceContainerOverview: (entity:
|
|
16
|
+
export declare const getInstanceContainerOverview: (entity: EntityDecl, instanceContainer: InstanceContainer, getInstanceById: GetInstanceById, locales?: string[]) => InstanceContainerOverview;
|
|
16
17
|
export type InstancesByEntityName = Record<string, InstanceContainer[]>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { getDisplayNameFromEntityInstance } from "
|
|
2
|
-
export const getInstanceContainerOverview = (entity, instanceContainer, locales) => {
|
|
1
|
+
import { getDisplayNameFromEntityInstance } from "../../node/utils/displayName.js";
|
|
2
|
+
export const getInstanceContainerOverview = (entity, instanceContainer, getInstanceById, locales) => {
|
|
3
3
|
const { content: _, ...rest } = instanceContainer;
|
|
4
4
|
return {
|
|
5
5
|
...rest,
|
|
6
|
-
displayName: getDisplayNameFromEntityInstance(entity, instanceContainer.content,
|
|
6
|
+
displayName: getDisplayNameFromEntityInstance(entity, instanceContainer.content, getInstanceById, locales),
|
|
7
7
|
};
|
|
8
8
|
};
|
|
@@ -61,7 +61,7 @@ const listRule = {
|
|
|
61
61
|
}),
|
|
62
62
|
};
|
|
63
63
|
const paragraphRule = {
|
|
64
|
-
pattern: /^((?:[^\n]+?)(?:\n[^\n]+?)*)(?:\n{2,}
|
|
64
|
+
pattern: /^((?:[^\n]+?)(?:\n[^\n]+?)*)(?:\n{2,}|\s*$)/,
|
|
65
65
|
map: result => ({ kind: "paragraph", content: parseInlineMarkdown(result[1] ?? "") }),
|
|
66
66
|
};
|
|
67
67
|
const blockRules = [listRule, paragraphRule];
|
|
@@ -8,7 +8,7 @@ const filterFilesForDisplay = (predicate, entities, data) => Object.entries(data
|
|
|
8
8
|
entities.find(entity => entity.name === entityName)?.namePlural ?? entityName,
|
|
9
9
|
instances
|
|
10
10
|
.filter(instance => instance.gitStatus !== undefined && predicate(instance.gitStatus))
|
|
11
|
-
.sort((a, b) => a.displayName.localeCompare(b.displayName)),
|
|
11
|
+
.sort((a, b) => a.displayName.localeCompare(b.displayName, undefined, { numeric: true })),
|
|
12
12
|
])
|
|
13
13
|
.filter(([_1, _2, instances]) => instances.length > 0)
|
|
14
14
|
.sort((a, b) => a[1].localeCompare(b[1]));
|
|
@@ -7,11 +7,11 @@ import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
|
7
7
|
export const ArrayTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
8
8
|
const errors = validateArrayConstraints(type, value);
|
|
9
9
|
const isTuple = typeof type.minItems === "number" && type.minItems === type.maxItems;
|
|
10
|
-
return (_jsxs("div", { class: "field field--container field--array", children: [value.length > 0 && (_jsx("ol", { children: value.map((item, i) => (_jsxs("li", { class: "container-item array-item", children: [isTuple ? null : (_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }),
|
|
10
|
+
return (_jsxs("div", { class: "field field--container field--array", children: [value.length > 0 && (_jsx("ol", { children: value.map((item, i) => (_jsxs("li", { class: "container-item array-item", children: [isTuple ? null : (_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }), _jsxs("button", { class: "destructive", onClick: () => {
|
|
11
11
|
onChange(removeAt(value, i));
|
|
12
|
-
}, disabled: type.minItems !== undefined && value.length <= type.minItems, children: "Delete Item" })] })), _jsx(TypeInput, { type: type.items, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
|
|
12
|
+
}, disabled: type.minItems !== undefined && value.length <= type.minItems, children: ["Delete Item #", i + 1] })] })), _jsx(TypeInput, { type: type.items, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
|
|
13
13
|
onChange(value.with(i, newItem));
|
|
14
|
-
} })] }, i))) })), isTuple ? null : (_jsx("div", { class: "add-item-container", children:
|
|
14
|
+
} })] }, i))) })), isTuple ? null : (_jsx("div", { class: "add-item-container", children: _jsxs("button", { onClick: () => {
|
|
15
15
|
onChange([...value, createTypeSkeleton(getDeclFromDeclName, type.items)]);
|
|
16
|
-
}, disabled: type.maxItems !== undefined && value.length >= type.maxItems, children: "Add Item" }) })), _jsx(ValidationErrors, { errors: errors })] }));
|
|
16
|
+
}, disabled: type.maxItems !== undefined && value.length >= type.maxItems, children: ["Add Item #", value.length + 1] }) })), _jsx(ValidationErrors, { errors: errors })] }));
|
|
17
17
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useState } from "preact/hooks";
|
|
3
3
|
import { sortObjectKeysAlphabetically } from "../../../shared/utils/object.js";
|
|
4
|
+
import { toTitleCase } from "../../../shared/utils/string.js";
|
|
4
5
|
import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
|
|
5
6
|
import { Select } from "../Select.js";
|
|
6
7
|
import { TypeInput } from "./TypeInput.js";
|
|
@@ -10,21 +11,25 @@ export const NestedEntityMapTypeInput = ({ type, value, instanceNamesByEntity, g
|
|
|
10
11
|
const secondaryInstances = (instanceNamesByEntity[type.secondaryEntity] ?? [])
|
|
11
12
|
.slice()
|
|
12
13
|
.filter(instance => !existingKeys.includes(instance.id))
|
|
13
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
14
|
-
return (_jsxs("div", { class: "field field--container field--nestedentitymap", children: [existingKeys.length > 0 && (_jsx("ul", { children: Object.entries(value).map(([key, item]) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
|
|
15
|
+
return (_jsxs("div", { class: "field field--container field--nestedentitymap", children: [existingKeys.length > 0 && (_jsx("ul", { children: Object.entries(value).map(([key, item]) => {
|
|
16
|
+
const name = instanceNamesByEntity[type.secondaryEntity]?.find(instance => instance.id === key)
|
|
17
|
+
?.name ?? key;
|
|
18
|
+
return (_jsxs("li", { class: "container-item dict-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsx("div", { className: "container-item-title", children: _jsxs("span", { children: [_jsx("strong", { children: name }), " ", _jsx("span", { className: "id", children: key })] }) }), _jsx("div", { className: "btns", children: _jsxs("button", { class: "destructive", onClick: () => {
|
|
19
|
+
const newObj = { ...value };
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
21
|
+
delete newObj[key];
|
|
22
|
+
onChange(newObj);
|
|
23
|
+
}, children: ["Delete ", name] }) })] }), _jsx(TypeInput, { type: type.type, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
|
|
24
|
+
onChange(sortObjectKeysAlphabetically({ ...value, [key]: newItem }));
|
|
25
|
+
} })] }, key));
|
|
26
|
+
}) })), _jsxs("div", { class: "add-item-container", children: [_jsxs(Select, { value: newKey, onInput: event => {
|
|
22
27
|
setNewKey(event.currentTarget.value);
|
|
23
|
-
}, disabled: secondaryInstances.length === 0, children: [secondaryInstances.length === 0 ? (_jsx("option", { value: "", disabled: true, children: "No instances available" })) : (_jsx("option", { value: "", disabled: true, children: "No selected instance" })), secondaryInstances.map(instance => (_jsx("option", { value: instance.id, children: instance.name }, instance.id)))] }),
|
|
28
|
+
}, disabled: secondaryInstances.length === 0, children: [secondaryInstances.length === 0 ? (_jsx("option", { value: "", disabled: true, children: "No instances available" })) : (_jsx("option", { value: "", disabled: true, children: "No selected instance" })), secondaryInstances.map(instance => (_jsx("option", { value: instance.id, children: instance.name }, instance.id)))] }), _jsxs("button", { onClick: () => {
|
|
24
29
|
onChange(sortObjectKeysAlphabetically({
|
|
25
30
|
...value,
|
|
26
31
|
[newKey]: createTypeSkeleton(getDeclFromDeclName, type.type),
|
|
27
32
|
}));
|
|
28
33
|
setNewKey("");
|
|
29
|
-
}, disabled: newKey === "", children: "Add Key" })] })] }));
|
|
34
|
+
}, disabled: newKey === "", children: ["Add ", newKey === "" ? "Key" : toTitleCase(newKey)] })] })] }));
|
|
30
35
|
};
|
|
@@ -8,17 +8,17 @@ import { TypeInput } from "./TypeInput.js";
|
|
|
8
8
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
9
9
|
export const ObjectTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
10
10
|
const errors = validateObjectConstraints(type, Object.keys(type.properties), value);
|
|
11
|
-
return (_jsxs("div", { class: "field field--container field--object", children: [_jsx("ul", { children: Object.entries(type.properties).map(([key, memberDecl]) => (_jsxs("li", { class: "container-item object-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [_jsx("strong", { children: toTitleCase(key) }), memberDecl.comment === undefined ? null : (_jsx(Markdown, { class: "comment", string: memberDecl.comment }))] }), memberDecl.isRequired ? null : value[key] === undefined ? (
|
|
11
|
+
return (_jsxs("div", { class: "field field--container field--object", children: [_jsx("ul", { children: Object.entries(type.properties).map(([key, memberDecl]) => (_jsxs("li", { class: "container-item object-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [_jsx("strong", { children: toTitleCase(key) }), memberDecl.comment === undefined ? null : (_jsx(Markdown, { class: "comment", string: memberDecl.comment }))] }), memberDecl.isRequired ? null : value[key] === undefined ? (_jsxs("button", { onClick: () => {
|
|
12
12
|
onChange(sortObjectKeys({
|
|
13
13
|
...value,
|
|
14
14
|
[key]: createTypeSkeleton(getDeclFromDeclName, memberDecl.type),
|
|
15
15
|
}, Object.keys(type.properties)));
|
|
16
|
-
}, children: "Add
|
|
16
|
+
}, children: ["Add ", toTitleCase(key)] })) : (_jsxs("button", { class: "destructive", onClick: () => {
|
|
17
17
|
const newObj = { ...value };
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
19
19
|
delete newObj[key];
|
|
20
20
|
onChange(newObj);
|
|
21
|
-
}, children: "Remove
|
|
21
|
+
}, children: ["Remove ", toTitleCase(key)] }))] }), memberDecl.isRequired || value[key] !== undefined ? (_jsx(TypeInput, { type: memberDecl.type, value: value[key], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
|
|
22
22
|
onChange(sortObjectKeys({ ...value, [key]: newItem }, Object.keys(type.properties)));
|
|
23
23
|
} })) : null] }, key))) }), _jsx(ValidationErrors, { errors: errors })] }));
|
|
24
24
|
};
|
|
@@ -4,7 +4,7 @@ import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
|
4
4
|
export const ReferenceIdentifierTypeInput = ({ type, value, instanceNamesByEntity, onChange, }) => {
|
|
5
5
|
const instances = (instanceNamesByEntity[type.entity] ?? [])
|
|
6
6
|
.slice()
|
|
7
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
7
|
+
.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
|
|
8
8
|
return (_jsxs("div", { class: "field", children: [_jsxs(Select, { value: value, onInput: event => {
|
|
9
9
|
onChange(event.currentTarget.value);
|
|
10
10
|
}, disabled: instances.length === 0, "aria-invalid": !value, children: [instances.length === 0 ? (_jsx("option", { value: "", disabled: true, children: "No instances available" })) : (_jsx("option", { value: "", disabled: true, children: "No selected instance" })), instances.map(instance => (_jsx("option", { value: instance.id, children: instance.name }, instance.id)))] }), _jsx(ValidationErrors, { errors: !value ? [ReferenceError("no reference provided")] : [] })] }));
|