@platforma-sdk/ui-vue 1.43.29 → 1.44.1
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/.turbo/turbo-build.log +7 -7
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +18 -0
- package/dist/composition/fileContent.d.ts +69 -12
- package/dist/composition/fileContent.js +147 -52
- package/dist/composition/fileContent.js.map +1 -1
- package/dist/lib/util/helpers/dist/utils.js +37 -23
- package/dist/lib/util/helpers/dist/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/composition/fileContent.ts +166 -34
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
WARN Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
|
|
2
2
|
|
|
3
|
-
> @platforma-sdk/ui-vue@1.
|
|
3
|
+
> @platforma-sdk/ui-vue@1.44.1 build /home/runner/_work/platforma/platforma/sdk/ui-vue
|
|
4
4
|
> ts-builder build --target browser-lib
|
|
5
5
|
|
|
6
6
|
Building browser-lib project...
|
|
@@ -23,8 +23,8 @@ computing gzip size...
|
|
|
23
23
|
[2mdist/[22m[36mcomponents/PlAgCellFile/PlAgCellFile.vue2.js [39m[1m[2m 0.11 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.10 kB[22m
|
|
24
24
|
[2mdist/[22m[36mcomponents/PlAgDataTable/PlAgRowCount.vue2.js [39m[1m[2m 0.11 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.10 kB[22m
|
|
25
25
|
[2mdist/[22m[36mcomponents/PlAgCsvExporter/PlAgCsvExporter.vue2.js [39m[1m[2m 0.12 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.11 kB[22m
|
|
26
|
-
[2mdist/[22m[36mcomponents/PlAgCellProgress/PlAgCellProgress.vue2.js [39m[1m[2m 0.12 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.11 kB[22m
|
|
27
26
|
[2mdist/[22m[36mcomponents/PlAgColumnHeader/PlAgColumnHeader.vue2.js [39m[1m[2m 0.12 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.11 kB[22m
|
|
27
|
+
[2mdist/[22m[36mcomponents/PlAgCellProgress/PlAgCellProgress.vue2.js [39m[1m[2m 0.12 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.11 kB[22m
|
|
28
28
|
[2mdist/[22m[36mcomponents/PlAgRowNumHeader.vue2.js [39m[1m[2m 0.12 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.11 kB[22m
|
|
29
29
|
[2mdist/[22m[36mcomponents/PlAgDataTable/PlAgOverlayLoading.vue2.js [39m[1m[2m 0.12 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.11 kB[22m
|
|
30
30
|
[2mdist/[22m[36mcomponents/PlAgRowNumCheckbox/PlAgRowNumCheckbox.vue2.js [39m[1m[2m 0.12 kB[22m[1m[22m[2m │ gzip: 0.12 kB[22m[2m │ map: 0.11 kB[22m
|
|
@@ -84,8 +84,8 @@ computing gzip size...
|
|
|
84
84
|
[2mdist/[22m[36mcomponents/PlMultiSequenceAlignment/PhylogeneticTree.vue.js [39m[1m[2m 0.30 kB[22m[1m[22m[2m │ gzip: 0.22 kB[22m[2m │ map: 0.11 kB[22m
|
|
85
85
|
[2mdist/[22m[36mnode_modules/.pnpm/semver@7.7.2/node_modules/semver/ranges/to-comparators.js [39m[1m[2m 0.30 kB[22m[1m[22m[2m │ gzip: 0.23 kB[22m[2m │ map: 0.78 kB[22m
|
|
86
86
|
[2mdist/[22m[36mcomponents/PlAnnotations/components/PlAnnotationsModal.vue.js [39m[1m[2m 0.31 kB[22m[1m[22m[2m │ gzip: 0.22 kB[22m[2m │ map: 0.11 kB[22m
|
|
87
|
-
[2mdist/[22m[36mcomponents/PlAgDataTable/PlAgDataTableSheets.vue.js [39m[1m[2m 0.31 kB[22m[1m[22m[2m │ gzip: 0.22 kB[22m[2m │ map: 0.11 kB[22m
|
|
88
87
|
[2mdist/[22m[36mcomponents/PlAnnotations/components/AnnotationsSidebar.vue.js [39m[1m[2m 0.31 kB[22m[1m[22m[2m │ gzip: 0.22 kB[22m[2m │ map: 0.11 kB[22m
|
|
88
|
+
[2mdist/[22m[36mcomponents/PlAgDataTable/PlAgDataTableSheets.vue.js [39m[1m[2m 0.31 kB[22m[1m[22m[2m │ gzip: 0.22 kB[22m[2m │ map: 0.11 kB[22m
|
|
89
89
|
[2mdist/[22m[36mnode_modules/.pnpm/semver@7.7.2/node_modules/semver/ranges/valid.js [39m[1m[2m 0.31 kB[22m[1m[22m[2m │ gzip: 0.22 kB[22m[2m │ map: 0.76 kB[22m
|
|
90
90
|
[2mdist/[22m[36mcomponents/PlAgGridColumnManager/PlAgGridColumnManager.vue.js [39m[1m[2m 0.32 kB[22m[1m[22m[2m │ gzip: 0.23 kB[22m[2m │ map: 0.12 kB[22m
|
|
91
91
|
[2mdist/[22m[36mnode_modules/.pnpm/semver@7.7.2/node_modules/semver/functions/satisfies.js [39m[1m[2m 0.32 kB[22m[1m[22m[2m │ gzip: 0.23 kB[22m[2m │ map: 0.74 kB[22m
|
|
@@ -132,7 +132,6 @@ computing gzip size...
|
|
|
132
132
|
[2mdist/[22m[36mcomponents/PlAgChartStackedBarCell/PlAgChartStackedBarCell.vue.js [39m[1m[2m 0.77 kB[22m[1m[22m[2m │ gzip: 0.45 kB[22m[2m │ map: 1.25 kB[22m
|
|
133
133
|
[2mdist/[22m[36mplugins/Monetization/EndOfPeriod.vue2.js [39m[1m[2m 0.77 kB[22m[1m[22m[2m │ gzip: 0.47 kB[22m[2m │ map: 1.55 kB[22m
|
|
134
134
|
[2mdist/[22m[36mcomponents/BlockLoader.vue.js [39m[1m[2m 0.82 kB[22m[1m[22m[2m │ gzip: 0.48 kB[22m[2m │ map: 1.12 kB[22m
|
|
135
|
-
[2mdist/[22m[36mlib/util/helpers/dist/utils.js [39m[1m[2m 0.83 kB[22m[1m[22m[2m │ gzip: 0.42 kB[22m[2m │ map: 6.86 kB[22m
|
|
136
135
|
[2mdist/[22m[36mnode_modules/.pnpm/semver@7.7.2/node_modules/semver/ranges/simplify.js [39m[1m[2m 0.83 kB[22m[1m[22m[2m │ gzip: 0.44 kB[22m[2m │ map: 2.64 kB[22m
|
|
137
136
|
[2mdist/[22m[36mcomponents/PlMultiSequenceAlignment/Legend.vue2.js [39m[1m[2m 0.85 kB[22m[1m[22m[2m │ gzip: 0.49 kB[22m[2m │ map: 1.09 kB[22m
|
|
138
137
|
[2mdist/[22m[36mcomponents/NotFound.vue.js [39m[1m[2m 0.89 kB[22m[1m[22m[2m │ gzip: 0.53 kB[22m[2m │ map: 0.75 kB[22m
|
|
@@ -143,6 +142,7 @@ computing gzip size...
|
|
|
143
142
|
[2mdist/[22m[36mcomponents/PlMultiSequenceAlignment/Toolbar.vue3.js [39m[1m[2m 1.05 kB[22m[1m[22m[2m │ gzip: 0.53 kB[22m[2m │ map: 0.10 kB[22m
|
|
144
143
|
[2mdist/[22m[36mcomponents/PlAgCellFile/PlAgCellFile.vue.js [39m[1m[2m 1.09 kB[22m[1m[22m[2m │ gzip: 0.59 kB[22m[2m │ map: 2.86 kB[22m
|
|
145
144
|
[2mdist/[22m[36mnode_modules/.pnpm/semver@7.7.2/node_modules/semver/functions/cmp.js [39m[1m[2m 1.11 kB[22m[1m[22m[2m │ gzip: 0.40 kB[22m[2m │ map: 2.14 kB[22m
|
|
145
|
+
[2mdist/[22m[36mlib/util/helpers/dist/utils.js [39m[1m[2m 1.13 kB[22m[1m[22m[2m │ gzip: 0.53 kB[22m[2m │ map: 7.55 kB[22m
|
|
146
146
|
[2mdist/[22m[36mcomponents/PlAgGridColumnManager/useFilteredItems.js [39m[1m[2m 1.13 kB[22m[1m[22m[2m │ gzip: 0.58 kB[22m[2m │ map: 3.95 kB[22m
|
|
147
147
|
[2mdist/[22m[36mcomponents/PlAgCsvExporter/PlAgCsvExporter.vue.js [39m[1m[2m 1.14 kB[22m[1m[22m[2m │ gzip: 0.61 kB[22m[2m │ map: 1.39 kB[22m
|
|
148
148
|
[2mdist/[22m[36mcomponents/PlAgDataTable/sources/value-rendering.js [39m[1m[2m 1.14 kB[22m[1m[22m[2m │ gzip: 0.54 kB[22m[2m │ map: 3.41 kB[22m
|
|
@@ -179,7 +179,6 @@ computing gzip size...
|
|
|
179
179
|
[2mdist/[22m[36mnode_modules/.pnpm/fast-json-patch@3.1.1/node_modules/fast-json-patch/module/helpers.js [39m[1m[2m 2.79 kB[22m[1m[22m[2m │ gzip: 1.21 kB[22m[2m │ map: 8.92 kB[22m
|
|
180
180
|
[2mdist/[22m[36mcomponents/PlAgDataTable/sources/row-number.js [39m[1m[2m 3.10 kB[22m[1m[22m[2m │ gzip: 1.34 kB[22m[2m │ map: 8.36 kB[22m
|
|
181
181
|
[2mdist/[22m[36mplugins/Monetization/LimitCard.vue3.js [39m[1m[2m 3.10 kB[22m[1m[22m[2m │ gzip: 1.09 kB[22m[2m │ map: 0.11 kB[22m
|
|
182
|
-
[2mdist/[22m[36mcomposition/fileContent.js [39m[1m[2m 3.15 kB[22m[1m[22m[2m │ gzip: 1.17 kB[22m[2m │ map: 9.49 kB[22m
|
|
183
182
|
[2mdist/[22m[36mcomponents/PlMultiSequenceAlignment/SeqLogo.vue2.js [39m[1m[2m 3.21 kB[22m[1m[22m[2m │ gzip: 1.32 kB[22m[2m │ map: 6.15 kB[22m
|
|
184
183
|
[2mdist/[22m[36mcomponents/PlTableFilters/PlTableAddFilterV2.vue.js [39m[1m[2m 3.22 kB[22m[1m[22m[2m │ gzip: 1.28 kB[22m[2m │ map: 3.95 kB[22m
|
|
185
184
|
[2mdist/[22m[36mcomponents/PlAgDataTable/sources/table-state-v2.js [39m[1m[2m 3.24 kB[22m[1m[22m[2m │ gzip: 1.17 kB[22m[2m │ map: 9.94 kB[22m
|
|
@@ -204,6 +203,7 @@ computing gzip size...
|
|
|
204
203
|
[2mdist/[22m[36mcomponents/PlAnnotations/components/FilterSidebar.vue3.js [39m[1m[2m 5.48 kB[22m[1m[22m[2m │ gzip: 1.59 kB[22m[2m │ map: 0.11 kB[22m
|
|
205
204
|
[2mdist/[22m[36mcomponents/PlAnnotations/components/AnnotationsSidebar.vue3.js [39m[1m[2m 5.67 kB[22m[1m[22m[2m │ gzip: 1.67 kB[22m[2m │ map: 0.11 kB[22m
|
|
206
205
|
[2mdist/[22m[36minternal/createAppV1.js [39m[1m[2m 5.81 kB[22m[1m[22m[2m │ gzip: 1.85 kB[22m[2m │ map: 15.33 kB[22m
|
|
206
|
+
[2mdist/[22m[36mcomposition/fileContent.js [39m[1m[2m 5.83 kB[22m[1m[22m[2m │ gzip: 1.96 kB[22m[2m │ map: 14.16 kB[22m
|
|
207
207
|
[2mdist/[22m[36mcomponents/PlTableFilters/PlTableFiltersV2.vue2.js [39m[1m[2m 6.15 kB[22m[1m[22m[2m │ gzip: 2.03 kB[22m[2m │ map: 9.27 kB[22m
|
|
208
208
|
[2mdist/[22m[36mnode_modules/.pnpm/semver@7.7.2/node_modules/semver/classes/semver.js [39m[1m[2m 6.70 kB[22m[1m[22m[2m │ gzip: 1.86 kB[22m[2m │ map: 15.20 kB[22m
|
|
209
209
|
[2mdist/[22m[36minternal/createAppV2.js [39m[1m[2m 6.99 kB[22m[1m[22m[2m │ gzip: 2.44 kB[22m[2m │ map: 19.37 kB[22m
|
|
@@ -223,7 +223,7 @@ computing gzip size...
|
|
|
223
223
|
[2mdist/[22m[36mcomponents/PlAgRowNumHeader.vue.js [39m[1m[2m 44.67 kB[22m[1m[22m[2m │ gzip: 29.30 kB[22m[2m │ map: 3.70 kB[22m
|
|
224
224
|
[2mdist/[22m[36mAgGridVue/useAgGridOptions.js [39m[1m[2m 49.30 kB[22m[1m[22m[2m │ gzip: 30.16 kB[22m[2m │ map: 16.35 kB[22m
|
|
225
225
|
[2mdist/[22m[36mcomponents/PlAgDataTable/PlAgDataTableV2.vue2.js [39m[1m[2m 50.39 kB[22m[1m[22m[2m │ gzip: 31.05 kB[22m[2m │ map: 33.70 kB[22m
|
|
226
|
-
[vite:dts] Declaration files built in
|
|
226
|
+
[vite:dts] Declaration files built in 8941ms.
|
|
227
227
|
|
|
228
|
-
[32m✓ built in
|
|
228
|
+
[32m✓ built in 11.07s[39m
|
|
229
229
|
Build completed successfully
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
WARN Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
|
|
2
2
|
|
|
3
|
-
> @platforma-sdk/ui-vue@1.
|
|
3
|
+
> @platforma-sdk/ui-vue@1.44.1 type-check /home/runner/_work/platforma/platforma/sdk/ui-vue
|
|
4
4
|
> ts-builder types --target browser-lib
|
|
5
5
|
|
|
6
6
|
↳ vue-tsc.js --noEmit --project ./tsconfig.json
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @platforma-sdk/ui-vue
|
|
2
2
|
|
|
3
|
+
## 1.44.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @platforma-sdk/model@1.44.1
|
|
8
|
+
- @milaboratories/uikit@2.4.22
|
|
9
|
+
|
|
10
|
+
## 1.44.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- 261a742: Fixed: [blocks/mixcr-clonotyping] in the end of calculations results table starts to blink
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- @milaboratories/uikit@2.4.21
|
|
19
|
+
- @platforma-sdk/model@1.43.29
|
|
20
|
+
|
|
3
21
|
## 1.43.29
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import { BlobHandleAndSize } from '@platforma-sdk/model';
|
|
2
|
-
import {
|
|
2
|
+
import { LRUCache } from 'lru-cache';
|
|
3
|
+
import { ComputedRef, EffectScope } from 'vue';
|
|
3
4
|
import { ZodSchema } from 'zod';
|
|
5
|
+
import { Fetcher } from '@milaboratories/helpers';
|
|
4
6
|
type FileHandle = BlobHandleAndSize['handle'];
|
|
5
7
|
export type ReactiveFileContentOps = {
|
|
6
8
|
/** Maximum size in bytes of file content to cache */
|
|
7
9
|
cacheSize: number;
|
|
10
|
+
lruCache?: LRUCache<FileHandle, FileContentData>;
|
|
11
|
+
fetcher?: Fetcher<FileHandle, FileContentData>;
|
|
8
12
|
};
|
|
13
|
+
declare class FileContentData {
|
|
14
|
+
readonly bytes: Uint8Array;
|
|
15
|
+
private _str;
|
|
16
|
+
private _rawJson;
|
|
17
|
+
private _zodSchema;
|
|
18
|
+
private _validatedJson;
|
|
19
|
+
constructor(bytes: Uint8Array);
|
|
20
|
+
get str(): string;
|
|
21
|
+
get rawJson(): unknown;
|
|
22
|
+
validatedJson<T>(schema: ZodSchema<T>): T | undefined;
|
|
23
|
+
}
|
|
9
24
|
export declare class ReactiveFileContent {
|
|
25
|
+
private currentScope;
|
|
10
26
|
private readonly fileDataCache;
|
|
11
|
-
private readonly
|
|
12
|
-
|
|
27
|
+
private readonly fetcher;
|
|
28
|
+
private ns;
|
|
29
|
+
private currentKey;
|
|
30
|
+
private constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Experimental method to invalidate the refs map cache for a given key.
|
|
33
|
+
*/
|
|
34
|
+
withInvalidate<T>(key: string, cb: () => T): T;
|
|
35
|
+
stopScope(): void;
|
|
36
|
+
private doFetch;
|
|
37
|
+
private getSize;
|
|
38
|
+
private getRefsMap;
|
|
39
|
+
private invalidate;
|
|
40
|
+
private withHandle;
|
|
13
41
|
private getDataRef;
|
|
14
42
|
getContentBytes(handle: FileHandle): ComputedRef<Uint8Array | undefined>;
|
|
15
43
|
getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined;
|
|
@@ -20,14 +48,43 @@ export declare class ReactiveFileContent {
|
|
|
20
48
|
getContentJson<T = unknown>(handle: FileHandle): ComputedRef<T | undefined>;
|
|
21
49
|
getContentJson<T = unknown>(handle: FileHandle | undefined): ComputedRef<T | undefined> | undefined;
|
|
22
50
|
getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined;
|
|
23
|
-
private static
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
private static initScope;
|
|
52
|
+
/**
|
|
53
|
+
* Creates a ReactiveFileContent instance with isolated cache and fetcher.
|
|
54
|
+
* Use this when you need component-specific caching.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* import { ReactiveFileContent } from '@platforma-sdk/ui-vue';
|
|
59
|
+
* import { computed } from 'vue';
|
|
60
|
+
*
|
|
61
|
+
* const fileContent = ReactiveFileContent.use();
|
|
62
|
+
*
|
|
63
|
+
* const processedData = computed(() => {
|
|
64
|
+
* const content = fileContent.getContentString(fileHandle).value;
|
|
65
|
+
* return content?.split('\n').length ?? 0;
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
static use(_ops?: Partial<ReactiveFileContentOps>, _scope?: EffectScope): ReactiveFileContent;
|
|
70
|
+
/**
|
|
71
|
+
* Creates a ReactiveFileContent instance with globally shared cache and fetcher.
|
|
72
|
+
* Use this to share file content cache across multiple components.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { ReactiveFileContent } from '@platforma-sdk/ui-vue';
|
|
77
|
+
* import { computed } from 'vue';
|
|
78
|
+
*
|
|
79
|
+
* const fileContent = ReactiveFileContent.useGlobal();
|
|
80
|
+
*
|
|
81
|
+
* const combinedData = computed(() => {
|
|
82
|
+
* const data1 = fileContent.getContentJson(handle1).value;
|
|
83
|
+
* const data2 = fileContent.getContentJson(handle2).value;
|
|
84
|
+
* return { data1, data2 };
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
static useGlobal(_ops?: Partial<ReactiveFileContentOps>, _scope?: EffectScope): ReactiveFileContent;
|
|
32
89
|
}
|
|
33
90
|
export {};
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
var
|
|
2
|
-
var g = (
|
|
3
|
-
var
|
|
4
|
-
import { getRawPlatformaInstance as
|
|
5
|
-
import { LRUCache as
|
|
6
|
-
import { shallowRef as
|
|
7
|
-
|
|
1
|
+
var v = Object.defineProperty;
|
|
2
|
+
var g = (o, t, e) => t in o ? v(o, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : o[t] = e;
|
|
3
|
+
var r = (o, t, e) => g(o, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { getRawPlatformaInstance as m } from "@platforma-sdk/model";
|
|
5
|
+
import { LRUCache as d } from "lru-cache";
|
|
6
|
+
import { shallowRef as S, computed as f, getCurrentScope as R, effectScope as C, onScopeDispose as y } from "vue";
|
|
7
|
+
import { Fetcher as u } from "../lib/util/helpers/dist/utils.js";
|
|
8
|
+
import "../lib/util/helpers/dist/test_timeouts.js";
|
|
9
|
+
const p = {
|
|
8
10
|
cacheSize: 15e6
|
|
9
11
|
// 15 Mb
|
|
10
12
|
};
|
|
11
|
-
class
|
|
13
|
+
class D {
|
|
12
14
|
constructor(t) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
r(this, "_str");
|
|
16
|
+
r(this, "_rawJson");
|
|
17
|
+
r(this, "_zodSchema");
|
|
18
|
+
r(this, "_validatedJson");
|
|
17
19
|
this.bytes = t;
|
|
18
20
|
}
|
|
19
21
|
get str() {
|
|
@@ -27,40 +29,95 @@ class J {
|
|
|
27
29
|
return this._zodSchema !== t && (this._validatedJson = t.safeParse(this.rawJson), this._zodSchema = t), (e = this._validatedJson) != null && e.success ? this._validatedJson.data : void 0;
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
const c = /* @__PURE__ */ new WeakMap();
|
|
33
|
+
function J(o) {
|
|
34
|
+
c.set(o, /* @__PURE__ */ new Map());
|
|
35
|
+
}
|
|
36
|
+
const _ = new d({
|
|
37
|
+
maxSize: p.cacheSize,
|
|
38
|
+
sizeCalculation: (o) => o.bytes.length
|
|
39
|
+
}), z = new u();
|
|
40
|
+
class l {
|
|
41
|
+
constructor(t, e) {
|
|
42
|
+
r(this, "fileDataCache");
|
|
43
|
+
r(this, "fetcher");
|
|
44
|
+
r(this, "ns", /* @__PURE__ */ new Map());
|
|
45
|
+
r(this, "currentKey");
|
|
46
|
+
this.currentScope = t;
|
|
47
|
+
const s = { ...p, ...e ?? {} };
|
|
48
|
+
this.fileDataCache = s.lruCache ?? new d({
|
|
49
|
+
maxSize: s.cacheSize,
|
|
50
|
+
sizeCalculation: (i) => i.bytes.length
|
|
51
|
+
}), this.fetcher = s.fetcher ?? new u();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Experimental method to invalidate the refs map cache for a given key.
|
|
55
|
+
*/
|
|
56
|
+
withInvalidate(t, e) {
|
|
57
|
+
const s = this.ns.get(t);
|
|
58
|
+
this.ns.set(t, /* @__PURE__ */ new Set()), this.currentKey = t;
|
|
59
|
+
try {
|
|
60
|
+
const i = e();
|
|
61
|
+
return this.invalidate(t, s), i;
|
|
62
|
+
} finally {
|
|
63
|
+
this.currentKey = void 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
stopScope() {
|
|
67
|
+
c.delete(this.currentScope), this.currentScope.stop();
|
|
68
|
+
}
|
|
69
|
+
async doFetch(t) {
|
|
70
|
+
if (!this.fileDataCache.has(t)) {
|
|
71
|
+
const e = await this.fetcher.fetch(t, async () => new D(await m().blobDriver.getContent(t)));
|
|
72
|
+
this.fileDataCache.set(t, e);
|
|
73
|
+
}
|
|
74
|
+
return this.fileDataCache.get(t);
|
|
75
|
+
}
|
|
76
|
+
getSize() {
|
|
77
|
+
const t = this.getRefsMap();
|
|
78
|
+
return t ? t.size : 0;
|
|
79
|
+
}
|
|
80
|
+
getRefsMap() {
|
|
81
|
+
return c.get(this.currentScope);
|
|
82
|
+
}
|
|
83
|
+
invalidate(t, e) {
|
|
84
|
+
if (!e)
|
|
85
|
+
return;
|
|
86
|
+
const s = this.ns.get(t);
|
|
87
|
+
for (const a of s)
|
|
88
|
+
e.delete(a);
|
|
89
|
+
const i = this.getRefsMap();
|
|
90
|
+
for (const a of e)
|
|
91
|
+
i == null || i.delete(a);
|
|
92
|
+
}
|
|
93
|
+
withHandle(t) {
|
|
94
|
+
var e;
|
|
95
|
+
this.currentKey && ((e = this.ns.get(this.currentKey)) == null || e.add(t));
|
|
44
96
|
}
|
|
45
97
|
getDataRef(t) {
|
|
46
|
-
const e = this.
|
|
47
|
-
if (e
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
98
|
+
const e = this.getRefsMap();
|
|
99
|
+
if (!e)
|
|
100
|
+
throw new Error("ReactiveFileContent must be used within a Vue component or effect scope.");
|
|
101
|
+
this.withHandle(t);
|
|
102
|
+
const s = e.get(t);
|
|
103
|
+
if (s !== void 0)
|
|
104
|
+
return s;
|
|
105
|
+
const i = S();
|
|
106
|
+
return e.set(t, i), (async () => {
|
|
107
|
+
for (let n = 0; n < 8; n++)
|
|
51
108
|
try {
|
|
52
|
-
const
|
|
53
|
-
|
|
109
|
+
const h = await this.doFetch(t);
|
|
110
|
+
i.value = h;
|
|
54
111
|
return;
|
|
55
|
-
} catch (
|
|
56
|
-
console.error(`File download attempt ${n + 1}/
|
|
112
|
+
} catch (h) {
|
|
113
|
+
console.error(`File download attempt ${n + 1}/8 failed:`, h), n < 7 ? await new Promise((w) => setTimeout(w, 1e3)) : console.error("Failed to download file after 8 attempts");
|
|
57
114
|
}
|
|
58
|
-
})(),
|
|
115
|
+
})(), i;
|
|
59
116
|
}
|
|
60
117
|
getContentBytes(t) {
|
|
61
118
|
if (t === void 0) return;
|
|
62
119
|
const e = this.getDataRef(t);
|
|
63
|
-
return
|
|
120
|
+
return f(() => {
|
|
64
121
|
var s;
|
|
65
122
|
return (s = e.value) == null ? void 0 : s.bytes;
|
|
66
123
|
});
|
|
@@ -68,32 +125,70 @@ const o = class o {
|
|
|
68
125
|
getContentString(t) {
|
|
69
126
|
if (t === void 0) return;
|
|
70
127
|
const e = this.getDataRef(t);
|
|
71
|
-
return
|
|
128
|
+
return f(() => {
|
|
72
129
|
var s;
|
|
73
|
-
return (s = e
|
|
130
|
+
return (s = e.value) == null ? void 0 : s.str;
|
|
74
131
|
});
|
|
75
132
|
}
|
|
76
133
|
getContentJson(t, e) {
|
|
77
134
|
if (t === void 0) return;
|
|
78
135
|
const s = this.getDataRef(t);
|
|
79
|
-
return
|
|
80
|
-
var
|
|
81
|
-
return e === void 0 ? (
|
|
136
|
+
return f(() => {
|
|
137
|
+
var i, a;
|
|
138
|
+
return e === void 0 ? (i = s.value) == null ? void 0 : i.rawJson : (a = s.value) == null ? void 0 : a.validatedJson(e);
|
|
82
139
|
});
|
|
83
140
|
}
|
|
84
|
-
static
|
|
85
|
-
|
|
141
|
+
static initScope(t) {
|
|
142
|
+
let e = R() ?? t;
|
|
143
|
+
return e || (console.warn("Current scope not found, using new detached scope..."), e = C(!0)), J(e), y(() => {
|
|
144
|
+
c.delete(e);
|
|
145
|
+
}), e;
|
|
86
146
|
}
|
|
87
|
-
|
|
88
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Creates a ReactiveFileContent instance with isolated cache and fetcher.
|
|
149
|
+
* Use this when you need component-specific caching.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { ReactiveFileContent } from '@platforma-sdk/ui-vue';
|
|
154
|
+
* import { computed } from 'vue';
|
|
155
|
+
*
|
|
156
|
+
* const fileContent = ReactiveFileContent.use();
|
|
157
|
+
*
|
|
158
|
+
* const processedData = computed(() => {
|
|
159
|
+
* const content = fileContent.getContentString(fileHandle).value;
|
|
160
|
+
* return content?.split('\n').length ?? 0;
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
static use(t, e) {
|
|
165
|
+
const s = this.initScope(e);
|
|
166
|
+
return new l(s, { ...t });
|
|
89
167
|
}
|
|
90
|
-
|
|
91
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Creates a ReactiveFileContent instance with globally shared cache and fetcher.
|
|
170
|
+
* Use this to share file content cache across multiple components.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* import { ReactiveFileContent } from '@platforma-sdk/ui-vue';
|
|
175
|
+
* import { computed } from 'vue';
|
|
176
|
+
*
|
|
177
|
+
* const fileContent = ReactiveFileContent.useGlobal();
|
|
178
|
+
*
|
|
179
|
+
* const combinedData = computed(() => {
|
|
180
|
+
* const data1 = fileContent.getContentJson(handle1).value;
|
|
181
|
+
* const data2 = fileContent.getContentJson(handle2).value;
|
|
182
|
+
* return { data1, data2 };
|
|
183
|
+
* });
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
static useGlobal(t, e) {
|
|
187
|
+
const s = this.initScope(e);
|
|
188
|
+
return new l(s, { ...t, lruCache: _, fetcher: z });
|
|
92
189
|
}
|
|
93
|
-
}
|
|
94
|
-
a(o, "globalInstance", new o());
|
|
95
|
-
let f = o;
|
|
190
|
+
}
|
|
96
191
|
export {
|
|
97
|
-
|
|
192
|
+
l as ReactiveFileContent
|
|
98
193
|
};
|
|
99
194
|
//# sourceMappingURL=fileContent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileContent.js","sources":["../../src/composition/fileContent.ts"],"sourcesContent":["import type { BlobHandleAndSize } from '@platforma-sdk/model';\nimport { getRawPlatformaInstance } from '@platforma-sdk/model';\nimport { LRUCache } from 'lru-cache';\nimport type { ComputedRef, ShallowRef } from 'vue';\nimport { computed, shallowRef } from 'vue';\nimport type { ZodSchema } from 'zod';\n\ntype FileHandle = BlobHandleAndSize['handle'];\n\nexport type ReactiveFileContentOps = {\n /** Maximum size in bytes of file content to cache */\n cacheSize: number;\n};\n\nconst DefaultReactiveFileContentOps: ReactiveFileContentOps = {\n cacheSize: 15_000_000, // 15 Mb\n};\n\nclass FileContentData {\n private _str: string | undefined = undefined;\n private _rawJson: unknown | undefined = undefined;\n private _zodSchema: ZodSchema | undefined = undefined;\n private _validatedJson: Zod.SafeParseReturnType<unknown, unknown> | undefined = undefined;\n\n constructor(public readonly bytes: Uint8Array) {}\n\n public get str(): string {\n if (this._str === undefined) this._str = new TextDecoder().decode(this.bytes);\n return this._str;\n }\n\n public get rawJson(): unknown {\n if (this._rawJson === undefined) this._rawJson = JSON.parse(this.str);\n return this._rawJson;\n }\n\n public validatedJson<T>(schema: ZodSchema<T>): T | undefined {\n if (this._zodSchema !== schema) {\n this._validatedJson = schema.safeParse(this.rawJson);\n this._zodSchema = schema;\n }\n return this._validatedJson?.success ? (this._validatedJson.data as T) : undefined;\n }\n}\n\nexport class ReactiveFileContent {\n private readonly fileDataCache: LRUCache<FileHandle, FileContentData>;\n private readonly fileDataRefs = new Map<FileHandle, ShallowRef<FileContentData | undefined>>();\n constructor(_ops?: Partial<ReactiveFileContentOps>) {\n const ops: ReactiveFileContentOps = { ...DefaultReactiveFileContentOps, ...(_ops ?? {}) };\n this.fileDataCache = new LRUCache<FileHandle, FileContentData>({\n maxSize: ops.cacheSize,\n fetchMethod: async (key) => new FileContentData(await getRawPlatformaInstance().blobDriver.getContent(key)),\n sizeCalculation: (value) => value.bytes.length,\n /** Will also be called on error fetching the file */\n dispose: (_, key) => {\n this.fileDataRefs.delete(key);\n },\n });\n }\n\n private getDataRef(handle: FileHandle): ShallowRef<FileContentData | undefined> {\n const refFromMap = this.fileDataRefs.get(handle);\n if (refFromMap !== undefined) return refFromMap;\n const newRef = shallowRef<FileContentData>();\n this.fileDataRefs.set(handle, newRef);\n\n // Initiating actual fetch from the cache, that will in turn initiate upload\n (async () => {\n const maxRetries = 4;\n const retryDelay = 1000; // 1 second\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const data = await this.fileDataCache.fetch(handle);\n newRef.value = data;\n return;\n } catch (err: unknown) {\n console.error(`File download attempt ${attempt + 1}/${maxRetries} failed:`, err);\n\n if (attempt < maxRetries - 1) {\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n } else {\n console.error(`Failed to download file after ${maxRetries} attempts`);\n }\n }\n }\n })();\n\n return newRef;\n }\n\n public getContentBytes(handle: FileHandle): ComputedRef<Uint8Array | undefined>;\n public getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined;\n public getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined {\n if (handle === undefined) return undefined;\n const dataRef = this.getDataRef(handle);\n return computed(() => dataRef.value?.bytes);\n }\n\n public getContentString(handle: FileHandle): ComputedRef<string | undefined>;\n public getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined;\n public getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined {\n if (handle === undefined) return undefined;\n const dataRef = this.getDataRef(handle);\n return computed(() => dataRef?.value?.str);\n }\n\n public getContentJson<T>(handle: FileHandle, schema: ZodSchema<T>): ComputedRef<T | undefined>;\n public getContentJson<T>(handle: FileHandle | undefined, schema: ZodSchema<T>): ComputedRef<T | undefined> | undefined;\n public getContentJson<T = unknown>(handle: FileHandle): ComputedRef<T | undefined>;\n public getContentJson<T = unknown>(handle: FileHandle | undefined): ComputedRef<T | undefined> | undefined;\n public getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined;\n public getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined {\n if (handle === undefined) return undefined;\n const dataRef = this.getDataRef(handle);\n return computed(() => (schema === undefined ? dataRef?.value?.rawJson : dataRef?.value?.validatedJson(schema)) as T);\n }\n\n private static globalInstance = new ReactiveFileContent();\n\n public static getContentBytes(handle: FileHandle): ComputedRef<Uint8Array | undefined>;\n public static getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined;\n public static getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined {\n return ReactiveFileContent.globalInstance.getContentBytes(handle);\n }\n\n public static getContentString(handle: FileHandle): ComputedRef<string | undefined>;\n public static getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined;\n public static getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined {\n return ReactiveFileContent.globalInstance.getContentString(handle);\n }\n\n public static getContentJson<T>(handle: FileHandle, schema: ZodSchema<T>): ComputedRef<T | undefined>;\n public static getContentJson<T>(handle: FileHandle | undefined, schema: ZodSchema<T>): ComputedRef<T | undefined> | undefined;\n public static getContentJson<T = unknown>(handle: FileHandle): ComputedRef<T | undefined>;\n public static getContentJson<T = unknown>(handle: FileHandle | undefined): ComputedRef<T | undefined> | undefined;\n public static getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined {\n return ReactiveFileContent.globalInstance.getContentJson(handle, schema);\n }\n}\n"],"names":["DefaultReactiveFileContentOps","FileContentData","bytes","__publicField","schema","_a","_ReactiveFileContent","_ops","ops","LRUCache","key","getRawPlatformaInstance","value","_","handle","refFromMap","newRef","shallowRef","attempt","data","err","resolve","dataRef","computed","_b","ReactiveFileContent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fileContent.js","sources":["../../src/composition/fileContent.ts"],"sourcesContent":["import type { BlobHandleAndSize } from '@platforma-sdk/model';\nimport { getRawPlatformaInstance } from '@platforma-sdk/model';\nimport { LRUCache } from 'lru-cache';\nimport type { ComputedRef, ShallowRef, EffectScope } from 'vue';\nimport { computed, shallowRef, getCurrentScope, onScopeDispose, effectScope } from 'vue';\nimport type { ZodSchema, SafeParseReturnType } from 'zod';\nimport { Fetcher } from '@milaboratories/helpers';\n\ntype FileHandle = BlobHandleAndSize['handle'];\n\nexport type ReactiveFileContentOps = {\n /** Maximum size in bytes of file content to cache */\n cacheSize: number;\n lruCache?: LRUCache<FileHandle, FileContentData>;\n fetcher?: Fetcher<FileHandle, FileContentData>;\n};\n\nconst DefaultReactiveFileContentOps: ReactiveFileContentOps = {\n cacheSize: 15_000_000, // 15 Mb\n};\n\nclass FileContentData {\n private _str: string | undefined = undefined;\n private _rawJson: unknown | undefined = undefined;\n private _zodSchema: ZodSchema | undefined = undefined;\n private _validatedJson: SafeParseReturnType<unknown, unknown> | undefined = undefined;\n\n constructor(public readonly bytes: Uint8Array) {}\n\n public get str(): string {\n if (this._str === undefined) this._str = new TextDecoder().decode(this.bytes);\n return this._str;\n }\n\n public get rawJson(): unknown {\n if (this._rawJson === undefined) this._rawJson = JSON.parse(this.str);\n return this._rawJson;\n }\n\n public validatedJson<T>(schema: ZodSchema<T>): T | undefined {\n if (this._zodSchema !== schema) {\n this._validatedJson = schema.safeParse(this.rawJson);\n this._zodSchema = schema;\n }\n return this._validatedJson?.success ? (this._validatedJson.data as T) : undefined;\n }\n}\n\nconst scopes = new WeakMap<EffectScope, Map<FileHandle, ShallowRef<FileContentData | undefined>>>();\n\nfunction addScope(scope: EffectScope) {\n scopes.set(scope, new Map<FileHandle, ShallowRef<FileContentData | undefined>>());\n}\n\nconst globalCache = new LRUCache<FileHandle, FileContentData>({\n maxSize: DefaultReactiveFileContentOps.cacheSize,\n sizeCalculation: (value) => value.bytes.length,\n});\n\nconst globalFetcher = new Fetcher<FileHandle, FileContentData>();\n\nexport class ReactiveFileContent {\n private readonly fileDataCache: LRUCache<FileHandle, FileContentData>;\n private readonly fetcher: Fetcher<FileHandle, FileContentData>;\n private ns = new Map<string, Set<FileHandle>>();\n private currentKey: string | undefined;\n\n private constructor(private currentScope: EffectScope, _ops?: Partial<ReactiveFileContentOps>) {\n const ops: ReactiveFileContentOps = { ...DefaultReactiveFileContentOps, ...(_ops ?? {}) };\n this.fileDataCache = ops.lruCache ?? new LRUCache<FileHandle, FileContentData>({\n maxSize: ops.cacheSize,\n sizeCalculation: (value) => value.bytes.length,\n });\n this.fetcher = ops.fetcher ?? new Fetcher<FileHandle, FileContentData>();\n }\n\n /**\n * Experimental method to invalidate the refs map cache for a given key.\n */\n public withInvalidate<T>(key: string, cb: () => T) {\n const previous = this.ns.get(key);\n this.ns.set(key, new Set());\n this.currentKey = key;\n try {\n const res = cb();\n this.invalidate(key, previous);\n return res;\n } finally {\n this.currentKey = undefined;\n }\n }\n\n public stopScope() {\n scopes.delete(this.currentScope);\n this.currentScope.stop();\n }\n\n private async doFetch(handle: FileHandle) {\n if (!this.fileDataCache.has(handle)) {\n const fileContentData = await this.fetcher.fetch(handle, async () => new FileContentData(await getRawPlatformaInstance().blobDriver.getContent(handle)));\n this.fileDataCache.set(handle, fileContentData);\n }\n\n return this.fileDataCache.get(handle)!;\n }\n\n private getSize() {\n const refsMap = this.getRefsMap();\n return refsMap ? refsMap.size : 0;\n }\n\n private getRefsMap() {\n return scopes.get(this.currentScope);\n }\n\n private invalidate(key: string, previous: Set<FileHandle> | undefined) {\n if (!previous) {\n return;\n }\n\n const actual = this.ns.get(key)!;\n\n for (const handle of actual) {\n previous.delete(handle);\n }\n\n const map = this.getRefsMap();\n\n for (const handle of previous) {\n map?.delete(handle);\n }\n }\n\n private withHandle(handle: FileHandle) {\n if (this.currentKey) {\n this.ns.get(this.currentKey)?.add(handle);\n }\n }\n\n private getDataRef(handle: FileHandle): ShallowRef<FileContentData | undefined> {\n const refsMap = this.getRefsMap();\n\n if (!refsMap) {\n throw new Error('ReactiveFileContent must be used within a Vue component or effect scope.');\n }\n\n this.withHandle(handle);\n\n const refFromMap = refsMap.get(handle);\n\n if (refFromMap !== undefined) {\n return refFromMap;\n }\n\n const newRef = shallowRef<FileContentData>();\n refsMap.set(handle, newRef);\n\n // Initiating actual fetch from the cache, that will in turn initiate upload\n (async () => {\n const maxRetries = 8;\n const retryDelay = 1000; // 1 second\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const data = await this.doFetch(handle);\n newRef.value = data;\n return;\n } catch (err: unknown) {\n console.error(`File download attempt ${attempt + 1}/${maxRetries} failed:`, err);\n\n if (attempt < maxRetries - 1) {\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n } else {\n console.error(`Failed to download file after ${maxRetries} attempts`);\n }\n }\n }\n })();\n\n return newRef;\n }\n\n public getContentBytes(handle: FileHandle): ComputedRef<Uint8Array | undefined>;\n public getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined;\n public getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined {\n if (handle === undefined) return undefined;\n const dataRef = this.getDataRef(handle);\n return computed(() => dataRef.value?.bytes);\n }\n\n public getContentString(handle: FileHandle): ComputedRef<string | undefined>;\n public getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined;\n public getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined {\n if (handle === undefined) return undefined;\n const dataRef = this.getDataRef(handle);\n return computed(() => dataRef.value?.str);\n }\n\n public getContentJson<T>(handle: FileHandle, schema: ZodSchema<T>): ComputedRef<T | undefined>;\n public getContentJson<T>(handle: FileHandle | undefined, schema: ZodSchema<T>): ComputedRef<T | undefined> | undefined;\n public getContentJson<T = unknown>(handle: FileHandle): ComputedRef<T | undefined>;\n public getContentJson<T = unknown>(handle: FileHandle | undefined): ComputedRef<T | undefined> | undefined;\n public getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined;\n public getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined {\n if (handle === undefined) return undefined;\n const dataRef = this.getDataRef(handle);\n return computed(() => (schema === undefined ? dataRef.value?.rawJson : dataRef.value?.validatedJson(schema)) as T);\n }\n\n private static initScope(_scope: EffectScope | undefined) {\n let scope = getCurrentScope() ?? _scope;\n\n if (!scope) {\n console.warn('Current scope not found, using new detached scope...');\n scope = effectScope(true);\n }\n\n addScope(scope);\n\n onScopeDispose(() => {\n scopes.delete(scope);\n });\n\n return scope;\n }\n\n /**\n * Creates a ReactiveFileContent instance with isolated cache and fetcher.\n * Use this when you need component-specific caching.\n *\n * @example\n * ```ts\n * import { ReactiveFileContent } from '@platforma-sdk/ui-vue';\n * import { computed } from 'vue';\n *\n * const fileContent = ReactiveFileContent.use();\n *\n * const processedData = computed(() => {\n * const content = fileContent.getContentString(fileHandle).value;\n * return content?.split('\\n').length ?? 0;\n * });\n * ```\n */\n public static use(_ops?: Partial<ReactiveFileContentOps>, _scope?: EffectScope) {\n const scope = this.initScope(_scope);\n\n return new ReactiveFileContent(scope, { ..._ops });\n }\n\n /**\n * Creates a ReactiveFileContent instance with globally shared cache and fetcher.\n * Use this to share file content cache across multiple components.\n *\n * @example\n * ```ts\n * import { ReactiveFileContent } from '@platforma-sdk/ui-vue';\n * import { computed } from 'vue';\n *\n * const fileContent = ReactiveFileContent.useGlobal();\n *\n * const combinedData = computed(() => {\n * const data1 = fileContent.getContentJson(handle1).value;\n * const data2 = fileContent.getContentJson(handle2).value;\n * return { data1, data2 };\n * });\n * ```\n */\n public static useGlobal(_ops?: Partial<ReactiveFileContentOps>, _scope?: EffectScope) {\n const scope = this.initScope(_scope);\n\n return new ReactiveFileContent(scope, { ..._ops, lruCache: globalCache, fetcher: globalFetcher });\n }\n}\n"],"names":["DefaultReactiveFileContentOps","FileContentData","bytes","__publicField","schema","_a","scopes","addScope","scope","globalCache","LRUCache","value","globalFetcher","Fetcher","ReactiveFileContent","currentScope","_ops","ops","key","cb","previous","res","handle","fileContentData","getRawPlatformaInstance","refsMap","actual","map","refFromMap","newRef","shallowRef","attempt","data","err","resolve","dataRef","computed","_b","_scope","getCurrentScope","effectScope","onScopeDispose"],"mappings":"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"}
|
|
@@ -1,42 +1,56 @@
|
|
|
1
1
|
var i = Object.defineProperty;
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
function
|
|
2
|
+
var o = (e, r, t) => r in e ? i(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t;
|
|
3
|
+
var n = (e, r, t) => o(e, typeof r != "symbol" ? r + "" : r, t);
|
|
4
|
+
function f(e) {
|
|
5
5
|
return e == null;
|
|
6
6
|
}
|
|
7
|
-
function
|
|
7
|
+
function l(e, r) {
|
|
8
8
|
if (e == null)
|
|
9
|
-
throw Error(
|
|
9
|
+
throw Error(r);
|
|
10
10
|
return e;
|
|
11
11
|
}
|
|
12
|
-
class
|
|
12
|
+
class c {
|
|
13
13
|
constructor() {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
n(this, "promise");
|
|
15
|
+
n(this, "resolve", () => {
|
|
16
16
|
});
|
|
17
|
-
|
|
17
|
+
n(this, "reject", () => {
|
|
18
18
|
});
|
|
19
|
-
this.promise = new Promise((
|
|
20
|
-
this.resolve =
|
|
19
|
+
this.promise = new Promise((r, t) => {
|
|
20
|
+
this.resolve = r, this.reject = t;
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
return new Promise((
|
|
24
|
+
function p(e) {
|
|
25
|
+
return new Promise((r) => setTimeout(r, e));
|
|
26
26
|
}
|
|
27
|
-
function
|
|
28
|
-
return
|
|
27
|
+
function m(e, r) {
|
|
28
|
+
return r(e);
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
function h(e, r) {
|
|
31
31
|
if (e != null)
|
|
32
|
-
return
|
|
32
|
+
return r(e);
|
|
33
|
+
}
|
|
34
|
+
class d {
|
|
35
|
+
constructor() {
|
|
36
|
+
n(this, "promises", /* @__PURE__ */ new Map());
|
|
37
|
+
}
|
|
38
|
+
fetch(r, t) {
|
|
39
|
+
if (this.promises.has(r))
|
|
40
|
+
return this.promises.get(r);
|
|
41
|
+
const s = t().finally(() => {
|
|
42
|
+
this.promises.delete(r);
|
|
43
|
+
});
|
|
44
|
+
return this.promises.set(r, s), s;
|
|
45
|
+
}
|
|
33
46
|
}
|
|
34
47
|
export {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
f as
|
|
39
|
-
|
|
40
|
-
m as
|
|
48
|
+
c as Deferred,
|
|
49
|
+
d as Fetcher,
|
|
50
|
+
p as delay,
|
|
51
|
+
f as isNil,
|
|
52
|
+
l as notEmpty,
|
|
53
|
+
m as tap,
|
|
54
|
+
h as tapIf
|
|
41
55
|
};
|
|
42
56
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../../../../../lib/util/helpers/src/utils.ts"],"sourcesContent":["import type { AwaitedStruct, Option, Primitive, Result, Unionize } from './types';\n\nexport function isNil(v: unknown): v is null | undefined | void {\n return v === null || v === undefined;\n}\n\nexport function isPrimitive(v: unknown): v is Primitive {\n return isNil(v)\n || typeof v === 'string'\n || typeof v === 'number'\n || typeof v === 'boolean'\n || typeof v === 'bigint'\n ;\n}\n\nexport function notEmpty<T>(v: T | null | undefined, message?: string): T {\n if (v === null || v === undefined) {\n throw Error(message ?? 'Empty (null | undefined) value');\n }\n\n return v;\n}\n\nexport function notUndef<T>(v: T | undefined, message?: string): T {\n if (v === undefined) {\n throw Error(message ?? 'Undefined value');\n }\n\n return v;\n}\n\nexport function undef<V>(v: V | undefined = undefined): V | undefined {\n return v;\n}\n\nexport function bool<V extends boolean>(v: V): boolean {\n return v;\n}\n\nexport function uniqueValues<T>(items: T[]): T[] {\n return [...new Set(items)];\n}\n\nexport function checkIfNotEmpty<T>(v: T | null | undefined, message?: string): asserts v is T {\n if (v === undefined || v === null) {\n throw Error(message ?? 'Empty (null | undefined) value');\n }\n}\n\nexport function checkIfDefined<T>(v: T | undefined, message?: string): asserts v is T {\n if (v === undefined) {\n throw Error(message ?? 'Undefined value');\n }\n}\n\nexport function between(n: number, a: number, b: number) {\n const min = Math.min(a, b);\n const max = Math.max(a, b);\n\n return n >= min && n <= max;\n}\n\nexport function listToOptions<T>(list: T[] | readonly T[]): Option<T>[] {\n return list.map((value) => ({ text: String(value), value }));\n}\n\nexport function async<A extends unknown[]>(gf: (...args: A) => Generator) {\n return function (...args: A) {\n const generator = gf(...args);\n\n async function handle(result: IteratorResult<unknown>): Promise<unknown> {\n if (result.done) {\n return Promise.resolve(result.value);\n }\n\n return Promise.resolve(result.value).then((res) => {\n return handle(generator.next(res));\n }).catch((err) => {\n return handle(generator.throw(err));\n });\n }\n\n try {\n return handle(generator.next());\n } catch (ex) {\n return Promise.reject(ex instanceof Error ? ex : Error(String(ex)));\n }\n };\n}\n\nexport class Deferred<T> {\n public readonly promise: Promise<T>;\n\n constructor() {\n this.promise = new Promise<T>((res, rej) => {\n this.resolve = res;\n this.reject = rej;\n });\n }\n\n public resolve: (v: T) => void = () => {\n };\n\n public reject: (err: Error) => void = () => {\n };\n}\n\nexport function delay(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function tear() {\n return new Promise<void>((r) => queueMicrotask(r));\n}\n\nexport function timer() {\n const t = new Date().getTime();\n return function () {\n return new Date().getTime() - t;\n };\n}\n\nexport function performanceTimer() {\n const t = performance.now();\n return function () {\n return performance.now() - t;\n };\n}\n\nexport function call<R>(f: () => R): R {\n return f();\n}\n\nexport function clamp(n: number, lo: number, up: number) {\n return lo > n ? lo : n > up ? up : n;\n}\n\nexport function tap<T, R>(v: T, cb: (v: T) => R) {\n return cb(v);\n}\n\nexport function tapIf<T, R>(v: T | null | undefined, cb: (v: T) => R) {\n if (v !== null && v !== undefined) {\n return cb(v);\n }\n\n return;\n}\n\nexport function* range(from: number, to: number, step = 1) {\n for (let i = from; i < to; i += step) {\n yield i;\n }\n}\n\nexport function toList<T>(iterable: Iterable<T>): T[] {\n const lst: T[] = [];\n for (const it of iterable) {\n lst.push(it);\n }\n\n return lst;\n}\n\nexport function times<R>(n: number, cb: (i: number) => R): R[] {\n return toList(range(0, n)).map(cb);\n}\n\nexport class Interval {\n constructor(private _delay: number) {\n }\n\n async* generate(): AsyncGenerator<number> {\n let i = 0;\n while (true) {\n await delay(this._delay);\n yield i++;\n }\n }\n\n async* [Symbol.asyncIterator]() {\n let i = 0;\n while (true) {\n await delay(this._delay);\n yield i++;\n }\n }\n}\n\nexport function arrayFrom<T>(length: number, cb: (i: number) => T) {\n return Array.from({ length }, (_, i) => cb(i));\n}\n\nexport function exhaustive(v: never, message: string): never {\n throw Error(message);\n}\n\nexport type Matcher<T extends string, R = unknown> = {\n [P in T]: () => R;\n};\n\nexport function match<T extends string, R = unknown>(matcher: Matcher<T, R>) {\n return (key: T) => matcher[key]();\n}\n\nexport function okOptional<V>(v: { ok: true; value: V } | { ok: false } | undefined) {\n if (!v) {\n return undefined;\n }\n\n if (v.ok) {\n return v.value;\n }\n}\n\nexport function errorOptional<V>(v: Result<V> | undefined) {\n if (!v) {\n return undefined;\n }\n\n if (!v.ok) {\n return v.error;\n }\n}\n\nexport function unwrap<T>(r: Result<T>): T {\n if (r.ok) {\n return r.value;\n }\n\n throw Error(r.error);\n}\n\nexport function flatValue<T>(v: T | T[]): T[] {\n return Array.isArray(v) ? v : [v];\n}\n\nexport async function resolveAwaited<O extends Record<string, unknown>>(obj: O): Promise<AwaitedStruct<O>> {\n return Object.fromEntries(await Promise.all(Object.entries(obj).map(async ([k, v]) => [k, await v]))) as Promise<AwaitedStruct<O>>;\n}\n\nexport function alike(obj: Record<string, unknown>, to: Record<string, unknown>) {\n return Object.keys(to).every((bKey) => obj[bKey] === to[bKey]);\n}\n\nexport const identity = <T>(v: T): T => v;\n\nexport function asConst<const T>(v: T) {\n return v;\n}\n\nexport function unionize<K extends keyof O, V, O extends Record<K, V>>(obj: O): Unionize<O>[] {\n return Object.entries(obj).map(([key, value]) => ({\n key,\n value,\n })) as Unionize<O>[];\n}\n"],"names":["isNil","v","notEmpty","message","Deferred","__publicField","res","rej","delay","ms","resolve","tap","cb","tapIf"],"mappings":";;;AAEM,SAAUA,EAAMC,GAAU;AAC9B,SAAOA,KAAM;AACf;AAWM,SAAUC,EAAYD,GAAyBE,GAAgB;AACnE,MAAIF,KAAM;AACR,UAAM,MAAME,CAA2C;AAGzD,SAAOF;AACT;AAqEa,MAAAG,EAAQ;AAAA,EAGnB,cAAA;AAFgB,IAAAC,EAAA;AAST,IAAAA,EAAA,iBAA0B,MAAK;AAAA,IACtC;AAEO,IAAAA,EAAA,gBAA+B,MAAK;AAAA,IAC3C;AAVE,SAAK,UAAU,IAAI,QAAW,CAACC,GAAKC,MAAO;AACzC,WAAK,UAAUD,GACf,KAAK,SAASC;AAAA,IAChB,CAAC;AAAA,EACH;AAOD;AAEK,SAAUC,EAAMC,GAAU;AAC9B,SAAO,IAAI,QAAQ,CAACC,MAAY,WAAWA,GAASD,CAAE,CAAC;AACzD;AA4BM,SAAUE,EAAUV,GAAMW,GAAe;AAC7C,SAAOA,EAAGX,CAAC;AACb;AAEM,SAAUY,EAAYZ,GAAyBW,GAAe;AAClE,MAAIX,KAAM;AACR,WAAOW,EAAGX,CAAC;AAIf;"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../../../../../lib/util/helpers/src/utils.ts"],"sourcesContent":["import type { AwaitedStruct, Option, Primitive, Result, Unionize } from './types';\n\nexport function isNil(v: unknown): v is null | undefined | void {\n return v === null || v === undefined;\n}\n\nexport function isPrimitive(v: unknown): v is Primitive {\n return isNil(v)\n || typeof v === 'string'\n || typeof v === 'number'\n || typeof v === 'boolean'\n || typeof v === 'bigint'\n ;\n}\n\nexport function notEmpty<T>(v: T | null | undefined, message?: string): T {\n if (v === null || v === undefined) {\n throw Error(message ?? 'Empty (null | undefined) value');\n }\n\n return v;\n}\n\nexport function notUndef<T>(v: T | undefined, message?: string): T {\n if (v === undefined) {\n throw Error(message ?? 'Undefined value');\n }\n\n return v;\n}\n\nexport function undef<V>(v: V | undefined = undefined): V | undefined {\n return v;\n}\n\nexport function bool<V extends boolean>(v: V): boolean {\n return v;\n}\n\nexport function uniqueValues<T>(items: T[]): T[] {\n return [...new Set(items)];\n}\n\nexport function checkIfNotEmpty<T>(v: T | null | undefined, message?: string): asserts v is T {\n if (v === undefined || v === null) {\n throw Error(message ?? 'Empty (null | undefined) value');\n }\n}\n\nexport function checkIfDefined<T>(v: T | undefined, message?: string): asserts v is T {\n if (v === undefined) {\n throw Error(message ?? 'Undefined value');\n }\n}\n\nexport function between(n: number, a: number, b: number) {\n const min = Math.min(a, b);\n const max = Math.max(a, b);\n\n return n >= min && n <= max;\n}\n\nexport function listToOptions<T>(list: T[] | readonly T[]): Option<T>[] {\n return list.map((value) => ({ text: String(value), value }));\n}\n\nexport function async<A extends unknown[]>(gf: (...args: A) => Generator) {\n return function (...args: A) {\n const generator = gf(...args);\n\n async function handle(result: IteratorResult<unknown>): Promise<unknown> {\n if (result.done) {\n return Promise.resolve(result.value);\n }\n\n return Promise.resolve(result.value).then((res) => {\n return handle(generator.next(res));\n }).catch((err) => {\n return handle(generator.throw(err));\n });\n }\n\n try {\n return handle(generator.next());\n } catch (ex) {\n return Promise.reject(ex instanceof Error ? ex : Error(String(ex)));\n }\n };\n}\n\nexport class Deferred<T> {\n public readonly promise: Promise<T>;\n\n constructor() {\n this.promise = new Promise<T>((res, rej) => {\n this.resolve = res;\n this.reject = rej;\n });\n }\n\n public resolve: (v: T) => void = () => {\n };\n\n public reject: (err: Error) => void = () => {\n };\n}\n\nexport function delay(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function tear() {\n return new Promise<void>((r) => queueMicrotask(r));\n}\n\nexport function timer() {\n const t = new Date().getTime();\n return function () {\n return new Date().getTime() - t;\n };\n}\n\nexport function performanceTimer() {\n const t = performance.now();\n return function () {\n return performance.now() - t;\n };\n}\n\nexport function call<R>(f: () => R): R {\n return f();\n}\n\nexport function clamp(n: number, lo: number, up: number) {\n return lo > n ? lo : n > up ? up : n;\n}\n\nexport function tap<T, R>(v: T, cb: (v: T) => R) {\n return cb(v);\n}\n\nexport function tapIf<T, R>(v: T | null | undefined, cb: (v: T) => R) {\n if (v !== null && v !== undefined) {\n return cb(v);\n }\n\n return;\n}\n\nexport function* range(from: number, to: number, step = 1) {\n for (let i = from; i < to; i += step) {\n yield i;\n }\n}\n\nexport function toList<T>(iterable: Iterable<T>): T[] {\n const lst: T[] = [];\n for (const it of iterable) {\n lst.push(it);\n }\n\n return lst;\n}\n\nexport function times<R>(n: number, cb: (i: number) => R): R[] {\n return toList(range(0, n)).map(cb);\n}\n\nexport class Interval {\n constructor(private _delay: number) {\n }\n\n async* generate(): AsyncGenerator<number> {\n let i = 0;\n while (true) {\n await delay(this._delay);\n yield i++;\n }\n }\n\n async* [Symbol.asyncIterator]() {\n let i = 0;\n while (true) {\n await delay(this._delay);\n yield i++;\n }\n }\n}\n\nexport function arrayFrom<T>(length: number, cb: (i: number) => T) {\n return Array.from({ length }, (_, i) => cb(i));\n}\n\nexport function exhaustive(v: never, message: string): never {\n throw Error(message);\n}\n\nexport type Matcher<T extends string, R = unknown> = {\n [P in T]: () => R;\n};\n\nexport function match<T extends string, R = unknown>(matcher: Matcher<T, R>) {\n return (key: T) => matcher[key]();\n}\n\nexport function okOptional<V>(v: { ok: true; value: V } | { ok: false } | undefined) {\n if (!v) {\n return undefined;\n }\n\n if (v.ok) {\n return v.value;\n }\n}\n\nexport function errorOptional<V>(v: Result<V> | undefined) {\n if (!v) {\n return undefined;\n }\n\n if (!v.ok) {\n return v.error;\n }\n}\n\nexport function unwrap<T>(r: Result<T>): T {\n if (r.ok) {\n return r.value;\n }\n\n throw Error(r.error);\n}\n\nexport function flatValue<T>(v: T | T[]): T[] {\n return Array.isArray(v) ? v : [v];\n}\n\nexport async function resolveAwaited<O extends Record<string, unknown>>(obj: O): Promise<AwaitedStruct<O>> {\n return Object.fromEntries(await Promise.all(Object.entries(obj).map(async ([k, v]) => [k, await v]))) as Promise<AwaitedStruct<O>>;\n}\n\nexport function alike(obj: Record<string, unknown>, to: Record<string, unknown>) {\n return Object.keys(to).every((bKey) => obj[bKey] === to[bKey]);\n}\n\nexport const identity = <T>(v: T): T => v;\n\nexport function asConst<const T>(v: T) {\n return v;\n}\n\nexport function unionize<K extends keyof O, V, O extends Record<K, V>>(obj: O): Unionize<O>[] {\n return Object.entries(obj).map(([key, value]) => ({\n key,\n value,\n })) as Unionize<O>[];\n}\n\nexport class Fetcher<K, V> {\n private promises = new Map<K, Promise<V>>();\n fetch(key: K, callback: () => Promise<V>) {\n if (this.promises.has(key)) {\n return this.promises.get(key)!;\n }\n const promise = callback().finally(() => {\n this.promises.delete(key);\n });\n this.promises.set(key, promise);\n return promise;\n }\n}\n"],"names":["isNil","v","notEmpty","message","Deferred","__publicField","res","rej","delay","ms","resolve","tap","cb","tapIf","Fetcher","key","callback","promise"],"mappings":";;;AAEM,SAAUA,EAAMC,GAAU;AAC9B,SAAOA,KAAM;AACf;AAWM,SAAUC,EAAYD,GAAyBE,GAAgB;AACnE,MAAIF,KAAM;AACR,UAAM,MAAME,CAA2C;AAGzD,SAAOF;AACT;AAqEa,MAAAG,EAAQ;AAAA,EAGnB,cAAA;AAFgB,IAAAC,EAAA;AAST,IAAAA,EAAA,iBAA0B,MAAK;AAAA,IACtC;AAEO,IAAAA,EAAA,gBAA+B,MAAK;AAAA,IAC3C;AAVE,SAAK,UAAU,IAAI,QAAW,CAACC,GAAKC,MAAO;AACzC,WAAK,UAAUD,GACf,KAAK,SAASC;AAAA,IAChB,CAAC;AAAA,EACH;AAOD;AAEK,SAAUC,EAAMC,GAAU;AAC9B,SAAO,IAAI,QAAQ,CAACC,MAAY,WAAWA,GAASD,CAAE,CAAC;AACzD;AA4BM,SAAUE,EAAUV,GAAMW,GAAe;AAC7C,SAAOA,EAAGX,CAAC;AACb;AAEM,SAAUY,EAAYZ,GAAyBW,GAAe;AAClE,MAAIX,KAAM;AACR,WAAOW,EAAGX,CAAC;AAIf;AA+Ga,MAAAa,EAAO;AAAA,EAAP;AACH,IAAAT,EAAA,kBAAW,oBAAI,IAAG;AAAA;AAAA,EAC1B,MAAMU,GAAQC,GAA0B;AACtC,QAAI,KAAK,SAAS,IAAID,CAAG;AACvB,aAAO,KAAK,SAAS,IAAIA,CAAG;AAE9B,UAAME,IAAUD,IAAW,QAAQ,MAAK;AACtC,WAAK,SAAS,OAAOD,CAAG;AAAA,IAC1B,CAAC;AACD,gBAAK,SAAS,IAAIA,GAAKE,CAAO,GACvBA;AAAA,EACT;AACD;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-sdk/ui-vue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"styles": "dist/index.js",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"@vueuse/integrations": "^13.3.0",
|
|
26
26
|
"d3-format": "^3.1.0",
|
|
27
27
|
"zod": "~3.23.8",
|
|
28
|
+
"@platforma-sdk/model": "1.44.1",
|
|
28
29
|
"@milaboratories/biowasm-tools": "2.0.0",
|
|
29
|
-
"@milaboratories/uikit": "2.4.
|
|
30
|
-
"@platforma-sdk/model": "1.43.29"
|
|
30
|
+
"@milaboratories/uikit": "2.4.22"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"happy-dom": "^15.11.7",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"yarpm": "^1.2.0",
|
|
43
43
|
"fast-json-patch": "^3.1.1",
|
|
44
44
|
"@faker-js/faker": "^9.2.0",
|
|
45
|
-
"@milaboratories/build-configs": "1.0.8",
|
|
46
|
-
"@milaboratories/helpers": "1.9.0",
|
|
47
45
|
"@milaboratories/ts-configs": "1.0.6",
|
|
46
|
+
"@milaboratories/build-configs": "1.0.8",
|
|
47
|
+
"@milaboratories/helpers": "1.11.0",
|
|
48
48
|
"@milaboratories/eslint-config": "1.0.4",
|
|
49
49
|
"@milaboratories/ts-builder": "1.0.5"
|
|
50
50
|
},
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { BlobHandleAndSize } from '@platforma-sdk/model';
|
|
2
2
|
import { getRawPlatformaInstance } from '@platforma-sdk/model';
|
|
3
3
|
import { LRUCache } from 'lru-cache';
|
|
4
|
-
import type { ComputedRef, ShallowRef } from 'vue';
|
|
5
|
-
import { computed, shallowRef } from 'vue';
|
|
6
|
-
import type { ZodSchema } from 'zod';
|
|
4
|
+
import type { ComputedRef, ShallowRef, EffectScope } from 'vue';
|
|
5
|
+
import { computed, shallowRef, getCurrentScope, onScopeDispose, effectScope } from 'vue';
|
|
6
|
+
import type { ZodSchema, SafeParseReturnType } from 'zod';
|
|
7
|
+
import { Fetcher } from '@milaboratories/helpers';
|
|
7
8
|
|
|
8
9
|
type FileHandle = BlobHandleAndSize['handle'];
|
|
9
10
|
|
|
10
11
|
export type ReactiveFileContentOps = {
|
|
11
12
|
/** Maximum size in bytes of file content to cache */
|
|
12
13
|
cacheSize: number;
|
|
14
|
+
lruCache?: LRUCache<FileHandle, FileContentData>;
|
|
15
|
+
fetcher?: Fetcher<FileHandle, FileContentData>;
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
const DefaultReactiveFileContentOps: ReactiveFileContentOps = {
|
|
@@ -20,7 +23,7 @@ class FileContentData {
|
|
|
20
23
|
private _str: string | undefined = undefined;
|
|
21
24
|
private _rawJson: unknown | undefined = undefined;
|
|
22
25
|
private _zodSchema: ZodSchema | undefined = undefined;
|
|
23
|
-
private _validatedJson:
|
|
26
|
+
private _validatedJson: SafeParseReturnType<unknown, unknown> | undefined = undefined;
|
|
24
27
|
|
|
25
28
|
constructor(public readonly bytes: Uint8Array) {}
|
|
26
29
|
|
|
@@ -43,36 +46,123 @@ class FileContentData {
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
const scopes = new WeakMap<EffectScope, Map<FileHandle, ShallowRef<FileContentData | undefined>>>();
|
|
50
|
+
|
|
51
|
+
function addScope(scope: EffectScope) {
|
|
52
|
+
scopes.set(scope, new Map<FileHandle, ShallowRef<FileContentData | undefined>>());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const globalCache = new LRUCache<FileHandle, FileContentData>({
|
|
56
|
+
maxSize: DefaultReactiveFileContentOps.cacheSize,
|
|
57
|
+
sizeCalculation: (value) => value.bytes.length,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const globalFetcher = new Fetcher<FileHandle, FileContentData>();
|
|
61
|
+
|
|
46
62
|
export class ReactiveFileContent {
|
|
47
63
|
private readonly fileDataCache: LRUCache<FileHandle, FileContentData>;
|
|
48
|
-
private readonly
|
|
49
|
-
|
|
64
|
+
private readonly fetcher: Fetcher<FileHandle, FileContentData>;
|
|
65
|
+
private ns = new Map<string, Set<FileHandle>>();
|
|
66
|
+
private currentKey: string | undefined;
|
|
67
|
+
|
|
68
|
+
private constructor(private currentScope: EffectScope, _ops?: Partial<ReactiveFileContentOps>) {
|
|
50
69
|
const ops: ReactiveFileContentOps = { ...DefaultReactiveFileContentOps, ...(_ops ?? {}) };
|
|
51
|
-
this.fileDataCache = new LRUCache<FileHandle, FileContentData>({
|
|
70
|
+
this.fileDataCache = ops.lruCache ?? new LRUCache<FileHandle, FileContentData>({
|
|
52
71
|
maxSize: ops.cacheSize,
|
|
53
|
-
fetchMethod: async (key) => new FileContentData(await getRawPlatformaInstance().blobDriver.getContent(key)),
|
|
54
72
|
sizeCalculation: (value) => value.bytes.length,
|
|
55
|
-
/** Will also be called on error fetching the file */
|
|
56
|
-
dispose: (_, key) => {
|
|
57
|
-
this.fileDataRefs.delete(key);
|
|
58
|
-
},
|
|
59
73
|
});
|
|
74
|
+
this.fetcher = ops.fetcher ?? new Fetcher<FileHandle, FileContentData>();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Experimental method to invalidate the refs map cache for a given key.
|
|
79
|
+
*/
|
|
80
|
+
public withInvalidate<T>(key: string, cb: () => T) {
|
|
81
|
+
const previous = this.ns.get(key);
|
|
82
|
+
this.ns.set(key, new Set());
|
|
83
|
+
this.currentKey = key;
|
|
84
|
+
try {
|
|
85
|
+
const res = cb();
|
|
86
|
+
this.invalidate(key, previous);
|
|
87
|
+
return res;
|
|
88
|
+
} finally {
|
|
89
|
+
this.currentKey = undefined;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public stopScope() {
|
|
94
|
+
scopes.delete(this.currentScope);
|
|
95
|
+
this.currentScope.stop();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private async doFetch(handle: FileHandle) {
|
|
99
|
+
if (!this.fileDataCache.has(handle)) {
|
|
100
|
+
const fileContentData = await this.fetcher.fetch(handle, async () => new FileContentData(await getRawPlatformaInstance().blobDriver.getContent(handle)));
|
|
101
|
+
this.fileDataCache.set(handle, fileContentData);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return this.fileDataCache.get(handle)!;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private getSize() {
|
|
108
|
+
const refsMap = this.getRefsMap();
|
|
109
|
+
return refsMap ? refsMap.size : 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private getRefsMap() {
|
|
113
|
+
return scopes.get(this.currentScope);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private invalidate(key: string, previous: Set<FileHandle> | undefined) {
|
|
117
|
+
if (!previous) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const actual = this.ns.get(key)!;
|
|
122
|
+
|
|
123
|
+
for (const handle of actual) {
|
|
124
|
+
previous.delete(handle);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const map = this.getRefsMap();
|
|
128
|
+
|
|
129
|
+
for (const handle of previous) {
|
|
130
|
+
map?.delete(handle);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private withHandle(handle: FileHandle) {
|
|
135
|
+
if (this.currentKey) {
|
|
136
|
+
this.ns.get(this.currentKey)?.add(handle);
|
|
137
|
+
}
|
|
60
138
|
}
|
|
61
139
|
|
|
62
140
|
private getDataRef(handle: FileHandle): ShallowRef<FileContentData | undefined> {
|
|
63
|
-
const
|
|
64
|
-
|
|
141
|
+
const refsMap = this.getRefsMap();
|
|
142
|
+
|
|
143
|
+
if (!refsMap) {
|
|
144
|
+
throw new Error('ReactiveFileContent must be used within a Vue component or effect scope.');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.withHandle(handle);
|
|
148
|
+
|
|
149
|
+
const refFromMap = refsMap.get(handle);
|
|
150
|
+
|
|
151
|
+
if (refFromMap !== undefined) {
|
|
152
|
+
return refFromMap;
|
|
153
|
+
}
|
|
154
|
+
|
|
65
155
|
const newRef = shallowRef<FileContentData>();
|
|
66
|
-
|
|
156
|
+
refsMap.set(handle, newRef);
|
|
67
157
|
|
|
68
158
|
// Initiating actual fetch from the cache, that will in turn initiate upload
|
|
69
159
|
(async () => {
|
|
70
|
-
const maxRetries =
|
|
160
|
+
const maxRetries = 8;
|
|
71
161
|
const retryDelay = 1000; // 1 second
|
|
72
162
|
|
|
73
163
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
74
164
|
try {
|
|
75
|
-
const data = await this.
|
|
165
|
+
const data = await this.doFetch(handle);
|
|
76
166
|
newRef.value = data;
|
|
77
167
|
return;
|
|
78
168
|
} catch (err: unknown) {
|
|
@@ -103,7 +193,7 @@ export class ReactiveFileContent {
|
|
|
103
193
|
public getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined {
|
|
104
194
|
if (handle === undefined) return undefined;
|
|
105
195
|
const dataRef = this.getDataRef(handle);
|
|
106
|
-
return computed(() => dataRef
|
|
196
|
+
return computed(() => dataRef.value?.str);
|
|
107
197
|
}
|
|
108
198
|
|
|
109
199
|
public getContentJson<T>(handle: FileHandle, schema: ZodSchema<T>): ComputedRef<T | undefined>;
|
|
@@ -114,28 +204,70 @@ export class ReactiveFileContent {
|
|
|
114
204
|
public getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined {
|
|
115
205
|
if (handle === undefined) return undefined;
|
|
116
206
|
const dataRef = this.getDataRef(handle);
|
|
117
|
-
return computed(() => (schema === undefined ? dataRef
|
|
207
|
+
return computed(() => (schema === undefined ? dataRef.value?.rawJson : dataRef.value?.validatedJson(schema)) as T);
|
|
118
208
|
}
|
|
119
209
|
|
|
120
|
-
private static
|
|
210
|
+
private static initScope(_scope: EffectScope | undefined) {
|
|
211
|
+
let scope = getCurrentScope() ?? _scope;
|
|
212
|
+
|
|
213
|
+
if (!scope) {
|
|
214
|
+
console.warn('Current scope not found, using new detached scope...');
|
|
215
|
+
scope = effectScope(true);
|
|
216
|
+
}
|
|
121
217
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
218
|
+
addScope(scope);
|
|
219
|
+
|
|
220
|
+
onScopeDispose(() => {
|
|
221
|
+
scopes.delete(scope);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return scope;
|
|
126
225
|
}
|
|
127
226
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Creates a ReactiveFileContent instance with isolated cache and fetcher.
|
|
229
|
+
* Use this when you need component-specific caching.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```ts
|
|
233
|
+
* import { ReactiveFileContent } from '@platforma-sdk/ui-vue';
|
|
234
|
+
* import { computed } from 'vue';
|
|
235
|
+
*
|
|
236
|
+
* const fileContent = ReactiveFileContent.use();
|
|
237
|
+
*
|
|
238
|
+
* const processedData = computed(() => {
|
|
239
|
+
* const content = fileContent.getContentString(fileHandle).value;
|
|
240
|
+
* return content?.split('\n').length ?? 0;
|
|
241
|
+
* });
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
public static use(_ops?: Partial<ReactiveFileContentOps>, _scope?: EffectScope) {
|
|
245
|
+
const scope = this.initScope(_scope);
|
|
246
|
+
|
|
247
|
+
return new ReactiveFileContent(scope, { ..._ops });
|
|
132
248
|
}
|
|
133
249
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Creates a ReactiveFileContent instance with globally shared cache and fetcher.
|
|
252
|
+
* Use this to share file content cache across multiple components.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```ts
|
|
256
|
+
* import { ReactiveFileContent } from '@platforma-sdk/ui-vue';
|
|
257
|
+
* import { computed } from 'vue';
|
|
258
|
+
*
|
|
259
|
+
* const fileContent = ReactiveFileContent.useGlobal();
|
|
260
|
+
*
|
|
261
|
+
* const combinedData = computed(() => {
|
|
262
|
+
* const data1 = fileContent.getContentJson(handle1).value;
|
|
263
|
+
* const data2 = fileContent.getContentJson(handle2).value;
|
|
264
|
+
* return { data1, data2 };
|
|
265
|
+
* });
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
public static useGlobal(_ops?: Partial<ReactiveFileContentOps>, _scope?: EffectScope) {
|
|
269
|
+
const scope = this.initScope(_scope);
|
|
270
|
+
|
|
271
|
+
return new ReactiveFileContent(scope, { ..._ops, lruCache: globalCache, fetcher: globalFetcher });
|
|
140
272
|
}
|
|
141
273
|
}
|