@milaboratories/pl-tree 1.3.6
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/index.cjs +1023 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +979 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
- package/src/accessors.ts +422 -0
- package/src/index.ts +8 -0
- package/src/snapshot.test.ts +101 -0
- package/src/snapshot.ts +219 -0
- package/src/state.test.ts +316 -0
- package/src/state.ts +681 -0
- package/src/sync.test.ts +101 -0
- package/src/sync.ts +129 -0
- package/src/synchronized_tree.test.ts +210 -0
- package/src/synchronized_tree.ts +189 -0
- package/src/test_utils.ts +156 -0
- package/src/traversal_ops.ts +56 -0
- package/src/value_and_error.ts +19 -0
- package/src/value_or_error.ts +9 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Optional } from 'utility-types';
|
|
2
|
+
import {
|
|
3
|
+
BasicResourceData,
|
|
4
|
+
FieldData,
|
|
5
|
+
FieldType,
|
|
6
|
+
NullResourceId,
|
|
7
|
+
OptionalResourceId,
|
|
8
|
+
ResourceData,
|
|
9
|
+
ResourceId,
|
|
10
|
+
ResourceType
|
|
11
|
+
} from '@milaboratories/pl-client';
|
|
12
|
+
import { ExtendedResourceData } from './state';
|
|
13
|
+
|
|
14
|
+
export const TestRootType1: ResourceType = {
|
|
15
|
+
name: 'TestRootResource1',
|
|
16
|
+
version: '0'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const TestRootType2: ResourceType = {
|
|
20
|
+
name: 'TestRootResource2',
|
|
21
|
+
version: '0'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const TestStructuralResourceType1: ResourceType = {
|
|
25
|
+
name: 'TestStructuralResource1',
|
|
26
|
+
version: '0'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const TestStructuralResourceType2: ResourceType = {
|
|
30
|
+
name: 'TestStructuralResource2',
|
|
31
|
+
version: '0'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const TestValueResourceType1: ResourceType = {
|
|
35
|
+
name: 'TestValueResource1',
|
|
36
|
+
version: '0'
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const TestValueResourceType2: ResourceType = {
|
|
40
|
+
name: 'TestValueResource2',
|
|
41
|
+
version: '0'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const TestErrorResourceType1: ResourceType = {
|
|
45
|
+
name: 'json/resourceError',
|
|
46
|
+
version: '1'
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const ResourceReady: Pick<
|
|
50
|
+
BasicResourceData,
|
|
51
|
+
'inputsLocked' | 'outputsLocked' | 'resourceReady' | 'final'
|
|
52
|
+
> = {
|
|
53
|
+
inputsLocked: true,
|
|
54
|
+
outputsLocked: true,
|
|
55
|
+
resourceReady: true,
|
|
56
|
+
final: true
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const InitialStructuralResourceState: Omit<ExtendedResourceData, 'id' | 'type' | 'fields'> =
|
|
60
|
+
{
|
|
61
|
+
kind: 'Structural',
|
|
62
|
+
originalResourceId: NullResourceId,
|
|
63
|
+
error: NullResourceId,
|
|
64
|
+
inputsLocked: false,
|
|
65
|
+
outputsLocked: false,
|
|
66
|
+
resourceReady: false,
|
|
67
|
+
final: false,
|
|
68
|
+
kv: []
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const InitialValueResourceState: Omit<ExtendedResourceData, 'id' | 'type' | 'data'> = {
|
|
72
|
+
kind: 'Value',
|
|
73
|
+
originalResourceId: NullResourceId,
|
|
74
|
+
error: NullResourceId,
|
|
75
|
+
...ResourceReady,
|
|
76
|
+
fields: [],
|
|
77
|
+
kv: []
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const TestStructuralResourceState1: Omit<ExtendedResourceData, 'id' | 'fields'> = {
|
|
81
|
+
...InitialStructuralResourceState,
|
|
82
|
+
type: TestStructuralResourceType1
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const TestStructuralResourceState2: Omit<ExtendedResourceData, 'id' | 'fields'> = {
|
|
86
|
+
...InitialStructuralResourceState,
|
|
87
|
+
type: TestStructuralResourceType2
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const TestValueResourceState1: Omit<ExtendedResourceData, 'id' | 'data'> = {
|
|
91
|
+
...InitialValueResourceState,
|
|
92
|
+
type: TestValueResourceType1
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const TestValueResourceState2: Omit<ExtendedResourceData, 'id' | 'data'> = {
|
|
96
|
+
...InitialValueResourceState,
|
|
97
|
+
type: TestValueResourceType2
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const TestErrorResourceState2: Omit<ExtendedResourceData, 'id' | 'data'> = {
|
|
101
|
+
...InitialValueResourceState,
|
|
102
|
+
type: TestErrorResourceType1
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const TestDynamicRootId1 = 1000001n as ResourceId;
|
|
106
|
+
export const TestDynamicRootState1: Omit<ExtendedResourceData, 'fields'> = {
|
|
107
|
+
...InitialStructuralResourceState,
|
|
108
|
+
inputsLocked: true,
|
|
109
|
+
outputsLocked: true,
|
|
110
|
+
resourceReady: true,
|
|
111
|
+
type: TestRootType1,
|
|
112
|
+
id: TestDynamicRootId1
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const TestDynamicRootId2 = 1000002n as ResourceId;
|
|
116
|
+
export const TestDynamicRootState2: Omit<ExtendedResourceData, 'fields'> = {
|
|
117
|
+
...InitialStructuralResourceState,
|
|
118
|
+
inputsLocked: true,
|
|
119
|
+
outputsLocked: true,
|
|
120
|
+
resourceReady: true,
|
|
121
|
+
type: TestRootType2,
|
|
122
|
+
id: TestDynamicRootId2
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export function field(
|
|
126
|
+
type: FieldType,
|
|
127
|
+
name: string,
|
|
128
|
+
value: OptionalResourceId = NullResourceId,
|
|
129
|
+
error: OptionalResourceId = NullResourceId,
|
|
130
|
+
valueIsFinal: boolean = false
|
|
131
|
+
): FieldData {
|
|
132
|
+
return {
|
|
133
|
+
name,
|
|
134
|
+
type,
|
|
135
|
+
value,
|
|
136
|
+
error,
|
|
137
|
+
status: value !== NullResourceId ? 'Resolved' : error !== NullResourceId ? 'Assigned' : 'Empty',
|
|
138
|
+
valueIsFinal
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function dField(
|
|
143
|
+
name: string,
|
|
144
|
+
value: OptionalResourceId = NullResourceId,
|
|
145
|
+
error: OptionalResourceId = NullResourceId
|
|
146
|
+
): FieldData {
|
|
147
|
+
return field('Dynamic', name, value, error);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function iField(
|
|
151
|
+
name: string,
|
|
152
|
+
value: OptionalResourceId = NullResourceId,
|
|
153
|
+
error: OptionalResourceId = NullResourceId
|
|
154
|
+
): FieldData {
|
|
155
|
+
return field('Input', name, value, error);
|
|
156
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { FieldType, ResourceType } from '@milaboratories/pl-client';
|
|
2
|
+
|
|
3
|
+
export type CommonTraversalOps = {
|
|
4
|
+
/**
|
|
5
|
+
* Don't terminate chain if current resource or field has an error associated
|
|
6
|
+
* with, by default resource or field error will be thrown. If field has error
|
|
7
|
+
* and no value, error will be thrown anyway, because this is the reason
|
|
8
|
+
* traversal is terminated.
|
|
9
|
+
* */
|
|
10
|
+
ignoreError?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type CommonFieldTraverseOps = {
|
|
14
|
+
/**
|
|
15
|
+
* Valid only if {@link assertFieldType} is defined and equal to 'Input',
|
|
16
|
+
* 'Service' or 'Output'. By default, if field is not found, and corresponding
|
|
17
|
+
* field list is locked, call will fail with exception.
|
|
18
|
+
* */
|
|
19
|
+
allowPermanentAbsence?: boolean;
|
|
20
|
+
|
|
21
|
+
/** Will not mark current context as unstable, if field is not found. */
|
|
22
|
+
stableIfNotFound?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* If encounter field with error and no value, will silently terminate the
|
|
26
|
+
* traversal and return undefined.
|
|
27
|
+
* */
|
|
28
|
+
pureFieldErrorToUndefined?: boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ResourceTraversalOps = CommonTraversalOps & {
|
|
32
|
+
/**
|
|
33
|
+
* Assert resource type of the resource the fields points to. Call will fail
|
|
34
|
+
* with exception if this assertion is not fulfilled.
|
|
35
|
+
* */
|
|
36
|
+
assertResourceType?: ResourceType | ResourceType[];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type GetFieldStep = CommonFieldTraverseOps & {
|
|
40
|
+
/** Field name */
|
|
41
|
+
field: string;
|
|
42
|
+
|
|
43
|
+
/** Field must exist, if this option is set, instead error will be thrown */
|
|
44
|
+
errorIfFieldNotFound?: boolean;
|
|
45
|
+
|
|
46
|
+
/** Field must be resolved into resource if this option is set, instead error will be thrown */
|
|
47
|
+
errorIfFieldNotSet?: boolean;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Assert field type. Call will fail with exception if this assertion is not
|
|
51
|
+
* fulfilled
|
|
52
|
+
* */
|
|
53
|
+
assertFieldType?: FieldType;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type FieldTraversalStep = GetFieldStep & ResourceTraversalOps;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ValueAndError<T> {
|
|
2
|
+
value?: T;
|
|
3
|
+
error?: T;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function mapValueAndErrorIfDefined<T1, T2>(
|
|
7
|
+
input: ValueAndError<T1> | undefined,
|
|
8
|
+
mapping: (v: T1) => T2
|
|
9
|
+
): ValueAndError<T2> | undefined {
|
|
10
|
+
if (input === undefined) return undefined;
|
|
11
|
+
else return mapValueAndError(input, mapping);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function mapValueAndError<T1, T2>(input: ValueAndError<T1>, mapping: (v: T1) => T2) {
|
|
15
|
+
const ret = {} as ValueAndError<T2>;
|
|
16
|
+
if (input.value !== undefined) ret.value = mapping(input.value);
|
|
17
|
+
if (input.error !== undefined) ret.error = mapping(input.error);
|
|
18
|
+
return ret;
|
|
19
|
+
}
|