@platforma-sdk/ui-vue 1.43.28 → 1.44.0

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.
@@ -1,13 +1,13 @@
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.43.28 build /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.44.0 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...
7
7
  ↳ vite.js build --config /configs/vite.browser-lib.config.js --mode production
8
8
  vite v6.3.5 building for production...
9
9
  transforming...
10
- ✓ 246 modules transformed.
10
+ ✓ 247 modules transformed.
11
11
  Export "default" of module "src/components/PlAgGridColumnManager/PlAgGridColumnManager.vue" was reexported through module "src/components/PlAgGridColumnManager/index.ts" while both modules are dependencies of each other and will end up in different chunks by current Rollup settings. This scenario is not well supported at the moment as it will produce a circular dependency between chunks and will likely lead to broken execution order.
12
12
  Either change the import in "src/components/PlAgDataTable/PlAgDataTableV2.vue?vue&type=script&setup=true&lang.ts" to point directly to the exporting module or do not use "output.preserveModules" to ensure these modules end up in the same chunk.
13
13
  rendering chunks...
@@ -132,7 +132,6 @@ computing gzip size...
132
132
  dist/components/PlAgChartStackedBarCell/PlAgChartStackedBarCell.vue.js  0.77 kB │ gzip: 0.45 kB │ map: 1.25 kB
133
133
  dist/plugins/Monetization/EndOfPeriod.vue2.js  0.77 kB │ gzip: 0.47 kB │ map: 1.55 kB
134
134
  dist/components/BlockLoader.vue.js  0.82 kB │ gzip: 0.48 kB │ map: 1.12 kB
135
- dist/lib/util/helpers/dist/utils.js  0.83 kB │ gzip: 0.42 kB │ map: 6.86 kB
136
135
  dist/node_modules/.pnpm/semver@7.7.2/node_modules/semver/ranges/simplify.js  0.83 kB │ gzip: 0.44 kB │ map: 2.64 kB
137
136
  dist/components/PlMultiSequenceAlignment/Legend.vue2.js  0.85 kB │ gzip: 0.49 kB │ map: 1.09 kB
138
137
  dist/components/NotFound.vue.js  0.89 kB │ gzip: 0.53 kB │ map: 0.75 kB
@@ -143,6 +142,7 @@ computing gzip size...
143
142
  dist/components/PlMultiSequenceAlignment/Toolbar.vue3.js  1.05 kB │ gzip: 0.53 kB │ map: 0.10 kB
144
143
  dist/components/PlAgCellFile/PlAgCellFile.vue.js  1.09 kB │ gzip: 0.59 kB │ map: 2.86 kB
145
144
  dist/node_modules/.pnpm/semver@7.7.2/node_modules/semver/functions/cmp.js  1.11 kB │ gzip: 0.40 kB │ map: 2.14 kB
145
+ dist/lib/util/helpers/dist/utils.js  1.13 kB │ gzip: 0.53 kB │ map: 7.55 kB
146
146
  dist/components/PlAgGridColumnManager/useFilteredItems.js  1.13 kB │ gzip: 0.58 kB │ map: 3.95 kB
147
147
  dist/components/PlAgCsvExporter/PlAgCsvExporter.vue.js  1.14 kB │ gzip: 0.61 kB │ map: 1.39 kB
148
148
  dist/components/PlAgDataTable/sources/value-rendering.js  1.14 kB │ gzip: 0.54 kB │ map: 3.41 kB
@@ -179,7 +179,6 @@ computing gzip size...
179
179
  dist/node_modules/.pnpm/fast-json-patch@3.1.1/node_modules/fast-json-patch/module/helpers.js  2.79 kB │ gzip: 1.21 kB │ map: 8.92 kB
180
180
  dist/components/PlAgDataTable/sources/row-number.js  3.10 kB │ gzip: 1.34 kB │ map: 8.36 kB
181
181
  dist/plugins/Monetization/LimitCard.vue3.js  3.10 kB │ gzip: 1.09 kB │ map: 0.11 kB
