@koolbase/core 12.19.0 → 12.20.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.
@@ -0,0 +1,39 @@
1
+ import type { KoolbaseFileInfo } from './storage-files.js';
2
+ export type FileStatus = 'loading' | 'loaded' | 'notFound' | 'error';
3
+ export interface FileState {
4
+ readonly status: FileStatus;
5
+ /** The file's details, while status is 'loaded'. */
6
+ readonly file: KoolbaseFileInfo | null;
7
+ /** Why the first load failed, while status is 'error'. */
8
+ readonly error: unknown;
9
+ /** True while refresh() runs. */
10
+ readonly refreshing: boolean;
11
+ }
12
+ export declare const initialFileState: FileState;
13
+ type FileReader = {
14
+ getFileInfo(bucket: string, path: string): Promise<KoolbaseFileInfo>;
15
+ };
16
+ export declare class KoolbaseFileController {
17
+ readonly bucket: string;
18
+ readonly path: string;
19
+ private readonly storage;
20
+ private state;
21
+ private readonly listeners;
22
+ private generation;
23
+ private disposed;
24
+ private unregister;
25
+ constructor(storage: FileReader, bucket: string, path: string);
26
+ getState(): FileState;
27
+ /** Called on every change. Returns the unsubscribe. */
28
+ subscribe(listener: () => void): () => void;
29
+ /** The first load. Call once; refresh() for later loads. */
30
+ load(): Promise<void>;
31
+ /** Read it again. The file stays shown while it runs, and stays if it fails. */
32
+ refresh(): Promise<void>;
33
+ /** Stops all updates. Results still in flight are dropped when they land. */
34
+ dispose(): void;
35
+ private run;
36
+ private isStale;
37
+ private set;
38
+ }
39
+ export {};
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ // One file's details, by path, framework-free (storage S4).
3
+ //
4
+ // The data half of a file detail screen: loading, loaded, not found or
5
+ // error, and refresh. No React here, so it is tested in this package; useFile
6
+ // in @koolbase/react-native and @koolbase/js/react is a thin wrapper.
7
+ // Mirrors the Flutter SDK's KoolbaseFileController, with the record view's
8
+ // rules:
9
+ //
10
+ // - an empty path is not found, with no request
11
+ // - not found is not an error: the API answers a file you may not view as
12
+ // not found too, so a detail screen says "not available"
13
+ // - stale beats blank: a failed refresh keeps the file shown
14
+ // - a change made through this SDK in the bucket (an upload, a delete, a
15
+ // move, a metadata change, a restore) reads it again silently: deleted or
16
+ // moved away, the file is not found; replaced, it shows its new size
17
+ // - a result a later load superseded, or one landing after dispose(), is
18
+ // dropped
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.KoolbaseFileController = exports.initialFileState = void 0;
21
+ const files_refresh_js_1 = require("./files-refresh.js");
22
+ const storage_errors_js_1 = require("./storage-errors.js");
23
+ exports.initialFileState = Object.freeze({
24
+ status: 'loading',
25
+ file: null,
26
+ error: null,
27
+ refreshing: false,
28
+ });
29
+ /** Whether [e] says there is no such file (or none this caller may view). */
30
+ function isNotFound(e) {
31
+ return e instanceof storage_errors_js_1.KoolbaseStorageNotFoundError || e?.code === 'not_found';
32
+ }
33
+ class KoolbaseFileController {
34
+ constructor(storage, bucket, path) {
35
+ this.state = exports.initialFileState;
36
+ this.listeners = new Set();
37
+ this.generation = 0;
38
+ this.disposed = false;
39
+ this.unregister = null;
40
+ this.storage = storage;
41
+ this.bucket = bucket;
42
+ this.path = path;
43
+ }
44
+ getState() {
45
+ return this.state;
46
+ }
47
+ /** Called on every change. Returns the unsubscribe. */
48
+ subscribe(listener) {
49
+ if (this.disposed)
50
+ return () => { };
51
+ this.listeners.add(listener);
52
+ return () => {
53
+ this.listeners.delete(listener);
54
+ };
55
+ }
56
+ /** The first load. Call once; refresh() for later loads. */
57
+ async load() {
58
+ if (this.disposed)
59
+ return;
60
+ if (this.path)
61
+ this.unregister ?? (this.unregister = (0, files_refresh_js_1.registerFilesRefresher)(this.bucket, () => this.run()));
62
+ await this.run();
63
+ }
64
+ /** Read it again. The file stays shown while it runs, and stays if it fails. */
65
+ async refresh() {
66
+ if (this.disposed)
67
+ return;
68
+ this.set({ refreshing: true });
69
+ await this.run();
70
+ }
71
+ /** Stops all updates. Results still in flight are dropped when they land. */
72
+ dispose() {
73
+ this.disposed = true;
74
+ this.unregister?.();
75
+ this.unregister = null;
76
+ this.listeners.clear();
77
+ }
78
+ async run() {
79
+ if (this.disposed)
80
+ return;
81
+ const gen = ++this.generation;
82
+ if (!this.path) {
83
+ this.set({ status: 'notFound', file: null, error: null, refreshing: false });
84
+ return;
85
+ }
86
+ try {
87
+ const file = await this.storage.getFileInfo(this.bucket, this.path);
88
+ if (this.isStale(gen))
89
+ return;
90
+ this.set({ status: 'loaded', file, error: null, refreshing: false });
91
+ }
92
+ catch (e) {
93
+ if (this.isStale(gen))
94
+ return;
95
+ if (isNotFound(e))
96
+ this.set({ status: 'notFound', file: null, error: null, refreshing: false });
97
+ // Only a first load with nothing to show is the error state.
98
+ else if (this.state.file === null)
99
+ this.set({ status: 'error', file: null, error: e, refreshing: false });
100
+ else
101
+ this.set({ refreshing: false });
102
+ }
103
+ }
104
+ isStale(gen) {
105
+ return this.disposed || gen !== this.generation;
106
+ }
107
+ set(change) {
108
+ if (this.disposed)
109
+ return;
110
+ this.state = Object.freeze({ ...this.state, ...change });
111
+ for (const l of [...this.listeners])
112
+ l();
113
+ }
114
+ }
115
+ exports.KoolbaseFileController = KoolbaseFileController;
@@ -25,6 +25,8 @@ export type { KoolbaseUploadProgress, KoolbaseUploadState } from './storage-tran
25
25
  export type { KoolbaseFileInfo, KoolbaseFilePage, KoolbaseFileSort, ListFilesOptions } from './storage-files.js';
