@milaboratories/pl-tree 1.8.33 → 1.8.35
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/accessors.cjs +23 -23
- package/dist/accessors.cjs.map +1 -1
- package/dist/accessors.d.ts +10 -10
- package/dist/accessors.d.ts.map +1 -1
- package/dist/accessors.js +23 -23
- package/dist/accessors.js.map +1 -1
- package/dist/dump.cjs.map +1 -1
- package/dist/dump.d.ts +1 -1
- package/dist/dump.js.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/snapshot.cjs +3 -3
- package/dist/snapshot.cjs.map +1 -1
- package/dist/snapshot.d.ts +12 -12
- package/dist/snapshot.js +3 -3
- package/dist/snapshot.js.map +1 -1
- package/dist/state.cjs +33 -33
- package/dist/state.cjs.map +1 -1
- package/dist/state.d.ts +10 -10
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +33 -33
- package/dist/state.js.map +1 -1
- package/dist/sync.cjs +1 -1
- package/dist/sync.cjs.map +1 -1
- package/dist/sync.d.ts +2 -2
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +1 -1
- package/dist/sync.js.map +1 -1
- package/dist/synchronized_tree.cjs +11 -11
- package/dist/synchronized_tree.cjs.map +1 -1
- package/dist/synchronized_tree.d.ts +6 -6
- package/dist/synchronized_tree.d.ts.map +1 -1
- package/dist/synchronized_tree.js +11 -11
- package/dist/synchronized_tree.js.map +1 -1
- package/dist/test_utils.d.ts +12 -12
- package/dist/test_utils.d.ts.map +1 -1
- package/dist/traversal_ops.d.ts +1 -1
- package/dist/value_or_error.d.ts.map +1 -1
- package/package.json +23 -22
- package/src/accessors.ts +44 -43
- package/src/dump.ts +1 -1
- package/src/index.ts +9 -9
- package/src/snapshot.test.ts +29 -29
- package/src/snapshot.ts +26 -26
- package/src/state.test.ts +88 -85
- package/src/state.ts +123 -71
- package/src/sync.test.ts +31 -31
- package/src/sync.ts +6 -8
- package/src/synchronized_tree.test.ts +60 -60
- package/src/synchronized_tree.ts +41 -38
- package/src/test_utils.ts +33 -35
- package/src/traversal_ops.ts +1 -1
- package/src/value_or_error.ts +6 -6
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { test, expect } from
|
|
2
|
-
import { field, TestHelpers } from
|
|
3
|
-
import { TestStructuralResourceType1 } from
|
|
4
|
-
import { Computable } from
|
|
5
|
-
import { SynchronizedTreeState } from
|
|
6
|
-
import { ConsoleLoggerAdapter } from
|
|
7
|
-
import tp from
|
|
8
|
-
|
|
9
|
-
test(
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import { field, TestHelpers } from "@milaboratories/pl-client";
|
|
3
|
+
import { TestStructuralResourceType1 } from "./test_utils";
|
|
4
|
+
import { Computable } from "@milaboratories/computable";
|
|
5
|
+
import { SynchronizedTreeState } from "./synchronized_tree";
|
|
6
|
+
import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
|
|
7
|
+
import tp from "timers/promises";
|
|
8
|
+
|
|
9
|
+
test("simple synchronized tree test", async () => {
|
|
10
10
|
await TestHelpers.withTempRoot(async (pl) => {
|
|
11
11
|
const r1 = await pl.withWriteTx(
|
|
12
|
-
|
|
12
|
+
"CreatingStructure1",
|
|
13
13
|
async (tx) => {
|
|
14
14
|
const rr1 = tx.createStruct(TestStructuralResourceType1);
|
|
15
|
-
const ff1 = field(tx.clientRoot,
|
|
16
|
-
tx.createField(ff1,
|
|
15
|
+
const ff1 = field(tx.clientRoot, "f1");
|
|
16
|
+
tx.createField(ff1, "Dynamic");
|
|
17
17
|
tx.setField(ff1, rr1);
|
|
18
18
|
await tx.commit();
|
|
19
19
|
return await rr1.globalId;
|
|
20
20
|
},
|
|
21
|
-
{ sync: true }
|
|
21
|
+
{ sync: true },
|
|
22
22
|
);
|
|
23
23
|
|
|
24
24
|
const treeState = await SynchronizedTreeState.init(
|
|
@@ -27,33 +27,33 @@ test('simple synchronized tree test', async () => {
|
|
|
27
27
|
{
|
|
28
28
|
stopPollingDelay: 10,
|
|
29
29
|
pollingInterval: 10,
|
|
30
|
-
logStat:
|
|
30
|
+
logStat: "cumulative",
|
|
31
31
|
},
|
|
32
|
-
new ConsoleLoggerAdapter(require(
|
|
32
|
+
new ConsoleLoggerAdapter(require("console")),
|
|
33
33
|
);
|
|
34
34
|
|
|
35
35
|
const theComputable = Computable.make((c) =>
|
|
36
|
-
c.accessor(treeState.entry()).node().traverse(
|
|
36
|
+
c.accessor(treeState.entry()).node().traverse("a", "b")?.getDataAsString(),
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
await theComputable.refreshState();
|
|
40
40
|
|
|
41
41
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
42
42
|
stable: false,
|
|
43
|
-
value: undefined
|
|
43
|
+
value: undefined,
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
const r2 = await pl.withWriteTx(
|
|
47
|
-
|
|
47
|
+
"CreatingStructure2",
|
|
48
48
|
async (tx) => {
|
|
49
49
|
const rr2 = tx.createStruct(TestStructuralResourceType1);
|
|
50
|
-
const ff2 = field(r1,
|
|
51
|
-
tx.createField(ff2,
|
|
50
|
+
const ff2 = field(r1, "a");
|
|
51
|
+
tx.createField(ff2, "Input");
|
|
52
52
|
tx.setField(ff2, rr2);
|
|
53
53
|
await tx.commit();
|
|
54
54
|
return await rr2.globalId;
|
|
55
55
|
},
|
|
56
|
-
{ sync: true }
|
|
56
|
+
{ sync: true },
|
|
57
57
|
);
|
|
58
58
|
await tp.setTimeout(10);
|
|
59
59
|
|
|
@@ -62,20 +62,20 @@ test('simple synchronized tree test', async () => {
|
|
|
62
62
|
expect(theComputable.isChanged()).toBe(true);
|
|
63
63
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
64
64
|
stable: false,
|
|
65
|
-
value: undefined
|
|
65
|
+
value: undefined,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
await pl.withWriteTx(
|
|
69
|
+
"CreatingStructure3",
|
|
70
70
|
async (tx) => {
|
|
71
|
-
const rr3 = tx.createValue(TestStructuralResourceType1,
|
|
72
|
-
const ff3 = field(r2,
|
|
73
|
-
tx.createField(ff3,
|
|
71
|
+
const rr3 = tx.createValue(TestStructuralResourceType1, "hi!");
|
|
72
|
+
const ff3 = field(r2, "b");
|
|
73
|
+
tx.createField(ff3, "Input");
|
|
74
74
|
tx.setField(ff3, rr3);
|
|
75
75
|
await tx.commit();
|
|
76
76
|
return await rr3.globalId;
|
|
77
77
|
},
|
|
78
|
-
{ sync: true }
|
|
78
|
+
{ sync: true },
|
|
79
79
|
);
|
|
80
80
|
await tp.setTimeout(10);
|
|
81
81
|
|
|
@@ -84,17 +84,17 @@ test('simple synchronized tree test', async () => {
|
|
|
84
84
|
expect(theComputable.isChanged()).toBe(true);
|
|
85
85
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
86
86
|
stable: true,
|
|
87
|
-
value:
|
|
87
|
+
value: "hi!",
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
await pl.withWriteTx(
|
|
91
|
-
|
|
91
|
+
"CreatingStructure3",
|
|
92
92
|
async (tx) => {
|
|
93
93
|
tx.lock(r1);
|
|
94
94
|
tx.lock(r2);
|
|
95
95
|
await tx.commit();
|
|
96
96
|
},
|
|
97
|
-
{ sync: true }
|
|
97
|
+
{ sync: true },
|
|
98
98
|
);
|
|
99
99
|
await tp.setTimeout(10);
|
|
100
100
|
|
|
@@ -103,26 +103,26 @@ test('simple synchronized tree test', async () => {
|
|
|
103
103
|
expect(theComputable.isChanged()).toBe(true);
|
|
104
104
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
105
105
|
stable: true,
|
|
106
|
-
value:
|
|
106
|
+
value: "hi!",
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
await treeState.awaitSyncLoopTermination();
|
|
110
110
|
});
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
test(
|
|
113
|
+
test("synchronized tree test with KV", async () => {
|
|
114
114
|
await TestHelpers.withTempRoot(async (pl) => {
|
|
115
115
|
const r1 = await pl.withWriteTx(
|
|
116
|
-
|
|
116
|
+
"CreatingStructure1",
|
|
117
117
|
async (tx) => {
|
|
118
118
|
const rr1 = tx.createStruct(TestStructuralResourceType1);
|
|
119
|
-
const ff1 = field(tx.clientRoot,
|
|
120
|
-
tx.createField(ff1,
|
|
119
|
+
const ff1 = field(tx.clientRoot, "f1");
|
|
120
|
+
tx.createField(ff1, "Dynamic");
|
|
121
121
|
tx.setField(ff1, rr1);
|
|
122
122
|
await tx.commit();
|
|
123
123
|
return await rr1.globalId;
|
|
124
124
|
},
|
|
125
|
-
{ sync: true }
|
|
125
|
+
{ sync: true },
|
|
126
126
|
);
|
|
127
127
|
|
|
128
128
|
const treeState = await SynchronizedTreeState.init(
|
|
@@ -131,33 +131,33 @@ test('synchronized tree test with KV', async () => {
|
|
|
131
131
|
{
|
|
132
132
|
stopPollingDelay: 10,
|
|
133
133
|
pollingInterval: 10,
|
|
134
|
-
logStat:
|
|
134
|
+
logStat: "cumulative",
|
|
135
135
|
},
|
|
136
|
-
new ConsoleLoggerAdapter(require(
|
|
136
|
+
new ConsoleLoggerAdapter(require("console")),
|
|
137
137
|
);
|
|
138
138
|
|
|
139
139
|
const theComputable = Computable.make((c) =>
|
|
140
|
-
c.accessor(treeState.entry()).node().traverse(
|
|
140
|
+
c.accessor(treeState.entry()).node().traverse("a")?.getKeyValueAsString("b", true),
|
|
141
141
|
);
|
|
142
142
|
|
|
143
143
|
await theComputable.refreshState();
|
|
144
144
|
|
|
145
145
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
146
146
|
stable: false,
|
|
147
|
-
value: undefined
|
|
147
|
+
value: undefined,
|
|
148
148
|
});
|
|
149
149
|
|
|
150
150
|
const r2 = await pl.withWriteTx(
|
|
151
|
-
|
|
151
|
+
"CreatingStructure2",
|
|
152
152
|
async (tx) => {
|
|
153
153
|
const rr2 = tx.createStruct(TestStructuralResourceType1);
|
|
154
|
-
const ff2 = field(r1,
|
|
155
|
-
tx.createField(ff2,
|
|
154
|
+
const ff2 = field(r1, "a");
|
|
155
|
+
tx.createField(ff2, "Input");
|
|
156
156
|
tx.setField(ff2, rr2);
|
|
157
157
|
await tx.commit();
|
|
158
158
|
return await rr2.globalId;
|
|
159
159
|
},
|
|
160
|
-
{ sync: true }
|
|
160
|
+
{ sync: true },
|
|
161
161
|
);
|
|
162
162
|
await tp.setTimeout(10);
|
|
163
163
|
|
|
@@ -166,11 +166,11 @@ test('synchronized tree test with KV', async () => {
|
|
|
166
166
|
expect(theComputable.isChanged()).toBe(true);
|
|
167
167
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
168
168
|
stable: false,
|
|
169
|
-
value: undefined
|
|
169
|
+
value: undefined,
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
-
await pl.withWriteTx(
|
|
173
|
-
tx.setKValue(r2,
|
|
172
|
+
await pl.withWriteTx("AssignKeyValue", async (tx) => {
|
|
173
|
+
tx.setKValue(r2, "b", "hi!");
|
|
174
174
|
await tx.commit();
|
|
175
175
|
});
|
|
176
176
|
await tp.setTimeout(10);
|
|
@@ -180,26 +180,26 @@ test('synchronized tree test with KV', async () => {
|
|
|
180
180
|
expect(theComputable.isChanged()).toBe(true);
|
|
181
181
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
182
182
|
stable: true,
|
|
183
|
-
value:
|
|
183
|
+
value: "hi!",
|
|
184
184
|
});
|
|
185
185
|
|
|
186
186
|
await treeState.awaitSyncLoopTermination();
|
|
187
187
|
});
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
-
test(
|
|
190
|
+
test("termination test", async () => {
|
|
191
191
|
await TestHelpers.withTempRoot(async (pl) => {
|
|
192
192
|
const r1 = await pl.withWriteTx(
|
|
193
|
-
|
|
193
|
+
"CreatingStructure1",
|
|
194
194
|
async (tx) => {
|
|
195
195
|
const rr1 = tx.createStruct(TestStructuralResourceType1);
|
|
196
|
-
const ff1 = field(tx.clientRoot,
|
|
197
|
-
tx.createField(ff1,
|
|
196
|
+
const ff1 = field(tx.clientRoot, "f1");
|
|
197
|
+
tx.createField(ff1, "Dynamic");
|
|
198
198
|
tx.setField(ff1, rr1);
|
|
199
199
|
await tx.commit();
|
|
200
200
|
return await rr1.globalId;
|
|
201
201
|
},
|
|
202
|
-
{ sync: true }
|
|
202
|
+
{ sync: true },
|
|
203
203
|
);
|
|
204
204
|
await tp.setTimeout(10);
|
|
205
205
|
|
|
@@ -209,29 +209,29 @@ test('termination test', async () => {
|
|
|
209
209
|
{
|
|
210
210
|
stopPollingDelay: 10,
|
|
211
211
|
pollingInterval: 10,
|
|
212
|
-
logStat:
|
|
212
|
+
logStat: "cumulative",
|
|
213
213
|
},
|
|
214
|
-
new ConsoleLoggerAdapter(require(
|
|
214
|
+
new ConsoleLoggerAdapter(require("console")),
|
|
215
215
|
);
|
|
216
216
|
|
|
217
217
|
const entry = treeState.entry();
|
|
218
218
|
const theComputable = Computable.make((c) =>
|
|
219
|
-
c.accessor(entry).node().traverse(
|
|
219
|
+
c.accessor(entry).node().traverse("a")?.getKeyValueAsString("b", true),
|
|
220
220
|
);
|
|
221
221
|
|
|
222
222
|
await theComputable.refreshState();
|
|
223
223
|
|
|
224
224
|
expect(await theComputable.getValueOrError()).toMatchObject({
|
|
225
225
|
stable: false,
|
|
226
|
-
value: undefined
|
|
226
|
+
value: undefined,
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
await treeState.terminate();
|
|
230
230
|
|
|
231
231
|
const resultAfterTermination = await theComputable.getValueOrError();
|
|
232
232
|
expect(resultAfterTermination).toMatchObject({
|
|
233
|
-
type:
|
|
233
|
+
type: "error",
|
|
234
234
|
});
|
|
235
|
-
expect((resultAfterTermination as any).errors[0].message).toMatch(
|
|
235
|
+
expect((resultAfterTermination as any).errors[0].message).toMatch("terminated");
|
|
236
236
|
});
|
|
237
237
|
});
|
package/src/synchronized_tree.ts
CHANGED
|
@@ -1,29 +1,20 @@
|
|
|
1
|
-
import { PollingComputableHooks } from
|
|
2
|
-
import { PlTreeEntry } from
|
|
1
|
+
import { PollingComputableHooks } from "@milaboratories/computable";
|
|
2
|
+
import { PlTreeEntry } from "./accessors";
|
|
3
3
|
import type {
|
|
4
4
|
FinalResourceDataPredicate,
|
|
5
5
|
PlClient,
|
|
6
6
|
ResourceId,
|
|
7
7
|
TxOps,
|
|
8
|
-
} from
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
} from
|
|
12
|
-
import type {
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
constructTreeLoadingRequest,
|
|
20
|
-
initialTreeLoadingStat,
|
|
21
|
-
loadTreeState,
|
|
22
|
-
} from './sync';
|
|
23
|
-
import * as tp from 'node:timers/promises';
|
|
24
|
-
import type { MiLogger } from '@milaboratories/ts-helpers';
|
|
25
|
-
|
|
26
|
-
type StatLoggingMode = 'cumulative' | 'per-request';
|
|
8
|
+
} from "@milaboratories/pl-client";
|
|
9
|
+
import { isTimeoutOrCancelError } from "@milaboratories/pl-client";
|
|
10
|
+
import type { ExtendedResourceData } from "./state";
|
|
11
|
+
import { PlTreeState, TreeStateUpdateError } from "./state";
|
|
12
|
+
import type { PruningFunction, TreeLoadingStat } from "./sync";
|
|
13
|
+
import { constructTreeLoadingRequest, initialTreeLoadingStat, loadTreeState } from "./sync";
|
|
14
|
+
import * as tp from "node:timers/promises";
|
|
15
|
+
import type { MiLogger } from "@milaboratories/ts-helpers";
|
|
16
|
+
|
|
17
|
+
type StatLoggingMode = "cumulative" | "per-request";
|
|
27
18
|
|
|
28
19
|
export type SynchronizedTreeOps = {
|
|
29
20
|
/** Override final predicate from the PlClient */
|
|
@@ -81,19 +72,19 @@ export class SynchronizedTreeState {
|
|
|
81
72
|
|
|
82
73
|
/** @deprecated use "entry" instead */
|
|
83
74
|
public accessor(rid: ResourceId = this.root): PlTreeEntry {
|
|
84
|
-
if (this.terminated) throw new Error(
|
|
75
|
+
if (this.terminated) throw new Error("tree synchronization is terminated");
|
|
85
76
|
return this.entry(rid);
|
|
86
77
|
}
|
|
87
78
|
|
|
88
79
|
public entry(rid: ResourceId = this.root): PlTreeEntry {
|
|
89
|
-
if (this.terminated) throw new Error(
|
|
80
|
+
if (this.terminated) throw new Error("tree synchronization is terminated");
|
|
90
81
|
return new PlTreeEntry({ treeProvider: () => this.state, hooks: this.hooks }, rid);
|
|
91
82
|
}
|
|
92
83
|
|
|
93
84
|
/** Can be used to externally kick off the synchronization polling loop, and
|
|
94
85
|
* await for the first synchronization to happen. */
|
|
95
86
|
public async refreshState(): Promise<void> {
|
|
96
|
-
if (this.terminated) throw new Error(
|
|
87
|
+
if (this.terminated) throw new Error("tree synchronization is terminated");
|
|
97
88
|
await this.hooks.refreshState();
|
|
98
89
|
}
|
|
99
90
|
|
|
@@ -102,7 +93,7 @@ export class SynchronizedTreeState {
|
|
|
102
93
|
|
|
103
94
|
/** Called from computable hooks when external observer asks for state refresh */
|
|
104
95
|
private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {
|
|
105
|
-
if (this.terminated) reject(new Error(
|
|
96
|
+
if (this.terminated) reject(new Error("tree synchronization is terminated"));
|
|
106
97
|
else {
|
|
107
98
|
this.scheduledOnNextState.push({ resolve, reject });
|
|
108
99
|
if (this.currentLoopDelayInterrupt) {
|
|
@@ -131,11 +122,15 @@ export class SynchronizedTreeState {
|
|
|
131
122
|
|
|
132
123
|
/** Executed from the main loop, and initialization procedure. */
|
|
133
124
|
private async refresh(stats?: TreeLoadingStat, txOps?: TxOps): Promise<void> {
|
|
134
|
-
if (this.terminated) throw new Error(
|
|
125
|
+
if (this.terminated) throw new Error("tree synchronization is terminated");
|
|
135
126
|
const request = constructTreeLoadingRequest(this.state, this.pruning);
|
|
136
|
-
const data = await this.pl.withReadTx(
|
|
137
|
-
|
|
138
|
-
|
|
127
|
+
const data = await this.pl.withReadTx(
|
|
128
|
+
"ReadingTree",
|
|
129
|
+
async (tx) => {
|
|
130
|
+
return await loadTreeState(tx, request, stats);
|
|
131
|
+
},
|
|
132
|
+
txOps,
|
|
133
|
+
);
|
|
139
134
|
this.state.updateFromResourceData(data, true);
|
|
140
135
|
}
|
|
141
136
|
|
|
@@ -162,20 +157,26 @@ export class SynchronizedTreeState {
|
|
|
162
157
|
|
|
163
158
|
try {
|
|
164
159
|
// resetting stats if we were asked to collect non-cumulative stats
|
|
165
|
-
if (this.logStat ===
|
|
160
|
+
if (this.logStat === "per-request") stat = initialTreeLoadingStat();
|
|
166
161
|
|
|
167
162
|
// actual tree synchronization
|
|
168
163
|
await this.refresh(stat);
|
|
169
164
|
|
|
170
165
|
// logging stats if we were asked to
|
|
171
|
-
if (stat && this.logger)
|
|
166
|
+
if (stat && this.logger)
|
|
167
|
+
this.logger.info(
|
|
168
|
+
`Tree stat (success, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`,
|
|
169
|
+
);
|
|
172
170
|
lastUpdate = Date.now();
|
|
173
171
|
|
|
174
172
|
// notifying that we got new state
|
|
175
173
|
if (toNotify !== undefined) for (const n of toNotify) n.resolve();
|
|
176
174
|
} catch (e: any) {
|
|
177
175
|
// logging stats if we were asked to (even if error occured)
|
|
178
|
-
if (stat && this.logger)
|
|
176
|
+
if (stat && this.logger)
|
|
177
|
+
this.logger.info(
|
|
178
|
+
`Tree stat (error, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`,
|
|
179
|
+
);
|
|
179
180
|
lastUpdate = Date.now();
|
|
180
181
|
|
|
181
182
|
// notifying that we failed to refresh the state
|
|
@@ -187,7 +188,7 @@ export class SynchronizedTreeState {
|
|
|
187
188
|
this.logger?.error(e);
|
|
188
189
|
|
|
189
190
|
// marking everybody who used previous state as changed
|
|
190
|
-
this.state.invalidateTree(
|
|
191
|
+
this.state.invalidateTree("stat update error");
|
|
191
192
|
// creating new tree
|
|
192
193
|
this.state = new PlTreeState(this.root, this.finalPredicate);
|
|
193
194
|
|
|
@@ -206,10 +207,12 @@ export class SynchronizedTreeState {
|
|
|
206
207
|
if (this.scheduledOnNextState.length === 0) {
|
|
207
208
|
try {
|
|
208
209
|
this.currentLoopDelayInterrupt = new AbortController();
|
|
209
|
-
await tp.setTimeout(
|
|
210
|
-
|
|
210
|
+
await tp.setTimeout(
|
|
211
|
+
this.pollingInterval,
|
|
212
|
+
AbortSignal.any([this.abortController.signal, this.currentLoopDelayInterrupt.signal]),
|
|
213
|
+
);
|
|
211
214
|
} catch (e: unknown) {
|
|
212
|
-
if (!isTimeoutOrCancelError(e)) throw new Error(
|
|
215
|
+
if (!isTimeoutOrCancelError(e)) throw new Error("Unexpected error", { cause: e });
|
|
213
216
|
break;
|
|
214
217
|
} finally {
|
|
215
218
|
this.currentLoopDelayInterrupt = undefined;
|
|
@@ -241,7 +244,7 @@ export class SynchronizedTreeState {
|
|
|
241
244
|
if (this.currentLoop === undefined) return;
|
|
242
245
|
await this.currentLoop;
|
|
243
246
|
|
|
244
|
-
this.state.invalidateTree(
|
|
247
|
+
this.state.invalidateTree("synchronization terminated for the tree");
|
|
245
248
|
}
|
|
246
249
|
|
|
247
250
|
/** @deprecated */
|
|
@@ -271,7 +274,7 @@ export class SynchronizedTreeState {
|
|
|
271
274
|
// logging stats if we were asked to (even if error occured)
|
|
272
275
|
if (stat && logger)
|
|
273
276
|
logger.info(
|
|
274
|
-
`Tree stat (initial load, ${ok ?
|
|
277
|
+
`Tree stat (initial load, ${ok ? "success" : "failure"}): ${JSON.stringify(stat)}`,
|
|
275
278
|
);
|
|
276
279
|
}
|
|
277
280
|
|
package/src/test_utils.ts
CHANGED
|
@@ -5,50 +5,48 @@ import type {
|
|
|
5
5
|
OptionalResourceId,
|
|
6
6
|
ResourceId,
|
|
7
7
|
ResourceType,
|
|
8
|
-
} from
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
} from '@milaboratories/pl-client';
|
|
12
|
-
import type { ExtendedResourceData } from './state';
|
|
8
|
+
} from "@milaboratories/pl-client";
|
|
9
|
+
import { NullResourceId } from "@milaboratories/pl-client";
|
|
10
|
+
import type { ExtendedResourceData } from "./state";
|
|
13
11
|
|
|
14
12
|
export const TestRootType1: ResourceType = {
|
|
15
|
-
name:
|
|
16
|
-
version:
|
|
13
|
+
name: "TestRootResource1",
|
|
14
|
+
version: "0",
|
|
17
15
|
};
|
|
18
16
|
|
|
19
17
|
export const TestRootType2: ResourceType = {
|
|
20
|
-
name:
|
|
21
|
-
version:
|
|
18
|
+
name: "TestRootResource2",
|
|
19
|
+
version: "0",
|
|
22
20
|
};
|
|
23
21
|
|
|
24
22
|
export const TestStructuralResourceType1: ResourceType = {
|
|
25
|
-
name:
|
|
26
|
-
version:
|
|
23
|
+
name: "TestStructuralResource1",
|
|
24
|
+
version: "0",
|
|
27
25
|
};
|
|
28
26
|
|
|
29
27
|
export const TestStructuralResourceType2: ResourceType = {
|
|
30
|
-
name:
|
|
31
|
-
version:
|
|
28
|
+
name: "TestStructuralResource2",
|
|
29
|
+
version: "0",
|
|
32
30
|
};
|
|
33
31
|
|
|
34
32
|
export const TestValueResourceType1: ResourceType = {
|
|
35
|
-
name:
|
|
36
|
-
version:
|
|
33
|
+
name: "TestValueResource1",
|
|
34
|
+
version: "0",
|
|
37
35
|
};
|
|
38
36
|
|
|
39
37
|
export const TestValueResourceType2: ResourceType = {
|
|
40
|
-
name:
|
|
41
|
-
version:
|
|
38
|
+
name: "TestValueResource2",
|
|
39
|
+
version: "0",
|
|
42
40
|
};
|
|
43
41
|
|
|
44
42
|
export const TestErrorResourceType1: ResourceType = {
|
|
45
|
-
name:
|
|
46
|
-
version:
|
|
43
|
+
name: "json/resourceError",
|
|
44
|
+
version: "1",
|
|
47
45
|
};
|
|
48
46
|
|
|
49
47
|
export const ResourceReady: Pick<
|
|
50
48
|
BasicResourceData,
|
|
51
|
-
|
|
49
|
+
"inputsLocked" | "outputsLocked" | "resourceReady" | "final"
|
|
52
50
|
> = {
|
|
53
51
|
inputsLocked: true,
|
|
54
52
|
outputsLocked: true,
|
|
@@ -56,9 +54,9 @@ export const ResourceReady: Pick<
|
|
|
56
54
|
final: true,
|
|
57
55
|
};
|
|
58
56
|
|
|
59
|
-
export const InitialStructuralResourceState: Omit<ExtendedResourceData,
|
|
60
|
-
|
|
61
|
-
kind:
|
|
57
|
+
export const InitialStructuralResourceState: Omit<ExtendedResourceData, "id" | "type" | "fields"> =
|
|
58
|
+
{
|
|
59
|
+
kind: "Structural",
|
|
62
60
|
originalResourceId: NullResourceId,
|
|
63
61
|
error: NullResourceId,
|
|
64
62
|
inputsLocked: false,
|
|
@@ -68,8 +66,8 @@ export const InitialStructuralResourceState: Omit<ExtendedResourceData, 'id' | '
|
|
|
68
66
|
kv: [],
|
|
69
67
|
};
|
|
70
68
|
|
|
71
|
-
export const InitialValueResourceState: Omit<ExtendedResourceData,
|
|
72
|
-
kind:
|
|
69
|
+
export const InitialValueResourceState: Omit<ExtendedResourceData, "id" | "type" | "data"> = {
|
|
70
|
+
kind: "Value",
|
|
73
71
|
originalResourceId: NullResourceId,
|
|
74
72
|
error: NullResourceId,
|
|
75
73
|
...ResourceReady,
|
|
@@ -77,33 +75,33 @@ export const InitialValueResourceState: Omit<ExtendedResourceData, 'id' | 'type'
|
|
|
77
75
|
kv: [],
|
|
78
76
|
};
|
|
79
77
|
|
|
80
|
-
export const TestStructuralResourceState1: Omit<ExtendedResourceData,
|
|
78
|
+
export const TestStructuralResourceState1: Omit<ExtendedResourceData, "id" | "fields"> = {
|
|
81
79
|
...InitialStructuralResourceState,
|
|
82
80
|
type: TestStructuralResourceType1,
|
|
83
81
|
};
|
|
84
82
|
|
|
85
|
-
export const TestStructuralResourceState2: Omit<ExtendedResourceData,
|
|
83
|
+
export const TestStructuralResourceState2: Omit<ExtendedResourceData, "id" | "fields"> = {
|
|
86
84
|
...InitialStructuralResourceState,
|
|
87
85
|
type: TestStructuralResourceType2,
|
|
88
86
|
};
|
|
89
87
|
|
|
90
|
-
export const TestValueResourceState1: Omit<ExtendedResourceData,
|
|
88
|
+
export const TestValueResourceState1: Omit<ExtendedResourceData, "id" | "data"> = {
|
|
91
89
|
...InitialValueResourceState,
|
|
92
90
|
type: TestValueResourceType1,
|
|
93
91
|
};
|
|
94
92
|
|
|
95
|
-
export const TestValueResourceState2: Omit<ExtendedResourceData,
|
|
93
|
+
export const TestValueResourceState2: Omit<ExtendedResourceData, "id" | "data"> = {
|
|
96
94
|
...InitialValueResourceState,
|
|
97
95
|
type: TestValueResourceType2,
|
|
98
96
|
};
|
|
99
97
|
|
|
100
|
-
export const TestErrorResourceState2: Omit<ExtendedResourceData,
|
|
98
|
+
export const TestErrorResourceState2: Omit<ExtendedResourceData, "id" | "data"> = {
|
|
101
99
|
...InitialValueResourceState,
|
|
102
100
|
type: TestErrorResourceType1,
|
|
103
101
|
};
|
|
104
102
|
|
|
105
103
|
export const TestDynamicRootId1 = 1000001n as ResourceId;
|
|
106
|
-
export const TestDynamicRootState1: Omit<ExtendedResourceData,
|
|
104
|
+
export const TestDynamicRootState1: Omit<ExtendedResourceData, "fields"> = {
|
|
107
105
|
...InitialStructuralResourceState,
|
|
108
106
|
inputsLocked: true,
|
|
109
107
|
outputsLocked: true,
|
|
@@ -113,7 +111,7 @@ export const TestDynamicRootState1: Omit<ExtendedResourceData, 'fields'> = {
|
|
|
113
111
|
};
|
|
114
112
|
|
|
115
113
|
export const TestDynamicRootId2 = 1000002n as ResourceId;
|
|
116
|
-
export const TestDynamicRootState2: Omit<ExtendedResourceData,
|
|
114
|
+
export const TestDynamicRootState2: Omit<ExtendedResourceData, "fields"> = {
|
|
117
115
|
...InitialStructuralResourceState,
|
|
118
116
|
inputsLocked: true,
|
|
119
117
|
outputsLocked: true,
|
|
@@ -134,7 +132,7 @@ export function field(
|
|
|
134
132
|
type,
|
|
135
133
|
value,
|
|
136
134
|
error,
|
|
137
|
-
status: value !== NullResourceId ?
|
|
135
|
+
status: value !== NullResourceId ? "Resolved" : error !== NullResourceId ? "Assigned" : "Empty",
|
|
138
136
|
valueIsFinal,
|
|
139
137
|
};
|
|
140
138
|
}
|
|
@@ -144,7 +142,7 @@ export function dField(
|
|
|
144
142
|
value: OptionalResourceId = NullResourceId,
|
|
145
143
|
error: OptionalResourceId = NullResourceId,
|
|
146
144
|
): FieldData {
|
|
147
|
-
return field(
|
|
145
|
+
return field("Dynamic", name, value, error);
|
|
148
146
|
}
|
|
149
147
|
|
|
150
148
|
export function iField(
|
|
@@ -152,5 +150,5 @@ export function iField(
|
|
|
152
150
|
value: OptionalResourceId = NullResourceId,
|
|
153
151
|
error: OptionalResourceId = NullResourceId,
|
|
154
152
|
): FieldData {
|
|
155
|
-
return field(
|
|
153
|
+
return field("Input", name, value, error);
|
|
156
154
|
}
|
package/src/traversal_ops.ts
CHANGED
package/src/value_or_error.ts
CHANGED