@milaboratories/pl-tree 1.8.47 → 1.8.49

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.
Files changed (57) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +29 -0
  2. package/dist/accessors.cjs +236 -273
  3. package/dist/accessors.cjs.map +1 -1
  4. package/dist/accessors.d.ts +93 -93
  5. package/dist/accessors.js +235 -271
  6. package/dist/accessors.js.map +1 -1
  7. package/dist/dump.cjs +79 -80
  8. package/dist/dump.cjs.map +1 -1
  9. package/dist/dump.d.ts +16 -18
  10. package/dist/dump.js +79 -79
  11. package/dist/dump.js.map +1 -1
  12. package/dist/index.cjs +31 -35
  13. package/dist/index.d.ts +10 -10
  14. package/dist/index.js +9 -8
  15. package/dist/snapshot.cjs +50 -71
  16. package/dist/snapshot.cjs.map +1 -1
  17. package/dist/snapshot.d.ts +36 -38
  18. package/dist/snapshot.js +49 -69
  19. package/dist/snapshot.js.map +1 -1
  20. package/dist/state.cjs +487 -624
  21. package/dist/state.cjs.map +1 -1
  22. package/dist/state.d.ts +117 -116
  23. package/dist/state.js +486 -622
  24. package/dist/state.js.map +1 -1
  25. package/dist/sync.cjs +113 -133
  26. package/dist/sync.cjs.map +1 -1
  27. package/dist/sync.d.ts +37 -33
  28. package/dist/sync.js +111 -131
  29. package/dist/sync.js.map +1 -1
  30. package/dist/synchronized_tree.cjs +167 -232
  31. package/dist/synchronized_tree.cjs.map +1 -1
  32. package/dist/synchronized_tree.d.ts +66 -68
  33. package/dist/synchronized_tree.js +165 -211
  34. package/dist/synchronized_tree.js.map +1 -1
  35. package/dist/traversal_ops.d.ts +42 -42
  36. package/dist/value_and_error.cjs +9 -12
  37. package/dist/value_and_error.cjs.map +1 -1
  38. package/dist/value_and_error.d.ts +8 -5
  39. package/dist/value_and_error.js +9 -11
  40. package/dist/value_and_error.js.map +1 -1
  41. package/dist/value_or_error.d.ts +8 -5
  42. package/package.json +9 -9
  43. package/src/synchronized_tree.ts +10 -5
  44. package/dist/accessors.d.ts.map +0 -1
  45. package/dist/dump.d.ts.map +0 -1
  46. package/dist/index.cjs.map +0 -1
  47. package/dist/index.d.ts.map +0 -1
  48. package/dist/index.js.map +0 -1
  49. package/dist/snapshot.d.ts.map +0 -1
  50. package/dist/state.d.ts.map +0 -1
  51. package/dist/sync.d.ts.map +0 -1
  52. package/dist/synchronized_tree.d.ts.map +0 -1
  53. package/dist/test_utils.d.ts +0 -25
  54. package/dist/test_utils.d.ts.map +0 -1
  55. package/dist/traversal_ops.d.ts.map +0 -1
  56. package/dist/value_and_error.d.ts.map +0 -1
  57. package/dist/value_or_error.d.ts.map +0 -1
@@ -1,214 +1,168 @@
1
- import { PollingComputableHooks } from '@milaboratories/computable';
2
- import { PlTreeEntry } from './accessors.js';
3
- import { isTimeoutOrCancelError } from '@milaboratories/pl-client';
4
- import { PlTreeState, TreeStateUpdateError } from './state.js';
5
- import { constructTreeLoadingRequest, loadTreeState, initialTreeLoadingStat } from './sync.js';
6
- import * as tp from 'node:timers/promises';
1
+ import { PlTreeEntry } from "./accessors.js";
2
+ import { PlTreeState, TreeStateUpdateError } from "./state.js";
3
+ import { constructTreeLoadingRequest, initialTreeLoadingStat, loadTreeState } from "./sync.js";
4
+ import { isTimeoutOrCancelError } from "@milaboratories/pl-client";
5
+ import { PollingComputableHooks } from "@milaboratories/computable";
6
+ import * as tp from "node:timers/promises";
7
7
 
