@milaboratories/pl-tree 1.7.4 → 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/accessors.cjs +297 -0
  2. package/dist/accessors.cjs.map +1 -0
  3. package/dist/accessors.d.ts +0 -1
  4. package/dist/accessors.js +288 -0
  5. package/dist/accessors.js.map +1 -0
  6. package/dist/dump.cjs +86 -0
  7. package/dist/dump.cjs.map +1 -0
  8. package/dist/dump.d.ts +0 -1
  9. package/dist/dump.js +84 -0
  10. package/dist/dump.js.map +1 -0
  11. package/dist/index.cjs +36 -0
  12. package/dist/index.cjs.map +1 -0
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.js +7 -10
  15. package/dist/index.js.map +1 -1
  16. package/dist/snapshot.cjs +88 -0
  17. package/dist/snapshot.cjs.map +1 -0
  18. package/dist/snapshot.d.ts +0 -1
  19. package/dist/snapshot.js +83 -0
  20. package/dist/snapshot.js.map +1 -0
  21. package/dist/state.cjs +631 -0
  22. package/dist/state.cjs.map +1 -0
  23. package/dist/state.d.ts +0 -1
  24. package/dist/state.js +627 -0
  25. package/dist/state.js.map +1 -0
  26. package/dist/sync.cjs +156 -0
  27. package/dist/sync.cjs.map +1 -0
  28. package/dist/sync.d.ts +0 -1
  29. package/dist/sync.js +151 -0
  30. package/dist/sync.js.map +1 -0
  31. package/dist/synchronized_tree.cjs +235 -0
  32. package/dist/synchronized_tree.cjs.map +1 -0
  33. package/dist/synchronized_tree.d.ts +0 -1
  34. package/dist/synchronized_tree.js +214 -0
  35. package/dist/synchronized_tree.js.map +1 -0
  36. package/dist/test_utils.d.ts +0 -1
  37. package/dist/traversal_ops.d.ts +0 -1
  38. package/dist/value_and_error.cjs +20 -0
  39. package/dist/value_and_error.cjs.map +1 -0
  40. package/dist/value_and_error.d.ts +0 -1
  41. package/dist/value_and_error.js +17 -0
  42. package/dist/value_and_error.js.map +1 -0
  43. package/dist/value_or_error.d.ts +0 -1
  44. package/package.json +16 -14
  45. package/src/snapshot.test.ts +4 -3
  46. package/src/state.test.ts +6 -4
  47. package/dist/accessors.d.ts.map +0 -1
  48. package/dist/dump.d.ts.map +0 -1
  49. package/dist/index.d.ts.map +0 -1
  50. package/dist/index.mjs +0 -904
  51. package/dist/index.mjs.map +0 -1
  52. package/dist/snapshot.d.ts.map +0 -1
  53. package/dist/state.d.ts.map +0 -1
  54. package/dist/sync.d.ts.map +0 -1
  55. package/dist/synchronized_tree.d.ts.map +0 -1
  56. package/dist/test_utils.d.ts.map +0 -1
  57. package/dist/traversal_ops.d.ts.map +0 -1
  58. package/dist/value_and_error.d.ts.map +0 -1
  59. package/dist/value_or_error.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sources":["../src/state.ts"],"sourcesContent":["import type {\n BasicResourceData,\n FieldData,\n FieldStatus,\n FieldType,\n KeyValue,\n OptionalResourceId,\n ResourceData,\n ResourceId,\n ResourceKind,\n ResourceType } from '@milaboratories/pl-client';\nimport {\n isNotNullResourceId,\n isNullResourceId,\n NullResourceId,\n resourceIdToString,\n stringifyWithResourceId,\n} from '@milaboratories/pl-client';\nimport type { Watcher } from '@milaboratories/computable';\nimport { ChangeSource, KeyedChangeSource } from '@milaboratories/computable';\nimport { PlTreeEntry } from './accessors';\nimport type { ValueAndError } from './value_and_error';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { cachedDecode, cachedDeserialize, notEmpty } from '@milaboratories/ts-helpers';\nimport type { FieldTraversalStep, GetFieldStep } from './traversal_ops';\nimport type { FinalResourceDataPredicate } from '@milaboratories/pl-client';\n\nexport type ExtendedResourceData = ResourceData & {\n kv: KeyValue[];\n};\n\nexport class TreeStateUpdateError extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nclass PlTreeField implements FieldData {\n readonly change = new ChangeSource();\n\n constructor(\n public readonly name: string,\n public type: FieldType,\n public value: OptionalResourceId,\n public error: OptionalResourceId,\n public status: FieldStatus,\n public valueIsFinal: boolean,\n /** Last version of resource this field was observed, used to garbage collect fields in tree patching procedure */\n public resourceVersion: number,\n ) {}\n\n get state(): FieldData {\n return {\n name: this.name,\n type: this.type,\n status: this.status,\n value: this.value,\n error: this.error,\n valueIsFinal: this.valueIsFinal,\n };\n }\n}\n\nconst InitialResourceVersion = 0;\n\nexport type ResourceDataWithFinalState = ResourceData & {\n finalState: boolean;\n};\n\n/** Never store instances of this class, always get fresh instance from {@link PlTreeState} */\nexport class PlTreeResource implements ResourceDataWithFinalState {\n /** Tracks number of other resources referencing this resource. Used to perform garbage collection in tree patching procedure */\n refCount: number = 0;\n\n /** Increments each time resource is checked for difference with new state */\n version: number = InitialResourceVersion;\n /** Set to resource version when resource state, or it's fields have changed */\n dataVersion: number = InitialResourceVersion;\n\n readonly fieldsMap: Map<string, PlTreeField> = new Map();\n\n readonly kv = new Map<string, Uint8Array>();\n\n readonly resourceRemoved = new ChangeSource();\n\n // following change source are removed when resource is marked as final\n\n finalChanged? = new ChangeSource();\n\n resourceStateChange? = new ChangeSource();\n\n lockedChange? = new ChangeSource();\n inputAndServiceFieldListChanged? = new ChangeSource();\n outputFieldListChanged? = new ChangeSource();\n dynamicFieldListChanged? = new ChangeSource();\n\n // kvChangedGlobal? = new ChangeSource();\n kvChangedPerKey? = new KeyedChangeSource();\n\n readonly id: ResourceId;\n originalResourceId: OptionalResourceId;\n\n readonly kind: ResourceKind;\n readonly type: ResourceType;\n\n readonly data?: Uint8Array;\n private dataAsString?: string;\n private dataAsJson?: unknown;\n\n error: OptionalResourceId;\n\n inputsLocked: boolean;\n outputsLocked: boolean;\n resourceReady: boolean;\n finalFlag: boolean;\n\n /** Set externally by the tree, using {@link FinalResourceDataPredicate} */\n _finalState: boolean = false;\n\n private readonly logger?: MiLogger;\n\n constructor(initialState: BasicResourceData, logger?: MiLogger) {\n this.id = initialState.id;\n this.originalResourceId = initialState.originalResourceId;\n this.kind = initialState.kind;\n this.type = initialState.type;\n this.data = initialState.data;\n this.error = initialState.error;\n this.inputsLocked = initialState.inputsLocked;\n this.outputsLocked = initialState.outputsLocked;\n this.resourceReady = initialState.resourceReady;\n this.finalFlag = initialState.final;\n this.logger = logger;\n }\n\n // TODO add logging\n\n private info(msg: string) {\n if (this.logger !== undefined) this.logger.info(msg);\n }\n\n private warn(msg: string) {\n if (this.logger !== undefined) this.logger.warn(msg);\n }\n\n get final(): boolean {\n return this.finalFlag;\n }\n\n get finalState(): boolean {\n return this._finalState;\n }\n\n get fields(): FieldData[] {\n return [...this.fieldsMap.values()];\n }\n\n public getField(\n watcher: Watcher,\n _step:\n | (Omit<GetFieldStep, 'errorIfFieldNotFound'> & { errorIfFieldNotFound: true })\n | (Omit<GetFieldStep, 'errorIfFieldNotSet'> & { errorIfFieldNotSet: true }),\n onUnstable: (marker: string) => void\n ): ValueAndError<ResourceId>;\n public getField(\n watcher: Watcher,\n _step: string | GetFieldStep,\n onUnstable: (marker: string) => void\n ): ValueAndError<ResourceId> | undefined;\n public getField(\n watcher: Watcher,\n _step: string | GetFieldStep,\n onUnstable: (marker: string) => void = () => {},\n ): ValueAndError<ResourceId> | undefined {\n const step: FieldTraversalStep = typeof _step === 'string' ? { field: _step } : _step;\n\n const field = this.fieldsMap.get(step.field);\n if (field === undefined) {\n if (step.errorIfFieldNotFound || step.errorIfFieldNotSet)\n throw new Error(\n `Field \"${step.field}\" not found in resource ${resourceIdToString(this.id)}`,\n );\n\n if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);\n else if (step.assertFieldType === 'Service' || step.assertFieldType === 'Input') {\n if (step.allowPermanentAbsence)\n // stable absence of field\n return undefined;\n else throw new Error(`Service or input field not found ${step.field}.`);\n }\n\n if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);\n else if (step.assertFieldType === 'Output') {\n if (step.allowPermanentAbsence)\n // stable absence of field\n return undefined;\n else throw new Error(`Output field not found ${step.field}.`);\n }\n\n this.dynamicFieldListChanged?.attachWatcher(watcher);\n if (!this._finalState && !step.stableIfNotFound) onUnstable('field_not_found:' + step.field);\n\n return undefined;\n } else {\n if (step.assertFieldType !== undefined && field.type !== step.assertFieldType)\n throw new Error(\n `Unexpected field type: expected ${step.assertFieldType} but got ${field.type} for the field name ${step.field}`,\n );\n\n const ret = {} as ValueAndError<ResourceId>;\n if (isNotNullResourceId(field.value)) ret.value = field.value;\n if (isNotNullResourceId(field.error)) ret.error = field.error;\n if (ret.value === undefined && ret.error === undefined)\n // this method returns value and error of the field, thus those values are considered to be accessed;\n // any existing but not resolved field here is considered to be unstable, in the sense it is\n // considered to acquire some resolved value eventually\n onUnstable('field_not_resolved:' + step.field);\n field.change.attachWatcher(watcher);\n return ret;\n }\n }\n\n public getInputsLocked(watcher: Watcher): boolean {\n if (!this.inputsLocked)\n // reverse transition can't happen, so there is no reason to wait for value to change\n this.resourceStateChange?.attachWatcher(watcher);\n return this.inputsLocked;\n }\n\n public getOutputsLocked(watcher: Watcher): boolean {\n if (!this.outputsLocked)\n // reverse transition can't happen, so there is no reason to wait for value to change\n this.resourceStateChange?.attachWatcher(watcher);\n return this.outputsLocked;\n }\n\n public get isReadyOrError(): boolean {\n return (\n this.error !== NullResourceId\n || this.resourceReady\n || this.originalResourceId !== NullResourceId\n );\n }\n\n public getIsFinal(watcher: Watcher): boolean {\n this.finalChanged?.attachWatcher(watcher);\n return this._finalState;\n }\n\n public getIsReadyOrError(watcher: Watcher): boolean {\n if (!this.isReadyOrError)\n // reverse transition can't happen, so there is no reason to wait for value to change if it is already true\n this.resourceStateChange?.attachWatcher(watcher);\n return this.isReadyOrError;\n }\n\n public getError(watcher: Watcher): ResourceId | undefined {\n if (isNullResourceId(this.error)) {\n this.resourceStateChange?.attachWatcher(watcher);\n return undefined;\n } else {\n // reverse transition can't happen, so there is no reason to wait for value to change, if error already set\n return this.error;\n }\n }\n\n public listInputFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type === 'Input' || field.type === 'Service') ret.push(name);\n });\n if (!this.inputsLocked) this.inputAndServiceFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public listOutputFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type === 'Output') ret.push(name);\n });\n if (!this.outputsLocked) this.outputFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public listDynamicFields(watcher: Watcher): string[] {\n const ret: string[] = [];\n this.fieldsMap.forEach((field, name) => {\n if (field.type !== 'Input' && field.type !== 'Output') ret.push(name);\n });\n this.dynamicFieldListChanged?.attachWatcher(watcher);\n\n return ret;\n }\n\n public getKeyValue(watcher: Watcher, key: string): Uint8Array | undefined {\n this.kvChangedPerKey?.attachWatcher(key, watcher);\n return this.kv.get(key);\n }\n\n public getKeyValueString(watcher: Watcher, key: string): string | undefined {\n const bytes = this.getKeyValue(watcher, key);\n if (bytes === undefined) return undefined;\n return cachedDecode(bytes);\n }\n\n public getKeyValueAsJson<T = unknown>(watcher: Watcher, key: string): T | undefined {\n const bytes = this.getKeyValue(watcher, key);\n if (bytes === undefined) return undefined;\n return cachedDeserialize(bytes);\n }\n\n public getDataAsString(): string | undefined {\n if (this.data === undefined) return undefined;\n if (this.dataAsString === undefined) this.dataAsString = cachedDecode(this.data);\n return this.dataAsString;\n }\n\n public getDataAsJson<T = unknown>(): T | undefined {\n if (this.data === undefined) return undefined;\n if (this.dataAsJson === undefined) this.dataAsJson = cachedDeserialize(this.data);\n return this.dataAsJson as T;\n }\n\n verifyReadyState() {\n if (this.resourceReady && !this.inputsLocked)\n throw new Error(`ready without input or output lock: ${stringifyWithResourceId(this.basicState)}`);\n }\n\n get basicState(): BasicResourceData {\n return {\n id: this.id,\n kind: this.kind,\n type: this.type,\n data: this.data,\n resourceReady: this.resourceReady,\n inputsLocked: this.inputsLocked,\n outputsLocked: this.outputsLocked,\n error: this.error,\n originalResourceId: this.originalResourceId,\n final: this.finalFlag,\n };\n }\n\n get extendedState(): ExtendedResourceData {\n return {\n ...this.basicState,\n fields: this.fields,\n kv: Array.from(this.kv.entries()).map(([key, value]) => ({ key, value })),\n };\n }\n\n /** Called when {@link FinalResourceDataPredicate} returns true for the state. */\n markFinal() {\n if (this._finalState) return;\n\n this._finalState = true;\n notEmpty(this.finalChanged).markChanged('marked final');\n this.finalChanged = undefined;\n this.resourceStateChange = undefined;\n this.dynamicFieldListChanged = undefined;\n this.inputAndServiceFieldListChanged = undefined;\n this.outputFieldListChanged = undefined;\n this.lockedChange = undefined;\n // this.kvChangedGlobal = undefined;\n this.kvChangedPerKey = undefined;\n }\n\n /** Used for invalidation */\n markAllChanged() {\n this.fieldsMap.forEach((field) => field.change.markChanged('marked all changed'));\n this.finalChanged?.markChanged('marked all changed');\n this.resourceStateChange?.markChanged('marked all changed');\n this.lockedChange?.markChanged('marked all changed');\n this.inputAndServiceFieldListChanged?.markChanged('marked all changed');\n this.outputFieldListChanged?.markChanged('marked all changed');\n this.dynamicFieldListChanged?.markChanged('marked all changed');\n // this.kvChangedGlobal?.markChanged('marked all changed');\n this.kvChangedPerKey?.markAllChanged('marked all changed');\n this.resourceRemoved.markChanged('marked all changed');\n }\n}\n\nexport class PlTreeState {\n /** resource heap */\n private resources: Map<ResourceId, PlTreeResource> = new Map();\n private readonly resourcesAdded = new ChangeSource();\n /** Resets to false if any invalid state transitions are registered,\n * after that tree will produce errors for any read or write operations */\n private _isValid: boolean = true;\n private invalidationMessage?: string;\n\n constructor(\n /** This will be the only resource not deleted during GC round */\n public readonly root: ResourceId,\n public readonly isFinalPredicate: FinalResourceDataPredicate,\n ) {}\n\n public forEachResource(cb: (res: ResourceDataWithFinalState) => void): void {\n this.resources.forEach((v) => cb(v));\n }\n\n private checkValid() {\n if (!this._isValid) throw new Error(this.invalidationMessage ?? 'tree is in invalid state');\n }\n\n public get(watcher: Watcher, rid: ResourceId): PlTreeResource {\n this.checkValid();\n const res = this.resources.get(rid);\n if (res === undefined) {\n // to make recovery from resource not found possible, considering some\n // race conditions, where computable is created before tree is updated\n this.resourcesAdded.attachWatcher(watcher);\n throw new Error(`resource ${resourceIdToString(rid)} not found in the tree`);\n }\n res.resourceRemoved.attachWatcher(watcher);\n return res;\n }\n\n updateFromResourceData(resourceData: ExtendedResourceData[], allowOrphanInputs: boolean = false) {\n this.checkValid();\n\n // All resources for which recount should be incremented, first are aggregated in this list\n const incrementRefs: ResourceId[] = [];\n const decrementRefs: ResourceId[] = [];\n\n // patching / creating resources\n for (const rd of resourceData) {\n let resource = this.resources.get(rd.id);\n\n const statBeforeMutation = resource?.basicState;\n const unexpectedTransitionError = (reason: string): never => {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { fields, ...rdWithoutFields } = rd;\n this.invalidateTree();\n throw new TreeStateUpdateError(\n `Unexpected resource state transition (${reason}): ${stringifyWithResourceId(\n rdWithoutFields,\n )} -> ${stringifyWithResourceId(statBeforeMutation)}`,\n );\n };\n\n if (resource !== undefined) {\n // updating existing resource\n\n if (resource.finalState)\n unexpectedTransitionError('resource state can\\t be updated after it is marked as final');\n\n let changed = false;\n // updating resource version, even if it was not changed\n resource.version += 1;\n\n // duplicate / original\n if (resource.originalResourceId !== rd.originalResourceId) {\n if (resource.originalResourceId !== NullResourceId)\n unexpectedTransitionError('originalResourceId can\\'t change after it is set');\n resource.originalResourceId = rd.originalResourceId;\n // duplicate status of the resource counts as ready for the external observer\n notEmpty(resource.resourceStateChange).markChanged(`originalResourceId changed for ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // error\n if (resource.error !== rd.error) {\n if (isNotNullResourceId(resource.error))\n unexpectedTransitionError('resource can\\'t change attached error after it is set');\n resource.error = rd.error;\n incrementRefs.push(resource.error as ResourceId);\n notEmpty(resource.resourceStateChange).markChanged(`error changed for ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // updating fields\n for (const fd of rd.fields) {\n let field = resource.fieldsMap.get(fd.name);\n\n if (!field) {\n // new field\n\n field = new PlTreeField(\n fd.name,\n fd.type,\n fd.value,\n fd.error,\n fd.status,\n fd.valueIsFinal,\n resource.version,\n );\n if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);\n if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);\n\n if (fd.type === 'Input' || fd.type === 'Service') {\n if (resource.inputsLocked)\n unexpectedTransitionError(\n `adding ${fd.type} (${fd.name}) field while inputs locked`,\n );\n notEmpty(resource.inputAndServiceFieldListChanged).markChanged(`new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`);\n } else if (fd.type === 'Output') {\n if (resource.outputsLocked)\n unexpectedTransitionError(\n `adding ${fd.type} (${fd.name}) field while outputs locked`,\n );\n notEmpty(resource.outputFieldListChanged).markChanged(`new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`);\n } else {\n notEmpty(resource.dynamicFieldListChanged).markChanged(`new ${fd.type} field ${fd.name} added to ${resourceIdToString(resource.id)}`);\n }\n\n resource.fieldsMap.set(fd.name, field);\n\n changed = true;\n } else {\n // change of old field\n\n // in principle this transition is possible, see assertions below\n if (field.type !== fd.type) {\n if (field.type !== 'Dynamic')\n unexpectedTransitionError(`field changed type ${field.type} -> ${fd.type}`);\n notEmpty(resource.dynamicFieldListChanged).markChanged(`field ${fd.name} changed type from Dynamic to ${fd.type} in ${resourceIdToString(resource.id)}`);\n if (field.type === 'Input' || field.type === 'Service') {\n if (resource.inputsLocked)\n unexpectedTransitionError(\n `adding input field \"${fd.name}\", while corresponding list is locked`,\n );\n notEmpty(resource.inputAndServiceFieldListChanged).markChanged(`field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`);\n }\n if (field.type === 'Output') {\n if (resource.outputsLocked)\n unexpectedTransitionError(\n `adding output field \"${fd.name}\", while corresponding list is locked`,\n );\n notEmpty(resource.outputFieldListChanged).markChanged(`field ${fd.name} changed to type ${fd.type} in ${resourceIdToString(resource.id)}`);\n }\n field.type = fd.type;\n field.change.markChanged(`field ${fd.name} type changed to ${fd.type} in ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // field value\n if (field.value !== fd.value) {\n if (isNotNullResourceId(field.value)) decrementRefs.push(field.value);\n field.value = fd.value;\n if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);\n field.change.markChanged(`field ${fd.name} value changed in ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // field error\n if (field.error !== fd.error) {\n if (isNotNullResourceId(field.error)) decrementRefs.push(field.error);\n field.error = fd.error;\n if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);\n field.change.markChanged(`field ${fd.name} error changed in ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // field status\n if (field.status !== fd.status) {\n field.status = fd.status;\n field.change.markChanged(`field ${fd.name} status changed to ${fd.status} in ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // field valueIsFinal flag\n if (field.valueIsFinal !== fd.valueIsFinal) {\n field.valueIsFinal = fd.valueIsFinal;\n field.change.markChanged(`field ${fd.name} valueIsFinal changed to ${fd.valueIsFinal} in ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n field.resourceVersion = resource.version;\n }\n }\n\n // detecting removed fields\n resource.fieldsMap.forEach((field, fieldName, fields) => {\n if (field.resourceVersion !== resource!.version) {\n if (field.type === 'Input' || field.type === 'Service' || field.type === 'Output')\n unexpectedTransitionError(`removal of ${field.type} field ${fieldName}`);\n field.change.markChanged(`dynamic field ${fieldName} removed from ${resourceIdToString(resource!.id)}`);\n fields.delete(fieldName);\n\n if (isNotNullResourceId(field.value)) decrementRefs.push(field.value);\n if (isNotNullResourceId(field.error)) decrementRefs.push(field.error);\n\n notEmpty(resource!.dynamicFieldListChanged).markChanged(`dynamic field ${fieldName} removed from ${resourceIdToString(resource!.id)}`);\n }\n });\n\n // inputsLocked\n if (resource.inputsLocked !== rd.inputsLocked) {\n if (resource.inputsLocked) unexpectedTransitionError('inputs unlocking is not permitted');\n resource.inputsLocked = rd.inputsLocked;\n notEmpty(resource.lockedChange).markChanged(`inputs locked for ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // outputsLocked\n if (resource.outputsLocked !== rd.outputsLocked) {\n if (resource.outputsLocked)\n unexpectedTransitionError('outputs unlocking is not permitted');\n resource.outputsLocked = rd.outputsLocked;\n notEmpty(resource.lockedChange).markChanged(`outputs locked for ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // ready flag\n if (resource.resourceReady !== rd.resourceReady) {\n const readyStateBefore = resource.resourceReady;\n resource.resourceReady = rd.resourceReady;\n resource.verifyReadyState();\n if (!resource.isReadyOrError)\n unexpectedTransitionError(\n `resource can't lose it's ready or error state (ready state before ${readyStateBefore})`,\n );\n notEmpty(resource.resourceStateChange).markChanged(`ready flag changed to ${rd.resourceReady} for ${resourceIdToString(resource.id)}`);\n changed = true;\n }\n\n // syncing kv\n for (const kv of rd.kv) {\n const current = resource.kv.get(kv.key);\n if (current === undefined) {\n resource.kv.set(kv.key, kv.value);\n notEmpty(resource.kvChangedPerKey).markChanged(kv.key, `kv added for ${resourceIdToString(resource.id)}: ${kv.key}`);\n } else if (Buffer.compare(current, kv.value) !== 0) {\n resource.kv.set(kv.key, kv.value);\n notEmpty(resource.kvChangedPerKey).markChanged(kv.key, `kv changed for ${resourceIdToString(resource.id)}: ${kv.key}`);\n }\n }\n\n if (resource.kv.size > rd.kv.length) {\n // only it this case it makes sense to check for deletions\n const newStateKeys = new Set(rd.kv.map((kv) => kv.key));\n\n // deleting keys not present in resource anymore\n resource.kv.forEach((_value, key, map) => {\n if (!newStateKeys.has(key)) {\n map.delete(key);\n notEmpty(resource!.kvChangedPerKey).markChanged(key, `kv deleted for ${resourceIdToString(resource!.id)}: ${key}`);\n }\n });\n }\n\n if (changed) {\n // if resource was changed, updating resource data version\n resource.dataVersion = resource.version;\n if (this.isFinalPredicate(resource)) resource.markFinal();\n }\n } else {\n // creating new resource\n\n resource = new PlTreeResource(rd);\n resource.verifyReadyState();\n if (isNotNullResourceId(resource.error)) incrementRefs.push(resource.error);\n for (const fd of rd.fields) {\n const field = new PlTreeField(\n fd.name,\n fd.type,\n fd.value,\n fd.error,\n fd.status,\n fd.valueIsFinal,\n InitialResourceVersion,\n );\n if (isNotNullResourceId(fd.value)) incrementRefs.push(fd.value);\n if (isNotNullResourceId(fd.error)) incrementRefs.push(fd.error);\n resource.fieldsMap.set(fd.name, field);\n }\n\n // adding kv\n for (const kv of rd.kv) resource.kv.set(kv.key, kv.value);\n\n // checking that resource is final, and if so, marking it\n if (this.isFinalPredicate(resource)) resource.markFinal();\n\n // adding the resource to the heap\n this.resources.set(resource.id, resource);\n this.resourcesAdded.markChanged(`new resource ${resourceIdToString(resource.id)} added`);\n }\n }\n\n // applying refCount increments\n for (const rid of incrementRefs) {\n const res = this.resources.get(rid);\n if (!res) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan resource ${rid}`);\n }\n res.refCount++;\n }\n\n // recursively applying refCount decrements / doing garbage collection\n let currentRefs = decrementRefs;\n while (currentRefs.length > 0) {\n const nextRefs: ResourceId[] = [];\n for (const rid of currentRefs) {\n const res = this.resources.get(rid);\n if (!res) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan resource ${rid}`);\n }\n res.refCount--;\n\n // garbage collection\n if (res.refCount === 0 && res.id !== this.root) {\n // removing fields\n res.fieldsMap.forEach((field) => {\n if (isNotNullResourceId(field.value)) nextRefs.push(field.value);\n if (isNotNullResourceId(field.error)) nextRefs.push(field.error);\n field.change.markChanged(`field ${field.name} removed during garbage collection of ${resourceIdToString(res.id)}`);\n });\n if (isNotNullResourceId(res.error)) nextRefs.push(res.error);\n res.resourceRemoved.markChanged(`resource removed during garbage collection: ${resourceIdToString(res.id)}`);\n this.resources.delete(rid);\n }\n }\n currentRefs = nextRefs;\n }\n\n // checking for orphans (maybe removed in the future)\n if (!allowOrphanInputs) {\n for (const rd of resourceData) {\n if (!this.resources.has(rd.id)) {\n this.invalidateTree();\n throw new TreeStateUpdateError(`orphan input resource ${rd.id}`);\n }\n }\n }\n }\n\n /** @deprecated use \"entry\" instead */\n public accessor(rid: ResourceId = this.root): PlTreeEntry {\n this.checkValid();\n return this.entry(rid);\n }\n\n public entry(rid: ResourceId = this.root): PlTreeEntry {\n this.checkValid();\n return new PlTreeEntry({ treeProvider: () => this }, rid);\n }\n\n public invalidateTree(msg?: string) {\n this._isValid = false;\n this.invalidationMessage = msg;\n this.resources.forEach((res) => {\n res.markAllChanged();\n });\n }\n\n public dumpState(): ExtendedResourceData[] {\n return Array.from(this.resources.values()).map((res) => res.extendedState);\n }\n}\n"],"names":[],"mappings":";;;;;AA+BM,MAAO,oBAAqB,SAAQ,KAAK,CAAA;AAC7C,IAAA,WAAA,CAAY,OAAe,EAAA;QACzB,KAAK,CAAC,OAAO,CAAC;IAChB;AACD;AAED,MAAM,WAAW,CAAA;AAIG,IAAA,IAAA;AACT,IAAA,IAAA;AACA,IAAA,KAAA;AACA,IAAA,KAAA;AACA,IAAA,MAAA;AACA,IAAA,YAAA;AAEA,IAAA,eAAA;AAVA,IAAA,MAAM,GAAG,IAAI,YAAY,EAAE;IAEpC,WAAA,CACkB,IAAY,EACrB,IAAe,EACf,KAAyB,EACzB,KAAyB,EACzB,MAAmB,EACnB,YAAqB;;IAErB,eAAuB,EAAA;QAPd,IAAA,CAAA,IAAI,GAAJ,IAAI;QACb,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,KAAK,GAAL,KAAK;QACL,IAAA,CAAA,KAAK,GAAL,KAAK;QACL,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,YAAY,GAAZ,YAAY;QAEZ,IAAA,CAAA,eAAe,GAAf,eAAe;IACrB;AAEH,IAAA,IAAI,KAAK,GAAA;QACP,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC;IACH;AACD;AAED,MAAM,sBAAsB,GAAG,CAAC;AAMhC;MACa,cAAc,CAAA;;IAEzB,QAAQ,GAAW,CAAC;;IAGpB,OAAO,GAAW,sBAAsB;;IAExC,WAAW,GAAW,sBAAsB;AAEnC,IAAA,SAAS,GAA6B,IAAI,GAAG,EAAE;AAE/C,IAAA,EAAE,GAAG,IAAI,GAAG,EAAsB;AAElC,IAAA,eAAe,GAAG,IAAI,YAAY,EAAE;;AAI7C,IAAA,YAAY,GAAI,IAAI,YAAY,EAAE;AAElC,IAAA,mBAAmB,GAAI,IAAI,YAAY,EAAE;AAEzC,IAAA,YAAY,GAAI,IAAI,YAAY,EAAE;AAClC,IAAA,+BAA+B,GAAI,IAAI,YAAY,EAAE;AACrD,IAAA,sBAAsB,GAAI,IAAI,YAAY,EAAE;AAC5C,IAAA,uBAAuB,GAAI,IAAI,YAAY,EAAE;;AAG7C,IAAA,eAAe,GAAI,IAAI,iBAAiB,EAAE;AAEjC,IAAA,EAAE;AACX,IAAA,kBAAkB;AAET,IAAA,IAAI;AACJ,IAAA,IAAI;AAEJ,IAAA,IAAI;AACL,IAAA,YAAY;AACZ,IAAA,UAAU;AAElB,IAAA,KAAK;AAEL,IAAA,YAAY;AACZ,IAAA,aAAa;AACb,IAAA,aAAa;AACb,IAAA,SAAS;;IAGT,WAAW,GAAY,KAAK;AAEX,IAAA,MAAM;IAEvB,WAAA,CAAY,YAA+B,EAAE,MAAiB,EAAA;AAC5D,QAAA,IAAI,CAAC,EAAE,GAAG,YAAY,CAAC,EAAE;AACzB,QAAA,IAAI,CAAC,kBAAkB,GAAG,YAAY,CAAC,kBAAkB;AACzD,QAAA,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI;AAC7B,QAAA,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI;AAC7B,QAAA,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI;AAC7B,QAAA,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC,KAAK;AAC/B,QAAA,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC,YAAY;AAC7C,QAAA,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC,aAAa;AAC/C,QAAA,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC,aAAa;AAC/C,QAAA,IAAI,CAAC,SAAS,GAAG,YAAY,CAAC,KAAK;AACnC,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;IACtB;;AAIQ,IAAA,IAAI,CAAC,GAAW,EAAA;AACtB,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;AAAE,YAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IACtD;AAEQ,IAAA,IAAI,CAAC,GAAW,EAAA;AACtB,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;AAAE,YAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IACtD;AAEA,IAAA,IAAI,KAAK,GAAA;QACP,OAAO,IAAI,CAAC,SAAS;IACvB;AAEA,IAAA,IAAI,UAAU,GAAA;QACZ,OAAO,IAAI,CAAC,WAAW;IACzB;AAEA,IAAA,IAAI,MAAM,GAAA;QACR,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;IACrC;IAcO,QAAQ,CACb,OAAgB,EAChB,KAA4B,EAC5B,UAAA,GAAuC,MAAK,EAAE,CAAC,EAAA;AAE/C,QAAA,MAAM,IAAI,GAAuB,OAAO,KAAK,KAAK,QAAQ,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK;AAErF,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;AAC5C,QAAA,IAAI,KAAK,KAAK,SAAS,EAAE;AACvB,YAAA,IAAI,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,kBAAkB;AACtD,gBAAA,MAAM,IAAI,KAAK,CACb,CAAA,OAAA,EAAU,IAAI,CAAC,KAAK,CAAA,wBAAA,EAA2B,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA,CAAE,CAC7E;YAEH,IAAI,CAAC,IAAI,CAAC,YAAY;AAAE,gBAAA,IAAI,CAAC,+BAA+B,EAAE,aAAa,CAAC,OAAO,CAAC;AAC/E,iBAAA,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,IAAI,IAAI,CAAC,eAAe,KAAK,OAAO,EAAE;gBAC/E,IAAI,IAAI,CAAC,qBAAqB;;AAE5B,oBAAA,OAAO,SAAS;;oBACb,MAAM,IAAI,KAAK,CAAC,CAAA,iCAAA,EAAoC,IAAI,CAAC,KAAK,CAAA,CAAA,CAAG,CAAC;YACzE;YAEA,IAAI,CAAC,IAAI,CAAC,aAAa;AAAE,gBAAA,IAAI,CAAC,sBAAsB,EAAE,aAAa,CAAC,OAAO,CAAC;AACvE,iBAAA,IAAI,IAAI,CAAC,eAAe,KAAK,QAAQ,EAAE;gBAC1C,IAAI,IAAI,CAAC,qBAAqB;;AAE5B,oBAAA,OAAO,SAAS;;oBACb,MAAM,IAAI,KAAK,CAAC,CAAA,uBAAA,EAA0B,IAAI,CAAC,KAAK,CAAA,CAAA,CAAG,CAAC;YAC/D;AAEA,YAAA,IAAI,CAAC,uBAAuB,EAAE,aAAa,CAAC,OAAO,CAAC;YACpD,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,gBAAgB;AAAE,gBAAA,UAAU,CAAC,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC;AAE5F,YAAA,OAAO,SAAS;QAClB;aAAO;AACL,YAAA,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,eAAe;AAC3E,gBAAA,MAAM,IAAI,KAAK,CACb,CAAA,gCAAA,EAAmC,IAAI,CAAC,eAAe,CAAA,SAAA,EAAY,KAAK,CAAC,IAAI,CAAA,oBAAA,EAAuB,IAAI,CAAC,KAAK,CAAA,CAAE,CACjH;YAEH,MAAM,GAAG,GAAG,EAA+B;AAC3C,YAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,gBAAA,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;AAC7D,YAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,gBAAA,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;YAC7D,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;;;;AAIpD,gBAAA,UAAU,CAAC,qBAAqB,GAAG,IAAI,CAAC,KAAK,CAAC;AAChD,YAAA,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC;AACnC,YAAA,OAAO,GAAG;QACZ;IACF;AAEO,IAAA,eAAe,CAAC,OAAgB,EAAA;QACrC,IAAI,CAAC,IAAI,CAAC,YAAY;;AAEpB,YAAA,IAAI,CAAC,mBAAmB,EAAE,aAAa,CAAC,OAAO,CAAC;QAClD,OAAO,IAAI,CAAC,YAAY;IAC1B;AAEO,IAAA,gBAAgB,CAAC,OAAgB,EAAA;QACtC,IAAI,CAAC,IAAI,CAAC,aAAa;;AAErB,YAAA,IAAI,CAAC,mBAAmB,EAAE,aAAa,CAAC,OAAO,CAAC;QAClD,OAAO,IAAI,CAAC,aAAa;IAC3B;AAEA,IAAA,IAAW,cAAc,GAAA;AACvB,QAAA,QACE,IAAI,CAAC,KAAK,KAAK;AACZ,eAAA,IAAI,CAAC;AACL,eAAA,IAAI,CAAC,kBAAkB,KAAK,cAAc;IAEjD;AAEO,IAAA,UAAU,CAAC,OAAgB,EAAA;AAChC,QAAA,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,OAAO,CAAC;QACzC,OAAO,IAAI,CAAC,WAAW;IACzB;AAEO,IAAA,iBAAiB,CAAC,OAAgB,EAAA;QACvC,IAAI,CAAC,IAAI,CAAC,cAAc;;AAEtB,YAAA,IAAI,CAAC,mBAAmB,EAAE,aAAa,CAAC,OAAO,CAAC;QAClD,OAAO,IAAI,CAAC,cAAc;IAC5B;AAEO,IAAA,QAAQ,CAAC,OAAgB,EAAA;AAC9B,QAAA,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;AAChC,YAAA,IAAI,CAAC,mBAAmB,EAAE,aAAa,CAAC,OAAO,CAAC;AAChD,YAAA,OAAO,SAAS;QAClB;aAAO;;YAEL,OAAO,IAAI,CAAC,KAAK;QACnB;IACF;AAEO,IAAA,eAAe,CAAC,OAAgB,EAAA;QACrC,MAAM,GAAG,GAAa,EAAE;QACxB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,KAAI;YACrC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;AAAE,gBAAA,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACxE,QAAA,CAAC,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,YAAY;AAAE,YAAA,IAAI,CAAC,+BAA+B,EAAE,aAAa,CAAC,OAAO,CAAC;AAEpF,QAAA,OAAO,GAAG;IACZ;AAEO,IAAA,gBAAgB,CAAC,OAAgB,EAAA;QACtC,MAAM,GAAG,GAAa,EAAE;QACxB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,KAAI;AACrC,YAAA,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;AAAE,gBAAA,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AAC7C,QAAA,CAAC,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,aAAa;AAAE,YAAA,IAAI,CAAC,sBAAsB,EAAE,aAAa,CAAC,OAAO,CAAC;AAE5E,QAAA,OAAO,GAAG;IACZ;AAEO,IAAA,iBAAiB,CAAC,OAAgB,EAAA;QACvC,MAAM,GAAG,GAAa,EAAE;QACxB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,KAAI;YACrC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;AAAE,gBAAA,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvE,QAAA,CAAC,CAAC;AACF,QAAA,IAAI,CAAC,uBAAuB,EAAE,aAAa,CAAC,OAAO,CAAC;AAEpD,QAAA,OAAO,GAAG;IACZ;IAEO,WAAW,CAAC,OAAgB,EAAE,GAAW,EAAA;QAC9C,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC;QACjD,OAAO,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;IACzB;IAEO,iBAAiB,CAAC,OAAgB,EAAE,GAAW,EAAA;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS;AAAE,YAAA,OAAO,SAAS;AACzC,QAAA,OAAO,YAAY,CAAC,KAAK,CAAC;IAC5B;IAEO,iBAAiB,CAAc,OAAgB,EAAE,GAAW,EAAA;QACjE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS;AAAE,YAAA,OAAO,SAAS;AACzC,QAAA,OAAO,iBAAiB,CAAC,KAAK,CAAC;IACjC;IAEO,eAAe,GAAA;AACpB,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;AAAE,YAAA,OAAO,SAAS;AAC7C,QAAA,IAAI,IAAI,CAAC,YAAY,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAChF,OAAO,IAAI,CAAC,YAAY;IAC1B;IAEO,aAAa,GAAA;AAClB,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;AAAE,YAAA,OAAO,SAAS;AAC7C,QAAA,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAAE,IAAI,CAAC,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QACjF,OAAO,IAAI,CAAC,UAAe;IAC7B;IAEA,gBAAgB,GAAA;AACd,QAAA,IAAI,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,YAAY;AAC1C,YAAA,MAAM,IAAI,KAAK,CAAC,CAAA,oCAAA,EAAuC,uBAAuB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA,CAAE,CAAC;IACtG;AAEA,IAAA,IAAI,UAAU,GAAA;QACZ,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;YAC3C,KAAK,EAAE,IAAI,CAAC,SAAS;SACtB;IACH;AAEA,IAAA,IAAI,aAAa,GAAA;QACf,OAAO;YACL,GAAG,IAAI,CAAC,UAAU;YAClB,MAAM,EAAE,IAAI,CAAC,MAAM;AACnB,YAAA,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;SAC1E;IACH;;IAGA,SAAS,GAAA;QACP,IAAI,IAAI,CAAC,WAAW;YAAE;AAEtB,QAAA,IAAI,CAAC,WAAW,GAAG,IAAI;QACvB,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC;AACvD,QAAA,IAAI,CAAC,YAAY,GAAG,SAAS;AAC7B,QAAA,IAAI,CAAC,mBAAmB,GAAG,SAAS;AACpC,QAAA,IAAI,CAAC,uBAAuB,GAAG,SAAS;AACxC,QAAA,IAAI,CAAC,+BAA+B,GAAG,SAAS;AAChD,QAAA,IAAI,CAAC,sBAAsB,GAAG,SAAS;AACvC,QAAA,IAAI,CAAC,YAAY,GAAG,SAAS;;AAE7B,QAAA,IAAI,CAAC,eAAe,GAAG,SAAS;IAClC;;IAGA,cAAc,GAAA;AACZ,QAAA,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC;AACjF,QAAA,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,oBAAoB,CAAC;AACpD,QAAA,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,oBAAoB,CAAC;AAC3D,QAAA,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,oBAAoB,CAAC;AACpD,QAAA,IAAI,CAAC,+BAA+B,EAAE,WAAW,CAAC,oBAAoB,CAAC;AACvE,QAAA,IAAI,CAAC,sBAAsB,EAAE,WAAW,CAAC,oBAAoB,CAAC;AAC9D,QAAA,IAAI,CAAC,uBAAuB,EAAE,WAAW,CAAC,oBAAoB,CAAC;;AAE/D,QAAA,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,oBAAoB,CAAC;AAC1D,QAAA,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC,oBAAoB,CAAC;IACxD;AACD;MAEY,WAAW,CAAA;AAWJ,IAAA,IAAA;AACA,IAAA,gBAAA;;AAVV,IAAA,SAAS,GAAoC,IAAI,GAAG,EAAE;AAC7C,IAAA,cAAc,GAAG,IAAI,YAAY,EAAE;AACpD;AAC0E;IAClE,QAAQ,GAAY,IAAI;AACxB,IAAA,mBAAmB;AAE3B,IAAA,WAAA;;AAEkB,IAAA,IAAgB,EAChB,gBAA4C,EAAA;QAD5C,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,gBAAgB,GAAhB,gBAAgB;IAC/B;AAEI,IAAA,eAAe,CAAC,EAA6C,EAAA;AAClE,QAAA,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACtC;IAEQ,UAAU,GAAA;QAChB,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,mBAAmB,IAAI,0BAA0B,CAAC;IAC7F;IAEO,GAAG,CAAC,OAAgB,EAAE,GAAe,EAAA;QAC1C,IAAI,CAAC,UAAU,EAAE;QACjB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;AACnC,QAAA,IAAI,GAAG,KAAK,SAAS,EAAE;;;AAGrB,YAAA,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,OAAO,CAAC;YAC1C,MAAM,IAAI,KAAK,CAAC,CAAA,SAAA,EAAY,kBAAkB,CAAC,GAAG,CAAC,CAAA,sBAAA,CAAwB,CAAC;QAC9E;AACA,QAAA,GAAG,CAAC,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC;AAC1C,QAAA,OAAO,GAAG;IACZ;AAEA,IAAA,sBAAsB,CAAC,YAAoC,EAAE,iBAAA,GAA6B,KAAK,EAAA;QAC7F,IAAI,CAAC,UAAU,EAAE;;QAGjB,MAAM,aAAa,GAAiB,EAAE;QACtC,MAAM,aAAa,GAAiB,EAAE;;AAGtC,QAAA,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE;AAC7B,YAAA,IAAI,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;AAExC,YAAA,MAAM,kBAAkB,GAAG,QAAQ,EAAE,UAAU;AAC/C,YAAA,MAAM,yBAAyB,GAAG,CAAC,MAAc,KAAW;;gBAE1D,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,EAAE,GAAG,EAAE;gBACzC,IAAI,CAAC,cAAc,EAAE;AACrB,gBAAA,MAAM,IAAI,oBAAoB,CAC5B,CAAA,sCAAA,EAAyC,MAAM,MAAM,uBAAuB,CAC1E,eAAe,CAChB,OAAO,uBAAuB,CAAC,kBAAkB,CAAC,CAAA,CAAE,CACtD;AACH,YAAA,CAAC;AAED,YAAA,IAAI,QAAQ,KAAK,SAAS,EAAE;;gBAG1B,IAAI,QAAQ,CAAC,UAAU;oBACrB,yBAAyB,CAAC,6DAA6D,CAAC;gBAE1F,IAAI,OAAO,GAAG,KAAK;;AAEnB,gBAAA,QAAQ,CAAC,OAAO,IAAI,CAAC;;gBAGrB,IAAI,QAAQ,CAAC,kBAAkB,KAAK,EAAE,CAAC,kBAAkB,EAAE;AACzD,oBAAA,IAAI,QAAQ,CAAC,kBAAkB,KAAK,cAAc;wBAChD,yBAAyB,CAAC,kDAAkD,CAAC;AAC/E,oBAAA,QAAQ,CAAC,kBAAkB,GAAG,EAAE,CAAC,kBAAkB;;AAEnD,oBAAA,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAA,+BAAA,EAAkC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;oBACvH,OAAO,GAAG,IAAI;gBAChB;;gBAGA,IAAI,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,EAAE;AAC/B,oBAAA,IAAI,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC;wBACrC,yBAAyB,CAAC,uDAAuD,CAAC;AACpF,oBAAA,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK;AACzB,oBAAA,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAmB,CAAC;AAChD,oBAAA,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAA,kBAAA,EAAqB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;oBAC1G,OAAO,GAAG,IAAI;gBAChB;;AAGA,gBAAA,KAAK,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE;AAC1B,oBAAA,IAAI,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC;oBAE3C,IAAI,CAAC,KAAK,EAAE;;AAGV,wBAAA,KAAK,GAAG,IAAI,WAAW,CACrB,EAAE,CAAC,IAAI,EACP,EAAE,CAAC,IAAI,EACP,EAAE,CAAC,KAAK,EACR,EAAE,CAAC,KAAK,EACR,EAAE,CAAC,MAAM,EACT,EAAE,CAAC,YAAY,EACf,QAAQ,CAAC,OAAO,CACjB;AACD,wBAAA,IAAI,mBAAmB,CAAC,EAAE,CAAC,KAAK,CAAC;AAAE,4BAAA,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;AAC/D,wBAAA,IAAI,mBAAmB,CAAC,EAAE,CAAC,KAAK,CAAC;AAAE,4BAAA,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;AAE/D,wBAAA,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,IAAI,EAAE,CAAC,IAAI,KAAK,SAAS,EAAE;4BAChD,IAAI,QAAQ,CAAC,YAAY;gCACvB,yBAAyB,CACvB,CAAA,OAAA,EAAU,EAAE,CAAC,IAAI,CAAA,EAAA,EAAK,EAAE,CAAC,IAAI,CAAA,2BAAA,CAA6B,CAC3D;4BACH,QAAQ,CAAC,QAAQ,CAAC,+BAA+B,CAAC,CAAC,WAAW,CAAC,CAAA,IAAA,EAAO,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI,CAAA,UAAA,EAAa,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;wBAC/I;AAAO,6BAAA,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ,EAAE;4BAC/B,IAAI,QAAQ,CAAC,aAAa;gCACxB,yBAAyB,CACvB,CAAA,OAAA,EAAU,EAAE,CAAC,IAAI,CAAA,EAAA,EAAK,EAAE,CAAC,IAAI,CAAA,4BAAA,CAA8B,CAC5D;4BACH,QAAQ,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,WAAW,CAAC,CAAA,IAAA,EAAO,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI,CAAA,UAAA,EAAa,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;wBACtI;6BAAO;4BACL,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC,WAAW,CAAC,CAAA,IAAA,EAAO,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI,CAAA,UAAA,EAAa,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;wBACvI;wBAEA,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC;wBAEtC,OAAO,GAAG,IAAI;oBAChB;yBAAO;;;wBAIL,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI,EAAE;AAC1B,4BAAA,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;gCAC1B,yBAAyB,CAAC,CAAA,mBAAA,EAAsB,KAAK,CAAC,IAAI,CAAA,IAAA,EAAO,EAAE,CAAC,IAAI,CAAA,CAAE,CAAC;4BAC7E,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,iCAAiC,EAAE,CAAC,IAAI,CAAA,IAAA,EAAO,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;AACxJ,4BAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE;gCACtD,IAAI,QAAQ,CAAC,YAAY;AACvB,oCAAA,yBAAyB,CACvB,CAAA,oBAAA,EAAuB,EAAE,CAAC,IAAI,CAAA,qCAAA,CAAuC,CACtE;gCACH,QAAQ,CAAC,QAAQ,CAAC,+BAA+B,CAAC,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,oBAAoB,EAAE,CAAC,IAAI,CAAA,IAAA,EAAO,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;4BACrJ;AACA,4BAAA,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE;gCAC3B,IAAI,QAAQ,CAAC,aAAa;AACxB,oCAAA,yBAAyB,CACvB,CAAA,qBAAA,EAAwB,EAAE,CAAC,IAAI,CAAA,qCAAA,CAAuC,CACvE;gCACH,QAAQ,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,oBAAoB,EAAE,CAAC,IAAI,CAAA,IAAA,EAAO,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;4BAC5I;AACA,4BAAA,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI;4BACpB,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,CAAA,iBAAA,EAAoB,EAAE,CAAC,IAAI,CAAA,IAAA,EAAO,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;4BAC7G,OAAO,GAAG,IAAI;wBAChB;;wBAGA,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,EAAE;AAC5B,4BAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,gCAAA,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AACrE,4BAAA,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK;AACtB,4BAAA,IAAI,mBAAmB,CAAC,EAAE,CAAC,KAAK,CAAC;AAAE,gCAAA,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;AAC/D,4BAAA,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,CAAA,kBAAA,EAAqB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;4BAChG,OAAO,GAAG,IAAI;wBAChB;;wBAGA,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,EAAE;AAC5B,4BAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,gCAAA,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AACrE,4BAAA,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK;AACtB,4BAAA,IAAI,mBAAmB,CAAC,EAAE,CAAC,KAAK,CAAC;AAAE,gCAAA,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;AAC/D,4BAAA,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,CAAA,kBAAA,EAAqB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;4BAChG,OAAO,GAAG,IAAI;wBAChB;;wBAGA,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,EAAE;AAC9B,4BAAA,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,MAAM;4BACxB,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,CAAA,mBAAA,EAAsB,EAAE,CAAC,MAAM,CAAA,IAAA,EAAO,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;4BACjH,OAAO,GAAG,IAAI;wBAChB;;wBAGA,IAAI,KAAK,CAAC,YAAY,KAAK,EAAE,CAAC,YAAY,EAAE;AAC1C,4BAAA,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY;4BACpC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,EAAE,CAAC,IAAI,CAAA,yBAAA,EAA4B,EAAE,CAAC,YAAY,CAAA,IAAA,EAAO,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;4BAC7H,OAAO,GAAG,IAAI;wBAChB;AAEA,wBAAA,KAAK,CAAC,eAAe,GAAG,QAAQ,CAAC,OAAO;oBAC1C;gBACF;;AAGA,gBAAA,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,KAAI;oBACtD,IAAI,KAAK,CAAC,eAAe,KAAK,QAAS,CAAC,OAAO,EAAE;AAC/C,wBAAA,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;4BAC/E,yBAAyB,CAAC,cAAc,KAAK,CAAC,IAAI,CAAA,OAAA,EAAU,SAAS,CAAA,CAAE,CAAC;AAC1E,wBAAA,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAiB,SAAS,CAAA,cAAA,EAAiB,kBAAkB,CAAC,QAAS,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;AACvG,wBAAA,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;AAExB,wBAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,4BAAA,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AACrE,wBAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,4BAAA,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AAErE,wBAAA,QAAQ,CAAC,QAAS,CAAC,uBAAuB,CAAC,CAAC,WAAW,CAAC,CAAA,cAAA,EAAiB,SAAS,CAAA,cAAA,EAAiB,kBAAkB,CAAC,QAAS,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;oBACxI;AACF,gBAAA,CAAC,CAAC;;gBAGF,IAAI,QAAQ,CAAC,YAAY,KAAK,EAAE,CAAC,YAAY,EAAE;oBAC7C,IAAI,QAAQ,CAAC,YAAY;wBAAE,yBAAyB,CAAC,mCAAmC,CAAC;AACzF,oBAAA,QAAQ,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY;AACvC,oBAAA,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,WAAW,CAAC,CAAA,kBAAA,EAAqB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;oBACnG,OAAO,GAAG,IAAI;gBAChB;;gBAGA,IAAI,QAAQ,CAAC,aAAa,KAAK,EAAE,CAAC,aAAa,EAAE;oBAC/C,IAAI,QAAQ,CAAC,aAAa;wBACxB,yBAAyB,CAAC,oCAAoC,CAAC;AACjE,oBAAA,QAAQ,CAAC,aAAa,GAAG,EAAE,CAAC,aAAa;AACzC,oBAAA,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,WAAW,CAAC,CAAA,mBAAA,EAAsB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;oBACpG,OAAO,GAAG,IAAI;gBAChB;;gBAGA,IAAI,QAAQ,CAAC,aAAa,KAAK,EAAE,CAAC,aAAa,EAAE;AAC/C,oBAAA,MAAM,gBAAgB,GAAG,QAAQ,CAAC,aAAa;AAC/C,oBAAA,QAAQ,CAAC,aAAa,GAAG,EAAE,CAAC,aAAa;oBACzC,QAAQ,CAAC,gBAAgB,EAAE;oBAC3B,IAAI,CAAC,QAAQ,CAAC,cAAc;AAC1B,wBAAA,yBAAyB,CACvB,CAAA,kEAAA,EAAqE,gBAAgB,CAAA,CAAA,CAAG,CACzF;oBACH,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,WAAW,CAAC,CAAA,sBAAA,EAAyB,EAAE,CAAC,aAAa,CAAA,KAAA,EAAQ,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;oBACtI,OAAO,GAAG,IAAI;gBAChB;;AAGA,gBAAA,KAAK,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE;AACtB,oBAAA,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC;AACvC,oBAAA,IAAI,OAAO,KAAK,SAAS,EAAE;AACzB,wBAAA,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC;wBACjC,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,EAAA,EAAK,EAAE,CAAC,GAAG,CAAA,CAAE,CAAC;oBACtH;AAAO,yBAAA,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;AAClD,wBAAA,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC;wBACjC,QAAQ,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,EAAA,EAAK,EAAE,CAAC,GAAG,CAAA,CAAE,CAAC;oBACxH;gBACF;AAEA,gBAAA,IAAI,QAAQ,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE;;oBAEnC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;;AAGvD,oBAAA,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,KAAI;wBACvC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AAC1B,4BAAA,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;4BACf,QAAQ,CAAC,QAAS,CAAC,eAAe,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,kBAAkB,kBAAkB,CAAC,QAAS,CAAC,EAAE,CAAC,CAAA,EAAA,EAAK,GAAG,CAAA,CAAE,CAAC;wBACpH;AACF,oBAAA,CAAC,CAAC;gBACJ;gBAEA,IAAI,OAAO,EAAE;;AAEX,oBAAA,QAAQ,CAAC,WAAW,GAAG,QAAQ,CAAC,OAAO;AACvC,oBAAA,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC;wBAAE,QAAQ,CAAC,SAAS,EAAE;gBAC3D;YACF;iBAAO;;AAGL,gBAAA,QAAQ,GAAG,IAAI,cAAc,CAAC,EAAE,CAAC;gBACjC,QAAQ,CAAC,gBAAgB,EAAE;AAC3B,gBAAA,IAAI,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC;AAAE,oBAAA,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;AAC3E,gBAAA,KAAK,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE;AAC1B,oBAAA,MAAM,KAAK,GAAG,IAAI,WAAW,CAC3B,EAAE,CAAC,IAAI,EACP,EAAE,CAAC,IAAI,EACP,EAAE,CAAC,KAAK,EACR,EAAE,CAAC,KAAK,EACR,EAAE,CAAC,MAAM,EACT,EAAE,CAAC,YAAY,EACf,sBAAsB,CACvB;AACD,oBAAA,IAAI,mBAAmB,CAAC,EAAE,CAAC,KAAK,CAAC;AAAE,wBAAA,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;AAC/D,oBAAA,IAAI,mBAAmB,CAAC,EAAE,CAAC,KAAK,CAAC;AAAE,wBAAA,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC;oBAC/D,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC;gBACxC;;AAGA,gBAAA,KAAK,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE;AAAE,oBAAA,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC;;AAGzD,gBAAA,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC;oBAAE,QAAQ,CAAC,SAAS,EAAE;;gBAGzD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;AACzC,gBAAA,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA,aAAA,EAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA,MAAA,CAAQ,CAAC;YAC1F;QACF;;AAGA,QAAA,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE;YAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;YACnC,IAAI,CAAC,GAAG,EAAE;gBACR,IAAI,CAAC,cAAc,EAAE;AACrB,gBAAA,MAAM,IAAI,oBAAoB,CAAC,mBAAmB,GAAG,CAAA,CAAE,CAAC;YAC1D;YACA,GAAG,CAAC,QAAQ,EAAE;QAChB;;QAGA,IAAI,WAAW,GAAG,aAAa;AAC/B,QAAA,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE;YAC7B,MAAM,QAAQ,GAAiB,EAAE;AACjC,YAAA,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE;gBAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBACnC,IAAI,CAAC,GAAG,EAAE;oBACR,IAAI,CAAC,cAAc,EAAE;AACrB,oBAAA,MAAM,IAAI,oBAAoB,CAAC,mBAAmB,GAAG,CAAA,CAAE,CAAC;gBAC1D;gBACA,GAAG,CAAC,QAAQ,EAAE;;AAGd,gBAAA,IAAI,GAAG,CAAC,QAAQ,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,IAAI,EAAE;;oBAE9C,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,KAAI;AAC9B,wBAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,4BAAA,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AAChE,wBAAA,IAAI,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC;AAAE,4BAAA,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;AAChE,wBAAA,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,MAAA,EAAS,KAAK,CAAC,IAAI,CAAA,sCAAA,EAAyC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;AACpH,oBAAA,CAAC,CAAC;AACF,oBAAA,IAAI,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC;AAAE,wBAAA,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;AAC5D,oBAAA,GAAG,CAAC,eAAe,CAAC,WAAW,CAAC,CAAA,4CAAA,EAA+C,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC;AAC5G,oBAAA,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC;gBAC5B;YACF;YACA,WAAW,GAAG,QAAQ;QACxB;;QAGA,IAAI,CAAC,iBAAiB,EAAE;AACtB,YAAA,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE;AAC7B,gBAAA,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;oBAC9B,IAAI,CAAC,cAAc,EAAE;oBACrB,MAAM,IAAI,oBAAoB,CAAC,CAAA,sBAAA,EAAyB,EAAE,CAAC,EAAE,CAAA,CAAE,CAAC;gBAClE;YACF;QACF;IACF;;AAGO,IAAA,QAAQ,CAAC,GAAA,GAAkB,IAAI,CAAC,IAAI,EAAA;QACzC,IAAI,CAAC,UAAU,EAAE;AACjB,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IACxB;AAEO,IAAA,KAAK,CAAC,GAAA,GAAkB,IAAI,CAAC,IAAI,EAAA;QACtC,IAAI,CAAC,UAAU,EAAE;AACjB,QAAA,OAAO,IAAI,WAAW,CAAC,EAAE,YAAY,EAAE,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC;IAC3D;AAEO,IAAA,cAAc,CAAC,GAAY,EAAA;AAChC,QAAA,IAAI,CAAC,QAAQ,GAAG,KAAK;AACrB,QAAA,IAAI,CAAC,mBAAmB,GAAG,GAAG;QAC9B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,GAAG,KAAI;YAC7B,GAAG,CAAC,cAAc,EAAE;AACtB,QAAA,CAAC,CAAC;IACJ;IAEO,SAAS,GAAA;QACd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,aAAa,CAAC;IAC5E;AACD;;;;"}
package/dist/sync.cjs ADDED
@@ -0,0 +1,156 @@
1
+ 'use strict';
2
+
3
+ var plClient = require('@milaboratories/pl-client');
4
+ var Denque = require('denque');
5
+ var tsHelpers = require('@milaboratories/ts-helpers');
6
+
7
+ /** Given the current tree state, build the request object to pass to
8
+ * {@link loadTreeState} to load updated state. */
9
+ function constructTreeLoadingRequest(tree, pruningFunction) {
10
+ const seedResources = [];
11
+ const finalResources = new Set();
12
+ tree.forEachResource((res) => {
13
+ if (res.finalState)
14
+ finalResources.add(res.id);
15
+ else
16
+ seedResources.push(res.id);
17
+ });
18
+ // if tree is empty, seeding tree reconstruction from the specified root
19
+ if (seedResources.length === 0 && finalResources.size === 0)
20
+ seedResources.push(tree.root);
21
+ return { seedResources, finalResources, pruningFunction };
22
+ }
23
+ function initialTreeLoadingStat() {
24
+ return {
25
+ requests: 0,
26
+ roundTrips: 0,
27
+ retrievedResources: 0,
28
+ retrievedFields: 0,
29
+ retrievedKeyValues: 0,
30
+ retrievedResourceDataBytes: 0,
31
+ retrievedKeyValueBytes: 0,
32
+ prunedFields: 0,
33
+ finalResourcesSkipped: 0,
34
+ millisSpent: 0,
35
+ };
36
+ }
37
+ function formatTreeLoadingStat(stat) {
38
+ let result = `Requests: ${stat.requests}\n`;
39
+ result += `Total time: ${tsHelpers.msToHumanReadable(stat.millisSpent)}\n`;
40
+ result += `Round-trips: ${stat.roundTrips}\n`;
41
+ result += `Resources: ${stat.retrievedResources}\n`;
42
+ result += `Fields: ${stat.retrievedFields}\n`;
43
+ result += `KV: ${stat.retrievedKeyValues}\n`;
44
+ result += `Data Bytes: ${stat.retrievedResourceDataBytes}\n`;
45
+ result += `KV Bytes: ${stat.retrievedKeyValueBytes}\n`;
46
+ result += `Pruned fields: ${stat.prunedFields}\n`;
47
+ result += `Final resources skipped: ${stat.finalResourcesSkipped}`;
48
+ return result;
49
+ }
50
+ /** Given the transaction (preferably read-only) and loading request, executes
51
+ * the tree traversal algorithm, and collects fresh states of resources
52
+ * to update the tree state. */
53
+ async function loadTreeState(tx, loadingRequest, stats) {
54
+ // saving start timestamp to add time spent in this function to the stats at the end of the method
55
+ const startTimestamp = Date.now();
56
+ // counting the request
57
+ if (stats)
58
+ stats.requests++;
59
+ const { seedResources, finalResources, pruningFunction } = loadingRequest;
60
+ // Main idea of using a queue here is that responses will arrive in the same order as they were
61
+ // sent, so we can only wait for the earliest sent unprocessed response promise at any given moment.
62
+ // In such a way logic become linear without recursion, and at the same time deal with data
63
+ // as soon as it arrives.
64
+ const pending = new Denque();
65
+ // vars to calculate number of roundtrips for stats
66
+ let roundTripToggle = true;
67
+ let numberOfRoundTrips = 0;
68
+ // tracking resources we already requested
69
+ const requested = new Set();
70
+ const requestState = (rid) => {
71
+ if (plClient.isNullResourceId(rid) || requested.has(rid))
72
+ return;
73
+ // separate check to collect stats
74
+ if (finalResources.has(rid)) {
75
+ if (stats)
76
+ stats.finalResourcesSkipped++;
77
+ return;
78
+ }
79
+ // adding the id, so we will not request it's state again if somebody else
80
+ // references the same resource
81
+ requested.add(rid);
82
+ // requesting resource and all kv records
83
+ const resourceData = tx.getResourceDataIfExists(rid, true);
84
+ const kvData = tx.listKeyValuesIfResourceExists(rid);
85
+ // counting round-trip (begin)
86
+ const addRT = roundTripToggle;
87
+ if (roundTripToggle)
88
+ roundTripToggle = false;
89
+ // pushing combined promise
90
+ pending.push((async () => {
91
+ const [resource, kv] = await Promise.all([resourceData, kvData]);
92
+ // counting round-trip, actually incrementing counter and returning toggle back, so the next request can acquire it
93
+ if (addRT) {
94
+ numberOfRoundTrips++;
95
+ roundTripToggle = true;
96
+ }
97
+ if (resource === undefined)
98
+ return undefined;
99
+ if (kv === undefined)
100
+ throw new Error('Inconsistent replies');
101
+ return { ...resource, kv };
102
+ })());
103
+ };
104
+ // sending seed requests
105
+ seedResources.forEach((rid) => requestState(rid));
106
+ const result = [];
107
+ while (true) {
108
+ // taking next pending request
109
+ const nextResourcePromise = pending.shift();
110
+ if (nextResourcePromise === undefined)
111
+ // this means we have no pending requests and traversal is over
112
+ break;
113
+ // at this point we pause and wait for the nest requested resource state to arrive
114
+ let nextResource = await nextResourcePromise;
115
+ if (nextResource === undefined)
116
+ // ignoring resources that were not found (this may happen for seed resource ids)
117
+ continue;
118
+ if (pruningFunction !== undefined) {
119
+ // apply field pruning, if requested
120
+ const fieldsAfterPruning = pruningFunction(nextResource);
121
+ // collecting stats
122
+ if (stats)
123
+ stats.prunedFields += nextResource.fields.length - fieldsAfterPruning.length;
124
+ nextResource = { ...nextResource, fields: fieldsAfterPruning };
125
+ }
126
+ // continue traversal over the referenced resource
127
+ requestState(nextResource.error);
128
+ for (const field of nextResource.fields) {
129
+ requestState(field.value);
130
+ requestState(field.error);
131
+ }
132
+ // collecting stats
133
+ if (stats) {
134
+ stats.retrievedResources++;
135
+ stats.retrievedFields += nextResource.fields.length;
136
+ stats.retrievedKeyValues += nextResource.kv.length;
137
+ stats.retrievedResourceDataBytes += nextResource.data?.length ?? 0;
138
+ for (const kv of nextResource.kv)
139
+ stats.retrievedKeyValueBytes += kv.value.length;
140
+ }
141
+ // aggregating the state
142
+ result.push(nextResource);
143
+ }
144
+ // adding the time we spent in this method to stats
145
+ if (stats) {
146
+ stats.millisSpent += Date.now() - startTimestamp;
147
+ stats.roundTrips += numberOfRoundTrips;
148
+ }
149
+ return result;
150
+ }
151
+
152
+ exports.constructTreeLoadingRequest = constructTreeLoadingRequest;
153
+ exports.formatTreeLoadingStat = formatTreeLoadingStat;
154
+ exports.initialTreeLoadingStat = initialTreeLoadingStat;
155
+ exports.loadTreeState = loadTreeState;
156
+ //# sourceMappingURL=sync.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.cjs","sources":["../src/sync.ts"],"sourcesContent":["import type {\n FieldData,\n OptionalResourceId,\n PlTransaction,\n ResourceId,\n} from '@milaboratories/pl-client';\nimport {\n isNullResourceId,\n} from '@milaboratories/pl-client';\nimport Denque from 'denque';\nimport type { ExtendedResourceData, PlTreeState } from './state';\nimport { msToHumanReadable } from '@milaboratories/ts-helpers';\n\n/** Applied to list of fields in resource data. */\nexport type PruningFunction = (resource: ExtendedResourceData) => FieldData[];\n\nexport interface TreeLoadingRequest {\n /** Resource to prime the traversal algorithm. It is ok, if some of them\n * doesn't exist anymore. Should not contain elements from final resource\n * set. */\n readonly seedResources: ResourceId[];\n\n /** Resource ids for which state is already known and not expected to change.\n * Algorithm will not continue traversal over those ids, and states will not\n * be retrieved for them. */\n readonly finalResources: Set<ResourceId>;\n\n /** This function is applied to each resource data field list, before\n * using it continue traversal. This modification also is applied to\n * output data to make result self-consistent in terms that it will contain\n * all referenced resources, this is required to be able to pass it to tree\n * to update the state. */\n readonly pruningFunction?: PruningFunction;\n}\n\n/** Given the current tree state, build the request object to pass to\n * {@link loadTreeState} to load updated state. */\nexport function constructTreeLoadingRequest(\n tree: PlTreeState,\n pruningFunction?: PruningFunction,\n): TreeLoadingRequest {\n const seedResources: ResourceId[] = [];\n const finalResources = new Set<ResourceId>();\n tree.forEachResource((res) => {\n if (res.finalState) finalResources.add(res.id);\n else seedResources.push(res.id);\n });\n\n // if tree is empty, seeding tree reconstruction from the specified root\n if (seedResources.length === 0 && finalResources.size === 0) seedResources.push(tree.root);\n\n return { seedResources, finalResources, pruningFunction };\n}\n\nexport type TreeLoadingStat = {\n requests: number;\n roundTrips: number;\n retrievedResources: number;\n retrievedFields: number;\n retrievedKeyValues: number;\n retrievedResourceDataBytes: number;\n retrievedKeyValueBytes: number;\n prunedFields: number;\n finalResourcesSkipped: number;\n millisSpent: number;\n};\n\nexport function initialTreeLoadingStat(): TreeLoadingStat {\n return {\n requests: 0,\n roundTrips: 0,\n retrievedResources: 0,\n retrievedFields: 0,\n retrievedKeyValues: 0,\n retrievedResourceDataBytes: 0,\n retrievedKeyValueBytes: 0,\n prunedFields: 0,\n finalResourcesSkipped: 0,\n millisSpent: 0,\n };\n}\n\nexport function formatTreeLoadingStat(stat: TreeLoadingStat): string {\n let result = `Requests: ${stat.requests}\\n`;\n result += `Total time: ${msToHumanReadable(stat.millisSpent)}\\n`;\n result += `Round-trips: ${stat.roundTrips}\\n`;\n result += `Resources: ${stat.retrievedResources}\\n`;\n result += `Fields: ${stat.retrievedFields}\\n`;\n result += `KV: ${stat.retrievedKeyValues}\\n`;\n result += `Data Bytes: ${stat.retrievedResourceDataBytes}\\n`;\n result += `KV Bytes: ${stat.retrievedKeyValueBytes}\\n`;\n result += `Pruned fields: ${stat.prunedFields}\\n`;\n result += `Final resources skipped: ${stat.finalResourcesSkipped}`;\n return result;\n}\n\n/** Given the transaction (preferably read-only) and loading request, executes\n * the tree traversal algorithm, and collects fresh states of resources\n * to update the tree state. */\nexport async function loadTreeState(\n tx: PlTransaction,\n loadingRequest: TreeLoadingRequest,\n stats?: TreeLoadingStat,\n): Promise<ExtendedResourceData[]> {\n // saving start timestamp to add time spent in this function to the stats at the end of the method\n const startTimestamp = Date.now();\n\n // counting the request\n if (stats) stats.requests++;\n\n const { seedResources, finalResources, pruningFunction } = loadingRequest;\n\n // Main idea of using a queue here is that responses will arrive in the same order as they were\n // sent, so we can only wait for the earliest sent unprocessed response promise at any given moment.\n // In such a way logic become linear without recursion, and at the same time deal with data\n // as soon as it arrives.\n\n const pending = new Denque<Promise<ExtendedResourceData | undefined>>();\n\n // vars to calculate number of roundtrips for stats\n let roundTripToggle: boolean = true;\n let numberOfRoundTrips = 0;\n\n // tracking resources we already requested\n const requested = new Set<ResourceId>();\n const requestState = (rid: OptionalResourceId) => {\n if (isNullResourceId(rid) || requested.has(rid)) return;\n\n // separate check to collect stats\n if (finalResources.has(rid)) {\n if (stats) stats.finalResourcesSkipped++;\n return;\n }\n\n // adding the id, so we will not request it's state again if somebody else\n // references the same resource\n requested.add(rid);\n\n // requesting resource and all kv records\n const resourceData = tx.getResourceDataIfExists(rid, true);\n const kvData = tx.listKeyValuesIfResourceExists(rid);\n\n // counting round-trip (begin)\n const addRT = roundTripToggle;\n if (roundTripToggle) roundTripToggle = false;\n\n // pushing combined promise\n pending.push(\n (async () => {\n const [resource, kv] = await Promise.all([resourceData, kvData]);\n\n // counting round-trip, actually incrementing counter and returning toggle back, so the next request can acquire it\n if (addRT) {\n numberOfRoundTrips++;\n roundTripToggle = true;\n }\n\n if (resource === undefined) return undefined;\n\n if (kv === undefined) throw new Error('Inconsistent replies');\n\n return { ...resource, kv };\n })(),\n );\n };\n\n // sending seed requests\n seedResources.forEach((rid) => requestState(rid));\n\n const result: ExtendedResourceData[] = [];\n while (true) {\n // taking next pending request\n const nextResourcePromise = pending.shift();\n if (nextResourcePromise === undefined)\n // this means we have no pending requests and traversal is over\n break;\n\n // at this point we pause and wait for the nest requested resource state to arrive\n let nextResource = await nextResourcePromise;\n if (nextResource === undefined)\n // ignoring resources that were not found (this may happen for seed resource ids)\n continue;\n\n if (pruningFunction !== undefined) {\n // apply field pruning, if requested\n const fieldsAfterPruning = pruningFunction(nextResource);\n // collecting stats\n if (stats) stats.prunedFields += nextResource.fields.length - fieldsAfterPruning.length;\n nextResource = { ...nextResource, fields: fieldsAfterPruning };\n }\n\n // continue traversal over the referenced resource\n requestState(nextResource.error);\n for (const field of nextResource.fields) {\n requestState(field.value);\n requestState(field.error);\n }\n\n // collecting stats\n if (stats) {\n stats.retrievedResources++;\n stats.retrievedFields += nextResource.fields.length;\n stats.retrievedKeyValues += nextResource.kv.length;\n stats.retrievedResourceDataBytes += nextResource.data?.length ?? 0;\n for (const kv of nextResource.kv) stats.retrievedKeyValueBytes += kv.value.length;\n }\n\n // aggregating the state\n result.push(nextResource);\n }\n\n // adding the time we spent in this method to stats\n if (stats) {\n stats.millisSpent += Date.now() - startTimestamp;\n stats.roundTrips += numberOfRoundTrips;\n }\n\n return result;\n}\n"],"names":["msToHumanReadable","isNullResourceId"],"mappings":"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"}
package/dist/sync.d.ts CHANGED
@@ -39,4 +39,3 @@ export declare function formatTreeLoadingStat(stat: TreeLoadingStat): string;
39
39
  * the tree traversal algorithm, and collects fresh states of resources
40
40
  * to update the tree state. */
41
41
  export declare function loadTreeState(tx: PlTransaction, loadingRequest: TreeLoadingRequest, stats?: TreeLoadingStat): Promise<ExtendedResourceData[]>;
42
- //# sourceMappingURL=sync.d.ts.map
package/dist/sync.js ADDED
@@ -0,0 +1,151 @@
1
+ import { isNullResourceId } from '@milaboratories/pl-client';
2
+ import Denque from 'denque';
3
+ import { msToHumanReadable } from '@milaboratories/ts-helpers';
4
+
5
+ /** Given the current tree state, build the request object to pass to
6
+ * {@link loadTreeState} to load updated state. */
7
+ function constructTreeLoadingRequest(tree, pruningFunction) {
8
+ const seedResources = [];
9
+ const finalResources = new Set();
10
+ tree.forEachResource((res) => {
11
+ if (res.finalState)
12
+ finalResources.add(res.id);
13
+ else
14
+ seedResources.push(res.id);
15
+ });
16
+ // if tree is empty, seeding tree reconstruction from the specified root
17
+ if (seedResources.length === 0 && finalResources.size === 0)
18
+ seedResources.push(tree.root);
19
+ return { seedResources, finalResources, pruningFunction };
20
+ }
21
+ function initialTreeLoadingStat() {
22
+ return {
23
+ requests: 0,
24
+ roundTrips: 0,
25
+ retrievedResources: 0,
26
+ retrievedFields: 0,
27
+ retrievedKeyValues: 0,
28
+ retrievedResourceDataBytes: 0,
29
+ retrievedKeyValueBytes: 0,
30
+ prunedFields: 0,
31
+ finalResourcesSkipped: 0,
32
+ millisSpent: 0,
33
+ };
34
+ }
35
+ function formatTreeLoadingStat(stat) {
36
+ let result = `Requests: ${stat.requests}\n`;
37
+ result += `Total time: ${msToHumanReadable(stat.millisSpent)}\n`;
38
+ result += `Round-trips: ${stat.roundTrips}\n`;
39
+ result += `Resources: ${stat.retrievedResources}\n`;
40
+ result += `Fields: ${stat.retrievedFields}\n`;
41
+ result += `KV: ${stat.retrievedKeyValues}\n`;
42
+ result += `Data Bytes: ${stat.retrievedResourceDataBytes}\n`;
43
+ result += `KV Bytes: ${stat.retrievedKeyValueBytes}\n`;
44
+ result += `Pruned fields: ${stat.prunedFields}\n`;
45
+ result += `Final resources skipped: ${stat.finalResourcesSkipped}`;
46
+ return result;
47
+ }
48
+ /** Given the transaction (preferably read-only) and loading request, executes
49
+ * the tree traversal algorithm, and collects fresh states of resources
50
+ * to update the tree state. */
51
+ async function loadTreeState(tx, loadingRequest, stats) {
52
+ // saving start timestamp to add time spent in this function to the stats at the end of the method
53
+ const startTimestamp = Date.now();
54
+ // counting the request
55
+ if (stats)
56
+ stats.requests++;
57
+ const { seedResources, finalResources, pruningFunction } = loadingRequest;
58
+ // Main idea of using a queue here is that responses will arrive in the same order as they were
59
+ // sent, so we can only wait for the earliest sent unprocessed response promise at any given moment.
60
+ // In such a way logic become linear without recursion, and at the same time deal with data
61
+ // as soon as it arrives.
62
+ const pending = new Denque();
63
+ // vars to calculate number of roundtrips for stats
64
+ let roundTripToggle = true;
65
+ let numberOfRoundTrips = 0;
66
+ // tracking resources we already requested
67
+ const requested = new Set();
68
+ const requestState = (rid) => {
69
+ if (isNullResourceId(rid) || requested.has(rid))
70
+ return;
71
+ // separate check to collect stats
72
+ if (finalResources.has(rid)) {
73
+ if (stats)
74
+ stats.finalResourcesSkipped++;
75
+ return;
76
+ }
77
+ // adding the id, so we will not request it's state again if somebody else
78
+ // references the same resource
79
+ requested.add(rid);
80
+ // requesting resource and all kv records
81
+ const resourceData = tx.getResourceDataIfExists(rid, true);
82
+ const kvData = tx.listKeyValuesIfResourceExists(rid);
83
+ // counting round-trip (begin)
84
+ const addRT = roundTripToggle;
85
+ if (roundTripToggle)
86
+ roundTripToggle = false;
87
+ // pushing combined promise
88
+ pending.push((async () => {
89
+ const [resource, kv] = await Promise.all([resourceData, kvData]);
90
+ // counting round-trip, actually incrementing counter and returning toggle back, so the next request can acquire it
91
+ if (addRT) {
92
+ numberOfRoundTrips++;
93
+ roundTripToggle = true;
94
+ }
95
+ if (resource === undefined)
96
+ return undefined;
97
+ if (kv === undefined)
98
+ throw new Error('Inconsistent replies');
99
+ return { ...resource, kv };
100
+ })());
101
+ };
102
+ // sending seed requests
103
+ seedResources.forEach((rid) => requestState(rid));
104
+ const result = [];
105
+ while (true) {
106
+ // taking next pending request
107
+ const nextResourcePromise = pending.shift();
108
+ if (nextResourcePromise === undefined)
109
+ // this means we have no pending requests and traversal is over
110
+ break;
111
+ // at this point we pause and wait for the nest requested resource state to arrive
112
+ let nextResource = await nextResourcePromise;
113
+ if (nextResource === undefined)
114
+ // ignoring resources that were not found (this may happen for seed resource ids)
115
+ continue;
116
+ if (pruningFunction !== undefined) {
117
+ // apply field pruning, if requested
118
+ const fieldsAfterPruning = pruningFunction(nextResource);
119
+ // collecting stats
120
+ if (stats)
121
+ stats.prunedFields += nextResource.fields.length - fieldsAfterPruning.length;
122
+ nextResource = { ...nextResource, fields: fieldsAfterPruning };
123
+ }
124
+ // continue traversal over the referenced resource
125
+ requestState(nextResource.error);
126
+ for (const field of nextResource.fields) {
127
+ requestState(field.value);
128
+ requestState(field.error);
129
+ }
130
+ // collecting stats
131
+ if (stats) {
132
+ stats.retrievedResources++;
133
+ stats.retrievedFields += nextResource.fields.length;
134
+ stats.retrievedKeyValues += nextResource.kv.length;
135
+ stats.retrievedResourceDataBytes += nextResource.data?.length ?? 0;
136
+ for (const kv of nextResource.kv)
137
+ stats.retrievedKeyValueBytes += kv.value.length;
138
+ }
139
+ // aggregating the state
140
+ result.push(nextResource);
141
+ }
142
+ // adding the time we spent in this method to stats
143
+ if (stats) {
144
+ stats.millisSpent += Date.now() - startTimestamp;
145
+ stats.roundTrips += numberOfRoundTrips;
146
+ }
147
+ return result;
148
+ }
149
+
150
+ export { constructTreeLoadingRequest, formatTreeLoadingStat, initialTreeLoadingStat, loadTreeState };
151
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sources":["../src/sync.ts"],"sourcesContent":["import type {\n FieldData,\n OptionalResourceId,\n PlTransaction,\n ResourceId,\n} from '@milaboratories/pl-client';\nimport {\n isNullResourceId,\n} from '@milaboratories/pl-client';\nimport Denque from 'denque';\nimport type { ExtendedResourceData, PlTreeState } from './state';\nimport { msToHumanReadable } from '@milaboratories/ts-helpers';\n\n/** Applied to list of fields in resource data. */\nexport type PruningFunction = (resource: ExtendedResourceData) => FieldData[];\n\nexport interface TreeLoadingRequest {\n /** Resource to prime the traversal algorithm. It is ok, if some of them\n * doesn't exist anymore. Should not contain elements from final resource\n * set. */\n readonly seedResources: ResourceId[];\n\n /** Resource ids for which state is already known and not expected to change.\n * Algorithm will not continue traversal over those ids, and states will not\n * be retrieved for them. */\n readonly finalResources: Set<ResourceId>;\n\n /** This function is applied to each resource data field list, before\n * using it continue traversal. This modification also is applied to\n * output data to make result self-consistent in terms that it will contain\n * all referenced resources, this is required to be able to pass it to tree\n * to update the state. */\n readonly pruningFunction?: PruningFunction;\n}\n\n/** Given the current tree state, build the request object to pass to\n * {@link loadTreeState} to load updated state. */\nexport function constructTreeLoadingRequest(\n tree: PlTreeState,\n pruningFunction?: PruningFunction,\n): TreeLoadingRequest {\n const seedResources: ResourceId[] = [];\n const finalResources = new Set<ResourceId>();\n tree.forEachResource((res) => {\n if (res.finalState) finalResources.add(res.id);\n else seedResources.push(res.id);\n });\n\n // if tree is empty, seeding tree reconstruction from the specified root\n if (seedResources.length === 0 && finalResources.size === 0) seedResources.push(tree.root);\n\n return { seedResources, finalResources, pruningFunction };\n}\n\nexport type TreeLoadingStat = {\n requests: number;\n roundTrips: number;\n retrievedResources: number;\n retrievedFields: number;\n retrievedKeyValues: number;\n retrievedResourceDataBytes: number;\n retrievedKeyValueBytes: number;\n prunedFields: number;\n finalResourcesSkipped: number;\n millisSpent: number;\n};\n\nexport function initialTreeLoadingStat(): TreeLoadingStat {\n return {\n requests: 0,\n roundTrips: 0,\n retrievedResources: 0,\n retrievedFields: 0,\n retrievedKeyValues: 0,\n retrievedResourceDataBytes: 0,\n retrievedKeyValueBytes: 0,\n prunedFields: 0,\n finalResourcesSkipped: 0,\n millisSpent: 0,\n };\n}\n\nexport function formatTreeLoadingStat(stat: TreeLoadingStat): string {\n let result = `Requests: ${stat.requests}\\n`;\n result += `Total time: ${msToHumanReadable(stat.millisSpent)}\\n`;\n result += `Round-trips: ${stat.roundTrips}\\n`;\n result += `Resources: ${stat.retrievedResources}\\n`;\n result += `Fields: ${stat.retrievedFields}\\n`;\n result += `KV: ${stat.retrievedKeyValues}\\n`;\n result += `Data Bytes: ${stat.retrievedResourceDataBytes}\\n`;\n result += `KV Bytes: ${stat.retrievedKeyValueBytes}\\n`;\n result += `Pruned fields: ${stat.prunedFields}\\n`;\n result += `Final resources skipped: ${stat.finalResourcesSkipped}`;\n return result;\n}\n\n/** Given the transaction (preferably read-only) and loading request, executes\n * the tree traversal algorithm, and collects fresh states of resources\n * to update the tree state. */\nexport async function loadTreeState(\n tx: PlTransaction,\n loadingRequest: TreeLoadingRequest,\n stats?: TreeLoadingStat,\n): Promise<ExtendedResourceData[]> {\n // saving start timestamp to add time spent in this function to the stats at the end of the method\n const startTimestamp = Date.now();\n\n // counting the request\n if (stats) stats.requests++;\n\n const { seedResources, finalResources, pruningFunction } = loadingRequest;\n\n // Main idea of using a queue here is that responses will arrive in the same order as they were\n // sent, so we can only wait for the earliest sent unprocessed response promise at any given moment.\n // In such a way logic become linear without recursion, and at the same time deal with data\n // as soon as it arrives.\n\n const pending = new Denque<Promise<ExtendedResourceData | undefined>>();\n\n // vars to calculate number of roundtrips for stats\n let roundTripToggle: boolean = true;\n let numberOfRoundTrips = 0;\n\n // tracking resources we already requested\n const requested = new Set<ResourceId>();\n const requestState = (rid: OptionalResourceId) => {\n if (isNullResourceId(rid) || requested.has(rid)) return;\n\n // separate check to collect stats\n if (finalResources.has(rid)) {\n if (stats) stats.finalResourcesSkipped++;\n return;\n }\n\n // adding the id, so we will not request it's state again if somebody else\n // references the same resource\n requested.add(rid);\n\n // requesting resource and all kv records\n const resourceData = tx.getResourceDataIfExists(rid, true);\n const kvData = tx.listKeyValuesIfResourceExists(rid);\n\n // counting round-trip (begin)\n const addRT = roundTripToggle;\n if (roundTripToggle) roundTripToggle = false;\n\n // pushing combined promise\n pending.push(\n (async () => {\n const [resource, kv] = await Promise.all([resourceData, kvData]);\n\n // counting round-trip, actually incrementing counter and returning toggle back, so the next request can acquire it\n if (addRT) {\n numberOfRoundTrips++;\n roundTripToggle = true;\n }\n\n if (resource === undefined) return undefined;\n\n if (kv === undefined) throw new Error('Inconsistent replies');\n\n return { ...resource, kv };\n })(),\n );\n };\n\n // sending seed requests\n seedResources.forEach((rid) => requestState(rid));\n\n const result: ExtendedResourceData[] = [];\n while (true) {\n // taking next pending request\n const nextResourcePromise = pending.shift();\n if (nextResourcePromise === undefined)\n // this means we have no pending requests and traversal is over\n break;\n\n // at this point we pause and wait for the nest requested resource state to arrive\n let nextResource = await nextResourcePromise;\n if (nextResource === undefined)\n // ignoring resources that were not found (this may happen for seed resource ids)\n continue;\n\n if (pruningFunction !== undefined) {\n // apply field pruning, if requested\n const fieldsAfterPruning = pruningFunction(nextResource);\n // collecting stats\n if (stats) stats.prunedFields += nextResource.fields.length - fieldsAfterPruning.length;\n nextResource = { ...nextResource, fields: fieldsAfterPruning };\n }\n\n // continue traversal over the referenced resource\n requestState(nextResource.error);\n for (const field of nextResource.fields) {\n requestState(field.value);\n requestState(field.error);\n }\n\n // collecting stats\n if (stats) {\n stats.retrievedResources++;\n stats.retrievedFields += nextResource.fields.length;\n stats.retrievedKeyValues += nextResource.kv.length;\n stats.retrievedResourceDataBytes += nextResource.data?.length ?? 0;\n for (const kv of nextResource.kv) stats.retrievedKeyValueBytes += kv.value.length;\n }\n\n // aggregating the state\n result.push(nextResource);\n }\n\n // adding the time we spent in this method to stats\n if (stats) {\n stats.millisSpent += Date.now() - startTimestamp;\n stats.roundTrips += numberOfRoundTrips;\n }\n\n return result;\n}\n"],"names":[],"mappings":"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"}