tsondb 0.5.0 → 0.5.2
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/node/Schema.js +7 -4
- package/lib/node/schema/declarations/Declaration.d.ts +2 -1
- package/lib/node/schema/declarations/Declaration.js +22 -0
- package/lib/node/schema/types/Type.js +29 -27
- package/lib/node/schema/types/references/IncludeIdentifierType.js +2 -2
- package/lib/node/server/index.js +11 -2
- package/lib/shared/utils/markdown.d.ts +10 -0
- package/lib/shared/utils/markdown.js +52 -8
- package/lib/web/components/typeInputs/ObjectTypeInput.js +2 -1
- package/lib/web/components/typeInputs/StringTypeInput.js +1 -1
- package/lib/web/hooks/useInstanceNamesByEntity.js +1 -0
- package/lib/web/routes/Entity.js +2 -1
- package/lib/web/routes/Home.js +2 -1
- package/lib/web/{components/typeInputs/utils → utils}/Markdown.d.ts +1 -0
- package/lib/web/utils/Markdown.js +39 -0
- package/package.json +4 -4
- package/public/css/styles.css +84 -21
- package/lib/web/components/typeInputs/utils/Markdown.js +0 -26
package/lib/node/Schema.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { getNestedDeclarations, getParameterNames } from "./schema/declarations/Declaration.js";
|
|
1
|
+
import { getNestedDeclarations, getParameterNames, walkNodeTree, } from "./schema/declarations/Declaration.js";
|
|
2
2
|
import { isEntityDecl } from "./schema/declarations/EntityDecl.js";
|
|
3
3
|
import { isStringType } from "./schema/types/primitives/StringType.js";
|
|
4
|
+
import { isIncludeIdentifierType } from "./schema/types/references/IncludeIdentifierType.js";
|
|
4
5
|
import { isNestedEntityMapType } from "./schema/types/references/NestedEntityMapType.js";
|
|
5
6
|
import { findTypeAtPath } from "./schema/types/Type.js";
|
|
6
7
|
const checkDuplicateIdentifier = (existingDecls, decl) => {
|
|
@@ -14,9 +15,11 @@ const checkDuplicateIdentifier = (existingDecls, decl) => {
|
|
|
14
15
|
const checkParameterNamesShadowing = (decls) => {
|
|
15
16
|
for (const decl of decls) {
|
|
16
17
|
for (const param of getParameterNames(decl)) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
walkNodeTree(node => {
|
|
19
|
+
if (isIncludeIdentifierType(node) && node.reference.name === param) {
|
|
20
|
+
throw new Error(`Parameter name "${param}" shadows declaration name in declaration "${decl.name}".`);
|
|
21
|
+
}
|
|
22
|
+
}, decl);
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BaseNode, GetReferences, Node, Serializer } from "../Node.js";
|
|
2
2
|
import type { SerializedTypeParameter, TypeParameter } from "../TypeParameter.js";
|
|
3
3
|
import type { EnumCaseDecl, SerializedEnumCaseDecl } from "../types/generic/EnumType.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type SerializedType, type Type } from "../types/Type.js";
|
|
5
5
|
import type { ValidatorHelpers } from "../validation/type.js";
|
|
6
6
|
import type { EntityDecl, SerializedEntityDecl } from "./EntityDecl.js";
|
|
7
7
|
import type { EnumDecl, SerializedEnumDecl } from "./EnumDecl.js";
|
|
@@ -40,6 +40,7 @@ export declare const validateDeclName: (name: string) => void;
|
|
|
40
40
|
export declare const resolveTypeArgumentsInDecl: <Params extends TypeParameter[]>(decl: DeclP<Params>, args: TypeArguments<Params>) => DeclP<[]>;
|
|
41
41
|
export declare const isDeclWithoutTypeParameters: (decl: Decl) => decl is DeclP<[]>;
|
|
42
42
|
export declare const resolveTypeArgumentsInDecls: (decls: readonly Decl[]) => DeclP<[]>[];
|
|
43
|
+
export declare function walkNodeTree(callbackFn: (node: Node) => void, decl: Decl): void;
|
|
43
44
|
export declare const serializeDecl: Serializer<Decl, SerializedDecl>;
|
|
44
45
|
export declare const getReferencesForDecl: GetReferences<Decl>;
|
|
45
46
|
export declare const groupDeclarationsBySourceUrl: (decls: readonly Decl[]) => Partial<Record<string, Decl[]>>;
|
|
@@ -6,6 +6,7 @@ import { getNestedDeclarationsInObjectType } from "../types/generic/ObjectType.j
|
|
|
6
6
|
import { getNestedDeclarationsInIncludeIdentifierType } from "../types/references/IncludeIdentifierType.js";
|
|
7
7
|
import { getNestedDeclarationsInNestedEntityMapType } from "../types/references/NestedEntityMapType.js";
|
|
8
8
|
import { getNestedDeclarationsInReferenceIdentifierType } from "../types/references/ReferenceIdentifierType.js";
|
|
9
|
+
import { walkTypeNodeTree } from "../types/Type.js";
|
|
9
10
|
import { getNestedDeclarationsInEntityDecl, getReferencesForEntityDecl, isEntityDecl, resolveTypeArgumentsInEntityDecl, serializeEntityDecl, validateEntityDecl, } from "./EntityDecl.js";
|
|
10
11
|
import { getNestedDeclarationsInEnumDecl, getReferencesForEnumDecl, isEnumDecl, resolveTypeArgumentsInEnumDecl, serializeEnumDecl, validateEnumDecl, } from "./EnumDecl.js";
|
|
11
12
|
import { getNestedDeclarationsInTypeAliasDecl, getReferencesForTypeAliasDecl, isTypeAliasDecl, resolveTypeArgumentsInTypeAliasDecl, serializeTypeAliasDecl, validateTypeAliasDecl, } from "./TypeAliasDecl.js";
|
|
@@ -77,6 +78,27 @@ export const resolveTypeArgumentsInDecl = (decl, args) => {
|
|
|
77
78
|
};
|
|
78
79
|
export const isDeclWithoutTypeParameters = (decl) => decl.parameters.length === 0;
|
|
79
80
|
export const resolveTypeArgumentsInDecls = (decls) => decls.filter(isDeclWithoutTypeParameters).map(decl => resolveTypeArgumentsInDecl(decl, []));
|
|
81
|
+
export function walkNodeTree(callbackFn, decl) {
|
|
82
|
+
switch (decl.kind) {
|
|
83
|
+
case NodeKind.EntityDecl: {
|
|
84
|
+
callbackFn(decl);
|
|
85
|
+
walkTypeNodeTree(callbackFn, decl.type.value);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
case NodeKind.EnumDecl: {
|
|
89
|
+
callbackFn(decl);
|
|
90
|
+
walkTypeNodeTree(callbackFn, decl.type.value);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
case NodeKind.TypeAliasDecl: {
|
|
94
|
+
callbackFn(decl);
|
|
95
|
+
walkTypeNodeTree(callbackFn, decl.type.value);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
default:
|
|
99
|
+
return assertExhaustive(decl);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
80
102
|
export const serializeDecl = decl => {
|
|
81
103
|
switch (decl.kind) {
|
|
82
104
|
case NodeKind.EntityDecl:
|
|
@@ -71,47 +71,49 @@ export const resolveTypeArgumentsInType = (args, type) => {
|
|
|
71
71
|
};
|
|
72
72
|
export function walkTypeNodeTree(callbackFn, type) {
|
|
73
73
|
switch (type.kind) {
|
|
74
|
-
case NodeKind.ArrayType:
|
|
74
|
+
case NodeKind.ArrayType: {
|
|
75
75
|
callbackFn(type);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
case NodeKind.ObjectType:
|
|
76
|
+
walkTypeNodeTree(callbackFn, type.items);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
case NodeKind.ObjectType: {
|
|
81
80
|
callbackFn(type);
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
case NodeKind.NestedEntityMapType:
|
|
81
|
+
Object.values(type.properties).forEach(prop => {
|
|
82
|
+
walkTypeNodeTree(callbackFn, prop.type);
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
case NodeKind.NestedEntityMapType: {
|
|
89
87
|
callbackFn(type);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
88
|
+
walkTypeNodeTree(callbackFn, type.type.value);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
94
91
|
case NodeKind.BooleanType:
|
|
95
92
|
case NodeKind.DateType:
|
|
96
93
|
case NodeKind.FloatType:
|
|
97
94
|
case NodeKind.IntegerType:
|
|
98
95
|
case NodeKind.StringType:
|
|
99
96
|
case NodeKind.TypeArgumentType:
|
|
100
|
-
case NodeKind.ReferenceIdentifierType:
|
|
97
|
+
case NodeKind.ReferenceIdentifierType: {
|
|
98
|
+
callbackFn(type);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
101
|
case NodeKind.IncludeIdentifierType: {
|
|
102
102
|
callbackFn(type);
|
|
103
|
+
type.args.forEach(arg => {
|
|
104
|
+
walkTypeNodeTree(callbackFn, arg);
|
|
105
|
+
});
|
|
103
106
|
return;
|
|
104
107
|
}
|
|
105
|
-
case NodeKind.EnumType:
|
|
108
|
+
case NodeKind.EnumType: {
|
|
106
109
|
callbackFn(type);
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
110
|
+
Object.values(type.values).forEach(value => {
|
|
111
|
+
if (value.type) {
|
|
112
|
+
walkTypeNodeTree(callbackFn, value.type);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
115
117
|
default:
|
|
116
118
|
return assertExhaustive(type);
|
|
117
119
|
}
|
|
@@ -15,9 +15,9 @@ export const IncludeIdentifierType = (reference) => ({
|
|
|
15
15
|
});
|
|
16
16
|
export { IncludeIdentifierType as IncludeIdentifier };
|
|
17
17
|
export const isIncludeIdentifierType = (node) => node.kind === NodeKind.IncludeIdentifierType;
|
|
18
|
-
export const getNestedDeclarationsInIncludeIdentifierType = (addedDecls, type) => addedDecls.includes(type.reference)
|
|
18
|
+
export const getNestedDeclarationsInIncludeIdentifierType = (addedDecls, type) => type.args.reduce((accAddedDecls, arg) => getNestedDeclarations(accAddedDecls, arg), addedDecls.includes(type.reference)
|
|
19
19
|
? addedDecls
|
|
20
|
-
: getNestedDeclarations([type.reference, ...addedDecls], type.reference);
|
|
20
|
+
: getNestedDeclarations([type.reference, ...addedDecls], type.reference));
|
|
21
21
|
export const validateIncludeIdentifierType = (helpers, type, value) => validateDecl(helpers, type.reference, type.args, value);
|
|
22
22
|
export const resolveTypeArgumentsInIncludeIdentifierType = (args, type) => type.args.length === 0
|
|
23
23
|
? type
|
package/lib/node/server/index.js
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import Debug from "debug";
|
|
2
2
|
import express from "express";
|
|
3
|
-
import {
|
|
3
|
+
import { findPackageJSON } from "node:module";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
4
5
|
import { api } from "./api/index.js";
|
|
5
6
|
import { init } from "./init.js";
|
|
6
7
|
const debug = Debug("tsondb:server");
|
|
7
8
|
const defaultOptions = {
|
|
8
9
|
port: 3000,
|
|
9
10
|
};
|
|
11
|
+
const staticNodeModule = (moduleName) => {
|
|
12
|
+
const pathToPackageJson = findPackageJSON(moduleName, import.meta.url);
|
|
13
|
+
if (!pathToPackageJson) {
|
|
14
|
+
throw new Error(`Could not find module "${moduleName}"`);
|
|
15
|
+
}
|
|
16
|
+
return express.static(dirname(pathToPackageJson));
|
|
17
|
+
};
|
|
10
18
|
export const createServer = async (schema, dataRootPath, instancesByEntityName, options) => {
|
|
11
19
|
const { port } = { ...defaultOptions, ...options };
|
|
12
20
|
const app = express();
|
|
13
21
|
app.use(express.static(join(import.meta.dirname, "../../../public")));
|
|
14
|
-
app.use("/js/node_modules",
|
|
22
|
+
app.use("/js/node_modules/preact", staticNodeModule("preact"));
|
|
23
|
+
app.use("/js/node_modules/preact-iso", staticNodeModule("preact-iso"));
|
|
15
24
|
app.use("/js/client", express.static(join(import.meta.dirname, "../../../lib/web")));
|
|
16
25
|
app.use("/js/shared", express.static(join(import.meta.dirname, "../../../lib/shared")));
|
|
17
26
|
app.use(express.json());
|
|
@@ -5,10 +5,20 @@ type TextNode = {
|
|
|
5
5
|
export type InlineMarkdownNode = {
|
|
6
6
|
kind: "bold" | "italic";
|
|
7
7
|
content: InlineMarkdownNode[];
|
|
8
|
+
} | {
|
|
9
|
+
kind: "code";
|
|
10
|
+
content: string;
|
|
8
11
|
} | TextNode;
|
|
9
12
|
export type BlockMarkdownNode = {
|
|
10
13
|
kind: "paragraph";
|
|
11
14
|
content: InlineMarkdownNode[];
|
|
15
|
+
} | {
|
|
16
|
+
kind: "list";
|
|
17
|
+
ordered: boolean;
|
|
18
|
+
content: {
|
|
19
|
+
kind: "listitem";
|
|
20
|
+
content: InlineMarkdownNode[];
|
|
21
|
+
}[];
|
|
12
22
|
} | TextNode;
|
|
13
23
|
export declare const parseBlockMarkdown: (text: string) => BlockMarkdownNode[];
|
|
14
24
|
export {};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const codeRule = {
|
|
2
|
+
pattern: /`(.*?)`/,
|
|
3
|
+
map: result => ({ kind: "code", content: result[1] ?? "" }),
|
|
4
|
+
};
|
|
1
5
|
const boldWithItalicRule = {
|
|
2
6
|
pattern: /\*\*(.*?\*.+?\*.*?)\*\*/,
|
|
3
7
|
map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1] ?? "") }),
|
|
@@ -14,9 +18,14 @@ const italicRule = {
|
|
|
14
18
|
pattern: /\*(.+?)\*/,
|
|
15
19
|
map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1] ?? "") }),
|
|
16
20
|
};
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
const inlineRules = [
|
|
22
|
+
codeRule,
|
|
23
|
+
boldWithItalicRule,
|
|
24
|
+
italicWithBoldRule,
|
|
25
|
+
boldRule,
|
|
26
|
+
italicRule,
|
|
27
|
+
];
|
|
28
|
+
const parseForInlineRules = (rules, text) => {
|
|
20
29
|
if (text.length === 0) {
|
|
21
30
|
return [];
|
|
22
31
|
}
|
|
@@ -30,13 +39,48 @@ const parseForRules = (rules, text) => {
|
|
|
30
39
|
const before = text.slice(0, index);
|
|
31
40
|
const after = text.slice(index + res[0].length);
|
|
32
41
|
return [
|
|
33
|
-
...(before.length > 0 ?
|
|
34
|
-
activeRule.map(res, text =>
|
|
35
|
-
...(after.length > 0 ?
|
|
42
|
+
...(before.length > 0 ? parseForInlineRules(rules.slice(1), before) : []),
|
|
43
|
+
activeRule.map(res, text => parseForInlineRules(rules.slice(1), text)),
|
|
44
|
+
...(after.length > 0 ? parseForInlineRules(rules, after) : []),
|
|
36
45
|
];
|
|
37
46
|
}
|
|
38
47
|
else {
|
|
39
|
-
return
|
|
48
|
+
return parseForInlineRules(rules.slice(1), text);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const parseInlineMarkdown = (text) => parseForInlineRules(inlineRules, text);
|
|
52
|
+
const listRule = {
|
|
53
|
+
pattern: /^((?:(?:\d+\.|[-*]) [^\n]+?)(?:\n(?:\d+\.|[-*]) [^\n]+?)*)(?:\n{2,}|$)/,
|
|
54
|
+
map: result => ({
|
|
55
|
+
kind: "list",
|
|
56
|
+
ordered: /^\d+\. /.test(result[0]),
|
|
57
|
+
content: (result[1] ?? "").split("\n").map(item => ({
|
|
58
|
+
kind: "listitem",
|
|
59
|
+
content: parseInlineMarkdown(item.replace(/^\d+\. |[-*] /, "")),
|
|
60
|
+
})),
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
const paragraphRule = {
|
|
64
|
+
pattern: /^((?:[^\n]+?)(?:\n[^\n]+?)*)(?:\n{2,}|$)/,
|
|
65
|
+
map: result => ({ kind: "paragraph", content: parseInlineMarkdown(result[1] ?? "") }),
|
|
66
|
+
};
|
|
67
|
+
const blockRules = [listRule, paragraphRule];
|
|
68
|
+
const parseForBlockRules = (rules, text, remainingRules = rules) => {
|
|
69
|
+
if (text.length === 0) {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
const activeRule = remainingRules[0];
|
|
73
|
+
if (activeRule === undefined) {
|
|
74
|
+
return [{ kind: "paragraph", content: [{ kind: "text", content: text }] }];
|
|
75
|
+
}
|
|
76
|
+
const res = activeRule.pattern.exec(text);
|
|
77
|
+
if (res && (activeRule.predicate?.(res) ?? true)) {
|
|
78
|
+
const { index } = res;
|
|
79
|
+
const after = text.slice(index + res[0].length);
|
|
80
|
+
return [activeRule.map(res), ...(after.length > 0 ? parseForBlockRules(rules, after) : [])];
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return parseForBlockRules(rules, text, remainingRules.slice(1));
|
|
40
84
|
}
|
|
41
85
|
};
|
|
42
|
-
const
|
|
86
|
+
export const parseBlockMarkdown = (text) => parseForBlockRules(blockRules, text);
|
|
@@ -2,12 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
|
2
2
|
import { sortObjectKeys } from "../../../shared/utils/object.js";
|
|
3
3
|
import { toTitleCase } from "../../../shared/utils/string.js";
|
|
4
4
|
import { validateObjectConstraints } from "../../../shared/validation/object.js";
|
|
5
|
+
import { Markdown } from "../../utils/Markdown.js";
|
|
5
6
|
import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
|
|
6
7
|
import { TypeInput } from "./TypeInput.js";
|
|
7
8
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
8
9
|
export const ObjectTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
9
10
|
const errors = validateObjectConstraints(type, Object.keys(type.properties), value);
|
|
10
|
-
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(
|
|
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 ? (_jsx("button", { onClick: () => {
|
|
11
12
|
onChange(sortObjectKeys({
|
|
12
13
|
...value,
|
|
13
14
|
[key]: createTypeSkeleton(getDeclFromDeclName, memberDecl.type),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
2
|
import { validateStringConstraints } from "../../../shared/validation/string.js";
|
|
3
|
-
import { Markdown } from "
|
|
3
|
+
import { Markdown } from "../../utils/Markdown.js";
|
|
4
4
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
5
5
|
export const StringTypeInput = ({ type, value, onChange }) => {
|
|
6
6
|
const { minLength, maxLength, pattern, isMarkdown } = type;
|
|
@@ -12,6 +12,7 @@ export const useInstanceNamesByEntity = (locales = []) => {
|
|
|
12
12
|
console.error("Error fetching data:", error.toString());
|
|
13
13
|
}
|
|
14
14
|
});
|
|
15
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
15
16
|
}, [locales.toString()]);
|
|
16
17
|
useEffect(() => {
|
|
17
18
|
updateInstanceNamesByEntity();
|
package/lib/web/routes/Entity.js
CHANGED
|
@@ -6,6 +6,7 @@ import { deleteInstanceByEntityNameAndId, getEntityByName, getInstancesByEntityN
|
|
|
6
6
|
import { Layout } from "../components/Layout.js";
|
|
7
7
|
import { useAPIResource } from "../hooks/useAPIResource.js";
|
|
8
8
|
import { useMappedAPIResource } from "../hooks/useMappedAPIResource.js";
|
|
9
|
+
import { Markdown } from "../utils/Markdown.js";
|
|
9
10
|
import { NotFound } from "./NotFound.js";
|
|
10
11
|
const mapInstances = (data) => data.instances;
|
|
11
12
|
export const Entity = () => {
|
|
@@ -26,7 +27,7 @@ export const Entity = () => {
|
|
|
26
27
|
if (!entity || !instances) {
|
|
27
28
|
return (_jsxs("div", { children: [_jsx("h1", { children: name }), _jsx("p", { className: "loading", children: "Loading \u2026" })] }));
|
|
28
29
|
}
|
|
29
|
-
return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsxs("div", { class: "header-with-btns", children: [_jsx("h1", { children: name }), _jsx("a", { class: "btn btn--primary", href: `/entities/${entity.declaration.name}/instances/create`, children: "Add" })] }), entity.declaration.comment && _jsx(
|
|
30
|
+
return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsxs("div", { class: "header-with-btns", children: [_jsx("h1", { children: name }), _jsx("a", { class: "btn btn--primary", href: `/entities/${entity.declaration.name}/instances/create`, children: "Add" })] }), entity.declaration.comment && (_jsx(Markdown, { class: "description", string: entity.declaration.comment })), _jsxs("p", { children: [instances.length, " instance", instances.length === 1 ? "" : "s"] }), _jsx("ul", { class: "instances", children: instances.map(instance => {
|
|
30
31
|
const gitStatusForDisplay = getGitStatusForDisplay(instance.gitStatus);
|
|
31
32
|
return (_jsxs("li", { id: `instance-${instance.id}`, class: `instance-item ${created === instance.id ? "instance-item--created" : ""} ${gitStatusForDisplay === undefined ? "" : `git-status--${gitStatusForDisplay}`}`, children: [_jsx("h2", { children: instance.displayName }), _jsx("p", { "aria-hidden": true, class: "id", children: instance.id }), gitStatusForDisplay !== undefined && (_jsx("p", { class: `git-status git-status--${gitStatusForDisplay}`, title: getLabelForGitStatus(gitStatusForDisplay), children: gitStatusForDisplay })), _jsxs("div", { className: "btns", children: [_jsx("a", { href: `/entities/${entity.declaration.name}/instances/${instance.id}`, class: "btn", children: "Edit" }), _jsx("button", { class: "destructive", onClick: () => {
|
|
32
33
|
if (confirm("Are you sure you want to delete this instance?")) {
|
package/lib/web/routes/Home.js
CHANGED
|
@@ -3,8 +3,9 @@ import { toTitleCase } from "../../shared/utils/string.js";
|
|
|
3
3
|
import { getAllEntities } from "../api.js";
|
|
4
4
|
import { Layout } from "../components/Layout.js";
|
|
5
5
|
import { useMappedAPIResource } from "../hooks/useMappedAPIResource.js";
|
|
6
|
+
import { Markdown } from "../utils/Markdown.js";
|
|
6
7
|
const mapEntities = (data) => data.declarations.sort((a, b) => a.declaration.name.localeCompare(b.declaration.name));
|
|
7
8
|
export const Home = () => {
|
|
8
9
|
const [entities] = useMappedAPIResource(getAllEntities, mapEntities);
|
|
9
|
-
return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsx("h1", { children: "Entities" }), _jsx("ul", { class: "entities", children: (entities ?? []).map(entity => (_jsxs("li", { class: "entity-item", children: [_jsxs("div", { className: "title", children: [_jsx("h2", { children: toTitleCase(entity.declaration.name) }), entity.declaration.comment && _jsx(
|
|
10
|
+
return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsx("h1", { children: "Entities" }), _jsx("ul", { class: "entities", children: (entities ?? []).map(entity => (_jsxs("li", { class: "entity-item", children: [_jsxs("div", { className: "title", children: [_jsx("h2", { children: toTitleCase(entity.declaration.name) }), entity.declaration.comment && (_jsx(Markdown, { class: "description", string: entity.declaration.comment }))] }), _jsxs("p", { class: "meta", children: [entity.instanceCount, " instance", entity.instanceCount === 1 ? "" : "s"] }), _jsx("div", { className: "btns", children: _jsx("a", { href: `/entities/${entity.declaration.name}`, class: "btn", children: "View" }) })] }, entity.declaration.name))) })] }));
|
|
10
11
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
|
+
import { parseBlockMarkdown } from "../../shared/utils/markdown.js";
|
|
3
|
+
export const Markdown = ({ class: className, string }) => {
|
|
4
|
+
const blocks = parseBlockMarkdown(string);
|
|
5
|
+
const blockElements = blocks.map((block, i) => (_jsx(BlockMarkdown, { node: block }, `md-block-${i.toString()}`)));
|
|
6
|
+
if (className) {
|
|
7
|
+
return _jsx("div", { class: className, children: blockElements });
|
|
8
|
+
}
|
|
9
|
+
return _jsx(_Fragment, { children: blockElements });
|
|
10
|
+
};
|
|
11
|
+
const BlockMarkdown = ({ node }) => {
|
|
12
|
+
switch (node.kind) {
|
|
13
|
+
case "paragraph":
|
|
14
|
+
return (_jsx("p", { children: node.content.map((inline, ii) => (_jsx(InlineMarkdown, { node: inline }, ii))) }));
|
|
15
|
+
case "list":
|
|
16
|
+
if (node.ordered) {
|
|
17
|
+
return (_jsx("ol", { children: node.content.map((item, ii) => (_jsx("li", { children: item.content.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ii))) }));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return (_jsx("ul", { children: node.content.map((item, ii) => (_jsx("li", { children: item.content.map((inline, iii) => (_jsx(InlineMarkdown, { node: inline }, iii))) }, ii))) }));
|
|
21
|
+
}
|
|
22
|
+
case "text":
|
|
23
|
+
return node.content;
|
|
24
|
+
default:
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const InlineMarkdown = ({ node }) => {
|
|
29
|
+
switch (node.kind) {
|
|
30
|
+
case "code":
|
|
31
|
+
return _jsx("code", { children: node.content });
|
|
32
|
+
case "bold":
|
|
33
|
+
return (_jsx("strong", { children: node.content.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i))) }));
|
|
34
|
+
case "italic":
|
|
35
|
+
return (_jsx("em", { children: node.content.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i))) }));
|
|
36
|
+
case "text":
|
|
37
|
+
return node.content;
|
|
38
|
+
}
|
|
39
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsondb",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "Lukas Obermann",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"release:sign": "commit-and-tag-version --sign --signoff"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@eslint/js": "^9.
|
|
35
|
+
"@eslint/js": "^9.31.0",
|
|
36
36
|
"@types/debug": "^4.1.12",
|
|
37
37
|
"@types/express": "^5.0.3",
|
|
38
|
-
"@types/node": "^24.0.
|
|
38
|
+
"@types/node": "^24.0.13",
|
|
39
39
|
"commit-and-tag-version": "^12.5.1",
|
|
40
|
-
"eslint": "^9.
|
|
40
|
+
"eslint": "^9.31.0",
|
|
41
41
|
"eslint-plugin-react": "^7.37.5",
|
|
42
42
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
43
43
|
"globals": "^16.3.0",
|
package/public/css/styles.css
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
--separator-color: #e9e5eb;
|
|
16
16
|
--secondary-color: #7e7f87;
|
|
17
17
|
--tertiary-color: #bcbdc2;
|
|
18
|
+
--input-border-color: black;
|
|
18
19
|
--button-background: #c7cad0;
|
|
19
20
|
--button-background-hover: #dedfe4;
|
|
20
21
|
--button-background-primary: black;
|
|
@@ -32,10 +33,51 @@
|
|
|
32
33
|
--highlight-color: #a08500;
|
|
33
34
|
--highlight-background: #fdfab9;
|
|
34
35
|
--disabled-color: #cdced2;
|
|
36
|
+
--git-status-untracked-color: rgb(13, 149, 101);
|
|
37
|
+
--git-status-untracked-background: rgba(13, 149, 101, 0.15);
|
|
38
|
+
--git-status-added-color: rgb(20, 148, 29);
|
|
39
|
+
--git-status-added-background: rgba(20, 148, 29, 0.15);
|
|
40
|
+
--git-status-modified-color: rgb(196, 155, 18);
|
|
41
|
+
--git-status-modified-background: rgba(196, 155, 18, 0.15);
|
|
42
|
+
--git-status-deleted-color: rgb(135, 22, 11);
|
|
43
|
+
--git-status-deleted-background: rgba(135, 22, 11, 0.15);
|
|
44
|
+
--git-status-renamed-color: rgb(20, 97, 148);
|
|
45
|
+
--git-status-renamed-background: rgba(20, 97, 148, 0.15);
|
|
46
|
+
--shadow-color: rgba(160, 163, 165, 0.5);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@media (prefers-color-scheme: dark) {
|
|
50
|
+
:root {
|
|
51
|
+
--background: hsl(260, 6%, 10%);
|
|
52
|
+
--markdown-background: hsl(260, 6%, 17%);
|
|
53
|
+
--color: hsl(260, 6%, 88%);
|
|
54
|
+
--error-color: #ff6b6b;
|
|
55
|
+
--separator-color: hsl(260, 6%, 24%);
|
|
56
|
+
--secondary-color: hsl(260, 6%, 69%);
|
|
57
|
+
--tertiary-color: hsl(260, 6%, 49%);
|
|
58
|
+
--input-border-color: hsl(260, 6%, 40%);
|
|
59
|
+
--button-background: hsl(260, 6%, 24%);
|
|
60
|
+
--button-background-hover: hsl(260, 6%, 30%);
|
|
61
|
+
--button-background-primary: hsl(260, 6%, 80%);
|
|
62
|
+
--button-background-primary-hover: hsl(260, 6%, 60%);
|
|
63
|
+
--button-background-destructive: #922727;
|
|
64
|
+
--button-background-destructive-hover: #ff8080;
|
|
65
|
+
--button-background-disabled: hsl(260, 6%, 35%);
|
|
66
|
+
--button-color: #fff;
|
|
67
|
+
--button-color-hover: #fff;
|
|
68
|
+
--button-color-primary: #000;
|
|
69
|
+
--button-color-primary-hover: #000;
|
|
70
|
+
--button-color-destructive: #fff;
|
|
71
|
+
--button-color-destructive-hover: #fff;
|
|
72
|
+
--button-color-disabled: hsl(260, 6%, 54%);
|
|
73
|
+
--highlight-color: #f2d600;
|
|
74
|
+
--highlight-background: #333300;
|
|
75
|
+
}
|
|
35
76
|
}
|
|
36
77
|
|
|
37
78
|
body {
|
|
38
79
|
font-family: var(--font-sans);
|
|
80
|
+
color: var(--color);
|
|
39
81
|
margin: 0;
|
|
40
82
|
padding: 1.5rem;
|
|
41
83
|
line-height: 1.4;
|
|
@@ -143,6 +185,13 @@ button.primary {
|
|
|
143
185
|
border: none;
|
|
144
186
|
}
|
|
145
187
|
|
|
188
|
+
@media (prefers-color-scheme: dark) {
|
|
189
|
+
a.btn--primary,
|
|
190
|
+
button.primary {
|
|
191
|
+
font-weight: 700;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
146
195
|
a.btn--primary:hover,
|
|
147
196
|
button.primary:hover {
|
|
148
197
|
background: var(--button-background-primary-hover);
|
|
@@ -179,8 +228,10 @@ button.destructive:disabled {
|
|
|
179
228
|
input,
|
|
180
229
|
textarea,
|
|
181
230
|
select {
|
|
231
|
+
background: var(--background);
|
|
232
|
+
color: var(--color);
|
|
182
233
|
font-family: var(--font-sans);
|
|
183
|
-
border: 1px solid var(--color);
|
|
234
|
+
border: 1px solid var(--input-border-color);
|
|
184
235
|
padding: 0.5rem;
|
|
185
236
|
display: block;
|
|
186
237
|
width: 100%;
|
|
@@ -301,7 +352,7 @@ select {
|
|
|
301
352
|
font-size: 1rem;
|
|
302
353
|
margin: 0;
|
|
303
354
|
flex: 1 1 auto;
|
|
304
|
-
padding: 0.
|
|
355
|
+
padding: 0.65rem 0;
|
|
305
356
|
}
|
|
306
357
|
|
|
307
358
|
.entity-item .title {
|
|
@@ -385,29 +436,29 @@ form > .field--container {
|
|
|
385
436
|
border: none;
|
|
386
437
|
}
|
|
387
438
|
|
|
388
|
-
.field--container ul,
|
|
389
|
-
.field--container ol {
|
|
439
|
+
.field--container > ul,
|
|
440
|
+
.field--container > ol {
|
|
390
441
|
margin: 0;
|
|
391
442
|
padding: 0;
|
|
392
443
|
list-style-type: " ";
|
|
393
444
|
}
|
|
394
445
|
|
|
395
|
-
.field--container ul li,
|
|
396
|
-
.field--container ol li {
|
|
446
|
+
.field--container > ul > li,
|
|
447
|
+
.field--container > ol > li {
|
|
397
448
|
padding-top: 0.5rem;
|
|
398
449
|
padding-bottom: 0.75rem;
|
|
399
450
|
border-top: 1px solid var(--separator-color);
|
|
400
451
|
display: block;
|
|
401
452
|
}
|
|
402
453
|
|
|
403
|
-
.field--container ul li:first-child,
|
|
404
|
-
.field--container ol li:first-child {
|
|
454
|
+
.field--container > ul > li:first-child,
|
|
455
|
+
.field--container > ol > li:first-child {
|
|
405
456
|
padding-top: 0;
|
|
406
457
|
border-top: none;
|
|
407
458
|
}
|
|
408
459
|
|
|
409
|
-
.field--container ul li:last-child,
|
|
410
|
-
.field--container ol li:last-child {
|
|
460
|
+
.field--container > ul > li:last-child,
|
|
461
|
+
.field--container > ol > li:last-child {
|
|
411
462
|
padding-bottom: 0;
|
|
412
463
|
}
|
|
413
464
|
|
|
@@ -488,8 +539,20 @@ button[type="submit"] {
|
|
|
488
539
|
|
|
489
540
|
.comment {
|
|
490
541
|
font-size: 0.8rem;
|
|
542
|
+
line-height: 1.4;
|
|
491
543
|
color: var(--secondary-color);
|
|
492
544
|
margin: 0.5rem 0 0;
|
|
545
|
+
max-width: 55em;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.comment p {
|
|
549
|
+
margin: 0.35rem 0 0;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.comment ul {
|
|
553
|
+
list-style-type: disc;
|
|
554
|
+
padding-left: 1.5rem;
|
|
555
|
+
margin: 0.35rem 0 0;
|
|
493
556
|
}
|
|
494
557
|
|
|
495
558
|
.git-status {
|
|
@@ -507,28 +570,28 @@ button[type="submit"] {
|
|
|
507
570
|
}
|
|
508
571
|
|
|
509
572
|
.git-status.git-status--U {
|
|
510
|
-
color:
|
|
511
|
-
background:
|
|
573
|
+
color: var(--git-status-untracked-color);
|
|
574
|
+
background: var(--git-status-untracked-background);
|
|
512
575
|
}
|
|
513
576
|
|
|
514
577
|
.git-status.git-status--A {
|
|
515
|
-
color:
|
|
516
|
-
background:
|
|
578
|
+
color: var(--git-status-added-color);
|
|
579
|
+
background: var(--git-status-added-background);
|
|
517
580
|
}
|
|
518
581
|
|
|
519
582
|
.git-status.git-status--M {
|
|
520
|
-
color:
|
|
521
|
-
background:
|
|
583
|
+
color: var(--git-status-modified-color);
|
|
584
|
+
background: var(--git-status-modified-background);
|
|
522
585
|
}
|
|
523
586
|
|
|
524
587
|
.git-status.git-status--D {
|
|
525
|
-
color:
|
|
526
|
-
background:
|
|
588
|
+
color: var(--git-status-deleted-color);
|
|
589
|
+
background: var(--git-status-deleted-background);
|
|
527
590
|
}
|
|
528
591
|
|
|
529
592
|
.git-status.git-status--R {
|
|
530
|
-
color:
|
|
531
|
-
background:
|
|
593
|
+
color: var(--git-status-renamed-color);
|
|
594
|
+
background: var(--git-status-renamed-background);
|
|
532
595
|
}
|
|
533
596
|
|
|
534
597
|
aside.git {
|
|
@@ -632,7 +695,7 @@ aside.git .branch .select-wrapper {
|
|
|
632
695
|
display: none;
|
|
633
696
|
position: absolute;
|
|
634
697
|
right: 1.5rem;
|
|
635
|
-
box-shadow: 0 0.5rem 2rem
|
|
698
|
+
box-shadow: 0 0.5rem 2rem var(--shadow-color);
|
|
636
699
|
z-index: 1000;
|
|
637
700
|
padding: 1rem;
|
|
638
701
|
background: var(--background);
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
-
import { parseBlockMarkdown } from "../../../../shared/utils/markdown.js";
|
|
3
|
-
export const Markdown = ({ string }) => {
|
|
4
|
-
const blocks = parseBlockMarkdown(string);
|
|
5
|
-
return blocks.map((block, i) => _jsx(BlockMarkdown, { node: block }, `md-block-${i.toString()}`));
|
|
6
|
-
};
|
|
7
|
-
const BlockMarkdown = ({ node }) => {
|
|
8
|
-
switch (node.kind) {
|
|
9
|
-
case "paragraph":
|
|
10
|
-
return (_jsx("p", { children: node.content.map((inline, ii) => (_jsx(InlineMarkdown, { node: inline }, ii))) }));
|
|
11
|
-
case "text":
|
|
12
|
-
return node.content;
|
|
13
|
-
default:
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
const InlineMarkdown = ({ node }) => {
|
|
18
|
-
switch (node.kind) {
|
|
19
|
-
case "bold":
|
|
20
|
-
return (_jsx("strong", { children: node.content.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i))) }));
|
|
21
|
-
case "italic":
|
|
22
|
-
return (_jsx("em", { children: node.content.map((inline, i) => (_jsx(InlineMarkdown, { node: inline }, i))) }));
|
|
23
|
-
case "text":
|
|
24
|
-
return node.content;
|
|
25
|
-
}
|
|
26
|
-
};
|