@arcanejs/react-toolkit 0.2.0 → 0.3.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.
- package/dist/data.d.mts +66 -15
- package/dist/data.d.ts +66 -15
- package/dist/data.js +169 -124
- package/dist/data.mjs +166 -123
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/data.d.mts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { ReactNode, FC, Context } from 'react';
|
|
2
2
|
import { ZodType } from 'zod';
|
|
3
3
|
|
|
4
|
-
type
|
|
4
|
+
type WithPathChange = {
|
|
5
|
+
/**
|
|
6
|
+
* When the file path changes and the file does not yet exist,
|
|
7
|
+
* should the previous data be stored in the new file
|
|
8
|
+
* or should the new file be reset to the default value?
|
|
9
|
+
*
|
|
10
|
+
* @default 'defaultValue'
|
|
11
|
+
*/
|
|
12
|
+
onPathChange?: 'transfer' | 'defaultValue';
|
|
13
|
+
};
|
|
14
|
+
type DataFileUsage = WithPathChange & {
|
|
15
|
+
/**
|
|
16
|
+
* The path to where the JSON data should be stored,
|
|
17
|
+
* this will be relative to the current working directory.
|
|
18
|
+
*/
|
|
5
19
|
path: string;
|
|
20
|
+
};
|
|
21
|
+
type ProviderProps = DataFileUsage & {
|
|
6
22
|
children: ReactNode;
|
|
7
23
|
};
|
|
8
24
|
type DataFileUpdater<T> = (update: (current: T) => T) => void;
|
|
@@ -21,25 +37,60 @@ type DataFileContext<T> = {
|
|
|
21
37
|
*/
|
|
22
38
|
error: unknown;
|
|
23
39
|
};
|
|
24
|
-
type
|
|
40
|
+
type DataFileDefinition<T> = {
|
|
25
41
|
Provider: FC<ProviderProps>;
|
|
26
42
|
context: Context<DataFileContext<T>>;
|
|
43
|
+
useDataFile: (props: DataFileUsage) => DataFileCore<T>;
|
|
27
44
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
declare function useDataFileData<T>(dataFile: DataFileDefinition<T>): T;
|
|
46
|
+
declare function useDataFileUpdater<T>(dataFile: DataFileDefinition<T>): DataFileUpdater<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Convenience hook to use the internal data-file context hook
|
|
49
|
+
*/
|
|
50
|
+
declare function useDataFileContext<T>(dataFile: DataFileDefinition<T>): DataFileContext<T>;
|
|
51
|
+
/**
|
|
52
|
+
* Convenience hook to use the internal data-file definition hook
|
|
53
|
+
* in a more react-like manner.
|
|
54
|
+
*/
|
|
55
|
+
declare function useDataFile<T>(dataFile: DataFileDefinition<T>, usage: DataFileUsage): DataFileCore<T>;
|
|
56
|
+
type DataState<T> = {
|
|
57
|
+
status: 'loading';
|
|
31
58
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
59
|
+
* The data is not yet loaded, so this will be undefined,
|
|
60
|
+
* but it's listed here as a property so that the data property can be
|
|
61
|
+
* directly used without a type-guard that uses `state`.
|
|
35
62
|
*
|
|
36
|
-
*
|
|
63
|
+
* This should hopefully also avoid situations where users may check for
|
|
64
|
+
* `state === 'ready'` instead of `state !== 'loading'`,
|
|
65
|
+
* and accidentally avoid displaying data that's available but unsaved.
|
|
37
66
|
*/
|
|
38
|
-
|
|
67
|
+
data: undefined;
|
|
68
|
+
} | {
|
|
69
|
+
status: 'error';
|
|
70
|
+
data: T | undefined;
|
|
71
|
+
error: unknown;
|
|
72
|
+
} | {
|
|
73
|
+
status: 'ready';
|
|
74
|
+
data: T;
|
|
75
|
+
};
|
|
76
|
+
type UseDataFileCoreProps<T> = WithPathChange & {
|
|
77
|
+
schema: ZodType<T>;
|
|
78
|
+
defaultValue: T;
|
|
79
|
+
path: string;
|
|
80
|
+
};
|
|
81
|
+
type DataFileCore<T> = {
|
|
82
|
+
data: DataState<T>;
|
|
83
|
+
updateData: DataFileUpdater<T>;
|
|
84
|
+
saveData: () => void;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Primary hook for & logic for using data files.
|
|
88
|
+
*/
|
|
89
|
+
declare function useDataFileCore<T>({ schema, defaultValue, path, onPathChange, }: UseDataFileCoreProps<T>): DataFileCore<T>;
|
|
90
|
+
type CreateDataFileDefinitionProps<T> = {
|
|
91
|
+
schema: ZodType<T>;
|
|
92
|
+
defaultValue: T;
|
|
39
93
|
};
|
|
40
|
-
declare function
|
|
41
|
-
declare function useDataFileUpdater<T>(dataFile: DataFile<T>): DataFileUpdater<T>;
|
|
42
|
-
declare function useDataFile<T>(dataFile: DataFile<T>): DataFileContext<T>;
|
|
43
|
-
declare function createDataFileSpec<T>({ schema, defaultValue, onPathChange, }: DataFileProps<T>): DataFile<T>;
|
|
94
|
+
declare function createDataFileDefinition<T>({ schema, defaultValue, }: CreateDataFileDefinitionProps<T>): DataFileDefinition<T>;
|
|
44
95
|
|
|
45
|
-
export { type ProviderProps,
|
|
96
|
+
export { type CreateDataFileDefinitionProps, type DataFileContext, type DataFileCore, type DataFileDefinition, type DataFileUpdater, type DataState, type ProviderProps, type UseDataFileCoreProps, createDataFileDefinition, useDataFile, useDataFileContext, useDataFileCore, useDataFileData, useDataFileUpdater };
|
package/dist/data.d.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { ReactNode, FC, Context } from 'react';
|
|
2
2
|
import { ZodType } from 'zod';
|
|
3
3
|
|
|
4
|
-
type
|
|
4
|
+
type WithPathChange = {
|
|
5
|
+
/**
|
|
6
|
+
* When the file path changes and the file does not yet exist,
|
|
7
|
+
* should the previous data be stored in the new file
|
|
8
|
+
* or should the new file be reset to the default value?
|
|
9
|
+
*
|
|
10
|
+
* @default 'defaultValue'
|
|
11
|
+
*/
|
|
12
|
+
onPathChange?: 'transfer' | 'defaultValue';
|
|
13
|
+
};
|
|
14
|
+
type DataFileUsage = WithPathChange & {
|
|
15
|
+
/**
|
|
16
|
+
* The path to where the JSON data should be stored,
|
|
17
|
+
* this will be relative to the current working directory.
|
|
18
|
+
*/
|
|
5
19
|
path: string;
|
|
20
|
+
};
|
|
21
|
+
type ProviderProps = DataFileUsage & {
|
|
6
22
|
children: ReactNode;
|
|
7
23
|
};
|
|
8
24
|
type DataFileUpdater<T> = (update: (current: T) => T) => void;
|
|
@@ -21,25 +37,60 @@ type DataFileContext<T> = {
|
|
|
21
37
|
*/
|
|
22
38
|
error: unknown;
|
|
23
39
|
};
|
|
24
|
-
type
|
|
40
|
+
type DataFileDefinition<T> = {
|
|
25
41
|
Provider: FC<ProviderProps>;
|
|
26
42
|
context: Context<DataFileContext<T>>;
|
|
43
|
+
useDataFile: (props: DataFileUsage) => DataFileCore<T>;
|
|
27
44
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
declare function useDataFileData<T>(dataFile: DataFileDefinition<T>): T;
|
|
46
|
+
declare function useDataFileUpdater<T>(dataFile: DataFileDefinition<T>): DataFileUpdater<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Convenience hook to use the internal data-file context hook
|
|
49
|
+
*/
|
|
50
|
+
declare function useDataFileContext<T>(dataFile: DataFileDefinition<T>): DataFileContext<T>;
|
|
51
|
+
/**
|
|
52
|
+
* Convenience hook to use the internal data-file definition hook
|
|
53
|
+
* in a more react-like manner.
|
|
54
|
+
*/
|
|
55
|
+
declare function useDataFile<T>(dataFile: DataFileDefinition<T>, usage: DataFileUsage): DataFileCore<T>;
|
|
56
|
+
type DataState<T> = {
|
|
57
|
+
status: 'loading';
|
|
31
58
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
59
|
+
* The data is not yet loaded, so this will be undefined,
|
|
60
|
+
* but it's listed here as a property so that the data property can be
|
|
61
|
+
* directly used without a type-guard that uses `state`.
|
|
35
62
|
*
|
|
36
|
-
*
|
|
63
|
+
* This should hopefully also avoid situations where users may check for
|
|
64
|
+
* `state === 'ready'` instead of `state !== 'loading'`,
|
|
65
|
+
* and accidentally avoid displaying data that's available but unsaved.
|
|
37
66
|
*/
|
|
38
|
-
|
|
67
|
+
data: undefined;
|
|
68
|
+
} | {
|
|
69
|
+
status: 'error';
|
|
70
|
+
data: T | undefined;
|
|
71
|
+
error: unknown;
|
|
72
|
+
} | {
|
|
73
|
+
status: 'ready';
|
|
74
|
+
data: T;
|
|
75
|
+
};
|
|
76
|
+
type UseDataFileCoreProps<T> = WithPathChange & {
|
|
77
|
+
schema: ZodType<T>;
|
|
78
|
+
defaultValue: T;
|
|
79
|
+
path: string;
|
|
80
|
+
};
|
|
81
|
+
type DataFileCore<T> = {
|
|
82
|
+
data: DataState<T>;
|
|
83
|
+
updateData: DataFileUpdater<T>;
|
|
84
|
+
saveData: () => void;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Primary hook for & logic for using data files.
|
|
88
|
+
*/
|
|
89
|
+
declare function useDataFileCore<T>({ schema, defaultValue, path, onPathChange, }: UseDataFileCoreProps<T>): DataFileCore<T>;
|
|
90
|
+
type CreateDataFileDefinitionProps<T> = {
|
|
91
|
+
schema: ZodType<T>;
|
|
92
|
+
defaultValue: T;
|
|
39
93
|
};
|
|
40
|
-
declare function
|
|
41
|
-
declare function useDataFileUpdater<T>(dataFile: DataFile<T>): DataFileUpdater<T>;
|
|
42
|
-
declare function useDataFile<T>(dataFile: DataFile<T>): DataFileContext<T>;
|
|
43
|
-
declare function createDataFileSpec<T>({ schema, defaultValue, onPathChange, }: DataFileProps<T>): DataFile<T>;
|
|
94
|
+
declare function createDataFileDefinition<T>({ schema, defaultValue, }: CreateDataFileDefinitionProps<T>): DataFileDefinition<T>;
|
|
44
95
|
|
|
45
|
-
export { type ProviderProps,
|
|
96
|
+
export { type CreateDataFileDefinitionProps, type DataFileContext, type DataFileCore, type DataFileDefinition, type DataFileUpdater, type DataState, type ProviderProps, type UseDataFileCoreProps, createDataFileDefinition, useDataFile, useDataFileContext, useDataFileCore, useDataFileData, useDataFileUpdater };
|
package/dist/data.js
CHANGED
|
@@ -20,8 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/data.tsx
|
|
21
21
|
var data_exports = {};
|
|
22
22
|
__export(data_exports, {
|
|
23
|
-
|
|
23
|
+
createDataFileDefinition: () => createDataFileDefinition,
|
|
24
24
|
useDataFile: () => useDataFile,
|
|
25
|
+
useDataFileContext: () => useDataFileContext,
|
|
26
|
+
useDataFileCore: () => useDataFileCore,
|
|
25
27
|
useDataFileData: () => useDataFileData,
|
|
26
28
|
useDataFileUpdater: () => useDataFileUpdater
|
|
27
29
|
});
|
|
@@ -37,149 +39,189 @@ function useDataFileData(dataFile) {
|
|
|
37
39
|
function useDataFileUpdater(dataFile) {
|
|
38
40
|
return (0, import_react.useContext)(dataFile.context).updateData;
|
|
39
41
|
}
|
|
40
|
-
function
|
|
42
|
+
function useDataFileContext(dataFile) {
|
|
41
43
|
return (0, import_react.useContext)(dataFile.context);
|
|
42
44
|
}
|
|
43
|
-
function
|
|
45
|
+
function useDataFile(dataFile, usage) {
|
|
46
|
+
return dataFile.useDataFile(usage);
|
|
47
|
+
}
|
|
48
|
+
function useDataFileCore({
|
|
44
49
|
schema,
|
|
45
50
|
defaultValue,
|
|
51
|
+
path,
|
|
46
52
|
onPathChange = "defaultValue"
|
|
47
53
|
}) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
error: void 0
|
|
54
|
+
const state = (0, import_react.useRef)({
|
|
55
|
+
initialized: false,
|
|
56
|
+
path: null,
|
|
57
|
+
data: void 0,
|
|
58
|
+
previousData: void 0,
|
|
59
|
+
state: {
|
|
60
|
+
state: "saved"
|
|
61
|
+
}
|
|
57
62
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
(0, import_react.useEffect)(() => {
|
|
64
|
+
if (!state.current.initialized) {
|
|
65
|
+
state.current.initialized = true;
|
|
66
|
+
} else {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"Cannot change schema or defaultValue after initialization"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}, [schema, defaultValue]);
|
|
72
|
+
const [data, setData] = (0, import_react.useState)({
|
|
73
|
+
status: "loading",
|
|
74
|
+
data: void 0
|
|
75
|
+
});
|
|
76
|
+
const updateDataFromState = (0, import_react.useMemo)(
|
|
77
|
+
() => () => {
|
|
78
|
+
const data2 = state.current.data;
|
|
79
|
+
if (state.current.state.state === "error") {
|
|
80
|
+
setData({
|
|
81
|
+
status: "error",
|
|
82
|
+
error: state.current.state.error,
|
|
83
|
+
data: data2
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
65
86
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
if (data2 === void 0) {
|
|
88
|
+
setData({
|
|
89
|
+
status: "loading",
|
|
90
|
+
data: void 0
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
setData({
|
|
95
|
+
status: "ready",
|
|
96
|
+
data: data2
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
[]
|
|
100
|
+
);
|
|
101
|
+
const saveData = (0, import_react.useMemo)(
|
|
102
|
+
() => (0, import_lodash.throttle)(
|
|
103
|
+
async () => {
|
|
104
|
+
if (state.current.state.state === "saved") {
|
|
79
105
|
return;
|
|
80
106
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
107
|
+
const currentPath = state.current.path;
|
|
108
|
+
const currentData = state.current.data;
|
|
109
|
+
if (!currentPath || currentData === void 0) {
|
|
85
110
|
return;
|
|
86
111
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
const requestFileWriteToDisk = (0, import_react.useMemo)(
|
|
95
|
-
() => (0, import_lodash.throttle)(
|
|
96
|
-
async () => {
|
|
97
|
-
if (state.current.state.state === "saved") {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const currentPath = state.current.path;
|
|
101
|
-
const currentData = state.current.data;
|
|
102
|
-
if (!currentPath || currentData === void 0) {
|
|
103
|
-
return;
|
|
112
|
+
try {
|
|
113
|
+
const json = JSON.stringify(currentData, null, 2);
|
|
114
|
+
await import_fs.promises.mkdir((0, import_path.dirname)(currentPath), { recursive: true });
|
|
115
|
+
await import_fs.promises.writeFile(currentPath, json, "utf8");
|
|
116
|
+
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
117
|
+
state.current.state = { state: "saved" };
|
|
104
118
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
110
|
-
state.current.state = { state: "saved" };
|
|
111
|
-
}
|
|
112
|
-
} catch (error) {
|
|
113
|
-
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
114
|
-
state.current.state = { state: "error", error };
|
|
115
|
-
updateDataFromState();
|
|
116
|
-
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
121
|
+
state.current.state = { state: "error", error };
|
|
122
|
+
updateDataFromState();
|
|
117
123
|
}
|
|
118
|
-
},
|
|
119
|
-
500,
|
|
120
|
-
{
|
|
121
|
-
// Write leading so that we always write to disk quickly when
|
|
122
|
-
// only single things have changed
|
|
123
|
-
leading: true,
|
|
124
|
-
// Trailing is important otherwise we may lose data
|
|
125
|
-
trailing: true
|
|
126
|
-
}
|
|
127
|
-
),
|
|
128
|
-
[]
|
|
129
|
-
);
|
|
130
|
-
(0, import_react.useEffect)(() => {
|
|
131
|
-
state.current = {
|
|
132
|
-
path,
|
|
133
|
-
data: void 0,
|
|
134
|
-
previousData: state.current.data ?? state.current.previousData,
|
|
135
|
-
state: {
|
|
136
|
-
state: "saved"
|
|
137
124
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
125
|
+
},
|
|
126
|
+
500,
|
|
127
|
+
{
|
|
128
|
+
// Write leading so that we always write to disk quickly when
|
|
129
|
+
// only single things have changed
|
|
130
|
+
leading: true,
|
|
131
|
+
// Trailing is important otherwise we may lose data
|
|
132
|
+
trailing: true
|
|
133
|
+
}
|
|
134
|
+
),
|
|
135
|
+
[]
|
|
136
|
+
);
|
|
137
|
+
(0, import_react.useEffect)(() => {
|
|
138
|
+
state.current = {
|
|
139
|
+
...state.current,
|
|
140
|
+
path,
|
|
141
|
+
data: void 0,
|
|
142
|
+
previousData: state.current.data ?? state.current.previousData,
|
|
143
|
+
state: {
|
|
144
|
+
state: "saved"
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
import_fs.promises.readFile(path, "utf8").then((data2) => {
|
|
148
|
+
const parsedData = schema.parse(JSON.parse(data2));
|
|
149
|
+
if (state.current.path === path) {
|
|
150
|
+
state.current.data = parsedData;
|
|
151
|
+
state.current.state = { state: "saved" };
|
|
160
152
|
updateDataFromState();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
state.current.data = update(state.current.data);
|
|
153
|
+
}
|
|
154
|
+
}).catch((error) => {
|
|
155
|
+
if (state.current.path !== path) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (error.code === "ENOENT") {
|
|
159
|
+
console.log("Creating new file");
|
|
160
|
+
const initialData = onPathChange === "transfer" && state.current.previousData !== void 0 ? state.current.previousData : defaultValue;
|
|
161
|
+
state.current.data = initialData;
|
|
172
162
|
state.current.state = { state: "dirty" };
|
|
173
|
-
|
|
163
|
+
saveData();
|
|
174
164
|
updateDataFromState();
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
state.current.state = { state: "error", error };
|
|
168
|
+
updateDataFromState();
|
|
169
|
+
});
|
|
170
|
+
}, [path, onPathChange]);
|
|
171
|
+
const updateData = (0, import_react.useMemo)(
|
|
172
|
+
() => (update) => {
|
|
173
|
+
if (state.current.path !== path) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (state.current.data === void 0) {
|
|
177
|
+
throw new Error("Attempt to update data before it has been loaded");
|
|
178
|
+
}
|
|
179
|
+
state.current.data = update(state.current.data);
|
|
180
|
+
state.current.state = { state: "dirty" };
|
|
181
|
+
saveData();
|
|
182
|
+
updateDataFromState();
|
|
183
|
+
},
|
|
184
|
+
[path]
|
|
185
|
+
);
|
|
186
|
+
return {
|
|
187
|
+
data,
|
|
188
|
+
updateData,
|
|
189
|
+
saveData
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function createDataFileDefinition({
|
|
193
|
+
schema,
|
|
194
|
+
defaultValue
|
|
195
|
+
}) {
|
|
196
|
+
const context = (0, import_react.createContext)({
|
|
197
|
+
data: defaultValue,
|
|
198
|
+
updateData: () => {
|
|
199
|
+
throw new Error("Data file provider not used");
|
|
200
|
+
},
|
|
201
|
+
saveData: () => {
|
|
202
|
+
throw new Error("Data file provider not used");
|
|
203
|
+
},
|
|
204
|
+
error: void 0
|
|
205
|
+
});
|
|
206
|
+
const useDataFile2 = ({
|
|
207
|
+
path,
|
|
208
|
+
onPathChange
|
|
209
|
+
}) => useDataFileCore({
|
|
210
|
+
schema,
|
|
211
|
+
defaultValue,
|
|
212
|
+
path,
|
|
213
|
+
onPathChange
|
|
214
|
+
});
|
|
215
|
+
const Provider = ({ path, onPathChange, children }) => {
|
|
216
|
+
const { data, updateData, saveData } = useDataFile2({
|
|
217
|
+
path,
|
|
218
|
+
onPathChange
|
|
219
|
+
});
|
|
178
220
|
const providedContext = (0, import_react.useMemo)(
|
|
179
221
|
() => ({
|
|
180
222
|
data: data.status !== "loading" && data.data !== void 0 ? data.data : defaultValue,
|
|
181
223
|
updateData,
|
|
182
|
-
saveData
|
|
224
|
+
saveData,
|
|
183
225
|
error: data.status === "error" ? data.error : void 0
|
|
184
226
|
}),
|
|
185
227
|
[data, updateData]
|
|
@@ -191,13 +233,16 @@ function createDataFileSpec({
|
|
|
191
233
|
};
|
|
192
234
|
return {
|
|
193
235
|
Provider,
|
|
194
|
-
context
|
|
236
|
+
context,
|
|
237
|
+
useDataFile: useDataFile2
|
|
195
238
|
};
|
|
196
239
|
}
|
|
197
240
|
// Annotate the CommonJS export names for ESM import in node:
|
|
198
241
|
0 && (module.exports = {
|
|
199
|
-
|
|
242
|
+
createDataFileDefinition,
|
|
200
243
|
useDataFile,
|
|
244
|
+
useDataFileContext,
|
|
245
|
+
useDataFileCore,
|
|
201
246
|
useDataFileData,
|
|
202
247
|
useDataFileUpdater
|
|
203
248
|
});
|
package/dist/data.mjs
CHANGED
|
@@ -17,149 +17,189 @@ function useDataFileData(dataFile) {
|
|
|
17
17
|
function useDataFileUpdater(dataFile) {
|
|
18
18
|
return useContext(dataFile.context).updateData;
|
|
19
19
|
}
|
|
20
|
-
function
|
|
20
|
+
function useDataFileContext(dataFile) {
|
|
21
21
|
return useContext(dataFile.context);
|
|
22
22
|
}
|
|
23
|
-
function
|
|
23
|
+
function useDataFile(dataFile, usage) {
|
|
24
|
+
return dataFile.useDataFile(usage);
|
|
25
|
+
}
|
|
26
|
+
function useDataFileCore({
|
|
24
27
|
schema,
|
|
25
28
|
defaultValue,
|
|
29
|
+
path,
|
|
26
30
|
onPathChange = "defaultValue"
|
|
27
31
|
}) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
error: void 0
|
|
32
|
+
const state = useRef({
|
|
33
|
+
initialized: false,
|
|
34
|
+
path: null,
|
|
35
|
+
data: void 0,
|
|
36
|
+
previousData: void 0,
|
|
37
|
+
state: {
|
|
38
|
+
state: "saved"
|
|
39
|
+
}
|
|
37
40
|
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!state.current.initialized) {
|
|
43
|
+
state.current.initialized = true;
|
|
44
|
+
} else {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Cannot change schema or defaultValue after initialization"
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}, [schema, defaultValue]);
|
|
50
|
+
const [data, setData] = useState({
|
|
51
|
+
status: "loading",
|
|
52
|
+
data: void 0
|
|
53
|
+
});
|
|
54
|
+
const updateDataFromState = useMemo(
|
|
55
|
+
() => () => {
|
|
56
|
+
const data2 = state.current.data;
|
|
57
|
+
if (state.current.state.state === "error") {
|
|
58
|
+
setData({
|
|
59
|
+
status: "error",
|
|
60
|
+
error: state.current.state.error,
|
|
61
|
+
data: data2
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
45
64
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
if (data2 === void 0) {
|
|
66
|
+
setData({
|
|
67
|
+
status: "loading",
|
|
68
|
+
data: void 0
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
setData({
|
|
73
|
+
status: "ready",
|
|
74
|
+
data: data2
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
[]
|
|
78
|
+
);
|
|
79
|
+
const saveData = useMemo(
|
|
80
|
+
() => throttle(
|
|
81
|
+
async () => {
|
|
82
|
+
if (state.current.state.state === "saved") {
|
|
59
83
|
return;
|
|
60
84
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
85
|
+
const currentPath = state.current.path;
|
|
86
|
+
const currentData = state.current.data;
|
|
87
|
+
if (!currentPath || currentData === void 0) {
|
|
65
88
|
return;
|
|
66
89
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
);
|
|
74
|
-
const requestFileWriteToDisk = useMemo(
|
|
75
|
-
() => throttle(
|
|
76
|
-
async () => {
|
|
77
|
-
if (state.current.state.state === "saved") {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const currentPath = state.current.path;
|
|
81
|
-
const currentData = state.current.data;
|
|
82
|
-
if (!currentPath || currentData === void 0) {
|
|
83
|
-
return;
|
|
90
|
+
try {
|
|
91
|
+
const json = JSON.stringify(currentData, null, 2);
|
|
92
|
+
await fs.mkdir(dirname(currentPath), { recursive: true });
|
|
93
|
+
await fs.writeFile(currentPath, json, "utf8");
|
|
94
|
+
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
95
|
+
state.current.state = { state: "saved" };
|
|
84
96
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
90
|
-
state.current.state = { state: "saved" };
|
|
91
|
-
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
94
|
-
state.current.state = { state: "error", error };
|
|
95
|
-
updateDataFromState();
|
|
96
|
-
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (state.current.path === currentPath && state.current.data === currentData) {
|
|
99
|
+
state.current.state = { state: "error", error };
|
|
100
|
+
updateDataFromState();
|
|
97
101
|
}
|
|
98
|
-
},
|
|
99
|
-
500,
|
|
100
|
-
{
|
|
101
|
-
// Write leading so that we always write to disk quickly when
|
|
102
|
-
// only single things have changed
|
|
103
|
-
leading: true,
|
|
104
|
-
// Trailing is important otherwise we may lose data
|
|
105
|
-
trailing: true
|
|
106
|
-
}
|
|
107
|
-
),
|
|
108
|
-
[]
|
|
109
|
-
);
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
state.current = {
|
|
112
|
-
path,
|
|
113
|
-
data: void 0,
|
|
114
|
-
previousData: state.current.data ?? state.current.previousData,
|
|
115
|
-
state: {
|
|
116
|
-
state: "saved"
|
|
117
102
|
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
103
|
+
},
|
|
104
|
+
500,
|
|
105
|
+
{
|
|
106
|
+
// Write leading so that we always write to disk quickly when
|
|
107
|
+
// only single things have changed
|
|
108
|
+
leading: true,
|
|
109
|
+
// Trailing is important otherwise we may lose data
|
|
110
|
+
trailing: true
|
|
111
|
+
}
|
|
112
|
+
),
|
|
113
|
+
[]
|
|
114
|
+
);
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
state.current = {
|
|
117
|
+
...state.current,
|
|
118
|
+
path,
|
|
119
|
+
data: void 0,
|
|
120
|
+
previousData: state.current.data ?? state.current.previousData,
|
|
121
|
+
state: {
|
|
122
|
+
state: "saved"
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
fs.readFile(path, "utf8").then((data2) => {
|
|
126
|
+
const parsedData = schema.parse(JSON.parse(data2));
|
|
127
|
+
if (state.current.path === path) {
|
|
128
|
+
state.current.data = parsedData;
|
|
129
|
+
state.current.state = { state: "saved" };
|
|
140
130
|
updateDataFromState();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
state.current.data = update(state.current.data);
|
|
131
|
+
}
|
|
132
|
+
}).catch((error) => {
|
|
133
|
+
if (state.current.path !== path) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (error.code === "ENOENT") {
|
|
137
|
+
console.log("Creating new file");
|
|
138
|
+
const initialData = onPathChange === "transfer" && state.current.previousData !== void 0 ? state.current.previousData : defaultValue;
|
|
139
|
+
state.current.data = initialData;
|
|
152
140
|
state.current.state = { state: "dirty" };
|
|
153
|
-
|
|
141
|
+
saveData();
|
|
154
142
|
updateDataFromState();
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
state.current.state = { state: "error", error };
|
|
146
|
+
updateDataFromState();
|
|
147
|
+
});
|
|
148
|
+
}, [path, onPathChange]);
|
|
149
|
+
const updateData = useMemo(
|
|
150
|
+
() => (update) => {
|
|
151
|
+
if (state.current.path !== path) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (state.current.data === void 0) {
|
|
155
|
+
throw new Error("Attempt to update data before it has been loaded");
|
|
156
|
+
}
|
|
157
|
+
state.current.data = update(state.current.data);
|
|
158
|
+
state.current.state = { state: "dirty" };
|
|
159
|
+
saveData();
|
|
160
|
+
updateDataFromState();
|
|
161
|
+
},
|
|
162
|
+
[path]
|
|
163
|
+
);
|
|
164
|
+
return {
|
|
165
|
+
data,
|
|
166
|
+
updateData,
|
|
167
|
+
saveData
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function createDataFileDefinition({
|
|
171
|
+
schema,
|
|
172
|
+
defaultValue
|
|
173
|
+
}) {
|
|
174
|
+
const context = createContext({
|
|
175
|
+
data: defaultValue,
|
|
176
|
+
updateData: () => {
|
|
177
|
+
throw new Error("Data file provider not used");
|
|
178
|
+
},
|
|
179
|
+
saveData: () => {
|
|
180
|
+
throw new Error("Data file provider not used");
|
|
181
|
+
},
|
|
182
|
+
error: void 0
|
|
183
|
+
});
|
|
184
|
+
const useDataFile2 = ({
|
|
185
|
+
path,
|
|
186
|
+
onPathChange
|
|
187
|
+
}) => useDataFileCore({
|
|
188
|
+
schema,
|
|
189
|
+
defaultValue,
|
|
190
|
+
path,
|
|
191
|
+
onPathChange
|
|
192
|
+
});
|
|
193
|
+
const Provider = ({ path, onPathChange, children }) => {
|
|
194
|
+
const { data, updateData, saveData } = useDataFile2({
|
|
195
|
+
path,
|
|
196
|
+
onPathChange
|
|
197
|
+
});
|
|
158
198
|
const providedContext = useMemo(
|
|
159
199
|
() => ({
|
|
160
200
|
data: data.status !== "loading" && data.data !== void 0 ? data.data : defaultValue,
|
|
161
201
|
updateData,
|
|
162
|
-
saveData
|
|
202
|
+
saveData,
|
|
163
203
|
error: data.status === "error" ? data.error : void 0
|
|
164
204
|
}),
|
|
165
205
|
[data, updateData]
|
|
@@ -171,12 +211,15 @@ function createDataFileSpec({
|
|
|
171
211
|
};
|
|
172
212
|
return {
|
|
173
213
|
Provider,
|
|
174
|
-
context
|
|
214
|
+
context,
|
|
215
|
+
useDataFile: useDataFile2
|
|
175
216
|
};
|
|
176
217
|
}
|
|
177
218
|
export {
|
|
178
|
-
|
|
219
|
+
createDataFileDefinition,
|
|
179
220
|
useDataFile,
|
|
221
|
+
useDataFileContext,
|
|
222
|
+
useDataFileCore,
|
|
180
223
|
useDataFileData,
|
|
181
224
|
useDataFileUpdater
|
|
182
225
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -11,7 +11,7 @@ import { Props as Props$7, Timeline as Timeline$1 } from '@arcanejs/toolkit/comp
|
|
|
11
11
|
import * as React from 'react';
|
|
12
12
|
import { Ref } from 'react';
|
|
13
13
|
|
|
14
|
-
type Child = JSX.Element | string | null | undefined;
|
|
14
|
+
type Child = JSX.Element | string | null | undefined | boolean;
|
|
15
15
|
type Children = Child | Child[];
|
|
16
16
|
interface LightDeskIntrinsicElements {
|
|
17
17
|
button: Props & {
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { Props as Props$7, Timeline as Timeline$1 } from '@arcanejs/toolkit/comp
|
|
|
11
11
|
import * as React from 'react';
|
|
12
12
|
import { Ref } from 'react';
|
|
13
13
|
|
|
14
|
-
type Child = JSX.Element | string | null | undefined;
|
|
14
|
+
type Child = JSX.Element | string | null | undefined | boolean;
|
|
15
15
|
type Children = Child | Child[];
|
|
16
16
|
interface LightDeskIntrinsicElements {
|
|
17
17
|
button: Props & {
|