tsondb 0.12.5 → 0.12.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.
Files changed (35) hide show
  1. package/dist/src/node/schema/declarations/EntityDecl.d.ts +9 -7
  2. package/dist/src/node/schema/declarations/EntityDecl.js +2 -2
  3. package/dist/src/node/schema/declarations/EnumDecl.d.ts +2 -0
  4. package/dist/src/node/schema/declarations/EnumDecl.js +2 -0
  5. package/dist/src/node/schema/types/Type.d.ts +20 -6
  6. package/dist/src/node/schema/types/primitives/FloatType.d.ts +5 -2
  7. package/dist/src/node/schema/types/primitives/FloatType.js +10 -4
  8. package/dist/src/node/server/api/git.js +4 -0
  9. package/dist/src/node/server/api/index.js +2 -0
  10. package/dist/src/node/server/api/search.d.ts +1 -0
  11. package/dist/src/node/server/api/search.js +50 -0
  12. package/dist/src/node/server/utils/instanceOperations.js +3 -3
  13. package/dist/src/node/server/utils/query.d.ts +2 -0
  14. package/dist/src/node/server/utils/query.js +7 -0
  15. package/dist/src/node/utils/childInstances.d.ts +1 -1
  16. package/dist/src/node/utils/childInstances.js +15 -9
  17. package/dist/src/node/utils/displayName.js +1 -0
  18. package/dist/src/shared/api.d.ts +4 -0
  19. package/dist/src/shared/schema/declarations/EntityDecl.d.ts +13 -9
  20. package/dist/src/shared/schema/declarations/EnumDecl.d.ts +8 -0
  21. package/dist/src/shared/schema/declarations/EnumDecl.js +5 -0
  22. package/dist/src/shared/schema/types/FloatType.d.ts +6 -5
  23. package/dist/src/shared/schema/types/FloatType.js +1 -0
  24. package/dist/src/shared/utils/object.d.ts +1 -1
  25. package/dist/src/web/api/search.d.ts +2 -0
  26. package/dist/src/web/api/search.js +7 -0
  27. package/dist/src/web/components/Layout.js +1 -1
  28. package/dist/src/web/components/git/GitStatusIndicator.js +1 -1
  29. package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +1 -1
  30. package/dist/src/web/components/typeInputs/FloatTypeInput.js +2 -1
  31. package/dist/src/web/index.js +2 -1
  32. package/dist/src/web/routes/Search.d.ts +2 -0
  33. package/dist/src/web/routes/Search.js +56 -0
  34. package/package.json +1 -1
  35. package/public/css/styles.css +10 -0
@@ -1,28 +1,30 @@
1
1
  import { Lazy } from "../../../shared/utils/lazy.ts";
2
- import type { Leaves } from "../../../shared/utils/object.ts";
3
2
  import type { DisplayNameCustomizer } from "../../utils/displayName.ts";
4
- import type { GetNestedDeclarations, GetReferences, Predicate, Serializer, TypeArgumentsResolver, Validator } from "../Node.ts";
3
+ import type { GetNestedDeclarations, GetReferences, Predicate, Serialized, TypeArgumentsResolver, Validator } from "../Node.ts";
5
4
  import { NodeKind } from "../Node.ts";
6
5
  import type { MemberDecl, ObjectType } from "../types/generic/ObjectType.ts";
7
6
  import { StringType } from "../types/primitives/StringType.ts";
8
- import type { AsType } from "../types/Type.ts";
7
+ import type { NestedEntityMapType } from "../types/references/NestedEntityMapType.ts";
9
8
  import type { BaseDecl } from "./Declaration.ts";
10
9
  import { TypeAliasDecl } from "./TypeAliasDecl.ts";
11
10
  export type GenericEntityDisplayName = string | {
12
11
  pathToLocaleMap?: string;
13
12
  pathInLocaleMap?: string;
14
13
  } | null;
15
- export type EntityDisplayName<T extends TConstraint> = Leaves<AsType<ObjectType<T>>> | {
14
+ export type EntityDisplayName<T extends TConstraint> = PathTo<T, StringType> | {
16
15
  /**
17
16
  * @default "translations"
18
17
  */
19
- pathToLocaleMap?: Leaves<AsType<ObjectType<T>>>;
18
+ pathToLocaleMap?: PathTo<T, NestedEntityMapType>;
20
19
  /**
21
20
  * @default "name"
22
21
  */
23
22
  pathInLocaleMap?: string;
24
23
  } | null;
25
24
  type TConstraint = Record<string, MemberDecl>;
25
+ type PathTo<T extends TConstraint, R> = {
26
+ [K in keyof T]: T[K] extends MemberDecl<infer V> ? V extends R ? K : R extends V ? string : T[K] extends ObjectType<infer P> ? `${Extract<K, string>}.${PathTo<P, R>}` : never : never;
27
+ }[Extract<keyof T, string>];
26
28
  export interface EntityDecl<Name extends string = string, T extends TConstraint = TConstraint, FK extends Extract<keyof T, string> | undefined = Extract<keyof T, string> | undefined> extends BaseDecl<Name, []> {
27
29
  kind: NodeKind["EntityDecl"];
28
30
  namePlural: string;
@@ -31,7 +33,7 @@ export interface EntityDecl<Name extends string = string, T extends TConstraint
31
33
  /**
32
34
  * @default "name"
33
35
  */
34
- displayName?: EntityDisplayName<T>;
36
+ displayName?: GenericEntityDisplayName;
35
37
  displayNameCustomizer?: DisplayNameCustomizer<ObjectType<T>>;
36
38
  isDeprecated?: boolean;
37
39
  }
@@ -75,5 +77,5 @@ export declare const addEphemeralUUIDToType: <T extends TConstraint>(decl: Entit
75
77
  }>;
76
78
  export declare const createEntityIdentifierType: () => StringType;
77
79
  export declare const createEntityIdentifierTypeAsDecl: <Name extends string>(decl: EntityDecl<Name>) => TypeAliasDecl<`${Name}_ID`, StringType, []>;
78
- export declare const serializeEntityDecl: Serializer<EntityDecl>;
80
+ export declare const serializeEntityDecl: <Name extends string, T extends TConstraint, FK extends Extract<keyof T, string> | undefined>(type: EntityDecl<Name, T, FK>) => Serialized<EntityDecl<Name, T, FK>>;
79
81
  export declare const getReferencesForEntityDecl: GetReferences<EntityDecl>;
@@ -49,12 +49,12 @@ export const createEntityIdentifierTypeAsDecl = (decl) => TypeAliasDecl(decl.sou
49
49
  name: (decl.name + "_ID"),
50
50
  type: createEntityIdentifierType,
51
51
  });
