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.
@@ -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.homeLayoutSections, config.serverOptions, config.validationOptions, config.customStylesheetPath, config.customAssetsPath);
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.
@@ -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.
@@ -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) ? value.map(item => formatValue(type.items, item)) : 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, homeLayoutSections?: HomeLayoutSection[], options?: Partial<ServerOptions>, validationOptions?: Partial<ValidationOptions>, customStylesheetPath?: string, customAssetsPath?: string) => void;
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, homeLayoutSections, options, validationOptions, customStylesheetPath, customAssetsPath) => {
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
  ]);
@@ -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)=>getSyntaxSetting.map((keepSyntax)=>asText(keepSyntax ? result : parseEscapedCharacters(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 parseBlockMarkdown = (syntax)=>{
3793
+ const _parseBlockMarkdown = (syntax, keepSyntax = false, preserveEscapes = false)=>{
3793
3794
  const results = finalBlockMarkdown.evalT({
3794
3795
  indentation: 0,
3795
- keepSyntax: false
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.5",
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.0",
40
+ "@rslib/core": "^1.0.1",
41
41
  "@types/debug": "^4.1.13",
42
42
  "@types/express": "^5.0.6",
43
- "@types/node": "^22.20.2",
43
+ "@types/node": "^26.6.2",
44
44
  "@typescript/native": "npm:typescript@^7.0.2",
45
- "commit-and-tag-version": "^13.2.0",
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.6",
51
- "tsx": "^4.23.13",
50
+ "prettier": "^3.9.8",
51
+ "tsx": "^4.23.15",
52
52
  "typescript": "npm:@typescript/typescript6@^6.0.2",
53
- "typescript-eslint": "^8.70.0"
53
+ "typescript-eslint": "^8.70.1"
54
54
  },
55
55
  "dependencies": {
56
- "@elyukai/markdown": "^0.3.2",
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",
@@ -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: calc(-0.325rem) 0 0 calc(-0.4rem);
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
  }