182
- dist/composition/fileContent.js  3.15 kB │ gzip: 1.17 kB │ map: 9.49 kB
183
182
  dist/components/PlMultiSequenceAlignment/SeqLogo.vue2.js  3.21 kB │ gzip: 1.32 kB │ map: 6.15 kB
184
183
  dist/components/PlTableFilters/PlTableAddFilterV2.vue.js  3.22 kB │ gzip: 1.28 kB │ map: 3.95 kB
185
184
  dist/components/PlAgDataTable/sources/table-state-v2.js  3.24 kB │ gzip: 1.17 kB │ map: 9.94 kB
@@ -204,6 +203,7 @@ computing gzip size...
204
203
  dist/components/PlAnnotations/components/FilterSidebar.vue3.js  5.48 kB │ gzip: 1.59 kB │ map: 0.11 kB
205
204
  dist/components/PlAnnotations/components/AnnotationsSidebar.vue3.js  5.67 kB │ gzip: 1.67 kB │ map: 0.11 kB
206
205
  dist/internal/createAppV1.js  5.81 kB │ gzip: 1.85 kB │ map: 15.33 kB
206
+ dist/composition/fileContent.js  5.83 kB │ gzip: 1.96 kB │ map: 14.16 kB
207
207
  dist/components/PlTableFilters/PlTableFiltersV2.vue2.js  6.15 kB │ gzip: 2.03 kB │ map: 9.27 kB
208
208
  dist/node_modules/.pnpm/semver@7.7.2/node_modules/semver/classes/semver.js  6.70 kB │ gzip: 1.86 kB │ map: 15.20 kB
209
209
  dist/internal/createAppV2.js  6.99 kB │ gzip: 2.44 kB │ map: 19.37 kB
@@ -223,7 +223,7 @@ computing gzip size...
223
223
  dist/components/PlAgRowNumHeader.vue.js  44.67 kB │ gzip: 29.30 kB │ map: 3.70 kB
224
224
  dist/AgGridVue/useAgGridOptions.js  49.30 kB │ gzip: 30.16 kB │ map: 16.35 kB
225
225
  dist/components/PlAgDataTable/PlAgDataTableV2.vue2.js  50.39 kB │ gzip: 31.05 kB │ map: 33.70 kB
226
- [vite:dts] Declaration files built in 11119ms.
226
+ [vite:dts] Declaration files built in 7669ms.
227
227
 
228
- ✓ built in 14.29s
228
+ ✓ built in 9.69s
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.43.28 type-check /home/runner/_work/platforma/platforma/sdk/ui-vue
3
+ > @platforma-sdk/ui-vue@1.44.0 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.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 261a742: Fixed: [blocks/mixcr-clonotyping] in the end of calculations results table starts to blink
8
+
9
+ ### Patch Changes
10
+
11
+ - @milaboratories/uikit@2.4.21
12
+ - @platforma-sdk/model@1.43.29
13
+
14
+ ## 1.43.29
15
+
16
+ ### Patch Changes
17
+
18
+ - @platforma-sdk/model@1.43.29
19
+ - @milaboratories/uikit@2.4.20
20
+
3
21
  ## 1.43.28
4
22
 
5
23
  ### Patch Changes
@@ -1,15 +1,43 @@
1
1
  import { BlobHandleAndSize } from '@platforma-sdk/model';