52
- export const serializeEntityDecl = (type) => ({
52
+ export const serializeEntityDecl = ((type) => ({
53
53
  ...type,
54
54
  type: serializeObjectType(type.type.value),
55
55
  displayName: typeof type.displayName === "function"
56
56
  ? null
57
57
  : type.displayName,
58
58
  displayNameCustomizer: type.displayNameCustomizer !== undefined,
59
- });
59
+ }));
60
60
  export const getReferencesForEntityDecl = (decl, value, inDecl) => getReferencesForObjectType(decl.type.value, value, [...inDecl, decl]);
@@ -1,3 +1,4 @@
1
+ import { type EnumValue } from "../../../shared/schema/declarations/EnumDecl.ts";
1
2
  import { Lazy } from "../../../shared/utils/lazy.ts";
2
3
  import type { GetNestedDeclarations, GetReferences, Predicate, Serializer, TypeArgumentsResolver, ValidatorOfParamDecl } from "../Node.ts";
3
4
  import { NodeKind } from "../Node.ts";
@@ -33,3 +34,4 @@ export declare const resolveTypeArgumentsInEnumDecl: TypeArgumentsResolver<EnumD
33
34
  export declare const serializeEnumDecl: Serializer<EnumDecl>;
34
35
  export declare const getReferencesForEnumDecl: GetReferences<EnumDecl>;
35
36
  export declare const cases: <T extends TConstraint>(decl: EnumDecl<string, T>) => EnumCaseDecl<T[keyof T]["type"]>[];
37
+ export declare const getAnyEnumCaseValue: <K extends string, V>(enumValue: { [Key in K]: EnumValue<Key, V>; }[K]) => V;
@@ -1,3 +1,4 @@
1
+ import { ENUM_DISCRIMINATOR_KEY, } from "../../../shared/schema/declarations/EnumDecl.js";
1
2
  import { Lazy } from "../../../shared/utils/lazy.js";
2
3
  import { onlyKeys } from "../../../shared/utils/object.js";
3
4
  import { NodeKind } from "../Node.js";
@@ -46,3 +47,4 @@ export const serializeEnumDecl = decl => ({
46
47
  });
47
48
  export const getReferencesForEnumDecl = (decl, value, inDecl) => getReferencesForEnumType(decl.type.value, value, [...inDecl, decl]);
48
49
  export const cases = (decl) => Object.values(decl.type.value.values);
50
+ export const getAnyEnumCaseValue = (enumValue) => enumValue[enumValue[ENUM_DISCRIMINATOR_KEY]];
@@ -25,14 +25,28 @@ type EnumCaseTypeAsType<Case extends string, T extends Type | null> = T extends
25
25
  } : {
26
26
  kind: Case;
27
27
  };