26
26
  export { KoolbaseFilesController, initialFilesState, filesQueryKey } from './files-controller.js';
27
27
  export type { FilesQuery, FilesState, FilesStatus } from './files-controller.js';
28
+ export { KoolbaseFileController, initialFileState } from './file-controller.js';
29
+ export type { FileState, FileStatus } from './file-controller.js';
28
30
  export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
29
31
  export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
30
32
  export type { FiscalIntentResult, FiscalStatus } from './fiscal.js';
package/dist/cjs/index.js CHANGED
@@ -19,7 +19,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
19
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.memoryPlatform = exports.getPlatform = exports.setPlatform = exports.RestoreResult = exports.koolbaseSdkVersion = exports.getOrCreateDeviceId = exports.KoolbaseMessaging = exports.isPending = exports.isFiscalized = exports.decodeFiscalResponse = exports.KoolbaseFiscalError = exports.KoolbaseFiscal = exports.disabledAnalytics = exports.KoolbaseAnalytics = exports.filesQueryKey = exports.initialFilesState = exports.KoolbaseFilesController = exports.KoolbaseUploadTask = exports.KoolbaseStorage = exports.KoolbaseConnectivity = exports.KoolbaseRealtime = exports.KoolbaseFunctions = exports.KoolbaseFlags = exports.initialRecordState = exports.KoolbaseRecordController = exports.initialCollectionState = exports.collectionQueryKey = exports.KoolbaseCollectionController = exports.KoolbaseDatabase = exports.KoolbaseAuth = void 0;
22
+ exports.memoryPlatform = exports.getPlatform = exports.setPlatform = exports.RestoreResult = exports.koolbaseSdkVersion = exports.getOrCreateDeviceId = exports.KoolbaseMessaging = exports.isPending = exports.isFiscalized = exports.decodeFiscalResponse = exports.KoolbaseFiscalError = exports.KoolbaseFiscal = exports.disabledAnalytics = exports.KoolbaseAnalytics = exports.initialFileState = exports.KoolbaseFileController = exports.filesQueryKey = exports.initialFilesState = exports.KoolbaseFilesController = exports.KoolbaseUploadTask = exports.KoolbaseStorage = exports.KoolbaseConnectivity = exports.KoolbaseRealtime = exports.KoolbaseFunctions = exports.KoolbaseFlags = exports.initialRecordState = exports.KoolbaseRecordController = exports.initialCollectionState = exports.collectionQueryKey = exports.KoolbaseCollectionController = exports.KoolbaseDatabase = exports.KoolbaseAuth = void 0;
23
23
  __exportStar(require("./types.js"), exports);