8
- class SynchronizedTreeState {
9
- pl;
10
- root;
11
- logger;
12
- finalPredicate;
13
- state;
14
- pollingInterval;
15
- pruning;
16
- logStat;
17
- hooks;
18
- abortController = new AbortController();
19
- constructor(pl, root, ops, logger) {
20
- this.pl = pl;
21
- this.root = root;
22
- this.logger = logger;
23
- const { finalPredicateOverride, pruning, pollingInterval, stopPollingDelay, logStat } = ops;
24
- this.pruning = pruning;
25
- this.pollingInterval = pollingInterval;
26
- this.finalPredicate = finalPredicateOverride ?? pl.finalPredicate;
27
- this.logStat = logStat;
28
- this.state = new PlTreeState(root, this.finalPredicate);
29
- this.hooks = new PollingComputableHooks(() => this.startUpdating(), () => this.stopUpdating(), { stopDebounce: stopPollingDelay }, (resolve, reject) => this.scheduleOnNextState(resolve, reject));
30
- }
31
- /** @deprecated use "entry" instead */
32
- accessor(rid = this.root) {
33
- if (this.terminated)
34
- throw new Error("tree synchronization is terminated");
35
- return this.entry(rid);
36
- }
37
- entry(rid = this.root) {
38
- if (this.terminated)
39
- throw new Error("tree synchronization is terminated");
40
- return new PlTreeEntry({ treeProvider: () => this.state, hooks: this.hooks }, rid);
41
- }
42
- /** Can be used to externally kick off the synchronization polling loop, and
43
- * await for the first synchronization to happen. */
44
- async refreshState() {
45
- if (this.terminated)
46
- throw new Error("tree synchronization is terminated");
47
- await this.hooks.refreshState();
48
- }
49
- currentLoopDelayInterrupt = undefined;
50
- scheduledOnNextState = [];
51
- /** Called from computable hooks when external observer asks for state refresh */
52
- scheduleOnNextState(resolve, reject) {
53
- if (this.terminated)
54
- reject(new Error("tree synchronization is terminated"));
55
- else {
56
- this.scheduledOnNextState.push({ resolve, reject });
57
- if (this.currentLoopDelayInterrupt) {
58
- this.currentLoopDelayInterrupt.abort();
59
- this.currentLoopDelayInterrupt = undefined;
60
- }
61
- }
62
- }
63
- /** Called from observer */
64
- startUpdating() {
65
- if (this.terminated)
66
- return;
67
- this.keepRunning = true;
68
- if (this.currentLoop === undefined)
69
- this.currentLoop = this.mainLoop();
70
- }
71
- /** Called from observer */
72
- stopUpdating() {
73
- this.keepRunning = false;
74
- }
75
- /** If true, main loop will continue polling pl state. */
76
- keepRunning = false;
77
- /** Actual state of main loop. */
78
- currentLoop = undefined;
79
- /** Executed from the main loop, and initialization procedure. */
80
- async refresh(stats, txOps) {
81
- if (this.terminated)
82
- throw new Error("tree synchronization is terminated");
83
- const request = constructTreeLoadingRequest(this.state, this.pruning);
84
- const data = await this.pl.withReadTx("ReadingTree", async (tx) => {
85
- return await loadTreeState(tx, request, stats);
86
- }, txOps);
87
- this.state.updateFromResourceData(data, true);
88
- }
89
- /** If true this tree state is permanently terminaed. */
90
- terminated = false;
91
- async mainLoop() {
92
- // will hold request stats
93
- let stat = this.logStat ? initialTreeLoadingStat() : undefined;
94
- let lastUpdate = Date.now();
95
- while (true) {
96
- if (!this.keepRunning || this.terminated)
97
- break;
98
- // saving those who want to be notified about new state here
99
- // because those who will be added during the tree retrieval
100
- // should be notified only on the next round
101
- let toNotify = undefined;
102
- if (this.scheduledOnNextState.length > 0) {
103
- toNotify = this.scheduledOnNextState;
104
- this.scheduledOnNextState = [];
105
- }
106
- try {
107
- // resetting stats if we were asked to collect non-cumulative stats
108
- if (this.logStat === "per-request")
109
- stat = initialTreeLoadingStat();
110
- // actual tree synchronization
111
- await this.refresh(stat);
112
- // logging stats if we were asked to
113
- if (stat && this.logger)
114
- this.logger.info(`Tree stat (success, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`);
115
- lastUpdate = Date.now();
116
- // notifying that we got new state
117
- if (toNotify !== undefined)
118
- for (const n of toNotify)
119
- n.resolve();
120
- }
121
- catch (e) {
122
- // logging stats if we were asked to (even if error occured)
123
- if (stat && this.logger)
124
- this.logger.info(`Tree stat (error, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`);
125
- lastUpdate = Date.now();
126
- // notifying that we failed to refresh the state
127
- if (toNotify !== undefined)
128
- for (const n of toNotify)
129
- n.reject(e);
130
- // catching tree update errors, as they may leave our tree in inconsistent state
131
- if (e instanceof TreeStateUpdateError) {
132
- // important error logging, this should never happen
133
- this.logger?.error(e);
134
- // marking everybody who used previous state as changed
135
- this.state.invalidateTree("stat update error");
136
- // creating new tree
137
- this.state = new PlTreeState(this.root, this.finalPredicate);
138
- // scheduling state update without delay
139
- continue;
140
- // unfortunately external observer may still see tree in its default
141
- // empty state, though this is best we can do in this exceptional
142
- // situation, and hope on caching layers inside computables to present
143
- // some stale state until we reconstruct the tree again
144
- }
145
- else
146
- this.logger?.warn(e);
147
- }
148
- if (!this.keepRunning || this.terminated)
149
- break;
150
- if (this.scheduledOnNextState.length === 0) {
151
- try {
152
- this.currentLoopDelayInterrupt = new AbortController();
153
- await tp.setTimeout(this.pollingInterval, AbortSignal.any([this.abortController.signal, this.currentLoopDelayInterrupt.signal]));
154
- }
155
- catch (e) {
156
- if (!isTimeoutOrCancelError(e))
157
- throw new Error("Unexpected error", { cause: e });
158
- break;
159
- }
160
- finally {
161
- this.currentLoopDelayInterrupt = undefined;
162
- }
163
- }
164
- }
165
- // reset only as a very last line
166
- this.currentLoop = undefined;
167
- }
168
- /**
169
- * Dumps the current state of the tree.
170
- * @returns An array of ExtendedResourceData objects representing the current state of the tree.
171
- */
172
- dumpState() {
173
- return this.state.dumpState();
174
- }
175
- /**
176
- * Terminates the internal loop, and permanently destoys all internal state, so
177
- * all computables using this state will resolve to errors.
178
- * */
179
- async terminate() {
180
- this.keepRunning = false;
181
- this.terminated = true;
182
- this.abortController.abort();
183
- if (this.currentLoop === undefined)
184
- return;
185
- await this.currentLoop;
186
- this.state.invalidateTree("synchronization terminated for the tree");
187
- }
188
- /** @deprecated */
189
- async awaitSyncLoopTermination() {
190
- if (this.currentLoop === undefined)
191
- return;
192
- await this.currentLoop;
193
- }
194
- static async init(pl, root, ops, logger) {
195
- const tree = new SynchronizedTreeState(pl, root, ops, logger);
196
- const stat = ops.logStat ? initialTreeLoadingStat() : undefined;
197
- let ok = false;
198
- try {
199
- await tree.refresh(stat, {
200
- timeout: ops.initialTreeLoadingTimeout,
201
- });
202
- ok = true;
203
- }
204
- finally {
205
- // logging stats if we were asked to (even if error occured)
206
- if (stat && logger)
207
- logger.info(`Tree stat (initial load, ${ok ? "success" : "failure"}): ${JSON.stringify(stat)}`);
208
- }
209
- return tree;
210
- }
211
- }
8
+ //#region src/synchronized_tree.ts
9
+ var SynchronizedTreeState = class SynchronizedTreeState {
10
+ finalPredicate;
11
+ state;
12
+ pollingInterval;
13
+ pruning;
14
+ logStat;
15
+ hooks;
16
+ abortController = new AbortController();
17
+ constructor(pl, root, ops, logger) {
18
+ this.pl = pl;
19
+ this.root = root;
20
+ this.logger = logger;
21
+ const { finalPredicateOverride, pruning, pollingInterval, stopPollingDelay, logStat } = ops;
22
+ this.pruning = pruning;
23
+ this.pollingInterval = pollingInterval;
24
+ this.finalPredicate = finalPredicateOverride ?? pl.finalPredicate;
25
+ this.logStat = logStat;
26
+ this.state = new PlTreeState(root, this.finalPredicate);
27
+ this.hooks = new PollingComputableHooks(() => this.startUpdating(), () => this.stopUpdating(), { stopDebounce: stopPollingDelay }, (resolve, reject) => this.scheduleOnNextState(resolve, reject));
28
+ }
29
+ /** @deprecated use "entry" instead */
30
+ accessor(rid = this.root) {
31
+ if (this.terminated) throw new Error("tree synchronization is terminated");
32
+ return this.entry(rid);
33
+ }
34
+ entry(rid = this.root) {
35
+ if (this.terminated) throw new Error("tree synchronization is terminated");
36
+ return new PlTreeEntry({
37
+ treeProvider: () => this.state,
38
+ hooks: this.hooks
39
+ }, rid);
40
+ }
41
+ /** Can be used to externally kick off the synchronization polling loop, and
42
+ * await for the first synchronization to happen. */
43
+ async refreshState() {
44
+ if (this.terminated) throw new Error("tree synchronization is terminated");
45
+ await this.hooks.refreshState();
46
+ }
47
+ currentLoopDelayInterrupt = void 0;
48
+ scheduledOnNextState = [];
49
+ /** Called from computable hooks when external observer asks for state refresh */
50
+ scheduleOnNextState(resolve, reject) {
51
+ if (this.terminated) reject(/* @__PURE__ */ new Error("tree synchronization is terminated"));
52
+ else {
53
+ this.scheduledOnNextState.push({
54
+ resolve,
55
+ reject
56
+ });
57
+ if (this.currentLoopDelayInterrupt) {
58
+ this.currentLoopDelayInterrupt.abort();
59
+ this.currentLoopDelayInterrupt = void 0;
60
+ }
61
+ }
62
+ }
63
+ /** Called from observer */
64
+ startUpdating() {
65
+ if (this.terminated) return;
66
+ this.keepRunning = true;
67
+ if (this.currentLoop === void 0) this.currentLoop = this.mainLoop();
68
+ }
69
+ /** Called from observer */
70
+ stopUpdating() {
71
+ this.keepRunning = false;
72
+ }
73
+ /** If true, main loop will continue polling pl state. */
74
+ keepRunning = false;
75
+ /** Actual state of main loop. */
76
+ currentLoop = void 0;
77
+ /** Executed from the main loop, and initialization procedure. */
78
+ async refresh(stats, txOps) {
79
+ if (this.terminated) throw new Error("tree synchronization is terminated");
80
+ const request = constructTreeLoadingRequest(this.state, this.pruning);
81
+ const data = await this.pl.withReadTx("ReadingTree", async (tx) => {
82
+ return await loadTreeState(tx, request, stats);
83
+ }, txOps);
84
+ this.state.updateFromResourceData(data, true);
85
+ }
86
+ /** If true this tree state is permanently terminaed. */
87
+ terminated = false;
88
+ async mainLoop() {
89
+ let stat = this.logStat ? initialTreeLoadingStat() : void 0;
90
+ let lastUpdate = Date.now();
91
+ while (true) {
92
+ if (!this.keepRunning || this.terminated) break;
93
+ let toNotify = void 0;
94
+ if (this.scheduledOnNextState.length > 0) {
95
+ toNotify = this.scheduledOnNextState;
96
+ this.scheduledOnNextState = [];
97
+ }
98
+ try {
99
+ if (this.logStat === "per-request") stat = initialTreeLoadingStat();
100
+ await this.refresh(stat);
101
+ if (stat && this.logger) this.logger.info(`Tree stat (success, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`);
102
+ lastUpdate = Date.now();
103
+ if (toNotify !== void 0) for (const n of toNotify) n.resolve();
104
+ } catch (e) {
105
+ if (stat && this.logger) this.logger.info(`Tree stat (error, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`);
106
+ lastUpdate = Date.now();
107
+ if (toNotify !== void 0) for (const n of toNotify) n.reject(e);
108
+ if (e instanceof TreeStateUpdateError) {
109
+ this.logger?.error(e);
110
+ this.state.invalidateTree("stat update error");
111
+ this.state = new PlTreeState(this.root, this.finalPredicate);
112
+ continue;
113
+ } else this.logger?.warn(e);
114
+ }
115
+ if (!this.keepRunning || this.terminated) break;
116
+ if (this.scheduledOnNextState.length === 0) try {
117
+ this.currentLoopDelayInterrupt = new AbortController();
118
+ await tp.setTimeout(this.pollingInterval, void 0, { signal: AbortSignal.any([this.abortController.signal, this.currentLoopDelayInterrupt.signal]) });
119
+ } catch (e) {
120
+ if (!isTimeoutOrCancelError(e)) throw new Error("Unexpected error", { cause: e });
121
+ if (this.abortController.signal.aborted) break;
122
+ } finally {
123
+ this.currentLoopDelayInterrupt = void 0;
124
+ }
125
+ }
126
+ this.currentLoop = void 0;
127
+ }
128
+ /**
129
+ * Dumps the current state of the tree.
130
+ * @returns An array of ExtendedResourceData objects representing the current state of the tree.
131
+ */
132
+ dumpState() {
133
+ return this.state.dumpState();
134
+ }
135
+ /**
136
+ * Terminates the internal loop, and permanently destoys all internal state, so
137
+ * all computables using this state will resolve to errors.
138
+ * */
139
+ async terminate() {
140
+ this.keepRunning = false;
141
+ this.terminated = true;
142
+ this.abortController.abort();
143
+ if (this.currentLoop === void 0) return;
144
+ await this.currentLoop;
145
+ this.state.invalidateTree("synchronization terminated for the tree");
146
+ }
147
+ /** @deprecated */
148
+ async awaitSyncLoopTermination() {
149
+ if (this.currentLoop === void 0) return;
150
+ await this.currentLoop;
151
+ }
152
+ static async init(pl, root, ops, logger) {
153
+ const tree = new SynchronizedTreeState(pl, root, ops, logger);
154
+ const stat = ops.logStat ? initialTreeLoadingStat() : void 0;
155
+ let ok = false;
156
+ try {
157
+ await tree.refresh(stat, { timeout: ops.initialTreeLoadingTimeout });
158
+ ok = true;
159
+ } finally {
160
+ if (stat && logger) logger.info(`Tree stat (initial load, ${ok ? "success" : "failure"}): ${JSON.stringify(stat)}`);
161
+ }
162
+ return tree;
163
+ }
164
+ };
212
165
 
