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.
- package/dist/src/node/schema/declarations/EntityDecl.d.ts +3 -3
- package/dist/src/node/schema/declarations/EntityDecl.js +2 -1
- package/dist/src/node/schema/helpers.d.ts +8 -0
- package/dist/src/node/utils/customConstraints.d.ts +7 -3
- package/dist/src/node/utils/customConstraints.js +22 -13
- package/dist/src/shared/schema/declarations/EntityDecl.d.ts +1 -0
- package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.js +22 -3
- package/package.json +1 -1
- package/public/css/styles.css +7 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
|
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
|
-
|
|
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, "."] }),
|
|
43
|
-
|
|
44
|
-
|
|
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
package/public/css/styles.css
CHANGED
|
@@ -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;
|