tsondb 0.15.0 → 0.16.0

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.
@@ -39,7 +39,7 @@ export interface EntityDecl<Name extends string = string, T extends TConstraint
39
39
  displayNameCustomizer?: DisplayNameCustomizer;
40
40
  isDeprecated?: boolean;
41
41
  uniqueConstraints?: UniqueConstraints;
42
- customConstraint?: CustomConstraint;
42
+ customConstraints?: CustomConstraint;
43
43
  }
44
44
  export interface EntityDeclWithParentReference<Name extends string = string, T extends TConstraint = TConstraint, FK extends Extract<keyof T, string> = Extract<keyof T, string>> extends EntityDecl<Name, T, FK> {
45
45
  }
@@ -56,7 +56,7 @@ export declare const EntityDecl: {
56
56
  displayNameCustomizer?: TypedDisplayNameCustomizer<Name>;
57
57
  isDeprecated?: boolean;
58
58
  uniqueConstraints?: UniqueConstraints;
59
- customConstraint?: TypedCustomConstraint<Name>;
59
+ customConstraints?: TypedCustomConstraint<Name>;
60
60
  }): EntityDecl<Name, T, undefined>;
61
61
  <Name extends string, T extends TConstraint, FK extends Extract<keyof T, string>>(sourceUrl: string, options: {
62
62
  name: Name;
@@ -71,7 +71,7 @@ export declare const EntityDecl: {
71
71
  displayNameCustomizer?: TypedDisplayNameCustomizer<Name>;
72
72
  isDeprecated?: boolean;
73
73
  uniqueConstraints?: UniqueConstraints;
74
- customConstraint?: TypedCustomConstraint<Name>;
74
+ customConstraints?: TypedCustomConstraint<Name>;
75
75
  }): EntityDecl<Name, T, FK>;
76
76
  };
77
77
  export { EntityDecl as Entity };