166
+ //#endregion
213
167
  export { SynchronizedTreeState };
214
- //# sourceMappingURL=synchronized_tree.js.map
168
+ //# sourceMappingURL=synchronized_tree.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"synchronized_tree.js","sources":["../src/synchronized_tree.ts"],"sourcesContent":["import { PollingComputableHooks } from \"@milaboratories/computable\";\nimport { PlTreeEntry } from \"./accessors\";\nimport type {\n FinalResourceDataPredicate,\n PlClient,\n ResourceId,\n TxOps,\n} from \"@milaboratories/pl-client\";\nimport { isTimeoutOrCancelError } from \"@milaboratories/pl-client\";\nimport type { ExtendedResourceData } from \"./state\";\nimport { PlTreeState, TreeStateUpdateError } from \"./state\";\nimport type { PruningFunction, TreeLoadingStat } from \"./sync\";\nimport { constructTreeLoadingRequest, initialTreeLoadingStat, loadTreeState } from \"./sync\";\nimport * as tp from \"node:timers/promises\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\n\ntype StatLoggingMode = \"cumulative\" | \"per-request\";\n\nexport type SynchronizedTreeOps = {\n /** Override final predicate from the PlClient */\n finalPredicateOverride?: FinalResourceDataPredicate;\n\n /** Pruning function to limit set of fields through which tree will\n * traverse during state synchronization */\n pruning?: PruningFunction;\n\n /** Interval after last sync to sleep before the next one */\n pollingInterval: number;\n /** For how long to continue polling after the last derived value access */\n stopPollingDelay: number;\n\n /** If one of the values, tree will log stats of each polling request */\n logStat?: StatLoggingMode;\n\n /** Timeout for initial tree loading. If not specified, will use default for RO tx from pl-client. */\n initialTreeLoadingTimeout?: number;\n};\n\ntype ScheduledRefresh = {\n resolve: () => void;\n reject: (err: any) => void;\n};\n\nexport class SynchronizedTreeState {\n private readonly finalPredicate: FinalResourceDataPredicate;\n private state: PlTreeState;\n private readonly pollingInterval: number;\n private readonly pruning?: PruningFunction;\n private readonly logStat?: StatLoggingMode;\n private readonly hooks: PollingComputableHooks;\n private readonly abortController = new AbortController();\n\n private constructor(\n private readonly pl: PlClient,\n private readonly root: ResourceId,\n ops: SynchronizedTreeOps,\n private readonly logger?: MiLogger,\n ) {\n const { finalPredicateOverride, pruning, pollingInterval, stopPollingDelay, logStat } = ops;\n this.pruning = pruning;\n this.pollingInterval = pollingInterval;\n this.finalPredicate = finalPredicateOverride ?? pl.finalPredicate;\n this.logStat = logStat;\n this.state = new PlTreeState(root, this.finalPredicate);\n this.hooks = new PollingComputableHooks(\n () => this.startUpdating(),\n () => this.stopUpdating(),\n { stopDebounce: stopPollingDelay },\n (resolve, reject) => this.scheduleOnNextState(resolve, reject),\n );\n }\n\n /** @deprecated use \"entry\" instead */\n public accessor(rid: ResourceId = this.root): PlTreeEntry {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n return this.entry(rid);\n }\n\n public entry(rid: ResourceId = this.root): PlTreeEntry {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n return new PlTreeEntry({ treeProvider: () => this.state, hooks: this.hooks }, rid);\n }\n\n /** Can be used to externally kick off the synchronization polling loop, and\n * await for the first synchronization to happen. */\n public async refreshState(): Promise<void> {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n await this.hooks.refreshState();\n }\n\n private currentLoopDelayInterrupt: AbortController | undefined = undefined;\n private scheduledOnNextState: ScheduledRefresh[] = [];\n\n /** Called from computable hooks when external observer asks for state refresh */\n private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {\n if (this.terminated) reject(new Error(\"tree synchronization is terminated\"));\n else {\n this.scheduledOnNextState.push({ resolve, reject });\n if (this.currentLoopDelayInterrupt) {\n this.currentLoopDelayInterrupt.abort();\n this.currentLoopDelayInterrupt = undefined;\n }\n }\n }\n\n /** Called from observer */\n private startUpdating(): void {\n if (this.terminated) return;\n this.keepRunning = true;\n if (this.currentLoop === undefined) this.currentLoop = this.mainLoop();\n }\n\n /** Called from observer */\n private stopUpdating(): void {\n this.keepRunning = false;\n }\n\n /** If true, main loop will continue polling pl state. */\n private keepRunning = false;\n /** Actual state of main loop. */\n private currentLoop: Promise<void> | undefined = undefined;\n\n /** Executed from the main loop, and initialization procedure. */\n private async refresh(stats?: TreeLoadingStat, txOps?: TxOps): Promise<void> {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n const request = constructTreeLoadingRequest(this.state, this.pruning);\n const data = await this.pl.withReadTx(\n \"ReadingTree\",\n async (tx) => {\n return await loadTreeState(tx, request, stats);\n },\n txOps,\n );\n this.state.updateFromResourceData(data, true);\n }\n\n /** If true this tree state is permanently terminaed. */\n private terminated = false;\n\n private async mainLoop() {\n // will hold request stats\n let stat = this.logStat ? initialTreeLoadingStat() : undefined;\n\n let lastUpdate = Date.now();\n\n while (true) {\n if (!this.keepRunning || this.terminated) break;\n\n // saving those who want to be notified about new state here\n // because those who will be added during the tree retrieval\n // should be notified only on the next round\n let toNotify: ScheduledRefresh[] | undefined = undefined;\n if (this.scheduledOnNextState.length > 0) {\n toNotify = this.scheduledOnNextState;\n this.scheduledOnNextState = [];\n }\n\n try {\n // resetting stats if we were asked to collect non-cumulative stats\n if (this.logStat === \"per-request\") stat = initialTreeLoadingStat();\n\n // actual tree synchronization\n await this.refresh(stat);\n\n // logging stats if we were asked to\n if (stat && this.logger)\n this.logger.info(\n `Tree stat (success, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`,\n );\n lastUpdate = Date.now();\n\n // notifying that we got new state\n if (toNotify !== undefined) for (const n of toNotify) n.resolve();\n } catch (e: any) {\n // logging stats if we were asked to (even if error occured)\n if (stat && this.logger)\n this.logger.info(\n `Tree stat (error, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`,\n );\n lastUpdate = Date.now();\n\n // notifying that we failed to refresh the state\n if (toNotify !== undefined) for (const n of toNotify) n.reject(e);\n\n // catching tree update errors, as they may leave our tree in inconsistent state\n if (e instanceof TreeStateUpdateError) {\n // important error logging, this should never happen\n this.logger?.error(e);\n\n // marking everybody who used previous state as changed\n this.state.invalidateTree(\"stat update error\");\n // creating new tree\n this.state = new PlTreeState(this.root, this.finalPredicate);\n\n // scheduling state update without delay\n continue;\n\n // unfortunately external observer may still see tree in its default\n // empty state, though this is best we can do in this exceptional\n // situation, and hope on caching layers inside computables to present\n // some stale state until we reconstruct the tree again\n } else this.logger?.warn(e);\n }\n\n if (!this.keepRunning || this.terminated) break;\n\n if (this.scheduledOnNextState.length === 0) {\n try {\n this.currentLoopDelayInterrupt = new AbortController();\n await tp.setTimeout(\n this.pollingInterval,\n AbortSignal.any([this.abortController.signal, this.currentLoopDelayInterrupt.signal]),\n );\n } catch (e: unknown) {\n if (!isTimeoutOrCancelError(e)) throw new Error(\"Unexpected error\", { cause: e });\n break;\n } finally {\n this.currentLoopDelayInterrupt = undefined;\n }\n }\n }\n\n // reset only as a very last line\n this.currentLoop = undefined;\n }\n\n /**\n * Dumps the current state of the tree.\n * @returns An array of ExtendedResourceData objects representing the current state of the tree.\n */\n public dumpState(): ExtendedResourceData[] {\n return this.state.dumpState();\n }\n\n /**\n * Terminates the internal loop, and permanently destoys all internal state, so\n * all computables using this state will resolve to errors.\n * */\n public async terminate(): Promise<void> {\n this.keepRunning = false;\n this.terminated = true;\n this.abortController.abort();\n\n if (this.currentLoop === undefined) return;\n await this.currentLoop;\n\n this.state.invalidateTree(\"synchronization terminated for the tree\");\n }\n\n /** @deprecated */\n public async awaitSyncLoopTermination(): Promise<void> {\n if (this.currentLoop === undefined) return;\n await this.currentLoop;\n }\n\n public static async init(\n pl: PlClient,\n root: ResourceId,\n ops: SynchronizedTreeOps,\n logger?: MiLogger,\n ) {\n const tree = new SynchronizedTreeState(pl, root, ops, logger);\n\n const stat = ops.logStat ? initialTreeLoadingStat() : undefined;\n\n let ok = false;\n\n try {\n await tree.refresh(stat, {\n timeout: ops.initialTreeLoadingTimeout,\n });\n ok = true;\n } finally {\n // logging stats if we were asked to (even if error occured)\n if (stat && logger)\n logger.info(\n `Tree stat (initial load, ${ok ? \"success\" : \"failure\"}): ${JSON.stringify(stat)}`,\n );\n }\n\n return tree;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"synchronized_tree.js","names":[],"sources":["../src/synchronized_tree.ts"],"sourcesContent":["import { PollingComputableHooks } from \"@milaboratories/computable\";\nimport { PlTreeEntry } from \"./accessors\";\nimport type {\n FinalResourceDataPredicate,\n PlClient,\n ResourceId,\n TxOps,\n} from \"@milaboratories/pl-client\";\nimport { isTimeoutOrCancelError } from \"@milaboratories/pl-client\";\nimport type { ExtendedResourceData } from \"./state\";\nimport { PlTreeState, TreeStateUpdateError } from \"./state\";\nimport type { PruningFunction, TreeLoadingStat } from \"./sync\";\nimport { constructTreeLoadingRequest, initialTreeLoadingStat, loadTreeState } from \"./sync\";\nimport * as tp from \"node:timers/promises\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\n\ntype StatLoggingMode = \"cumulative\" | \"per-request\";\n\nexport type SynchronizedTreeOps = {\n /** Override final predicate from the PlClient */\n finalPredicateOverride?: FinalResourceDataPredicate;\n\n /** Pruning function to limit set of fields through which tree will\n * traverse during state synchronization */\n pruning?: PruningFunction;\n\n /** Interval after last sync to sleep before the next one */\n pollingInterval: number;\n /** For how long to continue polling after the last derived value access */\n stopPollingDelay: number;\n\n /** If one of the values, tree will log stats of each polling request */\n logStat?: StatLoggingMode;\n\n /** Timeout for initial tree loading. If not specified, will use default for RO tx from pl-client. */\n initialTreeLoadingTimeout?: number;\n};\n\ntype ScheduledRefresh = {\n resolve: () => void;\n reject: (err: any) => void;\n};\n\nexport class SynchronizedTreeState {\n private readonly finalPredicate: FinalResourceDataPredicate;\n private state: PlTreeState;\n private readonly pollingInterval: number;\n private readonly pruning?: PruningFunction;\n private readonly logStat?: StatLoggingMode;\n private readonly hooks: PollingComputableHooks;\n private readonly abortController = new AbortController();\n\n private constructor(\n private readonly pl: PlClient,\n private readonly root: ResourceId,\n ops: SynchronizedTreeOps,\n private readonly logger?: MiLogger,\n ) {\n const { finalPredicateOverride, pruning, pollingInterval, stopPollingDelay, logStat } = ops;\n this.pruning = pruning;\n this.pollingInterval = pollingInterval;\n this.finalPredicate = finalPredicateOverride ?? pl.finalPredicate;\n this.logStat = logStat;\n this.state = new PlTreeState(root, this.finalPredicate);\n this.hooks = new PollingComputableHooks(\n () => this.startUpdating(),\n () => this.stopUpdating(),\n { stopDebounce: stopPollingDelay },\n (resolve, reject) => this.scheduleOnNextState(resolve, reject),\n );\n }\n\n /** @deprecated use \"entry\" instead */\n public accessor(rid: ResourceId = this.root): PlTreeEntry {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n return this.entry(rid);\n }\n\n public entry(rid: ResourceId = this.root): PlTreeEntry {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n return new PlTreeEntry({ treeProvider: () => this.state, hooks: this.hooks }, rid);\n }\n\n /** Can be used to externally kick off the synchronization polling loop, and\n * await for the first synchronization to happen. */\n public async refreshState(): Promise<void> {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n await this.hooks.refreshState();\n }\n\n private currentLoopDelayInterrupt: AbortController | undefined = undefined;\n private scheduledOnNextState: ScheduledRefresh[] = [];\n\n /** Called from computable hooks when external observer asks for state refresh */\n private scheduleOnNextState(resolve: () => void, reject: (err: any) => void): void {\n if (this.terminated) reject(new Error(\"tree synchronization is terminated\"));\n else {\n this.scheduledOnNextState.push({ resolve, reject });\n if (this.currentLoopDelayInterrupt) {\n this.currentLoopDelayInterrupt.abort();\n this.currentLoopDelayInterrupt = undefined;\n }\n }\n }\n\n /** Called from observer */\n private startUpdating(): void {\n if (this.terminated) return;\n this.keepRunning = true;\n if (this.currentLoop === undefined) this.currentLoop = this.mainLoop();\n }\n\n /** Called from observer */\n private stopUpdating(): void {\n this.keepRunning = false;\n }\n\n /** If true, main loop will continue polling pl state. */\n private keepRunning = false;\n /** Actual state of main loop. */\n private currentLoop: Promise<void> | undefined = undefined;\n\n /** Executed from the main loop, and initialization procedure. */\n private async refresh(stats?: TreeLoadingStat, txOps?: TxOps): Promise<void> {\n if (this.terminated) throw new Error(\"tree synchronization is terminated\");\n const request = constructTreeLoadingRequest(this.state, this.pruning);\n const data = await this.pl.withReadTx(\n \"ReadingTree\",\n async (tx) => {\n return await loadTreeState(tx, request, stats);\n },\n txOps,\n );\n this.state.updateFromResourceData(data, true);\n }\n\n /** If true this tree state is permanently terminaed. */\n private terminated = false;\n\n private async mainLoop() {\n // will hold request stats\n let stat = this.logStat ? initialTreeLoadingStat() : undefined;\n\n let lastUpdate = Date.now();\n\n while (true) {\n if (!this.keepRunning || this.terminated) break;\n\n // saving those who want to be notified about new state here\n // because those who will be added during the tree retrieval\n // should be notified only on the next round\n let toNotify: ScheduledRefresh[] | undefined = undefined;\n if (this.scheduledOnNextState.length > 0) {\n toNotify = this.scheduledOnNextState;\n this.scheduledOnNextState = [];\n }\n\n try {\n // resetting stats if we were asked to collect non-cumulative stats\n if (this.logStat === \"per-request\") stat = initialTreeLoadingStat();\n\n // actual tree synchronization\n await this.refresh(stat);\n\n // logging stats if we were asked to\n if (stat && this.logger)\n this.logger.info(\n `Tree stat (success, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`,\n );\n lastUpdate = Date.now();\n\n // notifying that we got new state\n if (toNotify !== undefined) for (const n of toNotify) n.resolve();\n } catch (e: any) {\n // logging stats if we were asked to (even if error occured)\n if (stat && this.logger)\n this.logger.info(\n `Tree stat (error, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`,\n );\n lastUpdate = Date.now();\n\n // notifying that we failed to refresh the state\n if (toNotify !== undefined) for (const n of toNotify) n.reject(e);\n\n // catching tree update errors, as they may leave our tree in inconsistent state\n if (e instanceof TreeStateUpdateError) {\n // important error logging, this should never happen\n this.logger?.error(e);\n\n // marking everybody who used previous state as changed\n this.state.invalidateTree(\"stat update error\");\n // creating new tree\n this.state = new PlTreeState(this.root, this.finalPredicate);\n\n // scheduling state update without delay\n continue;\n\n // unfortunately external observer may still see tree in its default\n // empty state, though this is best we can do in this exceptional\n // situation, and hope on caching layers inside computables to present\n // some stale state until we reconstruct the tree again\n } else this.logger?.warn(e);\n }\n\n if (!this.keepRunning || this.terminated) break;\n\n if (this.scheduledOnNextState.length === 0) {\n try {\n this.currentLoopDelayInterrupt = new AbortController();\n await tp.setTimeout(this.pollingInterval, undefined, {\n signal: AbortSignal.any([\n this.abortController.signal,\n this.currentLoopDelayInterrupt.signal,\n ]),\n });\n } catch (e: unknown) {\n if (!isTimeoutOrCancelError(e)) throw new Error(\"Unexpected error\", { cause: e });\n // If the main abort controller fired, this is a permanent termination\n if (this.abortController.signal.aborted) break;\n // Otherwise it was just the loop delay interrupt (scheduleOnNextState),\n // continue to the next iteration\n } finally {\n this.currentLoopDelayInterrupt = undefined;\n }\n }\n }\n\n // reset only as a very last line\n this.currentLoop = undefined;\n }\n\n /**\n * Dumps the current state of the tree.\n * @returns An array of ExtendedResourceData objects representing the current state of the tree.\n */\n public dumpState(): ExtendedResourceData[] {\n return this.state.dumpState();\n }\n\n /**\n * Terminates the internal loop, and permanently destoys all internal state, so\n * all computables using this state will resolve to errors.\n * */\n public async terminate(): Promise<void> {\n this.keepRunning = false;\n this.terminated = true;\n this.abortController.abort();\n\n if (this.currentLoop === undefined) return;\n await this.currentLoop;\n\n this.state.invalidateTree(\"synchronization terminated for the tree\");\n }\n\n /** @deprecated */\n public async awaitSyncLoopTermination(): Promise<void> {\n if (this.currentLoop === undefined) return;\n await this.currentLoop;\n }\n\n public static async init(\n pl: PlClient,\n root: ResourceId,\n ops: SynchronizedTreeOps,\n logger?: MiLogger,\n ) {\n const tree = new SynchronizedTreeState(pl, root, ops, logger);\n\n const stat = ops.logStat ? initialTreeLoadingStat() : undefined;\n\n let ok = false;\n\n try {\n await tree.refresh(stat, {\n timeout: ops.initialTreeLoadingTimeout,\n });\n ok = true;\n } finally {\n // logging stats if we were asked to (even if error occured)\n if (stat && logger)\n logger.info(\n `Tree stat (initial load, ${ok ? \"success\" : \"failure\"}): ${JSON.stringify(stat)}`,\n );\n }\n\n return tree;\n }\n}\n"],"mappings":"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"}
@@ -1,47 +1,47 @@
1
- import type { FieldType, ResourceType } from "@milaboratories/pl-client";
2
- export type CommonTraversalOps = {
3
- /**
4
- * Don't terminate chain if current resource or field has an error associated
5
- * with, by default resource or field error will be thrown. If field has error
6
- * and no value, error will be thrown anyway, because this is the reason
7
- * traversal is terminated.
8
- * */
9
- ignoreError?: boolean;
1
+ import { FieldType, ResourceType } from "@milaboratories/pl-client";
2
+
3
+ //#region src/traversal_ops.d.ts
4
+ type CommonTraversalOps = {
5
+ /**
6
+ * Don't terminate chain if current resource or field has an error associated
7
+ * with, by default resource or field error will be thrown. If field has error
8
+ * and no value, error will be thrown anyway, because this is the reason
9
+ * traversal is terminated.
10
+ * */
11
+ ignoreError?: boolean;
10
12
  };
11
- export type CommonFieldTraverseOps = {
12
- /**
13
- * Valid only if {@link assertFieldType} is defined and equal to 'Input',
14
- * 'Service' or 'Output'. By default, if field is not found, and corresponding
15
- * field list is locked, call will fail with exception.
16
- * */
17
- allowPermanentAbsence?: boolean;
18
- /** Will not mark current context as unstable, if field is not found. */
19
- stableIfNotFound?: boolean;
20
- /**
21
- * If encounter field with error and no value, will silently terminate the
22
- * traversal and return undefined.
23
- * */
24
- pureFieldErrorToUndefined?: boolean;
13
+ 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; /** Will not mark current context as unstable, if field is not found. */
20
+ stableIfNotFound?: boolean;
21
+ /**
22
+ * If encounter field with error and no value, will silently terminate the
23
+ * traversal and return undefined.
24
+ * */
25
+ pureFieldErrorToUndefined?: boolean;
25
26
  };
26
- export type ResourceTraversalOps = CommonTraversalOps & {
27
- /**
28
- * Assert resource type of the resource the fields points to. Call will fail
29
- * with exception if this assertion is not fulfilled.
30
- * */
31
- assertResourceType?: ResourceType | ResourceType[];
27
+ type ResourceTraversalOps = CommonTraversalOps & {
28
+ /**
29
+ * Assert resource type of the resource the fields points to. Call will fail
30
+ * with exception if this assertion is not fulfilled.
31
+ * */
32
+ assertResourceType?: ResourceType | ResourceType[];
32
33
  };
33
- export type GetFieldStep = CommonFieldTraverseOps & {
34
- /** Field name */
35
- field: string;
36
- /** Field must exist, if this option is set, instead error will be thrown */
37
- errorIfFieldNotFound?: boolean;
38
- /** Field must be resolved into resource if this option is set, instead error will be thrown */
39
- errorIfFieldNotSet?: boolean;
40
- /**
41
- * Assert field type. Call will fail with exception if this assertion is not
42
- * fulfilled
43
- * */
44
- assertFieldType?: FieldType;
34
+ type GetFieldStep = CommonFieldTraverseOps & {
35
+ /** Field name */field: string; /** Field must exist, if this option is set, instead error will be thrown */
36
+ errorIfFieldNotFound?: boolean; /** Field must be resolved into resource if this option is set, instead error will be thrown */
37
+ errorIfFieldNotSet?: boolean;
38
+ /**
39
+ * Assert field type. Call will fail with exception if this assertion is not
40
+ * fulfilled
41
+ * */
42
+ assertFieldType?: FieldType;
45
43
  };
46
- export type FieldTraversalStep = GetFieldStep & ResourceTraversalOps;
44
+ type FieldTraversalStep = GetFieldStep & ResourceTraversalOps;
45
+ //#endregion
46
+ export { CommonFieldTraverseOps, CommonTraversalOps, FieldTraversalStep, GetFieldStep, ResourceTraversalOps };
47
47
  //# sourceMappingURL=traversal_ops.d.ts.map
@@ -1,20 +1,17 @@
1
- 'use strict';
2
1
 
2
+ //#region src/value_and_error.ts
3
3
  function mapValueAndErrorIfDefined(input, mapping) {
4
- if (input === undefined)
5
- return undefined;
6
- else
7
- return mapValueAndError(input, mapping);
4
+ if (input === void 0) return void 0;
5
+ else return mapValueAndError(input, mapping);
8
6
  }
9
7
  function mapValueAndError(input, mapping) {
10
- const ret = {};
11
- if (input.value !== undefined)
12
- ret.value = mapping(input.value);
13
- if (input.error !== undefined)
14
- ret.error = mapping(input.error);
15
- return ret;
8
+ const ret = {};
9
+ if (input.value !== void 0) ret.value = mapping(input.value);
10
+ if (input.error !== void 0) ret.error = mapping(input.error);
11
+ return ret;
16
12
  }
17
13
 
14
+ //#endregion
18
15
  exports.mapValueAndError = mapValueAndError;
19
16
  exports.mapValueAndErrorIfDefined = mapValueAndErrorIfDefined;
20
- //# sourceMappingURL=value_and_error.cjs.map
17
+ //# sourceMappingURL=value_and_error.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"value_and_error.cjs","sources":["../src/value_and_error.ts"],"sourcesContent":["export interface ValueAndError<T> {\n value?: T;\n error?: T;\n}\n\nexport function mapValueAndErrorIfDefined<T1, T2>(\n input: ValueAndError<T1> | undefined,\n mapping: (v: T1) => T2,\n): ValueAndError<T2> | undefined {\n if (input === undefined) return undefined;\n else return mapValueAndError(input, mapping);\n}\n\nexport function mapValueAndError<T1, T2>(input: ValueAndError<T1>, mapping: (v: T1) => T2) {\n const ret = {} as ValueAndError<T2>;\n if (input.value !== undefined) ret.value = mapping(input.value);\n if (input.error !== undefined) ret.error = mapping(input.error);\n return ret;\n}\n"],"names":[],"mappings":";;AAKM,SAAU,yBAAyB,CACvC,KAAoC,EACpC,OAAsB,EAAA;IAEtB,IAAI,KAAK,KAAK,SAAS;AAAE,QAAA,OAAO,SAAS;;AACpC,QAAA,OAAO,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC;AAC9C;AAEM,SAAU,gBAAgB,CAAS,KAAwB,EAAE,OAAsB,EAAA;IACvF,MAAM,GAAG,GAAG,EAAuB;AACnC,IAAA,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AAC/D,IAAA,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;AAC/D,IAAA,OAAO,GAAG;AACZ;;;;;"}
1
+ {"version":3,"file":"value_and_error.cjs","names":[],"sources":["../src/value_and_error.ts"],"sourcesContent":["export interface ValueAndError<T> {\n value?: T;\n error?: T;\n}\n\nexport function mapValueAndErrorIfDefined<T1, T2>(\n input: ValueAndError<T1> | undefined,\n mapping: (v: T1) => T2,\n): ValueAndError<T2> | undefined {\n if (input === undefined) return undefined;\n else return mapValueAndError(input, mapping);\n}\n\nexport function mapValueAndError<T1, T2>(input: ValueAndError<T1>, mapping: (v: T1) => T2) {\n const ret = {} as ValueAndError<T2>;\n if (input.value !== undefined) ret.value = mapping(input.value);\n if (input.error !== undefined) ret.error = mapping(input.error);\n return ret;\n}\n"],"mappings":";;AAKA,SAAgB,0BACd,OACA,SAC+B;AAC/B,KAAI,UAAU,OAAW,QAAO;KAC3B,QAAO,iBAAiB,OAAO,QAAQ;;AAG9C,SAAgB,iBAAyB,OAA0B,SAAwB;CACzF,MAAM,MAAM,EAAE;AACd,KAAI,MAAM,UAAU,OAAW,KAAI,QAAQ,QAAQ,MAAM,MAAM;AAC/D,KAAI,MAAM,UAAU,OAAW,KAAI,QAAQ,QAAQ,MAAM,MAAM;AAC/D,QAAO"}
@@ -1,7 +1,10 @@
1
- export interface ValueAndError<T> {
2
- value?: T;
3
- error?: T;
1
+ //#region src/value_and_error.d.ts
2
+ interface ValueAndError<T> {
3
+ value?: T;
4
+ error?: T;
4
5
  }
5
- export declare function mapValueAndErrorIfDefined<T1, T2>(input: ValueAndError<T1> | undefined, mapping: (v: T1) => T2): ValueAndError<T2> | undefined;
6
- export declare function mapValueAndError<T1, T2>(input: ValueAndError<T1>, mapping: (v: T1) => T2): ValueAndError<T2>;
6
+ declare function mapValueAndErrorIfDefined<T1, T2>(input: ValueAndError<T1> | undefined, mapping: (v: T1) => T2): ValueAndError<T2> | undefined;
7
+ declare function mapValueAndError<T1, T2>(input: ValueAndError<T1>, mapping: (v: T1) => T2): ValueAndError<T2>;
8
+ //#endregion
9
+ export { ValueAndError, mapValueAndError, mapValueAndErrorIfDefined };
7
10
  //# sourceMappingURL=value_and_error.d.ts.map