24
24
  __exportStar(require("./errors.js"), exports);
25
25
  __exportStar(require("./network.js"), exports);
@@ -58,6 +58,10 @@ var files_controller_js_1 = require("./files-controller.js");
58
58
  Object.defineProperty(exports, "KoolbaseFilesController", { enumerable: true, get: function () { return files_controller_js_1.KoolbaseFilesController; } });
59
59
  Object.defineProperty(exports, "initialFilesState", { enumerable: true, get: function () { return files_controller_js_1.initialFilesState; } });
60
60
  Object.defineProperty(exports, "filesQueryKey", { enumerable: true, get: function () { return files_controller_js_1.filesQueryKey; } });
61
+ // One file's details as state (storage S4).
62
+ var file_controller_js_1 = require("./file-controller.js");
63
+ Object.defineProperty(exports, "KoolbaseFileController", { enumerable: true, get: function () { return file_controller_js_1.KoolbaseFileController; } });
64
+ Object.defineProperty(exports, "initialFileState", { enumerable: true, get: function () { return file_controller_js_1.initialFileState; } });
61
65
  var analytics_js_1 = require("./analytics.js");
62
66
  Object.defineProperty(exports, "KoolbaseAnalytics", { enumerable: true, get: function () { return analytics_js_1.KoolbaseAnalytics; } });
63
67
  Object.defineProperty(exports, "disabledAnalytics", { enumerable: true, get: function () { return analytics_js_1.disabledAnalytics; } });
@@ -1,3 +1,4 @@
1
1
  export { createUseCollection, type UseCollectionResult } from './use-collection.js';
2
2
  export { createUseRecord, type UseRecordResult } from './use-record.js';
3
3
  export { createUseFiles, type UseFilesResult } from './use-files.js';
4
+ export { createUseFile, type UseFileResult } from './use-file.js';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createUseFiles = exports.createUseRecord = exports.createUseCollection = void 0;
3
+ exports.createUseFile = exports.createUseFiles = exports.createUseRecord = exports.createUseCollection = void 0;
4
4
  // React hooks over core's controllers: ONE implementation, exported to apps by
5
5
  // @koolbase/react-native (from its root, as always) and @koolbase/js/react.
6
6
  // React only -- nothing native, nothing DOM. Deliberately not exported from
@@ -11,3 +11,5 @@ var use_record_js_1 = require("./use-record.js");
11
11
  Object.defineProperty(exports, "createUseRecord", { enumerable: true, get: function () { return use_record_js_1.createUseRecord; } });
12
12
  var use_files_js_1 = require("./use-files.js");
13
13
  Object.defineProperty(exports, "createUseFiles", { enumerable: true, get: function () { return use_files_js_1.createUseFiles; } });