@@ -9,7 +9,7 @@ export const EntityDecl = (sourceUrl, options) => {
9
9
  return {
10
10
  ...options,
11
11
  displayNameCustomizer: options.displayNameCustomizer, // ignore contravariance of registered entity type
12
- customConstraint: options.customConstraint, // ignore contravariance of registered entity type
12
+ customConstraints: options.customConstraints, // ignore contravariance of registered entity type
13
13
  kind: NodeKind.EntityDecl,
14
14
  sourceUrl,
15
15
  parameters: [],
@@ -58,5 +58,6 @@ export const serializeEntityDecl = ((type) => ({
58
58
  ? null
59
59
  : type.displayName,
60
60
  displayNameCustomizer: type.displayNameCustomizer !== undefined,
61
+ customConstraints: type.customConstraints !== undefined,
61
62
  }));
62
63
  export const getReferencesForEntityDecl = (decl, value, inDecl) => getReferencesForObjectType(decl.type.value, value, [...inDecl, decl]);
@@ -2,3 +2,11 @@ import type { RegisteredChildEntityMap, RegisteredEntityMap } from "./externalTy
2
2
  export type GetInstanceById = <U extends keyof RegisteredEntityMap>(entity: U, id: string) => RegisteredEntityMap[U] | undefined;
3
3
  export type GetAllInstances = <U extends keyof RegisteredEntityMap>(entity: U) => RegisteredEntityMap[U][];
4
4
  export type GetAllChildInstancesForParent = <U extends keyof RegisteredChildEntityMap>(entity: U, parentId: RegisteredChildEntityMap[U][2]) => RegisteredChildEntityMap[U][0][];
5
+ /**
6
+ * Displays the name of an entity instance including its ID. If no display name is found, `undefined` is returned.
7
+ */
8
+ export type GetDisplayName = (entity: keyof RegisteredEntityMap, id: string) => string | undefined;
9
+ /**
10
+ * Displays the name of an entity instance including its ID. If no display name is found, only the ID is returned.
11
+ */
12
+ export type GetDisplayNameWithId = (entity: keyof RegisteredEntityMap, id: string) => string;
@@ -1,7 +1,7 @@
1
1
  import type { InstanceContainerOverview, InstanceContent } from "../../shared/utils/instances.ts";
2
2
  import { type Result } from "../../shared/utils/result.ts";
3
3
  import type { RegisteredEntity } from "../schema/externalTypes.ts";
4
- import type { GetAllChildInstancesForParent, GetAllInstances, GetInstanceById } from "../schema/helpers.ts";
4
+ import type { GetAllChildInstancesForParent, GetAllInstances, GetDisplayName, GetDisplayNameWithId, GetInstanceById } from "../schema/helpers.ts";
5
5
  import type { EntityDecl } from "../schema/index.ts";
6
6
  import { type DatabaseInMemory } from "./databaseInMemory.ts";
7
7
  /**
@@ -17,8 +17,10 @@ export type CustomConstraint = (params: {
17
17
  instanceId: string;
18
18
  instanceContent: InstanceContent;
19
19
  getInstanceById: GetInstanceById;
20
- getAllInstance: GetAllInstances;
20
+ getAllInstances: GetAllInstances;
21
21
  getAllChildInstancesForParent: GetAllChildInstancesForParent;
22
+ getDisplayName: GetDisplayName;
23
+ getDisplayNameWithId: GetDisplayNameWithId;
22
24
  }) => string[];
23
25
  /**
24
26
  * A constraint that can be defined on an entity to enforce custom validation logic.
@@ -33,8 +35,10 @@ export type TypedCustomConstraint<Name extends string> = (params: {
33
35
  instanceId: string;
34
36
  instanceContent: RegisteredEntity<Name>;
35
37
  getInstanceById: GetInstanceById;
36
- getAllInstance: GetAllInstances;
38
+ getAllInstances: GetAllInstances;
37
39
  getAllChildInstancesForParent: GetAllChildInstancesForParent;
40
+ getDisplayName: GetDisplayName;
41
+ getDisplayNameWithId: GetDisplayNameWithId;
38
42
  }) => string[];
39
43
  /**
40
44
  * Checks all custom constraints for all provided entities and their instances.
@@ -9,20 +9,25 @@ import { getInstanceOfEntityFromDatabaseInMemory, getInstancesOfEntityFromDataba
9
9
  * constraint.
10
10
  */
11
11
  export const checkCustomConstraintsForAllEntities = (db, entitiesByName, instanceOverviewsByEntityName) => {
12
- const accessors = {
13
- getInstanceById: (entityName, id) => getInstanceOfEntityFromDatabaseInMemory(db, entityName, id),
14
- getAllInstance: entityName => getInstancesOfEntityFromDatabaseInMemory(db, entityName),
15
- getAllChildInstancesForParent: (entityName, parentId) => {
16
- const entity = entitiesByName[entityName];
17
- if (!entity || !entity.parentReferenceKey) {
18
- return [];
19
- }
20
- const parentKey = entity.parentReferenceKey;
21
- return getInstancesOfEntityFromDatabaseInMemory(db, entityName).filter(instance => deepEqual(instance.content[parentKey], parentId));
22
- },
12
+ const getInstanceById = (entityName, id) => getInstanceOfEntityFromDatabaseInMemory(db, entityName, id)?.content;
13
+ const getAllInstances = entityName => getInstancesOfEntityFromDatabaseInMemory(db, entityName).map(i => i.content);
14
+ const getAllChildInstancesForParent = (entityName, parentId) => {
15
+ const entity = entitiesByName[entityName];
16
+ if (!entity || !entity.parentReferenceKey) {
17
+ return [];
18
+ }
19
+ const parentKey = entity.parentReferenceKey;
20
+ return getInstancesOfEntityFromDatabaseInMemory(db, entityName)
21
+ .filter(instance => deepEqual(instance.content[parentKey], parentId))
22
+ .map(i => i.content);
23
+ };
24
+ const getDisplayName = (entityName, id) => instanceOverviewsByEntityName[entityName]?.find(o => o.id === id)?.displayName;
25
+ const getDisplayNameWithId = (entityName, id) => {
26
+ const displayName = getDisplayName(entityName, id);
27
+ return displayName ? `"${displayName}" (${id})` : id;
23
28
  };
24
29
  return mapError(Object.values(entitiesByName).reduce((acc, entity) => {
25
- const constraintFn = entity.customConstraint;
30
+ const constraintFn = entity.customConstraints;
26
31
  if (!constraintFn) {
27
32
  return acc;
28
33
  }
@@ -30,7 +35,11 @@ export const checkCustomConstraintsForAllEntities = (db, entitiesByName, instanc
30
35
  .map((instance) => [
31
36
  instance,
32
37
  constraintFn({
33
- ...accessors,
38
+ getInstanceById,
39
+ getAllInstances,
40
+ getAllChildInstancesForParent,
41
+ getDisplayName,
42
+ getDisplayNameWithId,
34
43
  instanceId: instance.id,
35
44
  instanceContent: instance.content,
36
45
  }),
@@ -57,6 +57,7 @@ export interface SerializedEntityDecl<Name extends string = string, T extends TS
57
57
  displayNameCustomizer: boolean;
58
58
  isDeprecated?: boolean;
59
59
  uniqueConstraints?: UniqueConstraints;
60
+ customConstraints: boolean;
60
61
  }
61
62
  export declare const isSerializedEntityDecl: (node: SerializedNode) => node is SerializedEntityDecl;
62
63
  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>>;
@@ -30,6 +30,23 @@ export const ChildEntitiesTypeInput = props => {
30
30
  { entityName, childInstances: [], id: undefined, content: value },
31
31
  ]);
32
32
  }, [setChildInstances]);
33
+ const onChildDuplicate = useCallback((index) => {
34
+ const setChildInstancesAsNew = (childInstances) => childInstances.map(ci => ({
35
+ ...ci,
36
+ childInstances: setChildInstancesAsNew(ci.childInstances),
37
+ id: undefined,
38
+ }));
39
+ setChildInstances(old => old[index]
40
+ ? [
41
+ ...old,
42
+ {
43
+ ...old[index],
44
+ childInstances: setChildInstancesAsNew(old[index].childInstances),
45
+ id: undefined,
46
+ },
47
+ ]
48
+ : old);
49
+ }, [setChildInstances]);
33
50
  const onChildRemove = useCallback((index) => {
34
51
  setChildInstances(old => removeAt(old, index));
35
52
  }, [setChildInstances]);
@@ -39,9 +56,11 @@ export const ChildEntitiesTypeInput = props => {
39
56
  if (path === undefined) {
40
57
  return _jsx("div", { role: "alert", children: "A child entities type cannot be the root type of a document." });
41
58
  }
42
- return (_jsxs("div", { class: "field field--container field--array", children: [childInstancesForEntity.length > 0 ? (_jsx("ol", { children: childInstancesForEntity.map(([item, originalIndex], i) => (_jsxs("li", { class: "container-item array-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }), _jsx("button", { class: "destructive", onClick: () => {
43
- onChildRemove(i);
44
- }, disabled: disabled, children: "Delete Item" })] }), _jsx(TypeInput, { ...props, type: childEntity.type, value: item.content, parentKey: childEntity.parentReferenceKey, onChange: newItem => {
59
+ return (_jsxs("div", { class: "field field--container field--array", children: [childInstancesForEntity.length > 0 ? (_jsx("ol", { children: childInstancesForEntity.map(([item, originalIndex], i) => (_jsxs("li", { class: "container-item array-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }), _jsxs("div", { class: "container-item-actions", children: [_jsxs("button", { onClick: () => {
60
+ onChildDuplicate(i);
61
+ }, disabled: disabled, children: ["Duplicate Item #", i + 1] }), _jsxs("button", { class: "destructive", onClick: () => {
62
+ onChildRemove(i);
63
+ }, disabled: disabled, children: ["Delete Item #", i + 1] })] })] }), _jsx(TypeInput, { ...props, type: childEntity.type, value: item.content, parentKey: childEntity.parentReferenceKey, onChange: newItem => {
45
64
  onChildChange(originalIndex, newItem); // guaranteed to be an object because of the ObjectType in the child entity
46
65
  }, childInstances: item.childInstances, setChildInstances: newChildInstances => {
47
66
  onGrandChildrenChange(originalIndex, newChildInstances);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsondb",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "Lukas Obermann",
@@ -856,6 +856,13 @@ form > .field--container {
856
856
  margin: 0.125rem 0 0;
857
857
  }
858
858
 
859
+ .container-item-actions {
860
+ flex: none;
861
+ display: flex;
862
+ gap: 0.5rem;
863
+ margin-left: 1rem;
864
+ }
865
+
859
866
  .add-item-container {
860
867
  display: flex;
861
868
  margin-top: 1rem;