tsondb 0.20.5 → 0.20.7
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/node/bin/tsondb.js +1 -1
- package/dist/src/node/config.d.ts +10 -0
- package/dist/src/node/index.d.ts +5 -0
- package/dist/src/node/index.js +4 -2
- package/dist/src/node/schema/treeOperations/format.d.ts +2 -1
- package/dist/src/node/schema/treeOperations/format.js +24 -7
- package/dist/src/node/server/index.d.ts +2 -2
- package/dist/src/node/server/index.js +2 -1
- package/dist/src/node/utils/databaseOnDisk.d.ts +2 -1
- package/dist/src/node/utils/databaseOnDisk.js +12 -12
- package/dist/src/node/utils/files.d.ts +3 -2
- package/dist/src/node/utils/files.js +2 -2
- package/dist/src/shared/validation/string.d.ts +2 -0
- package/dist/src/shared/validation/string.js +15 -0
- package/dist/src/web/web.js +82 -14
- package/package.json +8 -8
- package/public/css/styles.css +87 -3
|
@@ -120,7 +120,7 @@ else {
|
|
|
120
120
|
debug(`running command: serve`);
|
|
121
121
|
validateConfigForServer(config);
|
|
122
122
|
const db = await createDB(config, config.validationOptions, false);
|
|
123
|
-
createServer(db, config
|
|
123
|
+
createServer(db, config);
|
|
124
124
|
break;
|
|
125
125
|
}
|
|
126
126
|
case "validate": {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { FormatterOptions as MarkdownFormatterOptions } from "@elyukai/markdown/format";
|
|
1
2
|
import type { DefaultTSONDBTypes, ValidationOptions } from "./index.ts";
|
|
2
3
|
import type { Output } from "./output.ts";
|
|
3
4
|
import type { EntityDecl } from "./schema/dsl/index.ts";
|
|
@@ -18,6 +19,13 @@ export type Config<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> = {
|
|
|
18
19
|
customStylesheetPath?: string;
|
|
19
20
|
customAssetsPath?: string;
|
|
20
21
|
validationOptions?: Partial<ValidationOptions>;
|
|
22
|
+
formattingOptions?: Partial<FormatterOptions>;
|
|
23
|
+
};
|
|
24
|
+
export type FormatterOptions = {
|
|
25
|
+
/**
|
|
26
|
+
* If set to false, Markdown formatting will be disabled. Otherwise, the default or, if provided, provided Markdown formatting options will be used to format Markdown strings.
|
|
27
|
+
*/
|
|
28
|
+
markdown?: Partial<MarkdownFormatterOptions> | false;
|
|
21
29
|
};
|
|
22
30
|
export type ServerOptions = {
|
|
23
31
|
port: number;
|
|
@@ -41,6 +49,7 @@ export declare const validateConfigForGeneration: (config: Config) => asserts co
|
|
|
41
49
|
export type DataConfig<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> = Config<T> & {
|
|
42
50
|
schema: Schema<T>;
|
|
43
51
|
dataRootPath: string;
|
|
52
|
+
formattingOptions?: Partial<FormatterOptions>;
|
|
44
53
|
};
|
|
45
54
|
export declare const validateConfigForData: (config: Config) => asserts config is DataConfig;
|
|
46
55
|
/**
|
|
@@ -53,6 +62,7 @@ export type ServerConfig<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> = Da
|
|
|
53
62
|
validationOptions?: Partial<ValidationOptions>;
|
|
54
63
|
customStylesheetPath?: string;
|
|
55
64
|
customAssetsPath?: string;
|
|
65
|
+
formattingOptions?: Partial<FormatterOptions>;
|
|
56
66
|
};
|
|
57
67
|
/**
|
|
58
68
|
* The configuration type required for validating the contents of the database.
|
package/dist/src/node/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { InstanceContainer, InstanceContainerOverview } from "../shared/utils/instances.ts";
|
|
2
|
+
import type { FormatterOptions } from "./config.ts";
|
|
2
3
|
import { Git } from "./git.js";
|
|
3
4
|
import type { Output } from "./output.ts";
|
|
4
5
|
import { type EntityDecl } from "./schema/dsl/index.ts";
|
|
@@ -42,6 +43,10 @@ export interface TSONDBOptions<T extends DefaultTSONDBTypes = DefaultTSONDBTypes
|
|
|
42
43
|
* Options for validating the data in the database.
|
|
43
44
|
*/
|
|
44
45
|
validationOptions?: Partial<ValidationOptions>;
|
|
46
|
+
/**
|
|
47
|
+
* Options for formatting the data in the database.
|
|
48
|
+
*/
|
|
49
|
+
formatterOptions?: Partial<FormatterOptions>;
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
47
52
|
* Options for validating the data in the database.
|
package/dist/src/node/index.js
CHANGED
|
@@ -96,6 +96,7 @@ export class TSONDB {
|
|
|
96
96
|
#git;
|
|
97
97
|
#referencesToInstances;
|
|
98
98
|
#validationOptions;
|
|
99
|
+
#formatterOptions;
|
|
99
100
|
#gitWrapper;
|
|
100
101
|
#locked = false;
|
|
101
102
|
constructor(options) {
|
|
@@ -106,6 +107,7 @@ export class TSONDB {
|
|
|
106
107
|
this.#git = options.git;
|
|
107
108
|
this.#referencesToInstances = options.referencesToInstances;
|
|
108
109
|
this.#validationOptions = options.validationOptions;
|
|
110
|
+
this.#formatterOptions = options.formatterOptions;
|
|
109
111
|
this.#gitWrapper = Lazy.of(() => this.#git
|
|
110
112
|
? new Git(this, this.#git.client, this.#git.root, this.#dataRootPath, () => this.#data, (data) => {
|
|
111
113
|
this.#data = data;
|
|
@@ -281,7 +283,7 @@ export class TSONDB {
|
|
|
281
283
|
await this.#data.forEachInstance(async (entityName, instance) => {
|
|
282
284
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
283
285
|
const entity = this.#schema.getEntity(entityName);
|
|
284
|
-
await writeInstance(this.#dataRootPath, entity, instance.id, instance.content);
|
|
286
|
+
await writeInstance(this.#dataRootPath, entity, instance.id, instance.content, this.#formatterOptions);
|
|
285
287
|
}, true);
|
|
286
288
|
debug("All data is formatted");
|
|
287
289
|
}
|
|
@@ -331,7 +333,7 @@ export class TSONDB {
|
|
|
331
333
|
throw new AggregateError(errors, "Validation errors occurred");
|
|
332
334
|
}
|
|
333
335
|
debug("Applying changes to disk ...");
|
|
334
|
-
const diskResult = await applyStepsToDisk(this.#dataRootPath, steps);
|
|
336
|
+
const diskResult = await applyStepsToDisk(this.#dataRootPath, steps, this.#formatterOptions);
|
|
335
337
|
if (isError(diskResult)) {
|
|
336
338
|
debug("Error applying changes to disk: %s", diskResult.error.message);
|
|
337
339
|
throw diskResult.error;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { FormatterOptions } from "../../config.ts";
|
|
1
2
|
import type { Type } from "../dsl/index.ts";
|
|
2
3
|
/**
|
|
3
4
|
* Format the structure of a value to always look the same when serialized as JSON.
|
|
4
5
|
*/
|
|
5
|
-
export declare const formatValue: (type: Type, value: unknown) => unknown;
|
|
6
|
+
export declare const formatValue: (type: Type, value: unknown, options: Partial<FormatterOptions> | undefined) => unknown;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { format as formatMarkdown, formatInline as formatMarkdownInline, } from "@elyukai/markdown/format";
|
|
1
2
|
import { mapObject, sortObjectKeys, sortObjectKeysByIndex } from "@elyukai/utils/object";
|
|
2
3
|
import { assertExhaustive } from "@elyukai/utils/typeSafety";
|
|
3
4
|
import { ENUM_DISCRIMINATOR_KEY } from "../../../shared/schema/declarations/EnumDecl.js";
|
|
@@ -11,19 +12,21 @@ const formatTranslationObjectValue = (type, value) => typeof value === "object"
|
|
|
11
12
|
/**
|
|
12
13
|
* Format the structure of a value to always look the same when serialized as JSON.
|
|
13
14
|
*/
|
|
14
|
-
export const formatValue = (type, value) => {
|
|
15
|
+
export const formatValue = (type, value, options) => {
|
|
15
16
|
switch (type.kind) {
|
|
16
17
|
case NodeKind.ArrayType:
|
|
17
|
-
return Array.isArray(value)
|
|
18
|
+
return Array.isArray(value)
|
|
19
|
+
? value.map(item => formatValue(type.items, item, options))
|
|
20
|
+
: value;
|
|
18
21
|
case NodeKind.ObjectType:
|
|
19
22
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
20
|
-
? sortObjectKeysByIndex(mapObject(value, (item, key) => type.properties[key] ? formatValue(type.properties[key].type, item) : item), Object.keys(type.properties))
|
|
23
|
+
? sortObjectKeysByIndex(mapObject(value, (item, key) => type.properties[key] ? formatValue(type.properties[key].type, item, options) : item), Object.keys(type.properties))
|
|
21
24
|
: value;
|
|
22
25
|
case NodeKind.IncludeIdentifierType:
|
|
23
|
-
return formatValue(type.reference.type.value, value);
|
|
26
|
+
return formatValue(type.reference.type.value, value, options);
|
|
24
27
|
case NodeKind.NestedEntityMapType:
|
|
25
28
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
26
|
-
? sortObjectKeys(mapObject(value, item => formatValue(type.type.value, item)))
|
|
29
|
+
? sortObjectKeys(mapObject(value, item => formatValue(type.type.value, item, options)))
|
|
27
30
|
: value;
|
|
28
31
|
case NodeKind.EnumType: {
|
|
29
32
|
if (typeof value === "object" &&
|
|
@@ -38,7 +41,7 @@ export const formatValue = (type, value) => {
|
|
|
38
41
|
[ENUM_DISCRIMINATOR_KEY]: caseName,
|
|
39
42
|
...(caseValue == null || caseType == null
|
|
40
43
|
? {}
|
|
41
|
-
: { [caseName]: formatValue(caseType, caseValue) }),
|
|
44
|
+
: { [caseName]: formatValue(caseType, caseValue, options) }),
|
|
42
45
|
};
|
|
43
46
|
}
|
|
44
47
|
return value;
|
|
@@ -47,11 +50,25 @@ export const formatValue = (type, value) => {
|
|
|
47
50
|
return Array.isArray(value) ? value.toSorted() : value;
|
|
48
51
|
case NodeKind.TranslationObjectType:
|
|
49
52
|
return formatTranslationObjectValue(type.properties, value);
|
|
53
|
+
case NodeKind.StringType: {
|
|
54
|
+
if (options?.markdown === false) {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
switch (type.markdown) {
|
|
58
|
+
case "inline":
|
|
59
|
+
return typeof value === "string" ? formatMarkdownInline(value.trim()) : value;
|
|
60
|
+
case "block":
|
|
61
|
+
return typeof value === "string"
|
|
62
|
+
? formatMarkdown(value.trimEnd(), options?.markdown)
|
|
63
|
+
: value;
|
|
64
|
+
default:
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
50
68
|
case NodeKind.BooleanType:
|
|
51
69
|
case NodeKind.DateType:
|
|
52
70
|
case NodeKind.FloatType:
|
|
53
71
|
case NodeKind.IntegerType:
|
|
54
|
-
case NodeKind.StringType:
|
|
55
72
|
case NodeKind.TypeArgumentType:
|
|
56
73
|
case NodeKind.ReferenceIdentifierType:
|
|
57
74
|
return value;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HomeLayoutSection } from "../config.ts";
|
|
1
|
+
import type { HomeLayoutSection, ServerConfig } from "../config.ts";
|
|
2
2
|
import type { TSONDB, ValidationOptions } from "../index.ts";
|
|
3
3
|
export type ServerOptions = {
|
|
4
4
|
port: number;
|
|
@@ -16,4 +16,4 @@ declare global {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
export declare const createServer: (db: TSONDB,
|
|
19
|
+
export declare const createServer: (db: TSONDB, config: Omit<ServerConfig, "locales">) => void;
|
|
@@ -15,7 +15,8 @@ const staticNodeModule = (moduleName) => {
|
|
|
15
15
|
}
|
|
16
16
|
return express.static(dirname(pathToPackageJson));
|
|
17
17
|
};
|
|
18
|
-
export const createServer = (db,
|
|
18
|
+
export const createServer = (db, config) => {
|
|
19
|
+
const { serverOptions: options, homeLayoutSections, validationOptions, customAssetsPath, customStylesheetPath, } = config;
|
|
19
20
|
const { port } = { ...defaultOptions, ...options };
|
|
20
21
|
const app = express();
|
|
21
22
|
app.use(express.static(join(import.meta.dirname, "../../../../public")));
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import type { FormatterOptions } from "../config.ts";
|
|
1
2
|
import type { TransactionStep } from "../transaction.ts";
|
|
2
|
-
export declare const applyStepsToDisk: (root: string, steps: TransactionStep[]) => Promise<import("@elyukai/utils/result").Error<Error> | import("@elyukai/utils/result").Ok<void>>;
|
|
3
|
+
export declare const applyStepsToDisk: (root: string, steps: TransactionStep[], formatterOptions: Partial<FormatterOptions> | undefined) => Promise<import("@elyukai/utils/result").Error<Error> | import("@elyukai/utils/result").Ok<void>>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { error, isError, ok } from "@elyukai/utils/result";
|
|
2
2
|
import * as DatabaseFilesystem from "./files.js";
|
|
3
|
-
const setInstanceOnDisk = async (root, entity, instanceId, instanceContent) => {
|
|
3
|
+
const setInstanceOnDisk = async (root, entity, instanceId, instanceContent, formatterOptions) => {
|
|
4
4
|
try {
|
|
5
|
-
await DatabaseFilesystem.writeInstance(root, entity, instanceId, instanceContent);
|
|
5
|
+
await DatabaseFilesystem.writeInstance(root, entity, instanceId, instanceContent, formatterOptions);
|
|
6
6
|
return ok();
|
|
7
7
|
}
|
|
8
8
|
catch (e) {
|
|
@@ -18,41 +18,41 @@ const deleteInstanceOnDisk = async (root, entityName, instanceId) => {
|
|
|
18
18
|
return error(e);
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
|
-
const rollbackChanges = async (root, steps) => {
|
|
21
|
+
const rollbackChanges = async (root, steps, formatterOptions) => {
|
|
22
22
|
for (const step of steps) {
|
|
23
|
-
const res = await runReverseStepAction(root, step);
|
|
23
|
+
const res = await runReverseStepAction(root, step, formatterOptions);
|
|
24
24
|
if (isError(res)) {
|
|
25
25
|
return res;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
return ok();
|
|
29
29
|
};
|
|
30
|
-
const runStepAction = (root, step) => {
|
|
30
|
+
const runStepAction = (root, step, formatterOptions) => {
|
|
31
31
|
switch (step.kind) {
|
|
32
32
|
case "create":
|
|
33
33
|
case "update":
|
|
34
|
-
return setInstanceOnDisk(root, step.entity, step.instanceId, step.instanceContent);
|
|
34
|
+
return setInstanceOnDisk(root, step.entity, step.instanceId, step.instanceContent, formatterOptions);
|
|
35
35
|
case "delete":
|
|
36
36
|
return deleteInstanceOnDisk(root, step.entity.name, step.instanceId);
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
|
-
const runReverseStepAction = (root, step) => {
|
|
39
|
+
const runReverseStepAction = (root, step, formatterOptions) => {
|
|
40
40
|
switch (step.kind) {
|
|
41
41
|
case "create":
|
|
42
42
|
return deleteInstanceOnDisk(root, step.entity.name, step.instanceId);
|
|
43
43
|
case "update":
|
|
44
|
-
return setInstanceOnDisk(root, step.entity, step.instanceId, step.oldInstance);
|
|
44
|
+
return setInstanceOnDisk(root, step.entity, step.instanceId, step.oldInstance, formatterOptions);
|
|
45
45
|
case "delete":
|
|
46
|
-
return setInstanceOnDisk(root, step.entity, step.instanceId, step.oldInstance);
|
|
46
|
+
return setInstanceOnDisk(root, step.entity, step.instanceId, step.oldInstance, formatterOptions);
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
-
export const applyStepsToDisk = async (root, steps) => {
|
|
49
|
+
export const applyStepsToDisk = async (root, steps, formatterOptions) => {
|
|
50
50
|
for (let i = 0; i < steps.length; i++) {
|
|
51
51
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
52
52
|
const step = steps[i];
|
|
53
|
-
const res = await runStepAction(root, step);
|
|
53
|
+
const res = await runStepAction(root, step, formatterOptions);
|
|
54
54
|
if (isError(res)) {
|
|
55
|
-
await rollbackChanges(root, steps.slice(0, i));
|
|
55
|
+
await rollbackChanges(root, steps.slice(0, i), formatterOptions);
|
|
56
56
|
return res;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { InstanceContent } from "../../shared/utils/instances.ts";
|
|
2
|
+
import type { FormatterOptions } from "../config.ts";
|
|
2
3
|
import { type EntityDecl } from "../schema/dsl/index.ts";
|
|
3
4
|
export declare const getFileNameForId: (id: string) => string;
|
|
4
5
|
export declare const getPathToInstance: (dataRoot: string, entityName: string, id: string) => string;
|
|
5
|
-
export declare const writeInstance: (dataRoot: string, entity: EntityDecl, id: string, instance: InstanceContent) => Promise<void>;
|
|
6
|
+
export declare const writeInstance: (dataRoot: string, entity: EntityDecl, id: string, instance: InstanceContent, formatterOptions: Partial<FormatterOptions> | undefined) => Promise<void>;
|
|
6
7
|
export declare const deleteInstance: (dataRoot: string, entityName: string, id: string) => Promise<void>;
|
|
7
|
-
export declare const formatInstance: (entity: EntityDecl, instanceContent: InstanceContent) => string;
|
|
8
|
+
export declare const formatInstance: (entity: EntityDecl, instanceContent: InstanceContent, options: Partial<FormatterOptions> | undefined) => string;
|
|
@@ -4,8 +4,8 @@ import {} from "../schema/dsl/index.js";
|
|
|
4
4
|
import { formatValue } from "../schema/treeOperations/format.js";
|
|
5
5
|
export const getFileNameForId = (id) => `${id}.json`;
|
|
6
6
|
export const getPathToInstance = (dataRoot, entityName, id) => join(dataRoot, entityName, getFileNameForId(id));
|
|
7
|
-
export const writeInstance = (dataRoot, entity, id, instance) => writeFile(getPathToInstance(dataRoot, entity.name, id), formatInstance(entity, instance), {
|
|
7
|
+
export const writeInstance = (dataRoot, entity, id, instance, formatterOptions) => writeFile(getPathToInstance(dataRoot, entity.name, id), formatInstance(entity, instance, formatterOptions), {
|
|
8
8
|
encoding: "utf-8",
|
|
9
9
|
});
|
|
10
10
|
export const deleteInstance = (dataRoot, entityName, id) => rm(getPathToInstance(dataRoot, entityName, id));
|
|
11
|
-
export const formatInstance = (entity, instanceContent) => JSON.stringify(formatValue(entity.type.value, instanceContent), undefined, 2) + "\n";
|
|
11
|
+
export const formatInstance = (entity, instanceContent, options) => JSON.stringify(formatValue(entity.type.value, instanceContent, options), undefined, 2) + "\n";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { MarkdownStringOption } from "../schema/types/StringType.ts";
|
|
1
2
|
export interface StringConstraints {
|
|
2
3
|
minLength?: number;
|
|
3
4
|
maxLength?: number;
|
|
4
5
|
pattern?: string | RegExp;
|
|
6
|
+
markdown?: MarkdownStringOption;
|
|
5
7
|
}
|
|
6
8
|
export declare const validateStringConstraints: (constraints: StringConstraints, value: string) => Error[];
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { validate, validateInline } from "@elyukai/markdown/validate";
|
|
2
|
+
import { assertExhaustive } from "@elyukai/utils/typeSafety";
|
|
1
3
|
import { parallelizeErrors } from "../utils/validation.js";
|
|
2
4
|
export const validateStringConstraints = (constraints, value) => parallelizeErrors([
|
|
3
5
|
constraints.minLength !== undefined && value.length < constraints.minLength
|
|
@@ -17,4 +19,17 @@ export const validateStringConstraints = (constraints, value) => parallelizeErro
|
|
|
17
19
|
? TypeError(`string does not match the pattern ${pattern.toString()}`)
|
|
18
20
|
: undefined;
|
|
19
21
|
})(),
|
|
22
|
+
(() => {
|
|
23
|
+
if (constraints.markdown === undefined) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
switch (constraints.markdown) {
|
|
27
|
+
case "inline":
|
|
28
|
+
return validateInline(value);
|
|
29
|
+
case "block":
|
|
30
|
+
return validate(value);
|
|
31
|
+
default:
|
|
32
|
+
return assertExhaustive(constraints.markdown);
|
|
33
|
+
}
|
|
34
|
+
})(),
|
|
20
35
|
]);
|
package/dist/src/web/web.js
CHANGED
|
@@ -682,7 +682,7 @@ const request = async (method, url, options)=>{
|
|
|
682
682
|
if (!response.ok) throw new Error(await response.text());
|
|
683
683
|
return (options.getResult ?? ((response)=>{
|
|
684
684
|
if (response.headers.get("Content-Type")?.startsWith("application/json") === true) return response.json();
|
|
685
|
-
return Promise.resolve();
|
|
685
|
+
else return Promise.resolve();
|
|
686
686
|
}))(response);
|
|
687
687
|
};
|
|
688
688
|
const api_getResource = async (url, options)=>request("GET", url, options);
|
|
@@ -897,8 +897,7 @@ function signals_core_module_a(i) {
|
|
|
897
897
|
i.n = t;
|
|
898
898
|
if (32 & signals_core_module_o.f) i.S(t);
|
|
899
899
|
return t;
|
|
900
|
-
}
|
|
901
|
-
if (-1 === t.i) {
|
|
900
|
+
} else if (-1 === t.i) {
|
|
902
901
|
t.i = 0;
|
|
903
902
|
if (void 0 !== t.n) {
|
|
904
903
|
t.n.p = t.p;
|
|
@@ -2410,7 +2409,7 @@ const splitStringParts = (str)=>[
|
|
|
2410
2409
|
...acc.slice(0, -1),
|
|
2411
2410
|
lastPart + char
|
|
2412
2411
|
];
|
|
2413
|
-
return [
|
|
2412
|
+
else return [
|
|
2414
2413
|
...acc,
|
|
2415
2414
|
char
|
|
2416
2415
|
];
|
|
@@ -2420,7 +2419,7 @@ const splitStringParts = (str)=>[
|
|
|
2420
2419
|
lastPart + char
|
|
2421
2420
|
];
|
|
2422
2421
|
if ("" === lastPart) return acc;
|
|
2423
|
-
return [
|
|
2422
|
+
else return [
|
|
2424
2423
|
...acc,
|
|
2425
2424
|
""
|
|
2426
2425
|
];
|
|
@@ -2453,7 +2452,7 @@ const getSerializedDisplayNameFromEntityInstance = (entity, instance, defaultNam
|
|
|
2453
2452
|
name: getValueAtPath(instance, displayNamePath) ?? defaultName,
|
|
2454
2453
|
localeId: locales[0]
|
|
2455
2454
|
};
|
|
2456
|
-
{
|
|
2455
|
+
else {
|
|
2457
2456
|
const localeMapPath = displayNamePath.pathToLocaleMap ?? "translations";
|
|
2458
2457
|
const localeMap = getValueAtPath(instance, localeMapPath);
|
|
2459
2458
|
const pathInLocaleMap = displayNamePath.pathInLocaleMap ?? "name";
|
|
@@ -2467,6 +2466,7 @@ const getSerializedDisplayNameFromEntityInstance = (entity, instance, defaultNam
|
|
|
2467
2466
|
name: possibleName,
|
|
2468
2467
|
localeId: locale
|
|
2469
2468
|
};
|
|
2469
|
+
else return;
|
|
2470
2470
|
}, void 0) ?? {
|
|
2471
2471
|
name: getValueAtPath(localeMap[availableLocales[0]], pathInLocaleMap) ?? defaultName
|
|
2472
2472
|
};
|
|
@@ -3281,7 +3281,7 @@ const asText = (content)=>({
|
|
|
3281
3281
|
type: "text",
|
|
3282
3282
|
content
|
|
3283
3283
|
});
|
|
3284
|
-
const inline_text = (syntaxStartCharacters)=>anyStopOn(syntaxStartCharacters).then((result)=>
|
|
3284
|
+
const inline_text = (syntaxStartCharacters)=>anyStopOn(syntaxStartCharacters).then((result)=>StateTParser.getsT((state)=>state.keepSyntax || state.preserveEscapes).map((keepSyntax)=>asText(keepSyntax ? result : parseEscapedCharacters(result))));
|
|
3285
3285
|
const codeDelimiter = state_syntax(StateTParser.string("`"));
|
|
3286
3286
|
const codeText = anyStopOn([
|
|
3287
3287
|
"`"
|
|
@@ -3404,10 +3404,11 @@ const inlineNode = combineParsers([
|
|
|
3404
3404
|
leafParser(defaultInlineSyntaxStartSequences)
|
|
3405
3405
|
]);
|
|
3406
3406
|
const inlineMarkdown = inlineNode.many();
|
|
3407
|
-
const _parseInlineMarkdown = (syntax, keepSyntax = false)=>{
|
|
3407
|
+
const _parseInlineMarkdown = (syntax, keepSyntax = false, preserveEscapes = false)=>{
|
|
3408
3408
|
const results = inlineMarkdown.evalT({
|
|
3409
3409
|
indentation: 0,
|
|
3410
|
-
keepSyntax
|
|
3410
|
+
keepSyntax,
|
|
3411
|
+
preserveEscapes
|
|
3411
3412
|
}).parse(syntax);
|
|
3412
3413
|
if (!isNotEmpty(results)) throw new Error("Failed to parse");
|
|
3413
3414
|
const [result, remaining] = results[0];
|
|
@@ -3750,7 +3751,7 @@ const sectionRows = (rows)=>{
|
|
|
3750
3751
|
}
|
|
3751
3752
|
return acc;
|
|
3752
3753
|
}, []);
|
|
3753
|
-
return rows.map((row)=>({
|
|
3754
|
+
else return rows.map((row)=>({
|
|
3754
3755
|
type: "tableRow",
|
|
3755
3756
|
cells: row.map(mapCellObj)
|
|
3756
3757
|
}));
|
|
@@ -3789,10 +3790,11 @@ const finalBlockMarkdownSyntax = anyBlankLines.then((leadingsNewlines)=>blockMar
|
|
|
3789
3790
|
content: "\n"
|
|
3790
3791
|
} : node)
|
|
3791
3792
|
])));
|
|
3792
|
-
const
|
|
3793
|
+
const _parseBlockMarkdown = (syntax, keepSyntax = false, preserveEscapes = false)=>{
|
|
3793
3794
|
const results = finalBlockMarkdown.evalT({
|
|
3794
3795
|
indentation: 0,
|
|
3795
|
-
keepSyntax
|
|
3796
|
+
keepSyntax,
|
|
3797
|
+
preserveEscapes
|
|
3796
3798
|
}).parse(syntax);
|
|
3797
3799
|
if (!isNotEmpty(results)) throw new Error("Failed to parse");
|
|
3798
3800
|
const [result = [], remaining] = results[0];
|
|
@@ -3810,10 +3812,12 @@ const parseBlockMarkdown = (syntax)=>{
|
|
|
3810
3812
|
];
|
|
3811
3813
|
return result;
|
|
3812
3814
|
};
|
|
3815
|
+
const parseBlockMarkdown = (syntax)=>_parseBlockMarkdown(syntax);
|
|
3813
3816
|
const parseBlockMarkdownForSyntaxHighlighting = (syntax)=>{
|
|
3814
3817
|
const results = finalBlockMarkdownSyntax.evalT({
|
|
3815
3818
|
indentation: 0,
|
|
3816
|
-
keepSyntax: true
|
|
3819
|
+
keepSyntax: true,
|
|
3820
|
+
preserveEscapes: false
|
|
3817
3821
|
}).parse(syntax);
|
|
3818
3822
|
if (!isNotEmpty(results)) throw new Error("Failed to parse");
|
|
3819
3823
|
const [result, remaining] = results[0];
|
|
@@ -5440,6 +5444,59 @@ const ReferenceIdentifierTypeInput = ({ type, value, instanceNamesByEntity, disa
|
|
|
5440
5444
|
]
|
|
5441
5445
|
});
|
|
5442
5446
|
};
|
|
5447
|
+
class InternalMarkdownError extends Error {
|
|
5448
|
+
constructor(message){
|
|
5449
|
+
super(message);
|
|
5450
|
+
this.name = "InternalMarkdownError";
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
class MarkdownSyntaxError extends SyntaxError {
|
|
5454
|
+
row;
|
|
5455
|
+
column;
|
|
5456
|
+
remainingText;
|
|
5457
|
+
constructor(row, column, remainingText){
|
|
5458
|
+
super(`Unexpected Markdown syntax at row ${row.toFixed()}, column ${column.toFixed()}: ${remainingText}`);
|
|
5459
|
+
this.name = "MarkdownSyntaxError";
|
|
5460
|
+
this.row = row;
|
|
5461
|
+
this.column = column;
|
|
5462
|
+
this.remainingText = remainingText;
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
const positionInStringToColumnAndRow = (position, string)=>{
|
|
5466
|
+
const lines = string.slice(0, position).split("\n");
|
|
5467
|
+
const row = lines.length;
|
|
5468
|
+
const column = (lines.at(-1)?.length ?? 0) + 1;
|
|
5469
|
+
return {
|
|
5470
|
+
row,
|
|
5471
|
+
column
|
|
5472
|
+
};
|
|
5473
|
+
};
|
|
5474
|
+
const validateInline = (markdown)=>{
|
|
5475
|
+
const results = inlineMarkdown.evalT({
|
|
5476
|
+
indentation: 0,
|
|
5477
|
+
keepSyntax: false,
|
|
5478
|
+
preserveEscapes: false
|
|
5479
|
+
}).parse(markdown);
|
|
5480
|
+
if (!isNotEmpty(results)) return new InternalMarkdownError("Failed to parse inline Markdown");
|
|
5481
|
+
const [_, remaining] = results[0];
|
|
5482
|
+
if (remaining.length > 0) {
|
|
5483
|
+
const { row, column } = positionInStringToColumnAndRow(markdown.length - remaining.length, markdown);
|
|
5484
|
+
return new MarkdownSyntaxError(row, column, remaining);
|
|
5485
|
+
}
|
|
5486
|
+
};
|
|
5487
|
+
const validate = (markdown)=>{
|
|
5488
|
+
const results = finalBlockMarkdown.evalT({
|
|
5489
|
+
indentation: 0,
|
|
5490
|
+
keepSyntax: false,
|
|
5491
|
+
preserveEscapes: false
|
|
5492
|
+
}).parse(markdown);
|
|
5493
|
+
if (!isNotEmpty(results)) return new InternalMarkdownError("Failed to parse Markdown");
|
|
5494
|
+
const [_, remaining] = results[0];
|
|
5495
|
+
if (remaining.length > 0) {
|
|
5496
|
+
const { row, column } = positionInStringToColumnAndRow(markdown.length - remaining.length, markdown);
|
|
5497
|
+
return new MarkdownSyntaxError(row, column, remaining);
|
|
5498
|
+
}
|
|
5499
|
+
};
|
|
5443
5500
|
const validateStringConstraints = (constraints, value)=>parallelizeErrors([
|
|
5444
5501
|
void 0 !== constraints.minLength && value.length < constraints.minLength ? RangeError(`expected a string with at least ${constraints.minLength.toString()} character${1 === constraints.minLength ? "" : "s"}, but got ${value.length.toString()} character${1 === value.length ? "" : "s"}`) : void 0,
|
|
5445
5502
|
void 0 !== constraints.maxLength && value.length > constraints.maxLength ? RangeError(`expected a string with at most ${constraints.maxLength.toString()} character${1 === constraints.maxLength ? "" : "s"}, but got ${value.length.toString()} character${1 === value.length ? "" : "s"}`) : void 0,
|
|
@@ -5447,13 +5504,24 @@ const validateStringConstraints = (constraints, value)=>parallelizeErrors([
|
|
|
5447
5504
|
if (void 0 === constraints.pattern) return;
|
|
5448
5505
|
const pattern = "string" == typeof constraints.pattern ? new RegExp(constraints.pattern) : constraints.pattern;
|
|
5449
5506
|
return pattern.test(value) ? void 0 : TypeError(`string does not match the pattern ${pattern.toString()}`);
|
|
5507
|
+
})(),
|
|
5508
|
+
(()=>{
|
|
5509
|
+
if (void 0 === constraints.markdown) return;
|
|
5510
|
+
switch(constraints.markdown){
|
|
5511
|
+
case "inline":
|
|
5512
|
+
return validateInline(value);
|
|
5513
|
+
case "block":
|
|
5514
|
+
return validate(value);
|
|
5515
|
+
default:
|
|
5516
|
+
return typeSafety_assertExhaustive(constraints.markdown);
|
|
5517
|
+
}
|
|
5450
5518
|
})()
|
|
5451
5519
|
]);
|
|
5452
5520
|
const BlockMarkdownHighlighting = ({ node })=>{
|
|
5453
5521
|
if ("syntax" !== node.type) return /*#__PURE__*/ jsxRuntime_module_u(InlineMarkdown, {
|
|
5454
5522
|
node: node
|
|
5455
5523
|
});
|
|
5456
|
-
switch(node.blockType){
|
|
5524
|
+
else switch(node.blockType){
|
|
5457
5525
|
case "heading":
|
|
5458
5526
|
return /*#__PURE__*/ jsxRuntime_module_u("span", {
|
|
5459
5527
|
class: "heading-marker",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsondb",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "Lukas Obermann",
|
|
@@ -37,23 +37,23 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@eslint/js": "^9.39.2",
|
|
39
39
|
"@rsbuild/plugin-preact": "^2.1.0",
|
|
40
|
-
"@rslib/core": "^1.0.
|
|
40
|
+
"@rslib/core": "^1.0.1",
|
|
41
41
|
"@types/debug": "^4.1.13",
|
|
42
42
|
"@types/express": "^5.0.6",
|
|
43
|
-
"@types/node": "^
|
|
43
|
+
"@types/node": "^26.6.2",
|
|
44
44
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
45
|
-
"commit-and-tag-version": "^13.2.
|
|
45
|
+
"commit-and-tag-version": "^13.2.1",
|
|
46
46
|
"eslint": "^9.39.4",
|
|
47
47
|
"eslint-plugin-react": "^7.37.5",
|
|
48
48
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
49
49
|
"globals": "^17.12.0",
|
|
50
|
-
"prettier": "^3.9.
|
|
51
|
-
"tsx": "^4.23.
|
|
50
|
+
"prettier": "^3.9.8",
|
|
51
|
+
"tsx": "^4.23.15",
|
|
52
52
|
"typescript": "npm:@typescript/typescript6@^6.0.2",
|
|
53
|
-
"typescript-eslint": "^8.70.
|
|
53
|
+
"typescript-eslint": "^8.70.1"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@elyukai/markdown": "^0.3.
|
|
56
|
+
"@elyukai/markdown": "^0.3.4",
|
|
57
57
|
"@elyukai/utils": "^0.3.6",
|
|
58
58
|
"@preact/signals": "^2.11.2",
|
|
59
59
|
"debug": "^4.4.3",
|
package/public/css/styles.css
CHANGED
|
@@ -244,6 +244,14 @@ button {
|
|
|
244
244
|
cursor: pointer;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
@media (any-pointer: fine) {
|
|
248
|
+
a.btn,
|
|
249
|
+
button {
|
|
250
|
+
padding: 0.25rem 0.5rem;
|
|
251
|
+
font-size: 0.9rem;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
247
255
|
a.btn:hover,
|
|
248
256
|
button:hover {
|
|
249
257
|
background: var(--color-button-background-hover);
|
|
@@ -255,7 +263,6 @@ a.btn--primary,
|
|
|
255
263
|
button.primary {
|
|
256
264
|
background: var(--color-button-background-primary);
|
|
257
265
|
color: var(--color-button-foreground-primary);
|
|
258
|
-
padding: 0.5rem 1rem;
|
|
259
266
|
border: none;
|
|
260
267
|
}
|
|
261
268
|
|
|
@@ -276,7 +283,6 @@ a.btn--destructive,
|
|
|
276
283
|
button.destructive {
|
|
277
284
|
background: var(--color-button-background-destructive);
|
|
278
285
|
color: var(--color-button-foreground-destructive);
|
|
279
|
-
padding: 0.5rem 1rem;
|
|
280
286
|
border: none;
|
|
281
287
|
}
|
|
282
288
|
|
|
@@ -316,6 +322,16 @@ select,
|
|
|
316
322
|
border-radius: 0;
|
|
317
323
|
}
|
|
318
324
|
|
|
325
|
+
@media (any-pointer: fine) {
|
|
326
|
+
input,
|
|
327
|
+
textarea,
|
|
328
|
+
select,
|
|
329
|
+
.textarea-grow-wrap__mirror {
|
|
330
|
+
padding: 0.25rem 0.375rem;
|
|
331
|
+
font-size: 0.9rem;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
319
335
|
input::placeholder,
|
|
320
336
|
textarea::placeholder,
|
|
321
337
|
select::placeholder {
|
|
@@ -396,7 +412,7 @@ input[type="checkbox"]:checked::before {
|
|
|
396
412
|
height: 0.4rem;
|
|
397
413
|
border-color: var(--color-background-primary);
|
|
398
414
|
transform: rotate(-45deg);
|
|
399
|
-
margin:
|
|
415
|
+
margin: -0.15rem 0 0 -0.325rem;
|
|
400
416
|
}
|
|
401
417
|
|
|
402
418
|
input[type="radio"]:checked::before {
|
|
@@ -515,6 +531,13 @@ select {
|
|
|
515
531
|
container-type: inline-size;
|
|
516
532
|
}
|
|
517
533
|
|
|
534
|
+
@supports (grid-template-rows: subgrid) {
|
|
535
|
+
.entries {
|
|
536
|
+
display: grid;
|
|
537
|
+
grid-template-columns: 1fr auto auto;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
518
541
|
.entries-item {
|
|
519
542
|
display: grid;
|
|
520
543
|
gap: 0.125rem 1.5rem;
|
|
@@ -527,6 +550,20 @@ select {
|
|
|
527
550
|
--text-padding: 0.5rem;
|
|
528
551
|
}
|
|
529
552
|
|
|
553
|
+
@supports (grid-template-rows: subgrid) {
|
|
554
|
+
.entries-item {
|
|
555
|
+
grid-template-columns: subgrid;
|
|
556
|
+
grid-column: 1 / -1;
|
|
557
|
+
grid-template-areas: "title side side" "subtitle side side" ". side side";
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
@media (any-pointer: fine) {
|
|
562
|
+
.entries-item {
|
|
563
|
+
padding: 0.0625rem 0;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
530
567
|
@container (min-width: 40rem) {
|
|
531
568
|
.entries-item {
|
|
532
569
|
grid-template-columns: 1fr auto auto;
|
|
@@ -534,6 +571,12 @@ select {
|
|
|
534
571
|
grid-template-areas: "title subtitle side";
|
|
535
572
|
align-items: center;
|
|
536
573
|
}
|
|
574
|
+
|
|
575
|
+
@supports (grid-template-rows: subgrid) {
|
|
576
|
+
.entries-item {
|
|
577
|
+
grid-template-columns: subgrid;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
537
580
|
}
|
|
538
581
|
|
|
539
582
|
.entries-item:first-child {
|
|
@@ -607,6 +650,7 @@ select {
|
|
|
607
650
|
grid-area: side;
|
|
608
651
|
display: flex;
|
|
609
652
|
align-items: center;
|
|
653
|
+
justify-content: end;
|
|
610
654
|
gap: 1rem;
|
|
611
655
|
}
|
|
612
656
|
|
|
@@ -673,6 +717,11 @@ form > .field--container {
|
|
|
673
717
|
border: none;
|
|
674
718
|
}
|
|
675
719
|
|
|
720
|
+
.field--container > .empty {
|
|
721
|
+
font-size: 0.8rem;
|
|
722
|
+
color: var(--color-foreground-tertiary);
|
|
723
|
+
}
|
|
724
|
+
|
|
676
725
|
.field--container > ul,
|
|
677
726
|
.field--container > ol {
|
|
678
727
|
margin: 0;
|
|
@@ -690,6 +739,13 @@ form > .field--container {
|
|
|
690
739
|
grid-column: 1 / 4;
|
|
691
740
|
}
|
|
692
741
|
|
|
742
|
+
@media (any-pointer: fine) {
|
|
743
|
+
.field--container > ul > li,
|
|
744
|
+
.field--container > ol > li {
|
|
745
|
+
padding-block: 0.25rem;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
693
749
|
.field--child-entities,
|
|
694
750
|
.field--child-entities > ul > li,
|
|
695
751
|
.field--child-entities > ol > li {
|
|
@@ -937,6 +993,13 @@ form > .field--container {
|
|
|
937
993
|
align-self: center;
|
|
938
994
|
}
|
|
939
995
|
|
|
996
|
+
@media (any-pointer: fine) {
|
|
997
|
+
.container-item-title {
|
|
998
|
+
font-size: 0.9rem;
|
|
999
|
+
padding-block: 0.25rem;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
940
1003
|
.container-item-title .id {
|
|
941
1004
|
color: var(--color-foreground-tertiary);
|
|
942
1005
|
font-size: 0.8rem;
|
|
@@ -947,6 +1010,12 @@ form > .field--container {
|
|
|
947
1010
|
margin: 0.125rem 0 0;
|
|
948
1011
|
}
|
|
949
1012
|
|
|
1013
|
+
@media (any-pointer: fine) {
|
|
1014
|
+
.container-item-title .comment {
|
|
1015
|
+
margin: 0.25rem 0 0;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
950
1019
|
.container-item-actions {
|
|
951
1020
|
flex: none;
|
|
952
1021
|
display: flex;
|
|
@@ -1002,6 +1071,21 @@ button[type="submit"] {
|
|
|
1002
1071
|
margin: 0.35rem 0 0;
|
|
1003
1072
|
}
|
|
1004
1073
|
|
|
1074
|
+
@media (any-pointer: fine) {
|
|
1075
|
+
.comment {
|
|
1076
|
+
font-size: 0.75rem;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
.comment p {
|
|
1080
|
+
margin: 0.25rem 0 0;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
.comment ul {
|
|
1084
|
+
padding-left: 1.25rem;
|
|
1085
|
+
margin: 0.25rem 0 0;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1005
1089
|
main:has(.form-footer) {
|
|
1006
1090
|
margin-bottom: -1.5rem;
|
|
1007
1091
|
}
|