tsondb 0.5.10 → 0.5.13

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.
Files changed (49) hide show
  1. package/dist/src/node/index.js +2 -2
  2. package/dist/src/node/schema/Node.d.ts +2 -1
  3. package/dist/src/node/schema/Node.js +3 -2
  4. package/dist/src/node/schema/types/generic/ArrayType.js +2 -2
  5. package/dist/src/node/schema/types/generic/EnumType.js +7 -5
  6. package/dist/src/node/schema/types/generic/ObjectType.js +3 -3
  7. package/dist/src/node/schema/types/primitives/BooleanType.js +2 -2
  8. package/dist/src/node/schema/types/primitives/DateType.js +2 -2
  9. package/dist/src/node/schema/types/primitives/FloatType.js +4 -2
  10. package/dist/src/node/schema/types/primitives/IntegerType.js +2 -2
  11. package/dist/src/node/schema/types/primitives/StringType.js +2 -2
  12. package/dist/src/node/schema/types/references/NestedEntityMapType.js +2 -2
  13. package/dist/src/node/server/api/instanceOperations.js +2 -2
  14. package/dist/src/node/utils/errorFormatting.d.ts +3 -3
  15. package/dist/src/node/utils/errorFormatting.js +5 -3
  16. package/dist/src/web/components/typeInputs/ArrayTypeInput.d.ts +2 -1
  17. package/dist/src/web/components/typeInputs/ArrayTypeInput.js +7 -3
  18. package/dist/src/web/components/typeInputs/BooleanTypeInput.d.ts +1 -1
  19. package/dist/src/web/components/typeInputs/BooleanTypeInput.js +4 -0
  20. package/dist/src/web/components/typeInputs/DateTypeInput.d.ts +1 -1
  21. package/dist/src/web/components/typeInputs/DateTypeInput.js +4 -0
  22. package/dist/src/web/components/typeInputs/EnumTypeInput.d.ts +1 -0
  23. package/dist/src/web/components/typeInputs/EnumTypeInput.js +2 -2
  24. package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +1 -1
  25. package/dist/src/web/components/typeInputs/FloatTypeInput.js +5 -1
  26. package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.d.ts +1 -0
  27. package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.js +2 -2
  28. package/dist/src/web/components/typeInputs/IntegerTypeInput.d.ts +1 -1
  29. package/dist/src/web/components/typeInputs/IntegerTypeInput.js +5 -1
  30. package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.d.ts +2 -1
  31. package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.js +6 -2
  32. package/dist/src/web/components/typeInputs/ObjectTypeInput.d.ts +2 -1
  33. package/dist/src/web/components/typeInputs/ObjectTypeInput.js +8 -3
  34. package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.d.ts +1 -1
  35. package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.js +4 -0
  36. package/dist/src/web/components/typeInputs/StringTypeInput.d.ts +1 -1
  37. package/dist/src/web/components/typeInputs/StringTypeInput.js +4 -0
  38. package/dist/src/web/components/typeInputs/TypeInput.d.ts +1 -0
  39. package/dist/src/web/components/typeInputs/TypeInput.js +14 -60
  40. package/dist/src/web/routes/CreateInstance.js +3 -2
  41. package/dist/src/web/routes/Entity.js +2 -1
  42. package/dist/src/web/routes/Home.d.ts +1 -0
  43. package/dist/src/web/routes/Home.js +3 -2
  44. package/dist/src/web/routes/Instance.js +6 -5
  45. package/dist/src/web/routes/NotFound.js +2 -1
  46. package/dist/src/web/utils/debug.d.ts +1 -0
  47. package/dist/src/web/utils/debug.js +5 -0
  48. package/package.json +1 -1
  49. package/public/css/styles.css +8 -4
@@ -23,7 +23,7 @@ export const generateOutputs = async (schema, outputs) => {
23
23
  }
24
24
  };
