@house-rules/relations 0.0.0-stage → 0.1.1
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/AGENTS.md +40 -0
- package/LICENSE +21 -0
- package/README.md +78 -2
- package/dist/adapters/memory/relation.d.ts +3 -0
- package/dist/adapters/memory/relation.js +67 -0
- package/dist/adapters/postgres/relation.d.ts +4 -0
- package/dist/adapters/postgres/relation.js +49 -0
- package/dist/contract.d.ts +4 -0
- package/dist/contract.js +4 -0
- package/dist/facade.d.ts +313 -0
- package/dist/facade.js +19 -0
- package/dist/index.d.ts +128 -0
- package/dist/index.js +16 -0
- package/dist/relation/bind-relations.d.ts +3 -0
- package/dist/relation/bind-relations.js +8 -0
- package/dist/relation/events.d.ts +70 -0
- package/dist/relation/events.js +12 -0
- package/dist/relation/graph.d.ts +8 -0
- package/dist/relation/graph.js +15 -0
- package/dist/relation/operations.d.ts +117 -0
- package/dist/relation/operations.js +53 -0
- package/dist/relation/options.d.ts +30 -0
- package/dist/relation/options.js +27 -0
- package/dist/relation/permissions.d.ts +5 -0
- package/dist/relation/permissions.js +1 -0
- package/dist/relation/relation.d.ts +169 -0
- package/dist/relation/relation.js +105 -0
- package/dist/relation/relations.d.ts +136 -0
- package/dist/relation/relations.js +135 -0
- package/dist/storage.d.ts +18 -0
- package/dist/storage.js +3 -0
- package/migrations/0001_relations.sql +17 -0
- package/package.json +57 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Relations } from "./facade.js";
|
|
2
|
+
import { bindRelations } from "./relation/bind-relations.js";
|
|
3
|
+
export declare const RelationsMount: {
|
|
4
|
+
map: {
|
|
5
|
+
name: string;
|
|
6
|
+
kind: "package";
|
|
7
|
+
package: string;
|
|
8
|
+
};
|
|
9
|
+
service: typeof Relations;
|
|
10
|
+
operations: (import("@house-rules/domain").Operation<"Relations.listRelations", import("effect/Schema").Struct<{
|
|
11
|
+
readonly node: import("effect/Schema").Struct<{
|
|
12
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
13
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
14
|
+
}>;
|
|
15
|
+
readonly type: import("effect/Schema").optionalKey<import("effect/Schema").NonEmptyString>;
|
|
16
|
+
readonly direction: import("effect/Schema").Literals<readonly ["out", "in", "both"]>;
|
|
17
|
+
readonly page: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
|
|
18
|
+
readonly size: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
|
|
19
|
+
readonly cursor: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
|
|
20
|
+
readonly createdAt: import("effect/Schema").Int;
|
|
21
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
22
|
+
}>>;
|
|
23
|
+
}>>;
|
|
24
|
+
}>, import("effect/Schema").Struct<{
|
|
25
|
+
readonly edges: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
26
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
27
|
+
readonly type: import("effect/Schema").NonEmptyString;
|
|
28
|
+
readonly source: import("effect/Schema").Struct<{
|
|
29
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
30
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
31
|
+
}>;
|
|
32
|
+
readonly target: import("effect/Schema").Struct<{
|
|
33
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
34
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
35
|
+
}>;
|
|
36
|
+
readonly createdAt: import("effect/Schema").Int;
|
|
37
|
+
}>>;
|
|
38
|
+
readonly next: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
|
|
39
|
+
readonly createdAt: import("effect/Schema").Int;
|
|
40
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
41
|
+
}>>;
|
|
42
|
+
}>, import("effect/Schema").Union<readonly [typeof import("./contract.js").RelationTypeUnknown, typeof import("./contract.js").RelationEndpointInvalid, typeof import("./contract.js").RelationsInputInvalid, typeof import("./contract.js").RelationsStoreUnavailable]>, NoInfer<readonly []>, true, false, false, false> | import("@house-rules/domain").Operation<"Relations.relate", import("effect/Schema").Struct<{
|
|
43
|
+
readonly type: import("effect/Schema").NonEmptyString;
|
|
44
|
+
readonly source: import("effect/Schema").Struct<{
|
|
45
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
46
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
47
|
+
}>;
|
|
48
|
+
readonly target: import("effect/Schema").Struct<{
|
|
49
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
50
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
51
|
+
}>;
|
|
52
|
+
}>, import("effect/Schema").Struct<{
|
|
53
|
+
readonly relation: import("effect/Schema").Struct<{
|
|
54
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
55
|
+
readonly type: import("effect/Schema").NonEmptyString;
|
|
56
|
+
readonly source: import("effect/Schema").Struct<{
|
|
57
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
58
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
59
|
+
}>;
|
|
60
|
+
readonly target: import("effect/Schema").Struct<{
|
|
61
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
62
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
63
|
+
}>;
|
|
64
|
+
readonly createdAt: import("effect/Schema").Int;
|
|
65
|
+
}>;
|
|
66
|
+
readonly events: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
67
|
+
readonly type: import("effect/Schema").NonEmptyString;
|
|
68
|
+
readonly source: import("effect/Schema").Struct<{
|
|
69
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
70
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
71
|
+
}>;
|
|
72
|
+
readonly target: import("effect/Schema").Struct<{
|
|
73
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
74
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
75
|
+
}>;
|
|
76
|
+
readonly kind: import("effect/Schema").Literal<"Related">;
|
|
77
|
+
}>>;
|
|
78
|
+
}>, import("effect/Schema").Union<readonly [typeof import("./contract.js").RelationTypeUnknown, typeof import("./contract.js").RelationEndpointInvalid, typeof import("./contract.js").DuplicateRelation, typeof import("./contract.js").RelationCardinalityExceeded, typeof import("./contract.js").RelationCycle, typeof import("./contract.js").GraphTooLarge, typeof import("./contract.js").RelationsStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Relations.subgraph", import("effect/Schema").Struct<{
|
|
79
|
+
readonly root: import("effect/Schema").Struct<{
|
|
80
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
81
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
82
|
+
}>;
|
|
83
|
+
readonly type: import("effect/Schema").NonEmptyString;
|
|
84
|
+
readonly direction: import("effect/Schema").Literals<readonly ["out", "in", "both"]>;
|
|
85
|
+
readonly depth: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
|
|
86
|
+
}>, import("effect/Schema").Struct<{
|
|
87
|
+
readonly nodes: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
88
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
89
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
90
|
+
}>>;
|
|
91
|
+
readonly edges: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
92
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
93
|
+
readonly type: import("effect/Schema").NonEmptyString;
|
|
94
|
+
readonly source: import("effect/Schema").Struct<{
|
|
95
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
96
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
97
|
+
}>;
|
|
98
|
+
readonly target: import("effect/Schema").Struct<{
|
|
99
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
100
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
101
|
+
}>;
|
|
102
|
+
readonly createdAt: import("effect/Schema").Int;
|
|
103
|
+
}>>;
|
|
104
|
+
}>, import("effect/Schema").Union<readonly [import("effect/Schema").Union<readonly [typeof import("./contract.js").RelationTypeUnknown, typeof import("./contract.js").RelationEndpointInvalid, typeof import("./contract.js").RelationsInputInvalid, typeof import("./contract.js").RelationsStoreUnavailable]>, typeof import("./contract.js").GraphTooLarge]>, NoInfer<readonly []>, true, false, false, false> | import("@house-rules/domain").Operation<"Relations.unrelate", import("effect/Schema").Struct<{
|
|
105
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
106
|
+
}>, import("effect/Schema").Struct<{
|
|
107
|
+
readonly events: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
108
|
+
readonly type: import("effect/Schema").NonEmptyString;
|
|
109
|
+
readonly source: import("effect/Schema").Struct<{
|
|
110
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
111
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
112
|
+
}>;
|
|
113
|
+
readonly target: import("effect/Schema").Struct<{
|
|
114
|
+
readonly kind: import("effect/Schema").NonEmptyString;
|
|
115
|
+
readonly id: import("effect/Schema").NonEmptyString;
|
|
116
|
+
}>;
|
|
117
|
+
readonly kind: import("effect/Schema").Literal<"Unrelated">;
|
|
118
|
+
}>>;
|
|
119
|
+
}>, import("effect/Schema").Union<readonly [typeof import("./contract.js").RelationNotFound, typeof import("./contract.js").RelationsStoreUnavailable]>, NoInfer<readonly []>, false, true, true, false>)[];
|
|
120
|
+
bind: typeof bindRelations;
|
|
121
|
+
};
|
|
122
|
+
export { Relations } from "./facade.js";
|
|
123
|
+
export { bindRelations } from "./relation/bind-relations.js";
|
|
124
|
+
export { RelationsOptions, RelationType } from "./relation/options.js";
|
|
125
|
+
export { RelationStore } from "./storage.js";
|
|
126
|
+
export { memoryRelationStore } from "./adapters/memory/relation.js";
|
|
127
|
+
export { postgresRelationStore } from "./adapters/postgres/relation.js";
|
|
128
|
+
export { RelateResult, RelationEvents, UnrelateResult, DuplicateRelation, GraphTooLarge, ListRelationsInput, NodeRef, RelateInput, Relation, RelationCardinalityExceeded, RelationCursor, RelationCycle, RelationDirection, RelationEndpointInvalid, RelationGraph, RelationList, RelationNotFound, RelationPage, RelationsInputInvalid, RelationsStoreUnavailable, RelationTypeUnknown, SubgraphInput, RelationsOperations, RelationsPermissions, type RelationsPermission, } from "./contract.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Relations } from "./facade.js";
|
|
2
|
+
import { RelationsOperations } from "./contract.js";
|
|
3
|
+
import { bindRelations } from "./relation/bind-relations.js";
|
|
4
|
+
export const RelationsMount = {
|
|
5
|
+
map: { name: "relations", kind: "package", package: "@house-rules/relations" },
|
|
6
|
+
service: Relations,
|
|
7
|
+
operations: Object.values(RelationsOperations),
|
|
8
|
+
bind: bindRelations,
|
|
9
|
+
};
|
|
10
|
+
export { Relations } from "./facade.js";
|
|
11
|
+
export { bindRelations } from "./relation/bind-relations.js";
|
|
12
|
+
export { RelationsOptions, RelationType } from "./relation/options.js";
|
|
13
|
+
export { RelationStore } from "./storage.js";
|
|
14
|
+
export { memoryRelationStore } from "./adapters/memory/relation.js";
|
|
15
|
+
export { postgresRelationStore } from "./adapters/postgres/relation.js";
|
|
16
|
+
export { RelateResult, RelationEvents, UnrelateResult, DuplicateRelation, GraphTooLarge, ListRelationsInput, NodeRef, RelateInput, Relation, RelationCardinalityExceeded, RelationCursor, RelationCycle, RelationDirection, RelationEndpointInvalid, RelationGraph, RelationList, RelationNotFound, RelationPage, RelationsInputInvalid, RelationsStoreUnavailable, RelationTypeUnknown, SubgraphInput, RelationsOperations, RelationsPermissions, } from "./contract.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { bindOperation } from "@house-rules/application";
|
|
2
|
+
import { RelationsOperations } from "./operations.js";
|
|
3
|
+
export const bindRelations = (service) => [
|
|
4
|
+
bindOperation(RelationsOperations.relate, (input) => service.relate(input)),
|
|
5
|
+
bindOperation(RelationsOperations.unrelate, ({ id }) => service.unrelate(id)),
|
|
6
|
+
bindOperation(RelationsOperations.listRelations, (input) => service.listRelations(input)),
|
|
7
|
+
bindOperation(RelationsOperations.subgraph, (input) => service.subgraph(input)),
|
|
8
|
+
];
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare const RelationEvents: {
|
|
3
|
+
readonly Related: Schema.Struct<{
|
|
4
|
+
readonly type: Schema.NonEmptyString;
|
|
5
|
+
readonly source: Schema.Struct<{
|
|
6
|
+
readonly kind: Schema.NonEmptyString;
|
|
7
|
+
readonly id: Schema.NonEmptyString;
|
|
8
|
+
}>;
|
|
9
|
+
readonly target: Schema.Struct<{
|
|
10
|
+
readonly kind: Schema.NonEmptyString;
|
|
11
|
+
readonly id: Schema.NonEmptyString;
|
|
12
|
+
}>;
|
|
13
|
+
readonly kind: Schema.Literal<"Related">;
|
|
14
|
+
}>;
|
|
15
|
+
readonly Unrelated: Schema.Struct<{
|
|
16
|
+
readonly type: Schema.NonEmptyString;
|
|
17
|
+
readonly source: Schema.Struct<{
|
|
18
|
+
readonly kind: Schema.NonEmptyString;
|
|
19
|
+
readonly id: Schema.NonEmptyString;
|
|
20
|
+
}>;
|
|
21
|
+
readonly target: Schema.Struct<{
|
|
22
|
+
readonly kind: Schema.NonEmptyString;
|
|
23
|
+
readonly id: Schema.NonEmptyString;
|
|
24
|
+
}>;
|
|
25
|
+
readonly kind: Schema.Literal<"Unrelated">;
|
|
26
|
+
}>;
|
|
27
|
+
};
|
|
28
|
+
export declare const RelateResult: Schema.Struct<{
|
|
29
|
+
readonly relation: Schema.Struct<{
|
|
30
|
+
readonly id: Schema.NonEmptyString;
|
|
31
|
+
readonly type: Schema.NonEmptyString;
|
|
32
|
+
readonly source: Schema.Struct<{
|
|
33
|
+
readonly kind: Schema.NonEmptyString;
|
|
34
|
+
readonly id: Schema.NonEmptyString;
|
|
35
|
+
}>;
|
|
36
|
+
readonly target: Schema.Struct<{
|
|
37
|
+
readonly kind: Schema.NonEmptyString;
|
|
38
|
+
readonly id: Schema.NonEmptyString;
|
|
39
|
+
}>;
|
|
40
|
+
readonly createdAt: Schema.Int;
|
|
41
|
+
}>;
|
|
42
|
+
readonly events: Schema.$Array<Schema.Struct<{
|
|
43
|
+
readonly type: Schema.NonEmptyString;
|
|
44
|
+
readonly source: Schema.Struct<{
|
|
45
|
+
readonly kind: Schema.NonEmptyString;
|
|
46
|
+
readonly id: Schema.NonEmptyString;
|
|
47
|
+
}>;
|
|
48
|
+
readonly target: Schema.Struct<{
|
|
49
|
+
readonly kind: Schema.NonEmptyString;
|
|
50
|
+
readonly id: Schema.NonEmptyString;
|
|
51
|
+
}>;
|
|
52
|
+
readonly kind: Schema.Literal<"Related">;
|
|
53
|
+
}>>;
|
|
54
|
+
}>;
|
|
55
|
+
export type RelateResult = typeof RelateResult.Type;
|
|
56
|
+
export declare const UnrelateResult: Schema.Struct<{
|
|
57
|
+
readonly events: Schema.$Array<Schema.Struct<{
|
|
58
|
+
readonly type: Schema.NonEmptyString;
|
|
59
|
+
readonly source: Schema.Struct<{
|
|
60
|
+
readonly kind: Schema.NonEmptyString;
|
|
61
|
+
readonly id: Schema.NonEmptyString;
|
|
62
|
+
}>;
|
|
63
|
+
readonly target: Schema.Struct<{
|
|
64
|
+
readonly kind: Schema.NonEmptyString;
|
|
65
|
+
readonly id: Schema.NonEmptyString;
|
|
66
|
+
}>;
|
|
67
|
+
readonly kind: Schema.Literal<"Unrelated">;
|
|
68
|
+
}>>;
|
|
69
|
+
}>;
|
|
70
|
+
export type UnrelateResult = typeof UnrelateResult.Type;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { NodeRef, Relation } from "./relation.js";
|
|
3
|
+
const endpoints = { type: Schema.NonEmptyString, source: NodeRef, target: NodeRef };
|
|
4
|
+
export const RelationEvents = {
|
|
5
|
+
Related: Schema.Struct({ kind: Schema.Literal("Related"), ...endpoints }),
|
|
6
|
+
Unrelated: Schema.Struct({ kind: Schema.Literal("Unrelated"), ...endpoints }),
|
|
7
|
+
};
|
|
8
|
+
export const RelateResult = Schema.Struct({
|
|
9
|
+
relation: Relation,
|
|
10
|
+
events: Schema.Array(RelationEvents.Related),
|
|
11
|
+
});
|
|
12
|
+
export const UnrelateResult = Schema.Struct({ events: Schema.Array(RelationEvents.Unrelated) });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NodeRef, Relation, RelationDirection } from "./relation.js";
|
|
2
|
+
export declare const nodeKey: (node: NodeRef) => string;
|
|
3
|
+
export declare const sameNode: (a: NodeRef, b: NodeRef) => boolean;
|
|
4
|
+
export declare const compareNodes: (a: NodeRef, b: NodeRef) => number;
|
|
5
|
+
export declare const touches: (edge: Relation, node: NodeRef, direction: RelationDirection) => boolean;
|
|
6
|
+
export declare const neighbours: (edge: Relation, node: NodeRef, direction: RelationDirection) => ReadonlyArray<NodeRef>;
|
|
7
|
+
export declare const copyRelation: (edge: Relation) => Relation;
|
|
8
|
+
export declare const compareRelations: (a: Relation, b: Relation) => number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const nodeKey = (node) => JSON.stringify([node.kind, node.id]);
|
|
2
|
+
export const sameNode = (a, b) => a.kind === b.kind && a.id === b.id;
|
|
3
|
+
export const compareNodes = (a, b) => a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
4
|
+
export const touches = (edge, node, direction) => (direction !== "in" && sameNode(edge.source, node)) ||
|
|
5
|
+
(direction !== "out" && sameNode(edge.target, node));
|
|
6
|
+
export const neighbours = (edge, node, direction) => [
|
|
7
|
+
...(direction !== "in" && sameNode(edge.source, node) ? [edge.target] : []),
|
|
8
|
+
...(direction !== "out" && sameNode(edge.target, node) ? [edge.source] : []),
|
|
9
|
+
];
|
|
10
|
+
export const copyRelation = (edge) => ({
|
|
11
|
+
...edge,
|
|
12
|
+
source: { ...edge.source },
|
|
13
|
+
target: { ...edge.target },
|
|
14
|
+
});
|
|
15
|
+
export const compareRelations = (a, b) => a.createdAt - b.createdAt || (a.id < b.id ? -1 : a.id === b.id ? 0 : 1);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { DuplicateRelation, GraphTooLarge, RelationCardinalityExceeded, RelationCycle, RelationEndpointInvalid, RelationNotFound, RelationsInputInvalid, RelationsStoreUnavailable, RelationTypeUnknown } from "./relation.js";
|
|
3
|
+
export declare const RelationsOperations: {
|
|
4
|
+
readonly relate: import("@house-rules/domain/contract").Operation<"Relations.relate", Schema.Struct<{
|
|
5
|
+
readonly type: Schema.NonEmptyString;
|
|
6
|
+
readonly source: Schema.Struct<{
|
|
7
|
+
readonly kind: Schema.NonEmptyString;
|
|
8
|
+
readonly id: Schema.NonEmptyString;
|
|
9
|
+
}>;
|
|
10
|
+
readonly target: Schema.Struct<{
|
|
11
|
+
readonly kind: Schema.NonEmptyString;
|
|
12
|
+
readonly id: Schema.NonEmptyString;
|
|
13
|
+
}>;
|
|
14
|
+
}>, Schema.Struct<{
|
|
15
|
+
readonly relation: Schema.Struct<{
|
|
16
|
+
readonly id: Schema.NonEmptyString;
|
|
17
|
+
readonly type: Schema.NonEmptyString;
|
|
18
|
+
readonly source: Schema.Struct<{
|
|
19
|
+
readonly kind: Schema.NonEmptyString;
|
|
20
|
+
readonly id: Schema.NonEmptyString;
|
|
21
|
+
}>;
|
|
22
|
+
readonly target: Schema.Struct<{
|
|
23
|
+
readonly kind: Schema.NonEmptyString;
|
|
24
|
+
readonly id: Schema.NonEmptyString;
|
|
25
|
+
}>;
|
|
26
|
+
readonly createdAt: Schema.Int;
|
|
27
|
+
}>;
|
|
28
|
+
readonly events: Schema.$Array<Schema.Struct<{
|
|
29
|
+
readonly type: Schema.NonEmptyString;
|
|
30
|
+
readonly source: Schema.Struct<{
|
|
31
|
+
readonly kind: Schema.NonEmptyString;
|
|
32
|
+
readonly id: Schema.NonEmptyString;
|
|
33
|
+
}>;
|
|
34
|
+
readonly target: Schema.Struct<{
|
|
35
|
+
readonly kind: Schema.NonEmptyString;
|
|
36
|
+
readonly id: Schema.NonEmptyString;
|
|
37
|
+
}>;
|
|
38
|
+
readonly kind: Schema.Literal<"Related">;
|
|
39
|
+
}>>;
|
|
40
|
+
}>, Schema.Union<readonly [typeof RelationTypeUnknown, typeof RelationEndpointInvalid, typeof DuplicateRelation, typeof RelationCardinalityExceeded, typeof RelationCycle, typeof GraphTooLarge, typeof RelationsStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false>;
|
|
41
|
+
readonly unrelate: import("@house-rules/domain/contract").Operation<"Relations.unrelate", Schema.Struct<{
|
|
42
|
+
readonly id: Schema.NonEmptyString;
|
|
43
|
+
}>, Schema.Struct<{
|
|
44
|
+
readonly events: Schema.$Array<Schema.Struct<{
|
|
45
|
+
readonly type: Schema.NonEmptyString;
|
|
46
|
+
readonly source: Schema.Struct<{
|
|
47
|
+
readonly kind: Schema.NonEmptyString;
|
|
48
|
+
readonly id: Schema.NonEmptyString;
|
|
49
|
+
}>;
|
|
50
|
+
readonly target: Schema.Struct<{
|
|
51
|
+
readonly kind: Schema.NonEmptyString;
|
|
52
|
+
readonly id: Schema.NonEmptyString;
|
|
53
|
+
}>;
|
|
54
|
+
readonly kind: Schema.Literal<"Unrelated">;
|
|
55
|
+
}>>;
|
|
56
|
+
}>, Schema.Union<readonly [typeof RelationNotFound, typeof RelationsStoreUnavailable]>, NoInfer<readonly []>, false, true, true, false>;
|
|
57
|
+
readonly listRelations: import("@house-rules/domain/contract").Operation<"Relations.listRelations", Schema.Struct<{
|
|
58
|
+
readonly node: Schema.Struct<{
|
|
59
|
+
readonly kind: Schema.NonEmptyString;
|
|
60
|
+
readonly id: Schema.NonEmptyString;
|
|
61
|
+
}>;
|
|
62
|
+
readonly type: Schema.optionalKey<Schema.NonEmptyString>;
|
|
63
|
+
readonly direction: Schema.Literals<readonly ["out", "in", "both"]>;
|
|
64
|
+
readonly page: Schema.optionalKey<Schema.Struct<{
|
|
65
|
+
readonly size: Schema.optionalKey<Schema.Int>;
|
|
66
|
+
readonly cursor: Schema.optionalKey<Schema.Struct<{
|
|
67
|
+
readonly createdAt: Schema.Int;
|
|
68
|
+
readonly id: Schema.NonEmptyString;
|
|
69
|
+
}>>;
|
|
70
|
+
}>>;
|
|
71
|
+
}>, Schema.Struct<{
|
|
72
|
+
readonly edges: Schema.$Array<Schema.Struct<{
|
|
73
|
+
readonly id: Schema.NonEmptyString;
|
|
74
|
+
readonly type: Schema.NonEmptyString;
|
|
75
|
+
readonly source: Schema.Struct<{
|
|
76
|
+
readonly kind: Schema.NonEmptyString;
|
|
77
|
+
readonly id: Schema.NonEmptyString;
|
|
78
|
+
}>;
|
|
79
|
+
readonly target: Schema.Struct<{
|
|
80
|
+
readonly kind: Schema.NonEmptyString;
|
|
81
|
+
readonly id: Schema.NonEmptyString;
|
|
82
|
+
}>;
|
|
83
|
+
readonly createdAt: Schema.Int;
|
|
84
|
+
}>>;
|
|
85
|
+
readonly next: Schema.optionalKey<Schema.Struct<{
|
|
86
|
+
readonly createdAt: Schema.Int;
|
|
87
|
+
readonly id: Schema.NonEmptyString;
|
|
88
|
+
}>>;
|
|
89
|
+
}>, Schema.Union<readonly [typeof RelationTypeUnknown, typeof RelationEndpointInvalid, typeof RelationsInputInvalid, typeof RelationsStoreUnavailable]>, NoInfer<readonly []>, true, false, false, false>;
|
|
90
|
+
readonly subgraph: import("@house-rules/domain/contract").Operation<"Relations.subgraph", Schema.Struct<{
|
|
91
|
+
readonly root: Schema.Struct<{
|
|
92
|
+
readonly kind: Schema.NonEmptyString;
|
|
93
|
+
readonly id: Schema.NonEmptyString;
|
|
94
|
+
}>;
|
|
95
|
+
readonly type: Schema.NonEmptyString;
|
|
96
|
+
readonly direction: Schema.Literals<readonly ["out", "in", "both"]>;
|
|
97
|
+
readonly depth: Schema.optionalKey<Schema.Int>;
|
|
98
|
+
}>, Schema.Struct<{
|
|
99
|
+
readonly nodes: Schema.$Array<Schema.Struct<{
|
|
100
|
+
readonly kind: Schema.NonEmptyString;
|
|
101
|
+
readonly id: Schema.NonEmptyString;
|
|
102
|
+
}>>;
|
|
103
|
+
readonly edges: Schema.$Array<Schema.Struct<{
|
|
104
|
+
readonly id: Schema.NonEmptyString;
|
|
105
|
+
readonly type: Schema.NonEmptyString;
|
|
106
|
+
readonly source: Schema.Struct<{
|
|
107
|
+
readonly kind: Schema.NonEmptyString;
|
|
108
|
+
readonly id: Schema.NonEmptyString;
|
|
109
|
+
}>;
|
|
110
|
+
readonly target: Schema.Struct<{
|
|
111
|
+
readonly kind: Schema.NonEmptyString;
|
|
112
|
+
readonly id: Schema.NonEmptyString;
|
|
113
|
+
}>;
|
|
114
|
+
readonly createdAt: Schema.Int;
|
|
115
|
+
}>>;
|
|
116
|
+
}>, Schema.Union<readonly [Schema.Union<readonly [typeof RelationTypeUnknown, typeof RelationEndpointInvalid, typeof RelationsInputInvalid, typeof RelationsStoreUnavailable]>, typeof GraphTooLarge]>, NoInfer<readonly []>, true, false, false, false>;
|
|
117
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { defineOperation } from "@house-rules/domain/contract";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
import { RelateResult, UnrelateResult } from "./events.js";
|
|
4
|
+
import { RelationsPermissions } from "./permissions.js";
|
|
5
|
+
import { DuplicateRelation, GraphTooLarge, ListRelationsInput, RelateInput, RelationCardinalityExceeded, RelationCycle, RelationEndpointInvalid, RelationList, RelationNotFound, RelationGraph, RelationsInputInvalid, RelationsStoreUnavailable, RelationTypeUnknown, SubgraphInput, } from "./relation.js";
|
|
6
|
+
const readFailure = Schema.Union([
|
|
7
|
+
RelationTypeUnknown,
|
|
8
|
+
RelationEndpointInvalid,
|
|
9
|
+
RelationsInputInvalid,
|
|
10
|
+
RelationsStoreUnavailable,
|
|
11
|
+
]);
|
|
12
|
+
const relate = defineOperation("Relations.relate", {
|
|
13
|
+
description: "Connect two opaque nodes after structural validation. The app checks access and existence first.",
|
|
14
|
+
input: RelateInput,
|
|
15
|
+
output: RelateResult,
|
|
16
|
+
failure: Schema.Union([
|
|
17
|
+
RelationTypeUnknown,
|
|
18
|
+
RelationEndpointInvalid,
|
|
19
|
+
DuplicateRelation,
|
|
20
|
+
RelationCardinalityExceeded,
|
|
21
|
+
RelationCycle,
|
|
22
|
+
GraphTooLarge,
|
|
23
|
+
RelationsStoreUnavailable,
|
|
24
|
+
]),
|
|
25
|
+
permission: RelationsPermissions.write,
|
|
26
|
+
transactional: true,
|
|
27
|
+
});
|
|
28
|
+
const unrelate = defineOperation("Relations.unrelate", {
|
|
29
|
+
description: "Remove one structural edge by id. The app checks endpoint access first.",
|
|
30
|
+
input: Schema.Struct({ id: Schema.NonEmptyString }),
|
|
31
|
+
output: UnrelateResult,
|
|
32
|
+
failure: Schema.Union([RelationNotFound, RelationsStoreUnavailable]),
|
|
33
|
+
permission: RelationsPermissions.write,
|
|
34
|
+
transactional: true,
|
|
35
|
+
destructive: true,
|
|
36
|
+
});
|
|
37
|
+
const listRelations = defineOperation("Relations.listRelations", {
|
|
38
|
+
description: "Page edges touching an opaque node. Direction follows stored endpoint order.",
|
|
39
|
+
input: ListRelationsInput,
|
|
40
|
+
output: RelationList,
|
|
41
|
+
failure: readFailure,
|
|
42
|
+
permission: RelationsPermissions.read,
|
|
43
|
+
readOnly: true,
|
|
44
|
+
});
|
|
45
|
+
const subgraph = defineOperation("Relations.subgraph", {
|
|
46
|
+
description: "Traverse one relation type breadth first, within configured graph bounds.",
|
|
47
|
+
input: SubgraphInput,
|
|
48
|
+
output: RelationGraph,
|
|
49
|
+
failure: Schema.Union([readFailure, GraphTooLarge]),
|
|
50
|
+
permission: RelationsPermissions.read,
|
|
51
|
+
readOnly: true,
|
|
52
|
+
});
|
|
53
|
+
export const RelationsOperations = { relate, unrelate, listRelations, subgraph };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare const RelationType: Schema.Struct<{
|
|
3
|
+
readonly name: Schema.NonEmptyString;
|
|
4
|
+
readonly source: Schema.$Array<Schema.NonEmptyString>;
|
|
5
|
+
readonly target: Schema.$Array<Schema.NonEmptyString>;
|
|
6
|
+
readonly directed: Schema.Boolean;
|
|
7
|
+
readonly acyclic: Schema.Boolean;
|
|
8
|
+
readonly unique: Schema.Boolean;
|
|
9
|
+
readonly maxOutPerSource: Schema.optionalKey<Schema.Int>;
|
|
10
|
+
readonly maxInPerTarget: Schema.optionalKey<Schema.Int>;
|
|
11
|
+
readonly maxDegree: Schema.optionalKey<Schema.Int>;
|
|
12
|
+
}>;
|
|
13
|
+
export type RelationType = typeof RelationType.Type;
|
|
14
|
+
export declare const RelationsOptions: Schema.Struct<{
|
|
15
|
+
readonly nodeKinds: Schema.$Array<Schema.NonEmptyString>;
|
|
16
|
+
readonly relationTypes: Schema.$Array<Schema.Struct<{
|
|
17
|
+
readonly name: Schema.NonEmptyString;
|
|
18
|
+
readonly source: Schema.$Array<Schema.NonEmptyString>;
|
|
19
|
+
readonly target: Schema.$Array<Schema.NonEmptyString>;
|
|
20
|
+
readonly directed: Schema.Boolean;
|
|
21
|
+
readonly acyclic: Schema.Boolean;
|
|
22
|
+
readonly unique: Schema.Boolean;
|
|
23
|
+
readonly maxOutPerSource: Schema.optionalKey<Schema.Int>;
|
|
24
|
+
readonly maxInPerTarget: Schema.optionalKey<Schema.Int>;
|
|
25
|
+
readonly maxDegree: Schema.optionalKey<Schema.Int>;
|
|
26
|
+
}>>;
|
|
27
|
+
readonly maxGraphSize: Schema.Int;
|
|
28
|
+
readonly maxPageSize: Schema.Int;
|
|
29
|
+
}>;
|
|
30
|
+
export type RelationsOptions = typeof RelationsOptions.Type;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
const cap = Schema.Int.check(Schema.isGreaterThan(0));
|
|
3
|
+
export const RelationType = Schema.Struct({
|
|
4
|
+
name: Schema.NonEmptyString,
|
|
5
|
+
source: Schema.Array(Schema.NonEmptyString).check(Schema.isMinLength(1)),
|
|
6
|
+
target: Schema.Array(Schema.NonEmptyString).check(Schema.isMinLength(1)),
|
|
7
|
+
directed: Schema.Boolean,
|
|
8
|
+
acyclic: Schema.Boolean,
|
|
9
|
+
unique: Schema.Boolean,
|
|
10
|
+
maxOutPerSource: Schema.optionalKey(cap),
|
|
11
|
+
maxInPerTarget: Schema.optionalKey(cap),
|
|
12
|
+
maxDegree: Schema.optionalKey(cap),
|
|
13
|
+
});
|
|
14
|
+
export const RelationsOptions = Schema.Struct({
|
|
15
|
+
nodeKinds: Schema.Array(Schema.NonEmptyString).check(Schema.isMinLength(1)),
|
|
16
|
+
relationTypes: Schema.Array(RelationType),
|
|
17
|
+
maxGraphSize: cap,
|
|
18
|
+
maxPageSize: cap,
|
|
19
|
+
}).check(Schema.makeFilter((options) => new Set(options.nodeKinds).size === options.nodeKinds.length &&
|
|
20
|
+
new Set(options.relationTypes.map((type) => type.name)).size ===
|
|
21
|
+
options.relationTypes.length &&
|
|
22
|
+
options.relationTypes.every((type) => [...type.source, ...type.target].every((kind) => options.nodeKinds.includes(kind)) &&
|
|
23
|
+
(type.directed
|
|
24
|
+
? type.maxDegree === undefined
|
|
25
|
+
: !type.acyclic &&
|
|
26
|
+
type.maxOutPerSource === undefined &&
|
|
27
|
+
type.maxInPerTarget === undefined))));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const RelationsPermissions = { read: "relations:read", write: "relations:write" };
|