@milaboratories/pl-tree 1.8.34 → 1.8.36
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 +24 -23
- 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synchronized_tree.d.ts","sourceRoot":"","sources":["../src/synchronized_tree.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EACV,0BAA0B,EAC1B,QAAQ,EACR,UAAU,EAEX,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"synchronized_tree.d.ts","sourceRoot":"","sources":["../src/synchronized_tree.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EACV,0BAA0B,EAC1B,QAAQ,EACR,UAAU,EAEX,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,QAAQ,CAAC;AAG/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAE3D,KAAK,eAAe,GAAG,YAAY,GAAG,aAAa,CAAC;AAEpD,MAAM,MAAM,mBAAmB,GAAG;IAChC,iDAAiD;IACjD,sBAAsB,CAAC,EAAE,0BAA0B,CAAC;IAEpD;+CAC2C;IAC3C,OAAO,CAAC,EAAE,eAAe,CAAC;IAE1B,4DAA4D;IAC5D,eAAe,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;IAEzB,wEAAwE;IACxE,OAAO,CAAC,EAAE,eAAe,CAAC;IAE1B,qGAAqG;IACrG,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC,CAAC;AAOF,qBAAa,qBAAqB;IAU9B,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAErB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAZ1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IAEzD,OAAO;IAoBP,sCAAsC;IAC/B,QAAQ,CAAC,GAAG,GAAE,UAAsB,GAAG,WAAW;IAKlD,KAAK,CAAC,GAAG,GAAE,UAAsB,GAAG,WAAW;IAKtD;wDACoD;IACvC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAK1C,OAAO,CAAC,yBAAyB,CAA0C;IAC3E,OAAO,CAAC,oBAAoB,CAA0B;IAEtD,iFAAiF;IACjF,OAAO,CAAC,mBAAmB;IAW3B,2BAA2B;IAC3B,OAAO,CAAC,aAAa;IAMrB,2BAA2B;IAC3B,OAAO,CAAC,YAAY;IAIpB,yDAAyD;IACzD,OAAO,CAAC,WAAW,CAAS;IAC5B,iCAAiC;IACjC,OAAO,CAAC,WAAW,CAAwC;IAE3D,iEAAiE;YACnD,OAAO;IAarB,wDAAwD;IACxD,OAAO,CAAC,UAAU,CAAS;YAEb,QAAQ;IAuFtB;;;OAGG;IACI,SAAS,IAAI,oBAAoB,EAAE;IAI1C;;;SAGK;IACQ,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAWvC,kBAAkB;IACL,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;WAKlC,IAAI,CACtB,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,mBAAmB,EACxB,MAAM,CAAC,EAAE,QAAQ;CAuBpB"}
|
|
@@ -31,19 +31,19 @@ class SynchronizedTreeState {
|
|
|
31
31
|
/** @deprecated use "entry" instead */
|
|
32
32
|
accessor(rid = this.root) {
|
|
33
33
|
if (this.terminated)
|
|
34
|
-
throw new Error(
|
|
34
|
+
throw new Error("tree synchronization is terminated");
|
|
35
35
|
return this.entry(rid);
|
|
36
36
|
}
|
|
37
37
|
entry(rid = this.root) {
|
|
38
38
|
if (this.terminated)
|
|
39
|
-
throw new Error(
|
|
39
|
+
throw new Error("tree synchronization is terminated");
|
|
40
40
|
return new PlTreeEntry({ treeProvider: () => this.state, hooks: this.hooks }, rid);
|
|
41
41
|
}
|
|
42
42
|
/** Can be used to externally kick off the synchronization polling loop, and
|
|
43
43
|
* await for the first synchronization to happen. */
|
|
44
44
|
async refreshState() {
|
|
45
45
|
if (this.terminated)
|
|
46
|
-
throw new Error(
|
|
46
|
+
throw new Error("tree synchronization is terminated");
|
|
47
47
|
await this.hooks.refreshState();
|
|
48
48
|
}
|
|
49
49
|
currentLoopDelayInterrupt = undefined;
|
|
@@ -51,7 +51,7 @@ class SynchronizedTreeState {
|
|
|
51
51
|
/** Called from computable hooks when external observer asks for state refresh */
|
|
52
52
|
scheduleOnNextState(resolve, reject) {
|
|
53
53
|
if (this.terminated)
|
|
54
|
-
reject(new Error(
|
|
54
|
+
reject(new Error("tree synchronization is terminated"));
|
|
55
55
|
else {
|
|
56
56
|
this.scheduledOnNextState.push({ resolve, reject });
|
|
57
57
|
if (this.currentLoopDelayInterrupt) {
|
|
@@ -79,9 +79,9 @@ class SynchronizedTreeState {
|
|
|
79
79
|
/** Executed from the main loop, and initialization procedure. */
|
|
80
80
|
async refresh(stats, txOps) {
|
|
81
81
|
if (this.terminated)
|
|
82
|
-
throw new Error(
|
|
82
|
+
throw new Error("tree synchronization is terminated");
|
|
83
83
|
const request = constructTreeLoadingRequest(this.state, this.pruning);
|
|
84
|
-
const data = await this.pl.withReadTx(
|
|
84
|
+
const data = await this.pl.withReadTx("ReadingTree", async (tx) => {
|
|
85
85
|
return await loadTreeState(tx, request, stats);
|
|
86
86
|
}, txOps);
|
|
87
87
|
this.state.updateFromResourceData(data, true);
|
|
@@ -105,7 +105,7 @@ class SynchronizedTreeState {
|
|
|
105
105
|
}
|
|
106
106
|
try {
|
|
107
107
|
// resetting stats if we were asked to collect non-cumulative stats
|
|
108
|
-
if (this.logStat ===
|
|
108
|
+
if (this.logStat === "per-request")
|
|
109
109
|
stat = initialTreeLoadingStat();
|
|
110
110
|
// actual tree synchronization
|
|
111
111
|
await this.refresh(stat);
|
|
@@ -132,7 +132,7 @@ class SynchronizedTreeState {
|
|
|
132
132
|
// important error logging, this should never happen
|
|
133
133
|
this.logger?.error(e);
|
|
134
134
|
// marking everybody who used previous state as changed
|
|
135
|
-
this.state.invalidateTree(
|
|
135
|
+
this.state.invalidateTree("stat update error");
|
|
136
136
|
// creating new tree
|
|
137
137
|
this.state = new PlTreeState(this.root, this.finalPredicate);
|
|
138
138
|
// scheduling state update without delay
|
|
@@ -154,7 +154,7 @@ class SynchronizedTreeState {
|
|
|
154
154
|
}
|
|
155
155
|
catch (e) {
|
|
156
156
|
if (!isTimeoutOrCancelError(e))
|
|
157
|
-
throw new Error(
|
|
157
|
+
throw new Error("Unexpected error", { cause: e });
|
|
158
158
|
break;
|
|
159
159
|
}
|
|
160
160
|
finally {
|
|
@@ -183,7 +183,7 @@ class SynchronizedTreeState {
|
|
|
183
183
|
if (this.currentLoop === undefined)
|
|
184
184
|
return;
|
|
185
185
|
await this.currentLoop;
|
|
186
|
-
this.state.invalidateTree(
|
|
186
|
+
this.state.invalidateTree("synchronization terminated for the tree");
|
|
187
187
|
}
|
|
188
188
|
/** @deprecated */
|
|
189
189
|
async awaitSyncLoopTermination() {
|
|
@@ -204,7 +204,7 @@ class SynchronizedTreeState {
|
|
|
204
204
|
finally {
|
|
205
205
|
// logging stats if we were asked to (even if error occured)
|
|
206
206
|
if (stat && logger)
|
|
207
|
-
logger.info(`Tree stat (initial load, ${ok ?
|
|
207
|
+
logger.info(`Tree stat (initial load, ${ok ? "success" : "failure"}): ${JSON.stringify(stat)}`);
|
|
208
208
|
}
|
|
209
209
|
return tree;
|
|
210
210
|
}
|
|
@@ -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 {\n isTimeoutOrCancelError,\n} from '@milaboratories/pl-client';\nimport type { ExtendedResourceData } from './state';\nimport { PlTreeState, TreeStateUpdateError } from './state';\nimport type {\n PruningFunction,\n TreeLoadingStat,\n} from './sync';\nimport {\n constructTreeLoadingRequest,\n initialTreeLoadingStat,\n loadTreeState,\n} 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('ReadingTree', async (tx) => {\n return await loadTreeState(tx, request, stats);\n }, txOps);\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) this.logger.info(`Tree stat (success, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`);\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) this.logger.info(`Tree stat (error, after ${Date.now() - lastUpdate}ms): ${JSON.stringify(stat)}`);\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,\n AbortSignal.any([this.abortController.signal, this.currentLoopDelayInterrupt.signal]));\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","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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"}
|
package/dist/test_utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { BasicResourceData, FieldData, FieldType, OptionalResourceId, ResourceId, ResourceType } from
|
|
2
|
-
import type { ExtendedResourceData } from
|
|
1
|
+
import type { BasicResourceData, FieldData, FieldType, OptionalResourceId, ResourceId, ResourceType } from "@milaboratories/pl-client";
|
|
2
|
+
import type { ExtendedResourceData } from "./state";
|
|
3
3
|
export declare const TestRootType1: ResourceType;
|
|
4
4
|
export declare const TestRootType2: ResourceType;
|
|
5
5
|
export declare const TestStructuralResourceType1: ResourceType;
|
|
@@ -7,18 +7,18 @@ export declare const TestStructuralResourceType2: ResourceType;
|
|
|
7
7
|
export declare const TestValueResourceType1: ResourceType;
|
|
8
8
|
export declare const TestValueResourceType2: ResourceType;
|
|
9
9
|
export declare const TestErrorResourceType1: ResourceType;
|
|
10
|
-
export declare const ResourceReady: Pick<BasicResourceData,
|
|
11
|
-
export declare const InitialStructuralResourceState: Omit<ExtendedResourceData,
|
|
12
|
-
export declare const InitialValueResourceState: Omit<ExtendedResourceData,
|
|
13
|
-
export declare const TestStructuralResourceState1: Omit<ExtendedResourceData,
|
|
14
|
-
export declare const TestStructuralResourceState2: Omit<ExtendedResourceData,
|
|
15
|
-
export declare const TestValueResourceState1: Omit<ExtendedResourceData,
|
|
16
|
-
export declare const TestValueResourceState2: Omit<ExtendedResourceData,
|
|
17
|
-
export declare const TestErrorResourceState2: Omit<ExtendedResourceData,
|
|
10
|
+
export declare const ResourceReady: Pick<BasicResourceData, "inputsLocked" | "outputsLocked" | "resourceReady" | "final">;
|
|
11
|
+
export declare const InitialStructuralResourceState: Omit<ExtendedResourceData, "id" | "type" | "fields">;
|
|
12
|
+
export declare const InitialValueResourceState: Omit<ExtendedResourceData, "id" | "type" | "data">;
|
|
13
|
+
export declare const TestStructuralResourceState1: Omit<ExtendedResourceData, "id" | "fields">;
|
|
14
|
+
export declare const TestStructuralResourceState2: Omit<ExtendedResourceData, "id" | "fields">;
|
|
15
|
+
export declare const TestValueResourceState1: Omit<ExtendedResourceData, "id" | "data">;
|
|
16
|
+
export declare const TestValueResourceState2: Omit<ExtendedResourceData, "id" | "data">;
|
|
17
|
+
export declare const TestErrorResourceState2: Omit<ExtendedResourceData, "id" | "data">;
|
|
18
18
|
export declare const TestDynamicRootId1: ResourceId;
|
|
19
|
-
export declare const TestDynamicRootState1: Omit<ExtendedResourceData,
|
|
19
|
+
export declare const TestDynamicRootState1: Omit<ExtendedResourceData, "fields">;
|
|
20
20
|
export declare const TestDynamicRootId2: ResourceId;
|
|
21
|
-
export declare const TestDynamicRootState2: Omit<ExtendedResourceData,
|
|
21
|
+
export declare const TestDynamicRootState2: Omit<ExtendedResourceData, "fields">;
|
|
22
22
|
export declare function field(type: FieldType, name: string, value?: OptionalResourceId, error?: OptionalResourceId, valueIsFinal?: boolean): FieldData;
|
|
23
23
|
export declare function dField(name: string, value?: OptionalResourceId, error?: OptionalResourceId): FieldData;
|
|
24
24
|
export declare function iField(name: string, value?: OptionalResourceId, error?: OptionalResourceId): FieldData;
|
package/dist/test_utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test_utils.d.ts","sourceRoot":"","sources":["../src/test_utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,UAAU,EACV,YAAY,EACb,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"test_utils.d.ts","sourceRoot":"","sources":["../src/test_utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,UAAU,EACV,YAAY,EACb,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,eAAO,MAAM,aAAa,EAAE,YAG3B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,YAG3B,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,YAGzC,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,YAGzC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,YAGpC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,YAGpC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,YAGpC,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,IAAI,CAC9B,iBAAiB,EACjB,cAAc,GAAG,eAAe,GAAG,eAAe,GAAG,OAAO,CAM7D,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,MAAM,GAAG,QAAQ,CAU7F,CAAC;AAEJ,eAAO,MAAM,yBAAyB,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAOxF,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,QAAQ,CAGpF,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,QAAQ,CAGpF,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,MAAM,CAG7E,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,MAAM,CAG7E,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,MAAM,CAG7E,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAe,UAAU,CAAC;AACzD,eAAO,MAAM,qBAAqB,EAAE,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAOtE,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAe,UAAU,CAAC;AACzD,eAAO,MAAM,qBAAqB,EAAE,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAOtE,CAAC;AAEF,wBAAgB,KAAK,CACnB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,kBAAmC,EAC1C,KAAK,GAAE,kBAAmC,EAC1C,YAAY,GAAE,OAAe,GAC5B,SAAS,CASX;AAED,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,kBAAmC,EAC1C,KAAK,GAAE,kBAAmC,GACzC,SAAS,CAEX;AAED,wBAAgB,MAAM,CACpB,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,kBAAmC,EAC1C,KAAK,GAAE,kBAAmC,GACzC,SAAS,CAEX"}
|
package/dist/traversal_ops.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"value_or_error.d.ts","sourceRoot":"","sources":["../src/value_or_error.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,IACzB;
|
|
1
|
+
{"version":3,"file":"value_or_error.d.ts","sourceRoot":"","sources":["../src/value_or_error.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,IACzB;IACE,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,CAAC,CAAC;CACV,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,CAAC,CAAC;CACV,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-tree",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.36",
|
|
4
4
|
"description": "Reactive pl tree state",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
"files": [
|
|
6
|
+
"./dist/**/*",
|
|
7
|
+
"./src/**/*"
|
|
8
|
+
],
|
|
9
9
|
"main": "./dist/index.cjs",
|
|
10
10
|
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
11
12
|
"exports": {
|
|
12
13
|
".": {
|
|
13
14
|
"types": "./dist/index.d.ts",
|
|
@@ -15,37 +16,37 @@
|
|
|
15
16
|
"import": "./dist/index.js"
|
|
16
17
|
}
|
|
17
18
|
},
|
|
18
|
-
"files": [
|
|
19
|
-
"./dist/**/*",
|
|
20
|
-
"./src/**/*"
|
|
21
|
-
],
|
|
22
19
|
"dependencies": {
|
|
23
20
|
"denque": "^2.1.0",
|
|
24
21
|
"utility-types": "^3.11.0",
|
|
25
22
|
"zod": "~3.23.8",
|
|
26
|
-
"@milaboratories/computable": "2.8.
|
|
27
|
-
"@milaboratories/pl-
|
|
28
|
-
"@milaboratories/pl-
|
|
29
|
-
"@milaboratories/
|
|
30
|
-
"@milaboratories/
|
|
23
|
+
"@milaboratories/computable": "2.8.4",
|
|
24
|
+
"@milaboratories/pl-error-like": "1.12.7",
|
|
25
|
+
"@milaboratories/pl-client": "2.16.28",
|
|
26
|
+
"@milaboratories/ts-helpers": "1.7.2",
|
|
27
|
+
"@milaboratories/pl-errors": "1.1.60"
|
|
31
28
|
},
|
|
32
29
|
"devDependencies": {
|
|
33
|
-
"typescript": "~5.6.3",
|
|
34
30
|
"@types/node": "~24.5.2",
|
|
35
31
|
"@vitest/coverage-istanbul": "^4.0.16",
|
|
32
|
+
"typescript": "~5.6.3",
|
|
36
33
|
"vitest": "^4.0.16",
|
|
37
|
-
"
|
|
38
|
-
"@milaboratories/ts-
|
|
39
|
-
"@milaboratories/build-configs": "1.4.
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
"@milaboratories/ts-builder": "1.2.6",
|
|
35
|
+
"@milaboratories/ts-configs": "1.2.1",
|
|
36
|
+
"@milaboratories/build-configs": "1.4.2"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=22.19.0"
|
|
42
40
|
},
|
|
43
41
|
"scripts": {
|
|
44
|
-
"type-check": "ts-builder types --target node",
|
|
45
42
|
"build": "ts-builder build --target node",
|
|
46
43
|
"watch": "ts-builder build --target node --watch",
|
|
47
|
-
"
|
|
44
|
+
"check": "ts-builder check --target node",
|
|
45
|
+
"formatter:check": "ts-builder formatter --check",
|
|
46
|
+
"linter:check": "ts-builder linter --check",
|
|
47
|
+
"types:check": "ts-builder type-check --target node",
|
|
48
48
|
"test": "vitest run --coverage",
|
|
49
|
-
"do-pack": "rm -f *.tgz && pnpm pack && mv *.tgz package.tgz"
|
|
49
|
+
"do-pack": "rm -f *.tgz && pnpm pack && mv *.tgz package.tgz",
|
|
50
|
+
"fmt": "ts-builder format"
|
|
50
51
|
}
|
|
51
52
|
}
|
package/src/accessors.ts
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
|
-
import type { PlTreeResource, PlTreeState } from
|
|
1
|
+
import type { PlTreeResource, PlTreeState } from "./state";
|
|
2
2
|
import type {
|
|
3
3
|
AccessorProvider,
|
|
4
4
|
ComputableCtx,
|
|
5
5
|
ComputableHooks,
|
|
6
6
|
UsageGuard,
|
|
7
|
-
} from
|
|
8
|
-
import type {
|
|
9
|
-
ResourceId,
|
|
10
|
-
ResourceType,
|
|
11
|
-
OptionalResourceId } from '@milaboratories/pl-client';
|
|
7
|
+
} from "@milaboratories/computable";
|
|
8
|
+
import type { ResourceId, ResourceType, OptionalResourceId } from "@milaboratories/pl-client";
|
|
12
9
|
import {
|
|
13
10
|
resourceIdToString,
|
|
14
11
|
resourceTypesEqual,
|
|
15
12
|
resourceTypeToString,
|
|
16
13
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
17
14
|
NullResourceId,
|
|
18
|
-
} from
|
|
19
|
-
import type { ValueAndError } from
|
|
20
|
-
import { mapValueAndError } from
|
|
15
|
+
} from "@milaboratories/pl-client";
|
|
16
|
+
import type { ValueAndError } from "./value_and_error";
|
|
17
|
+
import { mapValueAndError } from "./value_and_error";
|
|
21
18
|
import type {
|
|
22
19
|
CommonFieldTraverseOps,
|
|
23
20
|
FieldTraversalStep,
|
|
24
21
|
GetFieldStep,
|
|
25
22
|
ResourceTraversalOps,
|
|
26
|
-
} from
|
|
27
|
-
import type { ValueOrError } from
|
|
28
|
-
import { parsePlError } from
|
|
29
|
-
import { notEmpty } from
|
|
23
|
+
} from "./traversal_ops";
|
|
24
|
+
import type { ValueOrError } from "./value_or_error";
|
|
25
|
+
import { parsePlError } from "@milaboratories/pl-errors";
|
|
26
|
+
import { notEmpty } from "@milaboratories/ts-helpers";
|
|
30
27
|
/** Error encountered during traversal in field or resource. */
|
|
31
28
|
export class PlError extends Error {
|
|
32
29
|
constructor(message: string) {
|
|
@@ -46,31 +43,31 @@ export type TreeAccessorInstanceData = {
|
|
|
46
43
|
|
|
47
44
|
export function isPlTreeEntry(obj: unknown): obj is PlTreeEntry {
|
|
48
45
|
return (
|
|
49
|
-
typeof obj ===
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
typeof obj === "object" &&
|
|
47
|
+
obj !== null &&
|
|
48
|
+
(obj as any)["__pl_tree_type_marker__"] === "PlTreeEntry"
|
|
52
49
|
);
|
|
53
50
|
}
|
|
54
51
|
|
|
55
52
|
export function isPlTreeEntryAccessor(obj: unknown): obj is PlTreeEntryAccessor {
|
|
56
53
|
return (
|
|
57
|
-
typeof obj ===
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
typeof obj === "object" &&
|
|
55
|
+
obj !== null &&
|
|
56
|
+
(obj as any)["__pl_tree_type_marker__"] === "PlTreeEntryAccessor"
|
|
60
57
|
);
|
|
61
58
|
}
|
|
62
59
|
|
|
63
60
|
export function isPlTreeNodeAccessor(obj: unknown): obj is PlTreeNodeAccessor {
|
|
64
61
|
return (
|
|
65
|
-
typeof obj ===
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
typeof obj === "object" &&
|
|
63
|
+
obj !== null &&
|
|
64
|
+
(obj as any)["__pl_tree_type_marker__"] === "PlTreeNodeAccessor"
|
|
68
65
|
);
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
/** Main entry point for using PlTree in reactive setting */
|
|
72
69
|
export class PlTreeEntry implements AccessorProvider<PlTreeEntryAccessor> {
|
|
73
|
-
private readonly __pl_tree_type_marker__ =
|
|
70
|
+
private readonly __pl_tree_type_marker__ = "PlTreeEntry";
|
|
74
71
|
|
|
75
72
|
constructor(
|
|
76
73
|
private readonly accessorData: TreeAccessorData,
|
|
@@ -114,8 +111,8 @@ function getResourceFromTree(
|
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
if (
|
|
117
|
-
ops.assertResourceType !== undefined
|
|
118
|
-
|
|
114
|
+
ops.assertResourceType !== undefined &&
|
|
115
|
+
(Array.isArray(ops.assertResourceType)
|
|
119
116
|
? ops.assertResourceType.findIndex((rt) => resourceTypesEqual(rt, acc.resourceType)) === -1
|
|
120
117
|
: !resourceTypesEqual(ops.assertResourceType, acc.resourceType))
|
|
121
118
|
)
|
|
@@ -128,7 +125,7 @@ function getResourceFromTree(
|
|
|
128
125
|
}
|
|
129
126
|
|
|
130
127
|
export class PlTreeEntryAccessor {
|
|
131
|
-
private readonly __pl_tree_type_marker__ =
|
|
128
|
+
private readonly __pl_tree_type_marker__ = "PlTreeEntryAccessor";
|
|
132
129
|
|
|
133
130
|
constructor(
|
|
134
131
|
private readonly accessorData: TreeAccessorData,
|
|
@@ -174,7 +171,7 @@ export function treeEntryToResourceInfo(res: PlTreeEntry | ResourceInfo, ctx: Co
|
|
|
174
171
|
* Important: never store instances of this class, always get fresh instance from {@link PlTreeState} accessor.
|
|
175
172
|
* */
|
|
176
173
|
export class PlTreeNodeAccessor {
|
|
177
|
-
private readonly __pl_tree_type_marker__ =
|
|
174
|
+
private readonly __pl_tree_type_marker__ = "PlTreeNodeAccessor";
|
|
178
175
|
|
|
179
176
|
constructor(
|
|
180
177
|
private readonly accessorData: TreeAccessorData,
|
|
@@ -208,7 +205,7 @@ export class PlTreeNodeAccessor {
|
|
|
208
205
|
|
|
209
206
|
public traverse(
|
|
210
207
|
...steps: [
|
|
211
|
-
Omit<FieldTraversalStep,
|
|
208
|
+
Omit<FieldTraversalStep, "errorIfFieldNotSet"> & {
|
|
212
209
|
errorIfFieldNotSet: true;
|
|
213
210
|
},
|
|
214
211
|
]
|
|
@@ -220,7 +217,7 @@ export class PlTreeNodeAccessor {
|
|
|
220
217
|
|
|
221
218
|
public traverseOrError(
|
|
222
219
|
...steps: [
|
|
223
|
-
Omit<FieldTraversalStep,
|
|
220
|
+
Omit<FieldTraversalStep, "errorIfFieldNotSet"> & {
|
|
224
221
|
errorIfFieldNotSet: true;
|
|
225
222
|
},
|
|
226
223
|
]
|
|
@@ -253,8 +250,8 @@ export class PlTreeNodeAccessor {
|
|
|
253
250
|
let current: PlTreeNodeAccessor = this;
|
|
254
251
|
|
|
255
252
|
for (const _step of steps) {
|
|
256
|
-
const step: FieldTraversalStep
|
|
257
|
-
|
|
253
|
+
const step: FieldTraversalStep =
|
|
254
|
+
typeof _step === "string"
|
|
258
255
|
? {
|
|
259
256
|
...commonOptions,
|
|
260
257
|
field: _step,
|
|
@@ -275,7 +272,9 @@ export class PlTreeNodeAccessor {
|
|
|
275
272
|
// FIXME: in next tickets we'll allow Errors to be thrown.
|
|
276
273
|
error: parsePlError(
|
|
277
274
|
notEmpty(next.error.getDataAsString()),
|
|
278
|
-
current.id,
|
|
275
|
+
current.id,
|
|
276
|
+
current.resourceType,
|
|
277
|
+
step.field,
|
|
279
278
|
),
|
|
280
279
|
};
|
|
281
280
|
|
|
@@ -283,10 +282,12 @@ export class PlTreeNodeAccessor {
|
|
|
283
282
|
if (step.errorIfFieldNotSet)
|
|
284
283
|
return {
|
|
285
284
|
ok: false,
|
|
286
|
-
error: new Error(
|
|
285
|
+
error: new Error(
|
|
286
|
+
`field have no assigned value ${step.field} of ${resourceIdToString(current.id)}`,
|
|
287
|
+
),
|
|
287
288
|
};
|
|
288
289
|
// existing but unpopulated field is unstable because it must be resolved at some point
|
|
289
|
-
this.onUnstableLambda(
|
|
290
|
+
this.onUnstableLambda("unpopulated_field:" + step.field);
|
|
290
291
|
return undefined;
|
|
291
292
|
}
|
|
292
293
|
|
|
@@ -301,13 +302,13 @@ export class PlTreeNodeAccessor {
|
|
|
301
302
|
|
|
302
303
|
public getField(
|
|
303
304
|
_step:
|
|
304
|
-
| (Omit<GetFieldStep,
|
|
305
|
-
| (Omit<GetFieldStep,
|
|
305
|
+
| (Omit<GetFieldStep, "errorIfFieldNotFound"> & { errorIfFieldNotFound: true })
|
|
306
|
+
| (Omit<GetFieldStep, "errorIfFieldNotSet"> & { errorIfFieldNotSet: true }),
|
|
306
307
|
): ValueAndError<PlTreeNodeAccessor>;
|
|
307
308
|
public getField(_step: GetFieldStep | string): ValueAndError<PlTreeNodeAccessor> | undefined;
|
|
308
309
|
public getField(_step: GetFieldStep | string): ValueAndError<PlTreeNodeAccessor> | undefined {
|
|
309
310
|
this.instanceData.guard();
|
|
310
|
-
const step: GetFieldStep = typeof _step ===
|
|
311
|
+
const step: GetFieldStep = typeof _step === "string" ? { field: _step } : _step;
|
|
311
312
|
|
|
312
313
|
const ve = this.resource.getField(this.instanceData.ctx.watcher, step, this.onUnstableLambda);
|
|
313
314
|
|
|
@@ -319,21 +320,21 @@ export class PlTreeNodeAccessor {
|
|
|
319
320
|
public getInputsLocked(): boolean {
|
|
320
321
|
this.instanceData.guard();
|
|
321
322
|
const result = this.resource.getInputsLocked(this.instanceData.ctx.watcher);
|
|
322
|
-
if (!result) this.instanceData.ctx.markUnstable(
|
|
323
|
+
if (!result) this.instanceData.ctx.markUnstable("inputs_unlocked:" + this.resourceType.name);
|
|
323
324
|
return result;
|
|
324
325
|
}
|
|
325
326
|
|
|
326
327
|
public getOutputsLocked(): boolean {
|
|
327
328
|
this.instanceData.guard();
|
|
328
329
|
const result = this.resource.getOutputsLocked(this.instanceData.ctx.watcher);
|
|
329
|
-
if (!result) this.instanceData.ctx.markUnstable(
|
|
330
|
+
if (!result) this.instanceData.ctx.markUnstable("outputs_unlocked:" + this.resourceType.name);
|
|
330
331
|
return result;
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
public getIsReadyOrError(): boolean {
|
|
334
335
|
this.instanceData.guard();
|
|
335
336
|
const result = this.resource.getIsReadyOrError(this.instanceData.ctx.watcher);
|
|
336
|
-
if (!result) this.instanceData.ctx.markUnstable(
|
|
337
|
+
if (!result) this.instanceData.ctx.markUnstable("not_ready:" + this.resourceType.name);
|
|
337
338
|
return result;
|
|
338
339
|
}
|
|
339
340
|
|
|
@@ -384,7 +385,7 @@ export class PlTreeNodeAccessor {
|
|
|
384
385
|
this.instanceData.guard();
|
|
385
386
|
const result = this.resource.getKeyValue(this.instanceData.ctx.watcher, key);
|
|
386
387
|
if (result === undefined && unstableIfNotFound)
|
|
387
|
-
this.instanceData.ctx.markUnstable(
|
|
388
|
+
this.instanceData.ctx.markUnstable("key_not_found_b:" + key);
|
|
388
389
|
return result;
|
|
389
390
|
}
|
|
390
391
|
|
|
@@ -397,7 +398,7 @@ export class PlTreeNodeAccessor {
|
|
|
397
398
|
this.instanceData.guard();
|
|
398
399
|
const result = this.resource.getKeyValueString(this.instanceData.ctx.watcher, key);
|
|
399
400
|
if (result === undefined && unstableIfNotFound)
|
|
400
|
-
this.instanceData.ctx.markUnstable(
|
|
401
|
+
this.instanceData.ctx.markUnstable("key_not_found_s:" + key);
|
|
401
402
|
return result;
|
|
402
403
|
}
|
|
403
404
|
|
|
@@ -407,7 +408,7 @@ export class PlTreeNodeAccessor {
|
|
|
407
408
|
): T | undefined {
|
|
408
409
|
const result = this.resource.getKeyValueAsJson<T>(this.instanceData.ctx.watcher, key);
|
|
409
410
|
if (result === undefined) {
|
|
410
|
-
if (unstableIfNotFound) this.instanceData.ctx.markUnstable(
|
|
411
|
+
if (unstableIfNotFound) this.instanceData.ctx.markUnstable("key_not_found_j:" + key);
|
|
411
412
|
return undefined;
|
|
412
413
|
}
|
|
413
414
|
return result;
|
package/src/dump.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
1
|
+
export * from "./traversal_ops";
|
|
2
|
+
export * from "./state";
|
|
3
|
+
export * from "./sync";
|
|
4
|
+
export * from "./accessors";
|
|
5
|
+
export * from "./snapshot";
|
|
6
|
+
export * from "./synchronized_tree";
|
|
7
|
+
export * from "./value_and_error";
|
|
8
|
+
export * from "./value_or_error";
|
|
9
|
+
export * from "./dump";
|