28
- export type AsDeepType<T extends Type> = T extends ArrayType<infer I> ? AsType<I>[] : T extends ObjectType<infer P> ? {
28
+ type EnumCaseTypeAsDeepType<Case extends string, T extends Type | null> = T extends object ? {
29
+ kind: Case;
30
+ } & {
31
+ [AV in Case]: AsDeepType<T>;
32
+ } : {
33
+ kind: Case;
34
+ };
35
+ type MemberDeclsAsType<P extends Record<string, MemberDecl>> = {
29
36
  [K in keyof P]: P[K] extends MemberDecl<Type, true> ? AsType<P[K]["type"]> : AsType<P[K]["type"]> | undefined;
30
- } : T extends BooleanType ? boolean : T extends DateType ? Date : T extends FloatType ? number : T extends IntegerType ? number : T extends StringType ? string : T extends TypeArgumentType ? unknown : T extends IncludeIdentifierType<TypeParameter[], infer Decl> ? Decl extends TypeAliasDecl<string, infer TA> ? AsType<TA> : Decl extends EnumDecl<string, infer EC> ? AsType<EnumType<EC>> : unknown : T extends NestedEntityMapType ? unknown : T extends ReferenceIdentifierType ? string : T extends ChildEntitiesType ? string[] : T extends EnumType<infer EC> ? EC extends Record<string, EnumCaseDecl> ? {
31
- [Case in keyof EC]: EnumCaseTypeAsType<Case & string, EC[Case]["type"]>;
37
+ };
38
+ type MemberDeclsAsDeepType<P extends Record<string, MemberDecl>> = {
39
+ [K in keyof P]: P[K] extends MemberDecl<Type, true> ? AsDeepType<P[K]["type"]> : // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents -- it does make a difference here
40
+ AsDeepType<P[K]["type"]> | undefined;
41
+ };
42
+ export type AsDeepType<T extends Type> = T extends ArrayType<infer I> ? AsDeepType<I>[] : T extends ObjectType<infer P> ? MemberDeclsAsDeepType<P> : T extends BooleanType ? boolean : T extends DateType ? Date : T extends FloatType ? number : T extends IntegerType ? number : T extends StringType ? string : T extends TypeArgumentType ? unknown : T extends IncludeIdentifierType<TypeParameter[], infer Decl> ? Decl extends TypeAliasDecl<string, infer TA> ? AsDeepType<TA> : Decl extends EnumDecl<string, infer EC> ? AsDeepType<EnumType<EC>> : unknown : T extends NestedEntityMapType<string, infer TC> ? {
43
+ [id: string]: MemberDeclsAsDeepType<TC>;
44
+ } : T extends ReferenceIdentifierType ? string : T extends ChildEntitiesType ? never : T extends EnumType<infer EC> ? EC extends Record<string, EnumCaseDecl> ? {
45
+ [Case in keyof EC]: EnumCaseTypeAsDeepType<Case & string, EC[Case]["type"]>;
32
46
  }[keyof EC] : never : never;
33
- export type AsType<T extends Type> = T extends ArrayType<infer I> ? AsType<I>[] : T extends ObjectType<infer P> ? {
34
- [K in keyof P]: P[K] extends MemberDecl<Type, true> ? AsType<P[K]["type"]> : AsType<P[K]["type"]> | undefined;
35
- } : T extends BooleanType ? boolean : T extends DateType ? Date : T extends FloatType ? number : T extends IntegerType ? number : T extends StringType ? string : T extends TypeArgumentType ? unknown : T extends IncludeIdentifierType ? unknown : T extends NestedEntityMapType ? unknown : T extends ReferenceIdentifierType ? string : T extends ChildEntitiesType ? string[] : T extends EnumType<infer EC> ? EC extends Record<string, EnumCaseDecl> ? {
47
+ export type AsType<T extends Type> = T extends ArrayType<infer I> ? AsType<I>[] : T extends ObjectType<infer P> ? MemberDeclsAsType<P> : T extends BooleanType ? boolean : T extends DateType ? Date : T extends FloatType ? number : T extends IntegerType ? number : T extends StringType ? string : T extends TypeArgumentType ? unknown : T extends IncludeIdentifierType ? unknown : T extends NestedEntityMapType<string, infer TC> ? {
48
+ [id: string]: MemberDeclsAsType<TC>;
49
+ } : T extends ReferenceIdentifierType ? string : T extends ChildEntitiesType ? string[] : T extends EnumType<infer EC> ? EC extends Record<string, EnumCaseDecl> ? {
36
50
  [Case in keyof EC]: EnumCaseTypeAsType<Case & string, EC[Case]["type"]>;
37
51
  }[keyof EC] : never : never;
38
52
  export type AsNode<T> = T extends (infer I)[] ? ArrayType<AsNode<I>> : T extends Record<string, unknown> ? ObjectType<{
@@ -2,10 +2,13 @@ import type { NumberConstraints } from "../../../../shared/validation/number.ts"
2
2
  import type { GetNestedDeclarations, GetReferences, Predicate, Serializer, TypeArgumentsResolver, Validator } from "../../Node.ts";
3
3
  import { NodeKind } from "../../Node.ts";
4
4
  import type { BaseType, StructureFormatter } from "../Type.ts";
5
- export interface FloatType extends BaseType, NumberConstraints {
5
+ export interface FloatConstraints extends NumberConstraints {
6
+ fractionDigits?: number;
7
+ }
8
+ export interface FloatType extends BaseType, FloatConstraints {
6
9
  kind: NodeKind["FloatType"];
7
10
  }
8
- export declare const FloatType: (options?: NumberConstraints) => FloatType;
11
+ export declare const FloatType: (options?: FloatConstraints) => FloatType;
9
12
  export { FloatType as Float };
10
13
  export declare const isFloatType: Predicate<FloatType>;
11
14
  export declare const getNestedDeclarationsInFloatType: GetNestedDeclarations<FloatType>;
@@ -1,10 +1,16 @@
1
1
  import { validateNumberConstraints } from "../../../../shared/validation/number.js";
2
2
  import { json } from "../../../utils/errorFormatting.js";
3
3
  import { NodeKind } from "../../Node.js";
4
- export const FloatType = (options = {}) => ({
5
- ...options,
6
- kind: NodeKind.FloatType,
7
- });
4
+ export const FloatType = (options = {}) => {
5
+ if (options.fractionDigits !== undefined &&
6
+ (!Number.isInteger(options.fractionDigits) || options.fractionDigits < 1)) {
7
+ throw new TypeError("The fractionDigits option must be a positive integer");
8
+ }
9
+ return {
10
+ ...options,
11
+ kind: NodeKind.FloatType,
12
+ };
13
+ };
8
14
  export { FloatType as Float };
9
15
  export const isFloatType = node => node.kind === NodeKind.FloatType;
10
16
  export const getNestedDeclarationsInFloatType = addedDecls => addedDecls;
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { hasFileChanges, splitBranchName } from "../../../shared/utils/git.js";
5
5
  import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
6
6
  import { getGroupedInstancesFromDatabaseInMemory } from "../../utils/databaseInMemory.js";
7
+ import { attachGitStatusToDatabaseInMemory } from "../../utils/git.js";
7
8
  import { reinit } from "../init.js";
8
9
  import { createChildInstancesForInstanceIdGetter } from "../utils/childInstances.js";
9
10
  const debug = Debug("tsondb:server:api:git");
@@ -24,6 +25,9 @@ gitApi.get("/", (req, res) => {
24
25
  });
25
26
  gitApi.get("/status", async (req, res) => {
26
27
  const status = await req.git.status();
28
+ req.setLocal("databaseInMemory", attachGitStatusToDatabaseInMemory(req.databaseInMemory, req.dataRoot,
29
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
30
+ req.gitRoot, status));
27
31
  const getChildInstancesForInstanceId = createChildInstancesForInstanceIdGetter(req);
28
32
  const body = {
29
33
  currentBranch: status.current,
@@ -2,10 +2,12 @@ import express from "express";
2
2
  import { declarationsApi } from "./declarations.js";
3
3
  import { gitApi } from "./git.js";
4
4
  import { instancesApi } from "./instances.js";
5
+ import { searchApi } from "./search.js";
5
6
  export const api = express.Router();
6
7
  api.use("/declarations", declarationsApi);
7
8
  api.use("/instances", instancesApi);
8
9
  api.use("/git", gitApi);
10
+ api.use("/search", searchApi);
9
11
  api.get("/config", (req, res) => {
10
12
  const body = {
11
13
  localeEntityName: req.localeEntity?.name,
@@ -0,0 +1 @@
1
+ export declare const searchApi: import("express-serve-static-core").Router;
@@ -0,0 +1,50 @@
1
+ import Debug from "debug";
2
+ import express from "express";
3
+ import { isEntityDeclWithParentReference } from "../../schema/index.js";
4
+ import { getGroupedInstancesFromDatabaseInMemory } from "../../utils/databaseInMemory.js";
5
+ import { getDisplayNameFromEntityInstance } from "../../utils/displayName.js";
6
+ import { createChildInstancesForInstanceIdGetter } from "../utils/childInstances.js";
7
+ import { getQueryParamString } from "../utils/query.js";
8
+ const debug = Debug("tsondb:server:api:search");
9
+ export const searchApi = express.Router();
10
+ searchApi.get("/", (req, res) => {
11
+ const query = getQueryParamString(req.query, "q")?.toLowerCase() ?? "";
12
+ debug('search for items containing "%s"', query);
13
+ const getChildInstancesForInstanceId = createChildInstancesForInstanceIdGetter(req);
14
+ const body = {
15
+ query,
16
+ results: query.length === 0
17
+ ? []
18
+ : getGroupedInstancesFromDatabaseInMemory(req.databaseInMemory)
19
+ .flatMap(([entityName, instances]) => {
20
+ const entity = req.entitiesByName[entityName];
21
+ if (entity && isEntityDeclWithParentReference(entity)) {
22
+ return [];
23
+ }
24
+ return instances
25
+ .map((instance) => {
26
+ const { name, localeId } = getDisplayNameFromEntityInstance(
27
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
28
+ req.entitiesByName[entityName], instance, req.getInstanceById, getChildInstancesForInstanceId, req.locales);
29
+ const searchableName = name.toLowerCase();
30
+ return [
31
+ entityName,
32
+ {
33
+ id: instance.id,
34
+ displayName: name,
35
+ displayNameLocaleId: localeId,
36
+ relevance: instance.id.startsWith(query) || searchableName.startsWith(query)
37
+ ? 1
38
+ : instance.id.includes(query) || searchableName.includes(query)
39
+ ? 0.5
40
+ : 0,
41
+ },
42
+ ];
43
+ })
44
+ .filter(instance => instance[1].relevance > 0);
45
+ })
46
+ .toSorted((a, b) => a[1].relevance - b[1].relevance ||
47
+ a[1].displayName.localeCompare(b[1].displayName, a[1].displayNameLocaleId)),
48
+ };
49
+ res.json(body);
50
+ });
@@ -8,7 +8,7 @@ export const createInstance = async (locals, instance, idQueryParam) => {
8
8
  if (entity === undefined) {
9
9
  return error(new HTTPError(400, "Entity not found"));
10
10
  }
11
- const databaseTransactionResult = await runDatabaseTransaction(locals.dataRoot, locals.gitRoot ? locals.git : undefined, locals.entitiesByName, locals.databaseInMemory, locals.referencesToInstances, res => saveInstanceTree(locals.entitiesByName, undefined, locals.localeEntity, instance.entityName, undefined, instance, idQueryParam, res));
11
+ const databaseTransactionResult = await runDatabaseTransaction(locals.dataRoot, locals.gitRoot ? locals.git : undefined, locals.entitiesByName, locals.databaseInMemory, locals.referencesToInstances, res => saveInstanceTree(locals.entitiesByName, undefined, undefined, locals.localeEntity, instance.entityName, undefined, instance, idQueryParam, res));
12
12
  if (isError(databaseTransactionResult)) {
13
13
  return databaseTransactionResult;
14
14
  }
@@ -27,7 +27,7 @@ export const updateInstance = async (locals, instance) => {
27
27
  return error(new HTTPError(400, "Entity not found"));
28
28
  }
29
29
  const oldChildInstances = getChildInstances(locals.databaseInMemory, entity, instance.id, true);
30
- const databaseTransactionResult = await runDatabaseTransaction(locals.dataRoot, locals.gitRoot ? locals.git : undefined, locals.entitiesByName, locals.databaseInMemory, locals.referencesToInstances, res => saveInstanceTree(locals.entitiesByName, undefined, locals.localeEntity, instance.entityName, {
30
+ const databaseTransactionResult = await runDatabaseTransaction(locals.dataRoot, locals.gitRoot ? locals.git : undefined, locals.entitiesByName, locals.databaseInMemory, locals.referencesToInstances, res => saveInstanceTree(locals.entitiesByName, undefined, undefined, locals.localeEntity, instance.entityName, {
31
31
  id: instance.id,
32
32
  content: instanceContainer.content,
33
33
  childInstances: oldChildInstances,
@@ -51,7 +51,7 @@ export const deleteInstance = async (locals, entityName, instanceId) => {
51
51
  return error(new HTTPError(400, "Entity not found"));
52
52
  }
53
53
  const oldChildInstances = getChildInstances(locals.databaseInMemory, entity, instanceId, true);
54
- const databaseTransactionResult = await runDatabaseTransaction(locals.dataRoot, locals.gitRoot ? locals.git : undefined, locals.entitiesByName, locals.databaseInMemory, locals.referencesToInstances, res => saveInstanceTree(locals.entitiesByName, undefined, locals.localeEntity, entityName, {
54
+ const databaseTransactionResult = await runDatabaseTransaction(locals.dataRoot, locals.gitRoot ? locals.git : undefined, locals.entitiesByName, locals.databaseInMemory, locals.referencesToInstances, res => saveInstanceTree(locals.entitiesByName, undefined, undefined, locals.localeEntity, entityName, {
55
55
  id: instanceId,
56
56
  content: instanceContainer.content,
57
57
  childInstances: oldChildInstances,
@@ -0,0 +1,2 @@
1
+ import type { Request } from "express";
2
+ export declare const getQueryParamString: (parsedQueryString: Request["query"], key: string) => string | undefined;
@@ -0,0 +1,7 @@
1
+ export const getQueryParamString = (parsedQueryString, key) => {
2
+ const value = parsedQueryString[key];
3
+ if (typeof value === "string") {
4
+ return value;
5
+ }
6
+ return;
7
+ };
@@ -29,6 +29,6 @@ export interface GenEntityTaggedInstanceContainerWithChildInstances<ID extends s
29
29
  }
30
30
  export declare const getChildInstancesFromEntity: (databaseInMemory: DatabaseInMemory, parentEntity: EntityDecl, parentId: string, childEntity: EntityDeclWithParentReference) => InstanceContainer[];
31
31
  export declare const getChildInstances: (databaseInMemory: DatabaseInMemory, parentEntity: EntityDecl, parentId: string, recursive?: boolean) => EntityTaggedInstanceContainerWithChildInstances[];
32
- export declare const saveInstanceTree: (entitiesByName: Record<string, EntityDecl>, parentId: string | undefined, localeEntity: EntityDecl | undefined, entityName: string, oldInstance: EntityTaggedInstanceContainerWithChildInstances | undefined, newInstance: UnsafeEntityTaggedInstanceContainerWithChildInstances | undefined, customId: unknown, res: TransactionResult) => TransactionResult<{
32
+ export declare const saveInstanceTree: (entitiesByName: Record<string, EntityDecl>, parentEntityName: string | undefined, parentId: string | undefined, localeEntity: EntityDecl | undefined, entityName: string, oldInstance: EntityTaggedInstanceContainerWithChildInstances | undefined, newInstance: UnsafeEntityTaggedInstanceContainerWithChildInstances | undefined, customId: unknown, res: TransactionResult) => TransactionResult<{
33
33
  instanceContainer: InstanceContainer;
34
34
  }>;
@@ -1,6 +1,7 @@
1
+ import { createEnumCaseValue } from "../../shared/schema/declarations/EnumDecl.js";
1
2
  import { hasKey } from "../../shared/utils/object.js";
2
3
  import { error, isError, map, ok } from "../../shared/utils/result.js";
3
- import { isEntityDeclWithParentReference, reduceNodes } from "../schema/index.js";
4
+ import { isEntityDeclWithParentReference, isEnumDecl, isIncludeIdentifierType, reduceNodes, } from "../schema/index.js";
4
5
  import { isChildEntitiesType } from "../schema/types/references/ChildEntitiesType.js";
5
6
  import { getInstancesOfEntityFromDatabaseInMemory, } from "./databaseInMemory.js";
6
7
  import { HTTPError } from "./error.js";
@@ -30,21 +31,26 @@ export const getChildInstances = (databaseInMemory, parentEntity, parentId, recu
30
31
  : [],
31
32
  })));
32
33
  };
33
- const prepareNewChildInstanceContent = (entity, parentId, content) => {
34
+ const prepareNewChildInstanceContent = (entity, parentEntityName, parentId, content) => {
34
35
  if (isEntityDeclWithParentReference(entity)) {
35
- if (parentId === undefined) {
36
+ if (parentEntityName === undefined || parentId === undefined) {
36
37
  return error(new HTTPError(400, `Cannot create instance of child entity "${entity.name}" without parent reference`));
37
38
  }
39
+ const parentReferenceType = entity.type.value.properties[entity.parentReferenceKey]?.type;
38
40
  return ok({
39
41
  ...content,
40
- [entity.parentReferenceKey]: parentId,
42
+ [entity.parentReferenceKey]: parentReferenceType &&
43
+ isIncludeIdentifierType(parentReferenceType) &&
44
+ isEnumDecl(parentReferenceType.reference)
45
+ ? createEnumCaseValue(parentEntityName, parentId)
46
+ : parentId,
41
47
  });
42
48
  }
43
49
  else {
44
50
  return ok(content);
45
51
  }
46
52
  };
47
- export const saveInstanceTree = (entitiesByName, parentId, localeEntity, entityName, oldInstance, newInstance, customId, res) => {
53
+ export const saveInstanceTree = (entitiesByName, parentEntityName, parentId, localeEntity, entityName, oldInstance, newInstance, customId, res) => {
48
54
  if (isError(res)) {
49
55
  return res;
50
56
  }
@@ -59,14 +65,14 @@ export const saveInstanceTree = (entitiesByName, parentId, localeEntity, entityN
59
65
  }
60
66
  // delete all child instances recursively
61
67
  const deletedRes = deleteInstance(res, entity, oldInstance.id);
62
- return map(oldInstance.childInstances.reduce((resAcc, oldChildInstance) => saveInstanceTree(entitiesByName, oldInstance.id, localeEntity, oldInstance.entityName, oldChildInstance, undefined, undefined, resAcc), deletedRes), data => ({
68
+ return map(oldInstance.childInstances.reduce((resAcc, oldChildInstance) => saveInstanceTree(entitiesByName, oldInstance.entityName, oldInstance.id, localeEntity, oldChildInstance.entityName, oldChildInstance, undefined, undefined, resAcc), deletedRes), data => ({
63
69
  ...data,
64
70
  instanceContainer: { id: oldInstance.id, content: oldInstance.content },
65
71
  }));
66
72
  }
67
73
  else {
68
74
  const preparedContent = newInstance.id === undefined
69
- ? prepareNewChildInstanceContent(entity, parentId, newInstance.content)
75
+ ? prepareNewChildInstanceContent(entity, parentEntityName, parentId, newInstance.content)
70
76
  : ok(newInstance.content);
71
77
  if (isError(preparedContent)) {
72
78
  return preparedContent;
@@ -85,8 +91,8 @@ export const saveInstanceTree = (entitiesByName, parentId, localeEntity, entityN
85
91
  const setResWithoutInfo = ok({ ...setRes.value, additionalInformation: undefined });
86
92
  return map(newInstance.childInstances
87
93
  .filter(newChildInstance => newChildInstance.id === undefined)
88
- .reduce((resAcc, newChildInstance) => saveInstanceTree(entitiesByName, instanceId, localeEntity, newChildInstance.entityName, undefined, newChildInstance, undefined, resAcc), oldInstance
89
- ? oldInstance.childInstances.reduce((resAcc, oldChildInstance) => saveInstanceTree(entitiesByName, instanceId, localeEntity, oldChildInstance.entityName, oldChildInstance, newInstance.childInstances.find(ci => ci.id === oldChildInstance.id), undefined, resAcc), setResWithoutInfo)
94
+ .reduce((resAcc, newChildInstance) => saveInstanceTree(entitiesByName, newInstance.entityName, instanceId, localeEntity, newChildInstance.entityName, undefined, newChildInstance, undefined, resAcc), oldInstance
95
+ ? oldInstance.childInstances.reduce((resAcc, oldChildInstance) => saveInstanceTree(entitiesByName, oldInstance.entityName, instanceId, localeEntity, oldChildInstance.entityName, oldChildInstance, newInstance.childInstances.find(ci => ci.id === oldChildInstance.id), undefined, resAcc), setResWithoutInfo)
90
96
  : setResWithoutInfo), data => ({
91
97
  ...data,
92
98
  instanceContainer: { id: instanceId, content: preparedContent.value },
@@ -4,6 +4,7 @@ export const getDisplayNameFromEntityInstance = (entity, instanceContainer, getI
4
4
  if (useCustomizer && entity.displayNameCustomizer) {
5
5
  const calculatedName = getDisplayNameFromEntityInstance(entity, instanceContainer, getInstanceById, getChildInstancesForInstanceId, locales, defaultName, false);
6
6
  return entity.displayNameCustomizer({
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment -- otherwise type instiatiation too deep
7
8
  instance: instanceContainer.content,
8
9
  instanceId: instanceContainer.id,
9
10
  instanceDisplayName: calculatedName.name,
@@ -87,3 +87,7 @@ export interface CreateCommitRequestBody {
87
87
  export interface CreateBranchRequestBody {
88
88
  branchName: string;
89
89
  }
90
+ export interface SearchResponseBody {
91
+ query: string;
92
+ results: [entityName: string, instane: InstanceContainerOverview][];
93
+ }
@@ -1,20 +1,24 @@
1
- import type { Leaves } from "../../../shared/utils/object.ts";
1
+ import type { GenericEntityDisplayName } from "../../../node/schema/index.ts";
2
2
  import { NodeKind, type GetReferencesSerialized, type SerializedNode, type SerializedTypeArgumentsResolver } from "../Node.ts";
3
+ import type { SerializedNestedEntityMapType } from "../types/NestedEntityMapType.ts";
3
4
  import { type SerializedMemberDecl, type SerializedObjectType } from "../types/ObjectType.ts";
4
- import type { SerializedAsType } from "../types/Type.ts";
5
+ import type { SerializedStringType } from "../types/StringType.ts";
5
6
  import type { SerializedBaseDecl } from "./Declaration.ts";
6
- export type SerializedEntityDisplayName<T extends SerializedObjectType> = Leaves<SerializedAsType<T>> | {
7
+ export type SerializedEntityDisplayName<T extends TSerializedConstraint> = SerializedPathTo<T, SerializedStringType> | {
7
8
  /**
8
9
  * @default "translations"
9
10
  */
10
- pathToLocaleMap?: Leaves<SerializedAsType<T>>;
11
+ pathToLocaleMap?: SerializedPathTo<T, SerializedNestedEntityMapType>;
11
12
  /**
12
13
  * @default "name"
13
14
  */
14
15
  pathInLocaleMap?: string;
15
16
  } | null;
16
- type TConstraint = Record<string, SerializedMemberDecl>;
17
- export interface SerializedEntityDecl<Name extends string = string, T extends TConstraint = TConstraint, FK extends Extract<keyof T, string> | undefined = Extract<keyof T, string> | undefined> extends SerializedBaseDecl<Name, []> {
17
+ type TSerializedConstraint = Record<string, SerializedMemberDecl>;
18
+ type SerializedPathTo<T extends TSerializedConstraint, R> = {
19
+ [K in keyof T]: T[K] extends SerializedMemberDecl<infer V> ? V extends R ? K : R extends V ? string : T[K] extends SerializedObjectType<infer P> ? `${Extract<K, string>}.${SerializedPathTo<P, R>}` : never : never;
20
+ }[Extract<keyof T, string>];
21
+ export interface SerializedEntityDecl<Name extends string = string, T extends TSerializedConstraint = TSerializedConstraint, FK extends Extract<keyof T, string> | undefined = Extract<keyof T, string> | undefined> extends SerializedBaseDecl<Name, []> {
18
22
  kind: NodeKind["EntityDecl"];
19
23
  namePlural: string;
20
24
  type: SerializedObjectType<T>;
@@ -22,13 +26,13 @@ export interface SerializedEntityDecl<Name extends string = string, T extends TC
22
26
  /**
23
27
  * @default "name"
24
28
  */
25
- displayName?: SerializedEntityDisplayName<SerializedObjectType<T>>;
29
+ displayName?: GenericEntityDisplayName;
26
30
  displayNameCustomizer: boolean;
27
31
  isDeprecated?: boolean;
28
32
  }
29
33
  export declare const isSerializedEntityDecl: (node: SerializedNode) => node is SerializedEntityDecl;
30
- export declare const isSerializedEntityDeclWithParentReference: <Name extends string, T extends TConstraint, FK extends Extract<keyof T, string> | undefined>(decl: SerializedEntityDecl<Name, T, FK>) => decl is SerializedEntityDecl<Name, T, NonNullable<FK>>;
31
- export declare const isSerializedEntityDeclWithoutParentReference: <Name extends string, T extends TConstraint>(decl: SerializedEntityDecl<Name, T>) => decl is SerializedEntityDecl<Name, T, undefined>;
34
+ export declare const isSerializedEntityDeclWithParentReference: <Name extends string, T extends TSerializedConstraint, FK extends Extract<keyof T, string> | undefined>(decl: SerializedEntityDecl<Name, T, FK>) => decl is SerializedEntityDecl<Name, T, NonNullable<FK>>;
35
+ export declare const isSerializedEntityDeclWithoutParentReference: <Name extends string, T extends TSerializedConstraint>(decl: SerializedEntityDecl<Name, T>) => decl is SerializedEntityDecl<Name, T, undefined>;
32
36
  export declare const resolveTypeArgumentsInSerializedEntityDecl: SerializedTypeArgumentsResolver<SerializedEntityDecl>;
33
37
  export declare const getReferencesForSerializedEntityDecl: GetReferencesSerialized<SerializedEntityDecl>;
34
38
  export {};
@@ -10,3 +10,11 @@ export interface SerializedEnumDecl<Name extends string = string, T extends Reco
10
10
  export declare const isSerializedEnumDecl: (node: SerializedNode) => node is SerializedEnumDecl;
11
11
  export declare const resolveTypeArgumentsInSerializedEnumDecl: SerializedTypeArgumentsResolver<SerializedEnumDecl>;
12
12
  export declare const getReferencesForSerializedEnumDecl: GetReferencesSerialized<SerializedEnumDecl>;
13
+ export declare const ENUM_DISCRIMINATOR_KEY = "kind";
14
+ export type ENUM_DISCRIMINATOR_KEY = typeof ENUM_DISCRIMINATOR_KEY;
15
+ export type EnumValue<K extends string, V> = {
16
+ [ENUM_DISCRIMINATOR_KEY]: K;
17
+ } & {
18
+ [Key2 in K]: V;
19
+ };
20
+ export declare const createEnumCaseValue: <K extends string, V>(caseName: K, caseValue: V) => EnumValue<K, V>;
@@ -9,3 +9,8 @@ export const resolveTypeArgumentsInSerializedEnumDecl = (decls, args, decl) => {
9
9
  };
10
10
  };
11
11
  export const getReferencesForSerializedEnumDecl = (decls, decl, value) => getReferencesForSerializedEnumType(decls, decl.type, value);
12
+ export const ENUM_DISCRIMINATOR_KEY = "kind";
13
+ export const createEnumCaseValue = (caseName, caseValue) => ({
14
+ [ENUM_DISCRIMINATOR_KEY]: caseName,
15
+ [caseName]: caseValue,
16
+ });
@@ -1,11 +1,12 @@
1
- import type { RangeBound } from "../../validation/number.ts";
1
+ import type { NumberConstraints } from "../../validation/number.ts";
2
2
  import type { GetReferencesSerialized, NodeKind, SerializedTypeArgumentsResolver } from "../Node.ts";
3
3
  import type { SerializedBaseType } from "./Type.ts";
4
- export interface SerializedFloatType extends SerializedBaseType {
4
+ export interface FloatConstraints extends NumberConstraints {
5
+ fractionDigits?: number;
6
+ }
7
+ export declare const DEFAULT_FRACTION_DIGITS = 2;
8
+ export interface SerializedFloatType extends SerializedBaseType, FloatConstraints {
5
9
  kind: NodeKind["FloatType"];
6
- minimum?: RangeBound;
7
- maximum?: RangeBound;
8
- multipleOf?: number;
9
10
  }
10
11
  export declare const resolveTypeArgumentsInSerializedFloatType: SerializedTypeArgumentsResolver<SerializedFloatType>;
11
12
  export declare const getReferencesForSerializedFloatType: GetReferencesSerialized<SerializedFloatType>;
@@ -1,2 +1,3 @@
1
+ export const DEFAULT_FRACTION_DIGITS = 2;
1
2
  export const resolveTypeArgumentsInSerializedFloatType = (_decls, _args, type) => type;
2
3
  export const getReferencesForSerializedFloatType = () => [];
@@ -2,7 +2,7 @@ export declare const sortObjectKeys: (obj: Record<string, unknown>, keys: string
2
2
  export declare const sortObjectKeysAlphabetically: (obj: Record<string, unknown>) => Record<string, unknown>;
3
3
  export declare const mergeObjects: <T>(obj1: Record<string, T>, obj2: Record<string, T>, solveConflict: (a: T, b: T) => T) => Record<string, T>;
4
4
  export type Leaves<T> = T extends object ? {
5
- [K in keyof T]: T[K] extends unknown[] ? never : `${Exclude<K, symbol>}${Leaves<T[K]> extends never ? "" : `.${Leaves<T[K]>}`}`;
5
+ [K in keyof T]: T[K] extends unknown[] ? never : `${Extract<K, string>}${Leaves<T[K]> extends never ? "" : `.${Leaves<T[K]>}`}`;
6
6
  }[keyof T] : never;
7
7
  export declare const onlyKeys: <T extends object, K extends keyof T>(obj: T, ...keys: K[]) => Pick<T, K>;
8
8
  export declare const hasKey: <T extends object, K extends PropertyKey>(obj: T, key: K) => obj is T & { [k in K]: unknown; };
@@ -0,0 +1,2 @@
1
+ import type { SearchResponseBody } from "../../shared/api.ts";
2
+ export declare const searchInstances: (locales: string[], query: string) => Promise<SearchResponseBody>;
@@ -0,0 +1,7 @@
1
+ import { getResource } from "../utils/api.js";
2
+ export const searchInstances = async (locales, query) => getResource("/api/search", {
3
+ locales,
4
+ modifyUrl: url => {
5
+ url.searchParams.set("q", query);
6
+ },
7
+ });
@@ -6,7 +6,7 @@ import { Settings } from "./Settings.js";
6
6
  export const Layout = ({ breadcrumbs, children }) => {
7
7
  const [_1, setIsGitOpen] = useContext(GitContext);
8
8
  const [isGitAlwaysOpen, _2] = useSetting("gitSidebar");
9
- return (_jsxs(_Fragment, { children: [_jsxs("header", { children: [_jsx("nav", { children: _jsx("ol", { children: breadcrumbs.map(({ url, label }) => (_jsx("li", { children: _jsx("a", { href: url, children: label }) }, url))) }) }), _jsxs("div", { class: "nav-buttons", children: [_jsx("button", { class: `git-toggle${!isGitAlwaysOpen ? " git-toggle--no-sidebar" : ""}`, onClick: () => {
9
+ return (_jsxs(_Fragment, { children: [_jsxs("header", { children: [_jsx("nav", { children: _jsx("ol", { children: breadcrumbs.map(({ url, label }) => (_jsx("li", { children: _jsx("a", { href: url, children: label }) }, url))) }) }), _jsxs("div", { class: "nav-buttons", children: [_jsx("a", { href: "/search", class: "btn", children: "Search" }), _jsx("button", { class: `git-toggle${!isGitAlwaysOpen ? " git-toggle--no-sidebar" : ""}`, onClick: () => {
10
10
  setIsGitOpen(b => !b);
11
11
  }, children: "File changes" }), _jsx(Settings, {})] })] }), _jsx("main", { children: children })] }));
12
12
  };
@@ -2,5 +2,5 @@ import { jsx as _jsx } from "preact/jsx-runtime";
2
2
  import { getGitStatusForDisplay, getLabelForGitStatus, } from "../../../shared/utils/git.js";
3
3
  export const GitStatusIndicator = ({ status }) => {
4
4
  const gitStatusForDisplay = getGitStatusForDisplay(status);
5
- return (_jsx("span", { class: `git-status git-status--${gitStatusForDisplay ?? ""}`, title: getLabelForGitStatus(gitStatusForDisplay), children: gitStatusForDisplay }));
5
+ return gitStatusForDisplay === undefined ? null : (_jsx("span", { class: `git-status git-status--${gitStatusForDisplay}`, title: getLabelForGitStatus(gitStatusForDisplay), children: gitStatusForDisplay }));
6
6
  };
@@ -1,5 +1,5 @@
1
1
  import type { FunctionComponent } from "preact";
2
- import type { SerializedFloatType } from "../../../shared/schema/types/FloatType.ts";
2
+ import { type SerializedFloatType } from "../../../shared/schema/types/FloatType.ts";
3
3
  import type { TypeInputProps } from "./TypeInput.tsx";
4
4
  type Props = TypeInputProps<SerializedFloatType, number>;
5
5
  export declare const FloatTypeInput: FunctionComponent<Props>;
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useState } from "preact/hooks";
3
+ import { DEFAULT_FRACTION_DIGITS, } from "../../../shared/schema/types/FloatType.js";
3
4
  import { validateNumberConstraints } from "../../../shared/validation/number.js";
4
5
  import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
5
6
  import { ValidationErrors } from "./utils/ValidationErrors.js";
@@ -15,5 +16,5 @@ export const FloatTypeInput = ({ type, value, disabled, onChange }) => {
15
16
  if (!Number.isNaN(numericValue)) {
16
17
  onChange(numericValue);
17
18
  }
18
- }, step: 0.01, "aria-invalid": errors.length > 0, disabled: disabled }), _jsx(ValidationErrors, { disabled: disabled, errors: errors })] }));
19
+ }, step: 1 / Math.pow(10, type.fractionDigits ?? DEFAULT_FRACTION_DIGITS), "aria-invalid": errors.length > 0, disabled: disabled }), _jsx(ValidationErrors, { disabled: disabled, errors: errors })] }));
19
20
  };
@@ -20,6 +20,7 @@ import { Entity } from "./routes/Entity.js";
20
20
  import { Home } from "./routes/Home.js";
21
21
  import { Instance } from "./routes/Instance.js";
22
22
  import { NotFound } from "./routes/NotFound.js";
23
+ import { Search } from "./routes/Search.js";
23
24
  const mapEntities = (data) => data.declarations
24
25
  .map(decl => ({ ...decl, isLocaleEntity: decl.declaration.name === data.localeEntity }))
25
26
  .sort((a, b) => a.declaration.name.localeCompare(b.declaration.name));
@@ -33,7 +34,7 @@ const App = ({ config }) => {
33
34
  alert("Error reloading entities: " + String(error));
34
35
  });
35
36
  }, [location.path, reloadEntities]);
36
- return (_jsx(ConfigContext.Provider, { value: config, children: _jsx(SettingsContext.Provider, { value: settingsContext, children: _jsx(GitContext.Provider, { value: [isGitOpen, setIsGitOpen], children: _jsx(LocationProvider, { children: _jsx(EntitiesContext.Provider, { value: { entities: entities ?? [], reloadEntities }, children: _jsxs(ContextProviderWrapper, { context: GitClientContext, useValue: useGitClient, children: [_jsx(LoadingOverlay, {}), _jsxs(Router, { children: [_jsx(Route, { path: "/", component: Home }), _jsx(Route, { path: "/entities/:name", component: Entity }), _jsx(Route, { path: "/entities/:name/instances/create", component: CreateInstance }), _jsx(Route, { path: "/entities/:name/instances/:id", component: Instance }), _jsx(Route, { default: true, component: NotFound })] }), _jsx(Git, {})] }) }) }) }) }) }));
37
+ return (_jsx(ConfigContext.Provider, { value: config, children: _jsx(SettingsContext.Provider, { value: settingsContext, children: _jsx(GitContext.Provider, { value: [isGitOpen, setIsGitOpen], children: _jsx(LocationProvider, { children: _jsx(EntitiesContext.Provider, { value: { entities: entities ?? [], reloadEntities }, children: _jsxs(ContextProviderWrapper, { context: GitClientContext, useValue: useGitClient, children: [_jsx(LoadingOverlay, {}), _jsxs(Router, { children: [_jsx(Route, { path: "/", component: Home }), _jsx(Route, { path: "/search", component: Search }), _jsx(Route, { path: "/entities/:name", component: Entity }), _jsx(Route, { path: "/entities/:name/instances/create", component: CreateInstance }), _jsx(Route, { path: "/entities/:name/instances/:id", component: Instance }), _jsx(Route, { default: true, component: NotFound })] }), _jsx(Git, {})] }) }) }) }) }) }));
37
38
  };
38
39
  const config = await getWebConfig();
39
40
  const root = document.getElementById("app");
@@ -0,0 +1,2 @@
1
+ import type { FunctionalComponent } from "preact";
2
+ export declare const Search: FunctionalComponent;
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from "preact/hooks";
3
+ import { getGitStatusForDisplay } from "../../shared/utils/git.js";
4
+ import { toTitleCase } from "../../shared/utils/string.js";
5
+ import { deleteInstanceByEntityNameAndId } from "../api/declarations.js";
6
+ import { searchInstances } from "../api/search.js";
7
+ import { GitStatusIndicator } from "../components/git/GitStatusIndicator.js";
8
+ import { Layout } from "../components/Layout.js";
9
+ import { useSetting } from "../hooks/useSettings.js";
10
+ import { logAndAlertError } from "../utils/debug.js";
11
+ import { homeTitle } from "./Home.js";
12
+ const MIN_CHARACTERS = 3;
13
+ export const Search = () => {
14
+ const [locales] = useSetting("displayedLocales");
15
+ const [query, setQuery] = useState(() => new URLSearchParams(window.location.search).get("q") ?? "");
16
+ const [results, setResults] = useState();
17
+ const search = useCallback(() => {
18
+ const url = new URL(window.location.href);
19
+ if (url.searchParams.get("q") !== query) {
20
+ if (query.length === 0) {
21
+ url.searchParams.delete("q");
22
+ }
23
+ else {
24
+ url.searchParams.set("q", query);
25
+ }
26
+ window.history.pushState({}, "", url);
27
+ }
28
+ if (query.length >= MIN_CHARACTERS) {
29
+ searchInstances(locales, query)
30
+ .then(res => {
31
+ setResults(res.results);
32
+ })
33
+ .catch(logAndAlertError);
34
+ }
35
+ }, [locales, query]);
36
+ useEffect(() => {
37
+ document.title = "Search — TSONDB";
38
+ search();
39
+ }, [search]);
40
+ return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: homeTitle }], children: [_jsx("h1", { children: "Search" }), _jsx("input", { type: "search", name: "q", value: query, onInput: event => {
41
+ setQuery(event.currentTarget.value);
42
+ } }), query.length < MIN_CHARACTERS ? (_jsx("p", { class: "help", children: "Provide at least 3 characters in the search field to start the search" })) : (results && (_jsxs("section", { class: "search-results", children: [_jsx("h2", { children: "Results" }), results.length === 0 ? (_jsx("p", { class: "empty", children: "No results" })) : (_jsx("ul", { class: "entries entries--instances", children: results.map(([entityName, instance]) => {
43
+ const gitStatusForDisplay = getGitStatusForDisplay(instance.gitStatus);
44
+ return (_jsxs("li", { id: `instance-${instance.id}`, class: `entries-item${gitStatusForDisplay === undefined
45
+ ? ""
46
+ : ` git-status--${gitStatusForDisplay}`}`, children: [_jsxs("h2", { class: "entries-item__title", children: [instance.displayName, _jsx("span", { "aria-hidden": true, class: "entries-item__title-entity", children: toTitleCase(entityName) })] }), _jsx("p", { "aria-hidden": true, class: "entries-item__subtitle entries-item__subtitle--id", children: instance.id }), _jsxs("div", { class: "entries-item__side", children: [_jsx(GitStatusIndicator, { status: instance.gitStatus }), _jsxs("div", { class: "btns", children: [_jsx("a", { href: `/entities/${entityName}/instances/${instance.id}`, class: "btn", children: "Edit" }), _jsx("button", { class: "destructive", onClick: () => {
47
+ if (confirm("Are you sure you want to delete this instance?")) {
48
+ deleteInstanceByEntityNameAndId(locales, entityName, instance.id)
49
+ .then(() => {
50
+ search();
51
+ })
52
+ .catch(logAndAlertError);
53
+ }
54
+ }, children: "Delete" })] })] })] }, instance.id));
55
+ }) }))] })))] }));
56
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsondb",
3
- "version": "0.12.5",
3
+ "version": "0.12.7",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "Lukas Obermann",
@@ -589,6 +589,12 @@ select {
589
589
  }
590
590
  }
591
591
 
592
+ .entries-item__title-entity {
593
+ font-weight: 400;
594
+ color: var(--color-foreground-secondary);
595
+ padding-left: 0.5em;
596
+ }
597
+
592
598
  .entries-item__subtitle--id {
593
599
  font-family: var(--font-mono);
594
600
  }
@@ -1205,3 +1211,7 @@ dialog[open]:has(> .loading-overlay--open)::backdrop {
1205
1211
  .object-item--translation button {
1206
1212
  align-self: center;
1207
1213
  }
1214
+
1215
+ section.search-results {
1216
+ margin-top: 3.6rem;
1217
+ }