25
25
  const _validate = (dataRootPath, entities, instancesByEntityName, options = {}) => {
26
- const { checkReferentialIntegrity = false, checkOnlyEntities = [] } = options;
26
+ const { checkReferentialIntegrity = true, checkOnlyEntities = [] } = options;
27
27
  for (const onlyEntity of checkOnlyEntities) {
28
28
  if (!entities.find(entity => entity.name === onlyEntity)) {
29
29
  throw new Error(`Entity "${onlyEntity}" not found in schema`);
@@ -32,7 +32,7 @@ const _validate = (dataRootPath, entities, instancesByEntityName, options = {})
32
32
  const errors = (checkOnlyEntities.length > 0
33
33
  ? entities.filter(entity => checkOnlyEntities.includes(entity.name))
34
34
  : entities)
35
- .flatMap(entity => parallelizeErrors(instancesByEntityName[entity.name]?.map(instance => wrapErrorsIfAny(`in file ${styleText("white", `"${dataRootPath}${sep}${styleText("bold", join(entity.name, instance.fileName))}"`)}`, validateEntityDecl(createValidators(instancesByEntityName, checkReferentialIntegrity), entity, instance.content))) ?? []))
35
+ .flatMap(entity => parallelizeErrors(instancesByEntityName[entity.name]?.map(instance => wrapErrorsIfAny(`in file ${styleText("white", `"${dataRootPath}${sep}${styleText("bold", join(entity.name, instance.fileName))}"`)}`, validateEntityDecl(createValidators(instancesByEntityName, true, checkReferentialIntegrity), entity, instance.content))) ?? []))
36
36
  .toSorted((a, b) => a.message.localeCompare(b.message));
37
37
  if (errors.length === 0) {
38
38
  debug("All entities are valid");
@@ -32,8 +32,9 @@ export type IdentifierToCheck = {
32
32
  value: unknown;
33
33
  };
34
34
  export interface Validators {
35
+ useStyling: boolean;
35
36
  checkReferentialIntegrity: (identifier: IdentifierToCheck) => Error[];
36
37
  }
37
- export declare const createValidators: (instancesByEntityName: InstancesByEntityName, checkReferentialIntegrity?: boolean) => Validators;
38
+ export declare const createValidators: (instancesByEntityName: InstancesByEntityName, useStyling: boolean, checkReferentialIntegrity?: boolean) => Validators;
38
39
  export type Serializer<T, U> = (node: T) => U;
39
40
  export type GetReferences<T extends Node> = (node: T, value: unknown) => string[];
@@ -75,7 +75,8 @@ export const flatMapAuxiliaryDecls = (callbackFn, declarations) => {
75
75
  };
76
76
  return declarations.reduce((decls, node) => mapNodeTree(reducer, node, [...decls, node]), []);
77
77
  };
78
- export const createValidators = (instancesByEntityName, checkReferentialIntegrity = true) => ({
78
+ export const createValidators = (instancesByEntityName, useStyling, checkReferentialIntegrity = true) => ({
79
+ useStyling,
79
80
  checkReferentialIntegrity: checkReferentialIntegrity
80
81
  ? ({ name, value }) => instancesByEntityName[name]?.some(instance => typeof instance.content === "object" &&
81
82
  instance.content !== null &&
@@ -83,7 +84,7 @@ export const createValidators = (instancesByEntityName, checkReferentialIntegrit
83
84
  instance.id === value)
84
85
  ? []
85
86
  : [
86
- ReferenceError(`Invalid reference to instance of entity ${entity(`"${name}"`)} with identifier ${json(value)}`),
87
+ ReferenceError(`Invalid reference to instance of entity ${entity(`"${name}"`, useStyling)} with identifier ${json(value, useStyling)}`),
87
88
  ]
88
89
  : () => [],
89
90
  });
@@ -22,11 +22,11 @@ export const isArrayType = (node) => node.kind === NodeKind.ArrayType;
22
22
  export const getNestedDeclarationsInArrayType = (addedDecls, type) => getNestedDeclarations(addedDecls, type.items);
23
23
  export const validateArrayType = (helpers, type, value) => {
24
24
  if (!Array.isArray(value)) {
25
- return [TypeError(`expected an array, but got ${json(value)}`)];
25
+ return [TypeError(`expected an array, but got ${json(value, helpers.useStyling)}`)];
26
26
  }
27
27
  return parallelizeErrors([
28
28
  ...validateArrayConstraints(type, value),
29
- ...value.map((item, index) => wrapErrorsIfAny(`at index ${key(index.toString())}`, validate(helpers, type.items, item))),
29
+ ...value.map((item, index) => wrapErrorsIfAny(`at index ${key(index.toString(), helpers.useStyling)}`, validate(helpers, type.items, item))),
30
30
  ]);
31
31
  };
32
32
  export const resolveTypeArgumentsInArrayType = (args, type) => ArrayType(resolveTypeArgumentsInType(args, type.items), {
@@ -18,24 +18,24 @@ export const isEnumType = (node) => node.kind === NodeKind.EnumType;
18
18
  export const getNestedDeclarationsInEnumType = (addedDecls, type) => Object.values(type.values).reduce((acc, caseMember) => caseMember.type === null ? acc : getNestedDeclarations(acc, caseMember.type), addedDecls);
19
19
  export const validateEnumType = (helpers, type, value) => {
20
20
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
21
- return [TypeError(`expected an object, but got ${json(value)}`)];
21
+ return [TypeError(`expected an object, but got ${json(value, helpers.useStyling)}`)];
22
22
  }
23
23
  const actualKeys = Object.keys(value);
24
24
  if (!(discriminatorKey in value) || typeof value[discriminatorKey] !== "string") {
25
25
  return [
26
- TypeError(`missing required discriminator value at key ${key(`"${discriminatorKey}"`)} of type string`),
26
+ TypeError(`missing required discriminator value at key ${key(`"${discriminatorKey}"`, helpers.useStyling)} of type string`),
27
27
  ];
28
28
  }
29
29
  const caseName = value[discriminatorKey];
30
30
  if (!(caseName in type.values)) {
31
31
  return [
32
- TypeError(`discriminator ${key(`"${caseName}"`)} is not a valid enum case, possible cases are: ${Object.keys(type.values).join(", ")}`),
32
+ TypeError(`discriminator ${key(`"${caseName}"`, helpers.useStyling)} is not a valid enum case, possible cases are: ${Object.keys(type.values).join(", ")}`),
33
33
  ];
34
34
  }
35
35
  const unknownKeyErrors = actualKeys.flatMap(actualKey => actualKey === discriminatorKey || actualKey in type.values
36
36
  ? []
37
37
  : [
38
- TypeError(`key ${key(`"${actualKey}"`)} is not the discriminator key ${key(`"${caseName}"`)} or a valid enum case, possible cases are: ${Object.keys(type.values).join(", ")}`),
38
+ TypeError(`key ${key(`"${actualKey}"`, helpers.useStyling)} is not the discriminator key ${key(`"${caseName}"`, helpers.useStyling)} or a valid enum case, possible cases are: ${Object.keys(type.values).join(", ")}`),
39
39
  ]);
40
40
  if (unknownKeyErrors.length > 0) {
41
41
  return unknownKeyErrors;
@@ -43,7 +43,9 @@ export const validateEnumType = (helpers, type, value) => {
43
43
  const associatedType = type.values[caseName]?.type;
44
44
  if (associatedType != null) {
45
45
  if (!(caseName in value)) {
46
- return [TypeError(`missing required associated value for case ${key(`"${caseName}"`)}`)];
46
+ return [
47
+ TypeError(`missing required associated value for case ${key(`"${caseName}"`, helpers.useStyling)}`),
48
+ ];
47
49
  }
48
50
  return validate(helpers, associatedType, value[caseName]);
49
51
  }
@@ -30,7 +30,7 @@ export const isObjectType = (node) => node.kind === NodeKind.ObjectType;
30
30
  export const getNestedDeclarationsInObjectType = (addedDecls, type) => Object.values(type.properties).reduce((acc, prop) => getNestedDeclarations(acc, prop.type), addedDecls);
31
31
  export const validateObjectType = (helpers, type, value) => {
32
32
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
33
- return [TypeError(`expected an object, but got ${json(value)}`)];
33
+ return [TypeError(`expected an object, but got ${json(value, helpers.useStyling)}`)];
34
34
  }
35
35
  const expectedKeys = Object.keys(type.properties);
36
36
  return parallelizeErrors([
@@ -39,10 +39,10 @@ export const validateObjectType = (helpers, type, value) => {
39
39
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
40
40
  const prop = type.properties[key];
41
41
  if (prop.isRequired && !(key in value)) {
42
- return TypeError(`missing required property ${keyColor(`"${key}"`)}`);
42
+ return TypeError(`missing required property ${keyColor(`"${key}"`, helpers.useStyling)}`);
43
43
  }
44
44
  else if (prop.isRequired || value[key] !== undefined) {
45
- return wrapErrorsIfAny(`at object key ${keyColor(`"${key}"`)}`, validate(helpers, prop.type, value[key]));
45
+ return wrapErrorsIfAny(`at object key ${keyColor(`"${key}"`, helpers.useStyling)}`, validate(helpers, prop.type, value[key]));
46
46
  }
47
47
  return undefined;
48
48
  }),
@@ -6,9 +6,9 @@ export const BooleanType = () => ({
6
6
  });
7
7
  export { BooleanType as Boolean };
8
8
  export const isBooleanType = (node) => node.kind === NodeKind.BooleanType;
9
- export const validateBooleanType = (_helpers, _type, value) => {
9
+ export const validateBooleanType = (helpers, _type, value) => {
10
10
  if (typeof value !== "boolean") {
11
- return [TypeError(`expected a boolean value, but got ${json(value)}`)];
11
+ return [TypeError(`expected a boolean value, but got ${json(value, helpers.useStyling)}`)];
12
12
  }
13
13
  return [];
14
14
  };
@@ -8,9 +8,9 @@ export const DateType = (options) => ({
8
8
  });
9
9
  export { DateType as Date };
10
10
  export const isDateType = (node) => node.kind === NodeKind.DateType;
11
- export const validateDateType = (_helpers, type, value) => {
11
+ export const validateDateType = (helpers, type, value) => {
12
12
  if (typeof value !== "string") {
13
- return [TypeError(`expected a string, but got ${json(value)}`)];
13
+ return [TypeError(`expected a string, but got ${json(value, helpers.useStyling)}`)];
14
14
  }
15
15
  return validateDateConstraints(type, value);
16
16
  };
@@ -8,9 +8,11 @@ export const FloatType = (options = {}) => ({
8
8
  });
9
9
  export { FloatType as Float };
10
10
  export const isFloatType = (node) => node.kind === NodeKind.FloatType;
11
- export const validateFloatType = (_helpers, type, value) => {
11
+ export const validateFloatType = (helpers, type, value) => {
12
12
  if (typeof value !== "number") {
13
- return [TypeError(`expected a floating-point number, but got ${json(value)}`)];
13
+ return [
14
+ TypeError(`expected a floating-point number, but got ${json(value, helpers.useStyling)}`),
15
+ ];
14
16
  }
15
17
  return validateNumberConstraints(type, value);
16
18
  };
@@ -12,9 +12,9 @@ export const IntegerType = (options = {}) => ({
12
12
  });
13
13
  export { IntegerType as Integer };
14
14
  export const isIntegerType = (node) => node.kind === NodeKind.IntegerType;
15
- export const validateIntegerType = (_helpers, type, value) => {
15
+ export const validateIntegerType = (helpers, type, value) => {
16
16
  if (typeof value !== "number" || !Number.isInteger(value)) {
17
- return [TypeError(`expected an integer, but got ${json(value)}`)];
17
+ return [TypeError(`expected an integer, but got ${json(value, helpers.useStyling)}`)];
18
18
  }
19
19
  return validateNumberConstraints(type, value);
20
20
  };
@@ -8,9 +8,9 @@ export const StringType = (options = {}) => ({
8
8
  });
9
9
  export { StringType as String };
10
10
  export const isStringType = (node) => node.kind === NodeKind.StringType;
11
- export const validateStringType = (_helpers, type, value) => {
11
+ export const validateStringType = (helpers, type, value) => {
12
12
  if (typeof value !== "string") {
13
- return [TypeError(`expected a string, but got ${json(value)}`)];
13
+ return [TypeError(`expected a string, but got ${json(value, helpers.useStyling)}`)];
14
14
  }
15
15
  return validateStringConstraints(type, value);
16
16
  };
@@ -27,9 +27,9 @@ export const isNestedEntityMapType = (node) => node.kind === NodeKind.NestedEnti
27
27
  export const getNestedDeclarationsInNestedEntityMapType = (addedDecls, type) => getNestedDeclarationsInObjectType(addedDecls.includes(type.secondaryEntity) ? addedDecls : [type.secondaryEntity, ...addedDecls], type.type.value);
28
28
  export const validateNestedEntityMapType = (helpers, type, value) => {
29
29
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
30
- return [TypeError(`expected an object, but got ${json(value)}`)];
30
+ return [TypeError(`expected an object, but got ${json(value, helpers.useStyling)}`)];
31
31
  }
32
- return parallelizeErrors(Object.keys(value).map(key => wrapErrorsIfAny(`at nested entity map ${entity(`"${type.name}"`)} at key ${keyColor(`"${key}"`)}`, validateObjectType(helpers, type.type.value, value[key]).concat(helpers.checkReferentialIntegrity({
32
+ return parallelizeErrors(Object.keys(value).map(key => wrapErrorsIfAny(`at nested entity map ${entity(`"${type.name}"`, helpers.useStyling)} at key ${keyColor(`"${key}"`, helpers.useStyling)}`, validateObjectType(helpers, type.type.value, value[key]).concat(helpers.checkReferentialIntegrity({
33
33
  name: type.secondaryEntity.name,
34
34
  value: key,
35
35
  })))));
@@ -14,7 +14,7 @@ export const createInstance = async (locals, entityName, instance, idQueryParam)
14
14
  if (entity === undefined) {
15
15
  return error([400, "Entity not found"]);
16
16
  }
17
- const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
17
+ const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName, false), entity, instance);
18
18
  if (validationErrors.length > 0) {
19
19
  return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
20
20
  }
@@ -54,7 +54,7 @@ export const updateInstance = async (locals, entityName, instanceId, instance) =
54
54
  if (entity === undefined) {
55
55
  return error([400, "Entity not found"]);
56
56
  }
57
- const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
57
+ const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName, false), entity, instance);
58
58
  if (validationErrors.length > 0) {
59
59
  return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
60
60
  }
@@ -1,3 +1,3 @@
1
- export declare const json: (value: unknown) => string;
2
- export declare const key: (str: string) => string;
3
- export declare const entity: (str: string) => string;
1
+ export declare const json: (value: unknown, useStyling: boolean) => string;
2
+ export declare const key: (str: string, useStyling: boolean) => string;
3
+ export declare const entity: (str: string, useStyling: boolean) => string;
@@ -1,4 +1,6 @@
1
1
  import { styleText } from "node:util";
2
- export const json = (value) => styleText("blue", JSON.stringify(value, undefined, 2));
3
- export const key = (str) => styleText(["yellow", "bold"], str);
4
- export const entity = (str) => styleText("green", str);
2
+ export const json = (value, useStyling) => useStyling
3
+ ? styleText("blue", JSON.stringify(value, undefined, 2))
4
+ : JSON.stringify(value, undefined, 2);
5
+ export const key = (str, useStyling) => useStyling ? styleText(["yellow", "bold"], str) : str;
6
+ export const entity = (str, useStyling) => useStyling ? styleText("green", str) : str;
@@ -4,7 +4,8 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedArrayType;
7
- value: unknown[];
7
+ path: string | undefined;
8
+ value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
10
11
  onChange: (value: unknown[]) => void;
@@ -1,15 +1,19 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "preact/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { removeAt } from "../../../shared/utils/array.js";
3
3
  import { validateArrayConstraints } from "../../../shared/validation/array.js";
4
4
  import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
5
5
  import { TypeInput } from "./TypeInput.js";
6
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
6
7
  import { ValidationErrors } from "./utils/ValidationErrors.js";
7
- export const ArrayTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
8
+ export const ArrayTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
9
+ if (!Array.isArray(value)) {
10
+ return _jsx(MismatchingTypeError, { expected: "array", actual: value });
11
+ }
8
12
  const errors = validateArrayConstraints(type, value);
9
13
  const isTuple = typeof type.minItems === "number" && type.minItems === type.maxItems;
10
14
  return (_jsxs("div", { class: "field field--container field--array", children: [value.length > 0 && (_jsx("ol", { children: value.map((item, i) => (_jsxs("li", { class: "container-item array-item", children: [isTuple ? null : (_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }), _jsxs("button", { class: "destructive", onClick: () => {
11
15
  onChange(removeAt(value, i));
12
- }, disabled: type.minItems !== undefined && value.length <= type.minItems, children: ["Delete Item #", i + 1] })] })), _jsx(TypeInput, { type: type.items, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
16
+ }, disabled: type.minItems !== undefined && value.length <= type.minItems, children: ["Delete Item #", i + 1] })] })), _jsx(TypeInput, { type: type.items, path: path === undefined ? `[${i.toString()}]` : `${path}[${i.toString()}]`, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
13
17
  onChange(value.with(i, newItem));
14
18
  } })] }, i))) })), isTuple ? null : (_jsx("div", { class: "add-item-container", children: _jsxs("button", { onClick: () => {
15
19
  onChange([...value, createTypeSkeleton(getDeclFromDeclName, type.items)]);
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedBooleanType } from "../../../node/schema/types/primitives/BooleanType.ts";
3
3
  type Props = {
4
4
  type: SerializedBooleanType;
5
- value: boolean;
5
+ value: unknown;
6
6
  onChange: (value: boolean) => void;
7
7
  };
8
8
  export declare const BooleanTypeInput: FunctionComponent<Props>;
@@ -1,5 +1,9 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
2
3
  export const BooleanTypeInput = ({ value, onChange }) => {
4
+ if (typeof value !== "boolean") {
5
+ return _jsx(MismatchingTypeError, { expected: "boolean", actual: value });
6
+ }
3
7
  return (_jsx("div", { class: "field", children: _jsx("input", { type: "checkbox", checked: value, onInput: event => {
4
8
  onChange(event.currentTarget.checked);
5
9
  } }) }));
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedDateType } from "../../../node/schema/types/primitives/DateType.ts";
3
3
  type Props = {
4
4
  type: SerializedDateType;
5
- value: string;
5
+ value: unknown;
6
6
  onChange: (value: string) => void;
7
7
  };
8
8
  export declare const DateTypeInput: FunctionComponent<Props>;
@@ -1,7 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { validateDateConstraints } from "../../../shared/validation/date.js";
3
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
3
4
  import { ValidationErrors } from "./utils/ValidationErrors.js";
4
5
  export const DateTypeInput = ({ type, value, onChange }) => {
6
+ if (typeof value !== "string") {
7
+ return _jsx(MismatchingTypeError, { expected: "date string", actual: value });
8
+ }
5
9
  const errors = validateDateConstraints(type, value);
6
10
  return (_jsxs("div", { class: "field", children: [_jsx("input", { type: "date", value: value, onInput: event => {
7
11
  onChange(event.currentTarget.value);
@@ -4,6 +4,7 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedEnumType;
7
+ path: string | undefined;
7
8
  value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
@@ -5,7 +5,7 @@ import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
5
5
  import { Select } from "../Select.js";
6
6
  import { TypeInput } from "./TypeInput.js";
7
7
  import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
8
- export const EnumTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
8
+ export const EnumTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
9
9
  if (typeof value !== "object" ||
10
10
  value === null ||
11
11
  Array.isArray(value) ||
@@ -29,7 +29,7 @@ export const EnumTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromD
29
29
  [event.currentTarget.value]: createTypeSkeleton(getDeclFromDeclName, caseMember.type),
30
30
  });
31
31
  }
32
- }, children: enumValues.map(enumValue => (_jsx("option", { value: enumValue, selected: enumValue === activeEnumCase, children: toTitleCase(enumValue) }, enumValue))) }), caseMember?.type == null ? null : (_jsx("div", { className: "associated-type", children: _jsx(TypeInput, { type: caseMember.type, value: value[activeEnumCase], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newValue => {
32
+ }, children: enumValues.map(enumValue => (_jsx("option", { value: enumValue, selected: enumValue === activeEnumCase, children: toTitleCase(enumValue) }, enumValue))) }), caseMember?.type == null ? null : (_jsx("div", { className: "associated-type", children: _jsx(TypeInput, { type: caseMember.type, path: path === undefined ? `{${activeEnumCase}}` : `${path}.{${activeEnumCase}}`, value: value[activeEnumCase], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newValue => {
33
33
  onChange({
34
34
  [discriminatorKey]: activeEnumCase,
35
35
  [activeEnumCase]: newValue,
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedFloatType } from "../../../node/schema/types/primitives/FloatType.ts";
3
3
  type Props = {
4
4
  type: SerializedFloatType;
5
- value: number;
5
+ value: unknown;
6
6
  onChange: (value: number) => void;
7
7
  };
8
8
  export declare const FloatTypeInput: FunctionComponent<Props>;
@@ -1,9 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useState } from "preact/hooks";
3
3
  import { validateNumberConstraints } from "../../../shared/validation/number.js";
4
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
4
5
  import { ValidationErrors } from "./utils/ValidationErrors.js";
5
6
  export const FloatTypeInput = ({ type, value, onChange }) => {
6
- const [stringValue, setStringValue] = useState(value.toString());
7
+ const [stringValue, setStringValue] = useState(typeof value === "number" ? value.toString() : "");
8
+ if (typeof value !== "number") {
9
+ return _jsx(MismatchingTypeError, { expected: "float", actual: value });
10
+ }
7
11
  const errors = validateNumberConstraints(type, value);
8
12
  return (_jsxs("div", { class: "field", children: [_jsx("input", { type: "number", value: stringValue, onInput: event => {
9
13
  setStringValue(event.currentTarget.value);
@@ -4,6 +4,7 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedIncludeIdentifierType;
7
+ path: string | undefined;
7
8
  value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { TypeInput } from "./TypeInput.js";
3
- export const IncludeIdentifierTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
3
+ export const IncludeIdentifierTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
4
4
  const decl = getDeclFromDeclName(type.reference);
5
5
  if (decl === undefined) {
6
6
  return (_jsxs("div", { role: "alert", children: ["Unresolved declaration identifier ", _jsx("code", { children: type.reference })] }));
7
7
  }
8
- return (_jsx(TypeInput, { type: decl.type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
8
+ return (_jsx(TypeInput, { type: decl.type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
9
9
  };
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedIntegerType } from "../../../node/schema/types/primitives/IntegerType.ts";
3
3
  type Props = {
4
4
  type: SerializedIntegerType;
5
- value: number;
5
+ value: unknown;
6
6
  onChange: (value: number) => void;
7
7
  };
8
8
  export declare const IntegerTypeInput: FunctionComponent<Props>;
@@ -1,9 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useState } from "preact/hooks";
3
3
  import { validateNumberConstraints } from "../../../shared/validation/number.js";
4
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
4
5
  import { ValidationErrors } from "./utils/ValidationErrors.js";
5
6
  export const IntegerTypeInput = ({ type, value, onChange }) => {
6
- const [stringValue, setStringValue] = useState(value.toString());
7
+ const [stringValue, setStringValue] = useState(typeof value === "number" ? value.toString() : "");
8
+ if (typeof value !== "number") {
9
+ return _jsx(MismatchingTypeError, { expected: "float", actual: value });
10
+ }
7
11
  const errors = validateNumberConstraints(type, value);
8
12
  return (_jsxs("div", { class: "field", children: [_jsx("input", { type: "number", value: stringValue, onInput: event => {
9
13
  setStringValue(event.currentTarget.value);
@@ -4,7 +4,8 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedNestedEntityMapType;
7
- value: Record<string, unknown>;
7
+ path: string | undefined;
8
+ value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
10
11
  onChange: (value: Record<string, unknown>) => void;
@@ -5,8 +5,12 @@ import { toTitleCase } from "../../../shared/utils/string.js";
5
5
  import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
6
6
  import { Select } from "../Select.js";
7
7
  import { TypeInput } from "./TypeInput.js";
8
- export const NestedEntityMapTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
8
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
9
+ export const NestedEntityMapTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
9
10
  const [newKey, setNewKey] = useState("");
11
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
12
+ return _jsx(MismatchingTypeError, { expected: "object", actual: value });
13
+ }
10
14
  const existingKeys = Object.keys(value);
11
15
  const secondaryInstances = (instanceNamesByEntity[type.secondaryEntity] ?? [])
12
16
  .slice()
@@ -20,7 +24,7 @@ export const NestedEntityMapTypeInput = ({ type, value, instanceNamesByEntity, g
20
24
  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
21
25
  delete newObj[key];
22
26
  onChange(newObj);
23
- }, children: ["Delete ", name] }) })] }), _jsx(TypeInput, { type: type.type, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
27
+ }, children: ["Delete ", name] }) })] }), _jsx(TypeInput, { type: type.type, path: path === undefined ? key : `${path}.${key}`, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
24
28
  onChange(sortObjectKeysAlphabetically({ ...value, [key]: newItem }));
25
29
  } })] }, key));
26
30
  }) })), _jsxs("div", { class: "add-item-container", children: [_jsxs(Select, { value: newKey, onInput: event => {
@@ -4,7 +4,8 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedObjectType;
7
- value: Record<string, unknown>;
7
+ path: string | undefined;
8
+ value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
10
11
  onChange: (value: Record<string, unknown>) => void;
@@ -5,10 +5,15 @@ import { validateObjectConstraints } from "../../../shared/validation/object.js"
5
5
  import { Markdown } from "../../utils/Markdown.js";
6
6
  import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
7
7
  import { TypeInput } from "./TypeInput.js";
8
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
8
9
  import { ValidationErrors } from "./utils/ValidationErrors.js";
9
- export const ObjectTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
10
+ export const ObjectTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
11
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
12
+ return _jsx(MismatchingTypeError, { expected: "object", actual: value });
13
+ }
10
14
  const errors = validateObjectConstraints(type, Object.keys(type.properties), value);
11
- return (_jsxs("div", { class: "field field--container field--object", children: [_jsx("ul", { children: Object.entries(type.properties).map(([key, memberDecl]) => (_jsxs("li", { class: "container-item object-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [_jsx("strong", { children: toTitleCase(key) }), memberDecl.comment === undefined ? null : (_jsx(Markdown, { class: "comment", string: memberDecl.comment }))] }), memberDecl.isRequired ? null : value[key] === undefined ? (_jsxs("button", { onClick: () => {
15
+ 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] ===
16
+ undefined ? (_jsxs("button", { onClick: () => {
12
17
  onChange(sortObjectKeys({
13
18
  ...value,
14
19
  [key]: createTypeSkeleton(getDeclFromDeclName, memberDecl.type),
@@ -18,7 +23,7 @@ export const ObjectTypeInput = ({ type, value, instanceNamesByEntity, getDeclFro
18
23
  // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
19
24
  delete newObj[key];
20
25
  onChange(newObj);
21
- }, children: ["Remove ", toTitleCase(key)] }))] }), memberDecl.isRequired || value[key] !== undefined ? (_jsx(TypeInput, { type: memberDecl.type, value: value[key], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
26
+ }, children: ["Remove ", toTitleCase(key)] }))] }), memberDecl.isRequired || value[key] !== undefined ? (_jsx(TypeInput, { type: memberDecl.type, path: path === undefined ? key : `${path}.${key}`, value: value[key], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
22
27
  onChange(sortObjectKeys({ ...value, [key]: newItem }, Object.keys(type.properties)));
23
28
  } })) : null] }, key))) }), _jsx(ValidationErrors, { errors: errors })] }));
24
29
  };
@@ -3,7 +3,7 @@ import type { SerializedReferenceIdentifierType } from "../../../node/schema/typ
3
3
  import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity.ts";
4
4
  type Props = {
5
5
  type: SerializedReferenceIdentifierType;
6
- value: string;
6
+ value: unknown;
7
7
  instanceNamesByEntity: InstanceNamesByEntity;
8
8
  onChange: (value: string) => void;
9
9
  };
@@ -1,7 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { Select } from "../Select.js";
3
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
3
4
  import { ValidationErrors } from "./utils/ValidationErrors.js";
4
5
  export const ReferenceIdentifierTypeInput = ({ type, value, instanceNamesByEntity, onChange, }) => {
6
+ if (typeof value !== "string") {
7
+ return _jsx(MismatchingTypeError, { expected: "string identifier", actual: value });
8
+ }
5
9
  const instances = (instanceNamesByEntity[type.entity] ?? [])
6
10
  .slice()
7
11
  .sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedStringType } from "../../../node/schema/types/primitives/StringType.ts";
3
3
  type Props = {
4
4
  type: SerializedStringType;
5
- value: string;
5
+ value: unknown;
6
6
  onChange: (value: string) => void;
7
7
  };
8
8
  export declare const StringTypeInput: FunctionComponent<Props>;
@@ -1,8 +1,12 @@
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
3
  import { Markdown } from "../../utils/Markdown.js";
4
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
4
5
  import { ValidationErrors } from "./utils/ValidationErrors.js";
5
6
  export const StringTypeInput = ({ type, value, onChange }) => {
7
+ if (typeof value !== "string") {
8
+ return _jsx(MismatchingTypeError, { expected: "string", actual: value });
9
+ }
6
10
  const { minLength, maxLength, pattern, isMarkdown } = type;
7
11
  const errors = validateStringConstraints(type, value);
8
12
  return (_jsx("div", { class: "field field--string", children: isMarkdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "editor", children: [_jsx("div", { class: "textarea-grow-wrap", "data-value": value, children: _jsx("textarea", { value: value, minLength: minLength, maxLength: maxLength, onInput: event => {
@@ -4,6 +4,7 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedType;
7
+ path: string | undefined;
7
8
  value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
@@ -1,6 +1,5 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
2
  import { memo } from "preact/compat";
3
- import { deepEqual } from "../../../shared/utils/compare.js";
4
3
  import { assertExhaustive } from "../../../shared/utils/typeSafety.js";
5
4
  import { ArrayTypeInput } from "./ArrayTypeInput.js";
6
5
  import { BooleanTypeInput } from "./BooleanTypeInput.js";
@@ -14,81 +13,36 @@ import { NestedEntityMapTypeInput } from "./NestedEntityMapTypeInput.js";
14
13
  import { ObjectTypeInput } from "./ObjectTypeInput.js";
15
14
  import { ReferenceIdentifierTypeInput } from "./ReferenceIdentifierTypeInput.js";
16
15
  import { StringTypeInput } from "./StringTypeInput.js";
17
- import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
18
- const TypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
16
+ const TypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
17
+ // console.log("rendering node at path ", path ?? "<root>")
19
18
  switch (type.kind) {
20
19
  case "BooleanType":
21
- if (typeof value === "boolean") {
22
- return _jsx(BooleanTypeInput, { type: type, value: value, onChange: onChange });
23
- }
24
- else {
25
- return _jsx(MismatchingTypeError, { expected: "boolean", actual: value });
26
- }
20
+ return _jsx(BooleanTypeInput, { type: type, value: value, onChange: onChange });
27
21
  case "DateType":
28
- if (typeof value === "string") {
29
- return _jsx(DateTypeInput, { type: type, value: value, onChange: onChange });
30
- }
31
- else {
32
- return _jsx(MismatchingTypeError, { expected: "date string", actual: value });
33
- }
22
+ return _jsx(DateTypeInput, { type: type, value: value, onChange: onChange });
34
23
  case "FloatType":
35
- if (typeof value === "number") {
36
- return _jsx(FloatTypeInput, { type: type, value: value, onChange: onChange });
37
- }
38
- else {
39
- return _jsx(MismatchingTypeError, { expected: "float", actual: value });
40
- }
24
+ return _jsx(FloatTypeInput, { type: type, value: value, onChange: onChange });
41
25
  case "IntegerType":
42
- if (typeof value === "number" && Number.isInteger(value)) {
43
- return _jsx(IntegerTypeInput, { type: type, value: value, onChange: onChange });
44
- }
45
- else {
46
- return _jsx(MismatchingTypeError, { expected: "integer", actual: value });
47
- }
26
+ return _jsx(IntegerTypeInput, { type: type, value: value, onChange: onChange });
48
27
  case "StringType":
49
- if (typeof value === "string") {
50
- return _jsx(StringTypeInput, { type: type, value: value, onChange: onChange });
51
- }
52
- else {
53
- return _jsx(MismatchingTypeError, { expected: "string", actual: value });
54
- }
28
+ return _jsx(StringTypeInput, { type: type, value: value, onChange: onChange });
55
29
  case "ArrayType":
56
- if (Array.isArray(value)) {
57
- return (_jsx(ArrayTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
58
- }
59
- else {
60
- return _jsx(MismatchingTypeError, { expected: "array", actual: value });
61
- }
30
+ return (_jsx(ArrayTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
62
31
  case "ObjectType":
63
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
64
- return (_jsx(ObjectTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
65
- }
66
- else {
67
- return _jsx(MismatchingTypeError, { expected: "object", actual: value });
68
- }
32
+ return (_jsx(ObjectTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
69
33
  case "TypeArgumentType":
70
34
  return _jsx(TypeArgumentTypeInput, { type: type });
71
35
  case "ReferenceIdentifierType":
72
- if (typeof value === "string") {
73
- return (_jsx(ReferenceIdentifierTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, onChange: onChange }));
74
- }
75
- else {
76
- return _jsx(MismatchingTypeError, { expected: "string identifier", actual: value });
77
- }
36
+ return (_jsx(ReferenceIdentifierTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, onChange: onChange }));
78
37
  case "IncludeIdentifierType":
79
- return (_jsx(IncludeIdentifierTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
38
+ return (_jsx(IncludeIdentifierTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
80
39
  case "NestedEntityMapType":
81
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
82
- return (_jsx(NestedEntityMapTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
83
- }
84
- else {
85
- return _jsx(MismatchingTypeError, { expected: "entity map", actual: value });
86
- }
40
+ return (_jsx(NestedEntityMapTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
87
41
  case "EnumType":
88
- return (_jsx(EnumTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
42
+ return (_jsx(EnumTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
89
43
  default:
90
44
  return assertExhaustive(type);
91
45
  }
92
46
  };
93
- const MemoizedTypeInput = memo(TypeInput, (prevProps, nextProps) => deepEqual(prevProps.value, nextProps.value));
47
+ const MemoizedTypeInput = memo(TypeInput, (prevProps, nextProps) => prevProps.value === nextProps.value && prevProps.onChange === nextProps.onChange);
94
48
  export { MemoizedTypeInput as TypeInput };
@@ -12,6 +12,7 @@ import { useEntityFromRoute } from "../hooks/useEntityFromRoute.js";
12
12
  import { useInstanceNamesByEntity } from "../hooks/useInstanceNamesByEntity.js";
13
13
  import { useGetDeclFromDeclName } from "../hooks/useSecondaryDeclarations.js";
14
14
  import { createTypeSkeleton } from "../utils/typeSkeleton.js";
15
+ import { homeTitle } from "./Home.js";
15
16
  import { NotFound } from "./NotFound.js";
16
17
  export const CreateInstance = () => {
17
18
  const { params: { name }, } = useRoute();
@@ -73,11 +74,11 @@ export const CreateInstance = () => {
73
74
  const instanceName = getSerializedDisplayNameFromEntityInstance(entity, instance, defaultName);
74
75
  const idErrors = isLocaleEntity ? validateLocaleIdentifier(customId) : [];
75
76
  return (_jsxs(Layout, { breadcrumbs: [
76
- { url: "/", label: "Home" },
77
+ { url: "/", label: homeTitle },
77
78
  { url: `/entities/${entity.name}`, label: entity.name },
78
79
  ], children: [_jsx("div", { class: "header-with-btns", children: _jsx("h1", { class: instanceName.length === 0 ? "empty-name" : undefined, children: instanceName || defaultName }) }), isLocaleEntity && (_jsxs("div", { class: "field field--id", children: [_jsx("label", { htmlFor: "id", children: "ID" }), _jsx("p", { className: "comment", children: "The instance\u2019s identifier. An IETF language tag (BCP47)." }), _jsx("input", { type: "text", id: "id", value: customId, required: true, pattern: "[a-z]{2,3}(-[A-Z]{2,3})?", placeholder: "en-US, de-DE, \u2026", onInput: event => {
79
80
  setCustomId(event.currentTarget.value);
80
- }, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, value: instance, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: value => {
81
+ }, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, path: undefined, value: instance, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: value => {
81
82
  console.log("onChange", value);
82
83
  setInstance(value);
83
84
  } }), _jsxs("div", { class: "form-footer btns", children: [_jsx("button", { type: "submit", class: "primary", name: "save", children: "Save" }), _jsx("button", { type: "submit", class: "primary", name: "saveandcontinue", children: "Save and Continue" }), _jsx("button", { type: "submit", class: "primary", name: "saveandaddanother", children: "Save and Add Another" })] })] })] }));
@@ -8,6 +8,7 @@ import { Layout } from "../components/Layout.js";
8
8
  import { useAPIResource } from "../hooks/useAPIResource.js";
9
9
  import { useMappedAPIResource } from "../hooks/useMappedAPIResource.js";
10
10
  import { Markdown } from "../utils/Markdown.js";
11
+ import { homeTitle } from "./Home.js";
11
12
  import { NotFound } from "./NotFound.js";
12
13
  const mapInstances = (data) => data.instances;
13
14
  export const Entity = () => {
@@ -37,7 +38,7 @@ export const Entity = () => {
37
38
  ? instances
38
39
  : instances.filter(instance => instance.id.includes(searchText) ||
39
40
  instance.displayName.toLowerCase().includes(lowerSearchText));
40
- return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsxs("div", { class: "header-with-btns", children: [_jsx("h1", { children: toTitleCase(entity.declaration.namePlural) }), _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("div", { className: "list-header", children: [_jsxs("p", { class: "instance-count", children: [searchText === "" ? "" : `${filteredInstances.length.toString()} of `, instances.length, " instance", instances.length === 1 ? "" : "s"] }), _jsxs("form", { action: "", rel: "search", onSubmit: e => {
41
+ return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: homeTitle }], children: [_jsxs("div", { class: "header-with-btns", children: [_jsx("h1", { children: toTitleCase(entity.declaration.namePlural) }), _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("div", { className: "list-header", children: [_jsxs("p", { class: "instance-count", children: [searchText === "" ? "" : `${filteredInstances.length.toString()} of `, instances.length, " instance", instances.length === 1 ? "" : "s"] }), _jsxs("form", { action: "", rel: "search", onSubmit: e => {
41
42
  e.preventDefault();
42
43
  }, children: [_jsx("label", { htmlFor: "instance-search", class: "visually-hidden", children: "Search" }), _jsx("input", { type: "text", id: "instance-search", value: searchText, onInput: event => {
43
44
  setSearchText(event.currentTarget.value);
@@ -1,2 +1,3 @@
1
1
  import type { FunctionalComponent } from "preact";
2
+ export declare const homeTitle = "Entities";
2
3
  export declare const Home: FunctionalComponent;
@@ -6,10 +6,11 @@ import { Layout } from "../components/Layout.js";
6
6
  import { useMappedAPIResource } from "../hooks/useMappedAPIResource.js";
7
7
  import { Markdown } from "../utils/Markdown.js";
8
8
  const mapEntities = (data) => data.declarations.sort((a, b) => a.declaration.name.localeCompare(b.declaration.name));
9
+ export const homeTitle = "Entities";
9
10
  export const Home = () => {
10
11
  const [entities] = useMappedAPIResource(getAllEntities, mapEntities);
11
12
  useEffect(() => {
12
- document.title = "Entities — TSONDB";
13
+ document.title = homeTitle + " — TSONDB";
13
14
  }, []);
14
15
  const [searchText, setSearchText] = useState("");
15
16
  const lowerSearchText = searchText.toLowerCase().replaceAll(" ", "");
@@ -17,7 +18,7 @@ export const Home = () => {
17
18
  ? entities
18
19
  : entities?.filter(entity => entity.declaration.name.toLowerCase().includes(lowerSearchText) ||
19
20
  entity.declaration.namePlural.toLowerCase().includes(lowerSearchText));
20
- return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsx("h1", { children: "Entities" }), _jsxs("div", { className: "list-header", children: [_jsxs("p", { class: "instance-count", children: [searchText === "" ? "" : `${(filteredEntities?.length ?? 0).toString()} of `, entities?.length ?? 0, " entit", entities?.length === 1 ? "y" : "ies"] }), _jsxs("form", { action: "", rel: "search", onSubmit: e => {
21
+ return (_jsxs(Layout, { breadcrumbs: [], children: [_jsx("h1", { children: homeTitle }), _jsxs("div", { className: "list-header", children: [_jsxs("p", { class: "instance-count", children: [searchText === "" ? "" : `${(filteredEntities?.length ?? 0).toString()} of `, entities?.length ?? 0, " entit", entities?.length === 1 ? "y" : "ies"] }), _jsxs("form", { action: "", rel: "search", onSubmit: e => {
21
22
  e.preventDefault();
22
23
  }, children: [_jsx("label", { htmlFor: "entity-search", class: "visually-hidden", children: "Search" }), _jsx("input", { type: "text", id: "entity-search", value: searchText, onInput: event => {
23
24
  setSearchText(event.currentTarget.value);
@@ -9,6 +9,7 @@ import { TypeInput } from "../components/typeInputs/TypeInput.js";
9
9
  import { useEntityFromRoute } from "../hooks/useEntityFromRoute.js";
10
10
  import { useInstanceNamesByEntity } from "../hooks/useInstanceNamesByEntity.js";
11
11
  import { useGetDeclFromDeclName } from "../hooks/useSecondaryDeclarations.js";
12
+ import { homeTitle } from "./Home.js";
12
13
  import { NotFound } from "./NotFound.js";
13
14
  export const Instance = () => {
14
15
  const { params: { name, id }, } = useRoute();
@@ -19,7 +20,7 @@ export const Instance = () => {
19
20
  const [originalInstance, setOriginalInstance] = useState();
20
21
  const { route } = useLocation();
21
22
  useEffect(() => {
22
- if (entityFromRoute && instance && id) {
23
+ if (entityFromRoute?.entity && instance?.content && id) {
23
24
  const defaultName = id;
24
25
  const instanceName = getSerializedDisplayNameFromEntityInstance(entityFromRoute.entity, instance.content, defaultName);
25
26
  const entityName = entityFromRoute.entity.name;
@@ -28,7 +29,7 @@ export const Instance = () => {
28
29
  else {
29
30
  document.title = "Not found — TSONDB";
30
31
  }
31
- }, [entityFromRoute, id, instance]);
32
+ }, [entityFromRoute?.entity, id, instance?.content]);
32
33
  useEffect(() => {
33
34
  if (name && id) {
34
35
  getInstanceByEntityNameAndId(name, id)
@@ -68,14 +69,14 @@ export const Instance = () => {
68
69
  !instanceNamesByEntity ||
69
70
  !declsLoaded) {
70
71
  return (_jsxs(Layout, { breadcrumbs: [
71
- { url: "/", label: "Home" },
72
+ { url: "/", label: homeTitle },
72
73
  { url: `/entities/${name}`, label: name },
73
74
  ], children: [_jsx("h1", { children: id }), _jsx("p", { className: "loading", children: "Loading \u2026" })] }));
74
75
  }
75
76
  const defaultName = id;
76
77
  const instanceName = getSerializedDisplayNameFromEntityInstance(entityFromRoute.entity, instance.content, defaultName);
77
78
  return (_jsxs(Layout, { breadcrumbs: [
78
- { url: "/", label: "Home" },
79
+ { url: "/", label: homeTitle },
79
80
  { url: `/entities/${name}`, label: entityFromRoute.entity.name },
80
81
  ], children: [_jsxs("div", { class: "header-with-btns", children: [_jsxs("h1", { class: instanceName.length === 0 ? "empty-name" : undefined, children: [_jsx("span", { children: instanceName || defaultName }), " ", _jsx("span", { className: "id", "aria-hidden": true, children: instance.id })] }), _jsx("button", { class: "destructive", onClick: () => {
81
82
  if (confirm("Are you sure you want to delete this instance?")) {
@@ -89,5 +90,5 @@ export const Instance = () => {
89
90
  }
90
91
  });
91
92
  }
92
- }, children: "Delete" })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entityFromRoute.entity.type, value: instance.content, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: handleOnChange }), _jsx("div", { class: "form-footer btns", children: _jsx("button", { type: "submit", name: "save", class: "primary", children: "Save" }) })] })] }));
93
+ }, children: "Delete" })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entityFromRoute.entity.type, value: instance.content, path: undefined, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: handleOnChange }), _jsx("div", { class: "form-footer btns", children: _jsx("button", { type: "submit", name: "save", class: "primary", children: "Save" }) })] })] }));
93
94
  };
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useEffect } from "preact/hooks";
3
3
  import { Layout } from "../components/Layout.js";
4
+ import { homeTitle } from "./Home.js";
4
5
  export const NotFound = () => {
5
6
  useEffect(() => {
6
7
  document.title = "Not found — TSONDB";
7
8
  }, []);
8
- return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsx("h1", { children: "404 Not Found" }), _jsx("p", { children: "The page you are looking for does not exist." })] }));
9
+ return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: homeTitle }], children: [_jsx("h1", { children: "404 Not Found" }), _jsx("p", { children: "The page you are looking for does not exist." })] }));
9
10
  };
@@ -0,0 +1 @@
1
+ export declare const printJson: (data: unknown) => string;
@@ -0,0 +1,5 @@
1
+ export const printJson = (data) => JSON.stringify(data, undefined, 2)
2
+ .replace(/\n *([}\]],?)/g, " $1")
3
+ .replace(/((?:^|\n *)[{[])\n +/g, "$1 ")
4
+ .replace(/"(.+?)":/g, '<span style="color: darkorange">$1</span>:')
5
+ .replace(/ "(.*?)"([ ,])/g, ' <span style="color: darkgreen">"$1"</span>$2');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsondb",
3
- "version": "0.5.10",
3
+ "version": "0.5.13",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "Lukas Obermann",
@@ -110,6 +110,10 @@ header {
110
110
  grid-area: header;
111
111
  }
112
112
 
113
+ header nav {
114
+ min-height: 1.4rem;
115
+ }
116
+
113
117
  header nav ol {
114
118
  list-style-type: " ";
115
119
  display: flex;
@@ -292,13 +296,13 @@ input[type="checkbox"]:checked {
292
296
  input[type="checkbox"]:checked::before {
293
297
  content: "";
294
298
  display: block;
295
- border-width: 0 0 1px 1px;
299
+ border-width: 0 0 0.125rem 0.125rem;
296
300
  border-style: solid;
297
- width: 0.6rem;
298
- height: 0.3rem;
301
+ width: 0.8rem;
302
+ height: 0.4rem;
299
303
  border-color: var(--background);
300
304
  transform: rotate(-45deg);
301
- margin: calc(-0.2rem - 1px) 0 0 calc(-0.275rem - 1px);
305
+ margin: calc(-0.325rem) 0 0 calc(-0.4rem);
302
306
  }
303
307
 
304
308
  .select-wrapper {