2
- import { ComputedRef } from 'vue';
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 fileDataRefs;
12
- constructor(_ops?: Partial<ReactiveFileContentOps>);
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 globalInstance;
24
- static getContentBytes(handle: FileHandle): ComputedRef<Uint8Array | undefined>;
25
- static getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined;
26
- static getContentString(handle: FileHandle): ComputedRef<string | undefined>;
27
- static getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined;
28
- static getContentJson<T>(handle: FileHandle, schema: ZodSchema<T>): ComputedRef<T | undefined>;
29
- static getContentJson<T>(handle: FileHandle | undefined, schema: ZodSchema<T>): ComputedRef<T | undefined> | undefined;
30
- static getContentJson<T = unknown>(handle: FileHandle): ComputedRef<T | undefined>;
31
- static getContentJson<T = unknown>(handle: FileHandle | undefined): ComputedRef<T | undefined> | undefined;
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 u = Object.defineProperty;
2
- var g = (i, t, e) => t in i ? u(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
3
- var a = (i, t, e) => g(i, typeof t != "symbol" ? t + "" : t, e);
4
- import { getRawPlatformaInstance as v } from "@platforma-sdk/model";
5
- import { LRUCache as w } from "lru-cache";
6
- import { shallowRef as m, computed as d } from "vue";
7
- const D = {
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 J {
13
+ class D {
12
14
  constructor(t) {
13
- a(this, "_str");
14
- a(this, "_rawJson");
15
- a(this, "_zodSchema");
16
- a(this, "_validatedJson");
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 o = class o {
31
- constructor(t) {
32
- a(this, "fileDataCache");
33
- a(this, "fileDataRefs", /* @__PURE__ */ new Map());
34
- const e = { ...D, ...t ?? {} };
35
- this.fileDataCache = new w({
36
- maxSize: e.cacheSize,
37
- fetchMethod: async (s) => new J(await v().blobDriver.getContent(s)),
38
- sizeCalculation: (s) => s.bytes.length,
39
- /** Will also be called on error fetching the file */
40
- dispose: (s, r) => {
41
- this.fileDataRefs.delete(r);
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.fileDataRefs.get(t);
47
- if (e !== void 0) return e;
48
- const s = m();
49
- return this.fileDataRefs.set(t, s), (async () => {
50
- for (let n = 0; n < 4; n++)
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 l = await this.fileDataCache.fetch(t);
53
- s.value = l;
109
+ const h = await this.doFetch(t);
110
+ i.value = h;
54
111
  return;
55
- } catch (l) {
56
- console.error(`File download attempt ${n + 1}/4 failed:`, l), n < 3 ? await new Promise((h) => setTimeout(h, 1e3)) : console.error("Failed to download file after 4 attempts");
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
- })(), s;
115
+ })(), i;
59
116
  }
60
117
  getContentBytes(t) {
61
118
  if (t === void 0) return;
62
119
  const e = this.getDataRef(t);
63
- return d(() => {
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 d(() => {
128
+ return f(() => {
72
129
  var s;
73
- return (s = e == null ? void 0 : e.value) == null ? void 0 : s.str;
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 d(() => {
80
- var r, c;
81
- return e === void 0 ? (r = s == null ? void 0 : s.value) == null ? void 0 : r.rawJson : (c = s == null ? void 0 : s.value) == null ? void 0 : c.validatedJson(e);
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 getContentBytes(t) {
85
- return o.globalInstance.getContentBytes(t);
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
- static getContentString(t) {
88
- return o.globalInstance.getContentString(t);
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
- static getContentJson(t, e) {
91
- return o.globalInstance.getContentJson(t, e);
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
- f as ReactiveFileContent
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 u = (e, n, r) => n in e ? i(e, n, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[n] = r;
3
- var t = (e, n, r) => u(e, typeof n != "symbol" ? n + "" : n, r);
4
- function s(e) {
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 f(e, n) {
7
+ function l(e, r) {
8
8
  if (e == null)
9
- throw Error(n);
9
+ throw Error(r);
10
10
  return e;
11
11
  }
12
- class l {
12
+ class c {
13
13
  constructor() {
14
- t(this, "promise");
15
- t(this, "resolve", () => {
14
+ n(this, "promise");
15
+ n(this, "resolve", () => {
16
16
  });
17
- t(this, "reject", () => {
17
+ n(this, "reject", () => {
18
18
  });
19
- this.promise = new Promise((n, r) => {
20
- this.resolve = n, this.reject = r;
19
+ this.promise = new Promise((r, t) => {
20
+ this.resolve = r, this.reject = t;
21
21
  });
22
22
  }
23
23
  }
24
- function c(e) {
25
- return new Promise((n) => setTimeout(n, e));
24
+ function p(e) {
25
+ return new Promise((r) => setTimeout(r, e));
26
26
  }
27
- function d(e, n) {
28
- return n(e);
27
+ function m(e, r) {
28
+ return r(e);
29
29
  }
30
- function m(e, n) {
30
+ function h(e, r) {
31
31
  if (e != null)
32
- return n(e);
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
- l as Deferred,
36
- c as delay,
37
- s as isNil,
38
- f as notEmpty,
39
- d as tap,
40
- m as tapIf
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.43.28",
3
+ "version": "1.44.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "styles": "dist/index.js",
@@ -26,8 +26,8 @@
26
26
  "d3-format": "^3.1.0",
27
27
  "zod": "~3.23.8",
28
28
  "@milaboratories/biowasm-tools": "2.0.0",
29
- "@platforma-sdk/model": "1.43.21",
30
- "@milaboratories/uikit": "2.4.19"
29
+ "@platforma-sdk/model": "1.43.29",
30
+ "@milaboratories/uikit": "2.4.21"
31
31
  },
32
32
  "devDependencies": {
33
33
  "happy-dom": "^15.11.7",
@@ -42,11 +42,11 @@
42
42
  "yarpm": "^1.2.0",
43
43
  "fast-json-patch": "^3.1.1",
44
44
  "@faker-js/faker": "^9.2.0",
45
- "@milaboratories/helpers": "1.9.0",
46
- "@milaboratories/build-configs": "1.0.8",
47
- "@milaboratories/ts-builder": "1.0.5",
48
45
  "@milaboratories/ts-configs": "1.0.6",
49
- "@milaboratories/eslint-config": "1.0.4"
46
+ "@milaboratories/eslint-config": "1.0.4",
47
+ "@milaboratories/helpers": "1.10.0",
48
+ "@milaboratories/ts-builder": "1.0.5",
49
+ "@milaboratories/build-configs": "1.0.8"
50
50
  },
51
51
  "scripts": {
52
52
  "test": "vitest run --passWithNoTests",
@@ -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: Zod.SafeParseReturnType<unknown, unknown> | undefined = undefined;
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 fileDataRefs = new Map<FileHandle, ShallowRef<FileContentData | undefined>>();
49
- constructor(_ops?: Partial<ReactiveFileContentOps>) {
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 refFromMap = this.fileDataRefs.get(handle);
64
- if (refFromMap !== undefined) return refFromMap;
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
- this.fileDataRefs.set(handle, newRef);
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 = 4;
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.fileDataCache.fetch(handle);
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?.value?.str);
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?.value?.rawJson : dataRef?.value?.validatedJson(schema)) as T);
207
+ return computed(() => (schema === undefined ? dataRef.value?.rawJson : dataRef.value?.validatedJson(schema)) as T);
118
208
  }
119
209
 
120
- private static globalInstance = new ReactiveFileContent();
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
- public static getContentBytes(handle: FileHandle): ComputedRef<Uint8Array | undefined>;
123
- public static getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined;
124
- public static getContentBytes(handle: FileHandle | undefined): ComputedRef<Uint8Array | undefined> | undefined {
125
- return ReactiveFileContent.globalInstance.getContentBytes(handle);
218
+ addScope(scope);
219
+
220
+ onScopeDispose(() => {
221
+ scopes.delete(scope);
222
+ });
223
+
224
+ return scope;
126
225
  }
127
226
 
128
- public static getContentString(handle: FileHandle): ComputedRef<string | undefined>;
129
- public static getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined;
130
- public static getContentString(handle: FileHandle | undefined): ComputedRef<string | undefined> | undefined {
131
- return ReactiveFileContent.globalInstance.getContentString(handle);
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
- public static getContentJson<T>(handle: FileHandle, schema: ZodSchema<T>): ComputedRef<T | undefined>;
135
- public static getContentJson<T>(handle: FileHandle | undefined, schema: ZodSchema<T>): ComputedRef<T | undefined> | undefined;
136
- public static getContentJson<T = unknown>(handle: FileHandle): ComputedRef<T | undefined>;
137
- public static getContentJson<T = unknown>(handle: FileHandle | undefined): ComputedRef<T | undefined> | undefined;
138
- public static getContentJson<T>(handle: FileHandle | undefined, schema?: ZodSchema<T>): ComputedRef<T | undefined> | undefined {
139
- return ReactiveFileContent.globalInstance.getContentJson(handle, schema);
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
  }