14
+ var use_file_js_1 = require("./use-file.js");
15
+ Object.defineProperty(exports, "createUseFile", { enumerable: true, get: function () { return use_file_js_1.createUseFile; } });
@@ -0,0 +1,6 @@
1
+ import { type FileState, type KoolbaseStorage } from '../index.js';
2
+ export interface UseFileResult extends FileState {
3
+ /** Read it again. The file stays shown while it runs, and stays if it fails. */
4
+ refresh: () => Promise<void>;
5
+ }
6
+ export declare function createUseFile(getStorage: () => Pick<KoolbaseStorage, 'getFileInfo'>): (bucket: string, path: string | null | undefined) => UseFileResult;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ // useFile: one file's details as React state (storage S4).
3
+ //
4
+ // A thin wrapper. Everything about getting the file right lives in
5
+ // @koolbase/core's KoolbaseFileController, which is tested there; this file
6
+ // only ties one controller's lifetime to a component's.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createUseFile = createUseFile;
9
+ const react_1 = require("react");
10
+ const index_js_1 = require("../index.js");
11
+ const noop = () => { };
12
+ const done = () => Promise.resolve();
13
+ function createUseFile(getStorage) {
14
+ return function useFile(bucket, path) {
15
+ const p = path ?? '';
16
+ const key = JSON.stringify([bucket, p]);
17
+ const [current, setCurrent] = (0, react_1.useState)(null);
18
+ (0, react_1.useEffect)(() => {
19
+ // Created here, not during render, so React strict mode's second run
20
+ // never keeps a disposed controller.
21
+ const controller = new index_js_1.KoolbaseFileController(getStorage(), bucket, p);
22
+ setCurrent({ key, controller });
23
+ void controller.load();
24
+ return () => controller.dispose();
25
+ }, [key]);
26
+ // A controller from the previous file reads as a fresh load, never as
27
+ // the previous file.
28
+ const controller = current?.key === key ? current.controller : null;
29
+ const subscribe = (0, react_1.useCallback)((onChange) => (controller ? controller.subscribe(onChange) : noop), [controller]);
30
+ const getSnapshot = (0, react_1.useCallback)(() => (controller ? controller.getState() : index_js_1.initialFileState), [controller]);
31
+ const state = (0, react_1.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
32
+ const refresh = (0, react_1.useCallback)(() => (controller ? controller.refresh() : done()), [controller]);
33
+ return { ...state, refresh };
34
+ };
35
+ }
@@ -0,0 +1,39 @@
1
+ import type { KoolbaseFileInfo } from './storage-files.js';
2
+ export type FileStatus = 'loading' | 'loaded' | 'notFound' | 'error';
3
+ export interface FileState {
4
+ readonly status: FileStatus;
5
+ /** The file's details, while status is 'loaded'. */
6
+ readonly file: KoolbaseFileInfo | null;
7
+ /** Why the first load failed, while status is 'error'. */
8
+ readonly error: unknown;
9
+ /** True while refresh() runs. */
10
+ readonly refreshing: boolean;
11
+ }
12
+ export declare const initialFileState: FileState;
13
+ type FileReader = {
14
+ getFileInfo(bucket: string, path: string): Promise<KoolbaseFileInfo>;
15
+ };
16
+ export declare class KoolbaseFileController {
17
+ readonly bucket: string;
18
+ readonly path: string;
19
+ private readonly storage;
20
+ private state;
21
+ private readonly listeners;
22
+ private generation;
23
+ private disposed;
24
+ private unregister;
25
+ constructor(storage: FileReader, bucket: string, path: string);
26
+ getState(): FileState;
27
+ /** Called on every change. Returns the unsubscribe. */
28
+ subscribe(listener: () => void): () => void;
29
+ /** The first load. Call once; refresh() for later loads. */
30
+ load(): Promise<void>;
31
+ /** Read it again. The file stays shown while it runs, and stays if it fails. */
32
+ refresh(): Promise<void>;
33
+ /** Stops all updates. Results still in flight are dropped when they land. */
34
+ dispose(): void;
35
+ private run;
36
+ private isStale;
37
+ private set;
38
+ }
39
+ export {};
@@ -0,0 +1,111 @@
1
+ // One file's details, by path, framework-free (storage S4).
2
+ //
3
+ // The data half of a file detail screen: loading, loaded, not found or
4
+ // error, and refresh. No React here, so it is tested in this package; useFile
5
+ // in @koolbase/react-native and @koolbase/js/react is a thin wrapper.
6
+ // Mirrors the Flutter SDK's KoolbaseFileController, with the record view's
7
+ // rules:
8
+ //
9
+ // - an empty path is not found, with no request
10
+ // - not found is not an error: the API answers a file you may not view as
11
+ // not found too, so a detail screen says "not available"
12
+ // - stale beats blank: a failed refresh keeps the file shown
13
+ // - a change made through this SDK in the bucket (an upload, a delete, a
14
+ // move, a metadata change, a restore) reads it again silently: deleted or
15
+ // moved away, the file is not found; replaced, it shows its new size
16
+ // - a result a later load superseded, or one landing after dispose(), is
17
+ // dropped
18
+ import { registerFilesRefresher } from './files-refresh.js';
19
+ import { KoolbaseStorageNotFoundError } from './storage-errors.js';
20
+ export const initialFileState = Object.freeze({
21
+ status: 'loading',
22
+ file: null,
23
+ error: null,
24
+ refreshing: false,
25
+ });
26
+ /** Whether [e] says there is no such file (or none this caller may view). */
27
+ function isNotFound(e) {
28
+ return e instanceof KoolbaseStorageNotFoundError || e?.code === 'not_found';
29
+ }
30
+ export class KoolbaseFileController {
31
+ constructor(storage, bucket, path) {
32
+ this.state = initialFileState;
33
+ this.listeners = new Set();
34
+ this.generation = 0;
35
+ this.disposed = false;
36
+ this.unregister = null;
37
+ this.storage = storage;
38
+ this.bucket = bucket;
39
+ this.path = path;
40
+ }
41
+ getState() {
42
+ return this.state;
43
+ }
44
+ /** Called on every change. Returns the unsubscribe. */
45
+ subscribe(listener) {
46
+ if (this.disposed)
47
+ return () => { };
48
+ this.listeners.add(listener);
49
+ return () => {
50
+ this.listeners.delete(listener);
51
+ };
52
+ }
53
+ /** The first load. Call once; refresh() for later loads. */
54
+ async load() {
55
+ if (this.disposed)
56
+ return;
57
+ if (this.path)
58
+ this.unregister ?? (this.unregister = registerFilesRefresher(this.bucket, () => this.run()));
59
+ await this.run();
60
+ }
61
+ /** Read it again. The file stays shown while it runs, and stays if it fails. */
62
+ async refresh() {
63
+ if (this.disposed)
64
+ return;
65
+ this.set({ refreshing: true });
66
+ await this.run();
67
+ }
68
+ /** Stops all updates. Results still in flight are dropped when they land. */
69
+ dispose() {
70
+ this.disposed = true;
71
+ this.unregister?.();
72
+ this.unregister = null;
73
+ this.listeners.clear();
74
+ }
75
+ async run() {
76
+ if (this.disposed)
77
+ return;
78
+ const gen = ++this.generation;
79
+ if (!this.path) {
80
+ this.set({ status: 'notFound', file: null, error: null, refreshing: false });
81
+ return;
82
+ }
83
+ try {
84
+ const file = await this.storage.getFileInfo(this.bucket, this.path);
85
+ if (this.isStale(gen))
86
+ return;
87
+ this.set({ status: 'loaded', file, error: null, refreshing: false });
88
+ }
89
+ catch (e) {
90
+ if (this.isStale(gen))
91
+ return;
92
+ if (isNotFound(e))
93
+ this.set({ status: 'notFound', file: null, error: null, refreshing: false });
94
+ // Only a first load with nothing to show is the error state.
95
+ else if (this.state.file === null)
96
+ this.set({ status: 'error', file: null, error: e, refreshing: false });
97
+ else
98
+ this.set({ refreshing: false });
99
+ }
100
+ }
101
+ isStale(gen) {
102
+ return this.disposed || gen !== this.generation;
103
+ }
104
+ set(change) {
105
+ if (this.disposed)
106
+ return;
107
+ this.state = Object.freeze({ ...this.state, ...change });
108
+ for (const l of [...this.listeners])
109
+ l();
110
+ }
111
+ }
@@ -25,6 +25,8 @@ export type { KoolbaseUploadProgress, KoolbaseUploadState } from './storage-tran
25
25
  export type { KoolbaseFileInfo, KoolbaseFilePage, KoolbaseFileSort, ListFilesOptions } from './storage-files.js';
26
26
  export { KoolbaseFilesController, initialFilesState, filesQueryKey } from './files-controller.js';
27
27
  export type { FilesQuery, FilesState, FilesStatus } from './files-controller.js';
28
+ export { KoolbaseFileController, initialFileState } from './file-controller.js';
29
+ export type { FileState, FileStatus } from './file-controller.js';
28
30
  export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
29
31
  export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
30
32
  export type { FiscalIntentResult, FiscalStatus } from './fiscal.js';
package/dist/esm/index.js CHANGED
@@ -25,6 +25,8 @@ export { KoolbaseStorage } from './storage.js';
25
25
  export { KoolbaseUploadTask } from './storage-transfer.js';
26
26
  // A bucket's files as list state (storage S4).
27
27
  export { KoolbaseFilesController, initialFilesState, filesQueryKey } from './files-controller.js';
28
+ // One file's details as state (storage S4).
29
+ export { KoolbaseFileController, initialFileState } from './file-controller.js';
28
30
  export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
29
31
  export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
30
32
  export { KoolbaseMessaging } from './messaging.js';
@@ -1,3 +1,4 @@
1
1
  export { createUseCollection, type UseCollectionResult } from './use-collection.js';
2
2
  export { createUseRecord, type UseRecordResult } from './use-record.js';
3
3
  export { createUseFiles, type UseFilesResult } from './use-files.js';
4
+ export { createUseFile, type UseFileResult } from './use-file.js';
@@ -5,3 +5,4 @@
5
5
  export { createUseCollection } from './use-collection.js';
6
6
  export { createUseRecord } from './use-record.js';
7
7
  export { createUseFiles } from './use-files.js';
8
+ export { createUseFile } from './use-file.js';
@@ -0,0 +1,6 @@
1
+ import { type FileState, type KoolbaseStorage } from '../index.js';
2
+ export interface UseFileResult extends FileState {
3
+ /** Read it again. The file stays shown while it runs, and stays if it fails. */
4
+ refresh: () => Promise<void>;
5
+ }
6
+ export declare function createUseFile(getStorage: () => Pick<KoolbaseStorage, 'getFileInfo'>): (bucket: string, path: string | null | undefined) => UseFileResult;
@@ -0,0 +1,32 @@
1
+ // useFile: one file's details as React state (storage S4).
2
+ //
3
+ // A thin wrapper. Everything about getting the file right lives in
4
+ // @koolbase/core's KoolbaseFileController, which is tested there; this file
5
+ // only ties one controller's lifetime to a component's.
6
+ import { useCallback, useEffect, useState, useSyncExternalStore } from 'react';
7
+ import { KoolbaseFileController, initialFileState } from '../index.js';
8
+ const noop = () => { };
9
+ const done = () => Promise.resolve();
10
+ export function createUseFile(getStorage) {
11
+ return function useFile(bucket, path) {
12
+ const p = path ?? '';
13
+ const key = JSON.stringify([bucket, p]);
14
+ const [current, setCurrent] = useState(null);
15
+ useEffect(() => {
16
+ // Created here, not during render, so React strict mode's second run
17
+ // never keeps a disposed controller.
18
+ const controller = new KoolbaseFileController(getStorage(), bucket, p);
19
+ setCurrent({ key, controller });
20
+ void controller.load();
21
+ return () => controller.dispose();
22
+ }, [key]);
23
+ // A controller from the previous file reads as a fresh load, never as
24
+ // the previous file.
25
+ const controller = current?.key === key ? current.controller : null;
26
+ const subscribe = useCallback((onChange) => (controller ? controller.subscribe(onChange) : noop), [controller]);
27
+ const getSnapshot = useCallback(() => (controller ? controller.getState() : initialFileState), [controller]);
28
+ const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
29
+ const refresh = useCallback(() => (controller ? controller.refresh() : done()), [controller]);
30
+ return { ...state, refresh };
31
+ };
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koolbase/core",
3
- "version": "12.19.0",
3
+ "version": "12.20.0",
4
4
  "description": "Koolbase SDK core \u2014 shared behaviour behind @koolbase/react-native and @koolbase/js. Install one of those, not this.",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "types": "./dist/esm/index.d.ts",