@arcanejs/react-toolkit 0.14.0 → 0.15.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 +7 -1
- package/dist/data.d.ts +7 -1
- package/dist/data.js +55 -15
- package/dist/data.mjs +55 -15
- package/package.json +1 -1
package/dist/data.d.mts
CHANGED
|
@@ -45,6 +45,10 @@ type DataFileContext<T> = {
|
|
|
45
45
|
*/
|
|
46
46
|
lastUpdatedMillis: number;
|
|
47
47
|
updateData: DataFileUpdater<T>;
|
|
48
|
+
/**
|
|
49
|
+
* Can be called to reset the data to the default value, and save that to disk.
|
|
50
|
+
*/
|
|
51
|
+
resetData: () => void;
|
|
48
52
|
/**
|
|
49
53
|
* Can be called to force an attempt to re-save the data to disk
|
|
50
54
|
*/
|
|
@@ -99,7 +103,8 @@ type DataFileOperation = 'load' | 'save' | 'usage';
|
|
|
99
103
|
declare class ArcaneDataFileError extends Error {
|
|
100
104
|
readonly operation: DataFileOperation;
|
|
101
105
|
readonly path: string | null;
|
|
102
|
-
|
|
106
|
+
readonly contents: string | null;
|
|
107
|
+
constructor(message: string, operation: DataFileOperation, path: string | null, contents: string | null, cause?: unknown);
|
|
103
108
|
}
|
|
104
109
|
type ErrorListener = (error: ArcaneDataFileError) => void;
|
|
105
110
|
type UseDataFileCoreProps<T> = WithPathChange & {
|
|
@@ -111,6 +116,7 @@ type UseDataFileCoreProps<T> = WithPathChange & {
|
|
|
111
116
|
type DataFileCore<T> = {
|
|
112
117
|
data: DataState<T>;
|
|
113
118
|
updateData: DataFileUpdater<T>;
|
|
119
|
+
resetData: () => void;
|
|
114
120
|
saveData: () => void;
|
|
115
121
|
};
|
|
116
122
|
/**
|
package/dist/data.d.ts
CHANGED
|
@@ -45,6 +45,10 @@ type DataFileContext<T> = {
|
|
|
45
45
|
*/
|
|
46
46
|
lastUpdatedMillis: number;
|
|
47
47
|
updateData: DataFileUpdater<T>;
|
|
48
|
+
/**
|
|
49
|
+
* Can be called to reset the data to the default value, and save that to disk.
|
|
50
|
+
*/
|
|
51
|
+
resetData: () => void;
|
|
48
52
|
/**
|
|
49
53
|
* Can be called to force an attempt to re-save the data to disk
|
|
50
54
|
*/
|
|
@@ -99,7 +103,8 @@ type DataFileOperation = 'load' | 'save' | 'usage';
|
|
|
99
103
|
declare class ArcaneDataFileError extends Error {
|
|
100
104
|
readonly operation: DataFileOperation;
|
|
101
105
|
readonly path: string | null;
|
|
102
|
-
|
|
106
|
+
readonly contents: string | null;
|
|
107
|
+
constructor(message: string, operation: DataFileOperation, path: string | null, contents: string | null, cause?: unknown);
|
|
103
108
|
}
|
|
104
109
|
type ErrorListener = (error: ArcaneDataFileError) => void;
|
|
105
110
|
type UseDataFileCoreProps<T> = WithPathChange & {
|
|
@@ -111,6 +116,7 @@ type UseDataFileCoreProps<T> = WithPathChange & {
|
|
|
111
116
|
type DataFileCore<T> = {
|
|
112
117
|
data: DataState<T>;
|
|
113
118
|
updateData: DataFileUpdater<T>;
|
|
119
|
+
resetData: () => void;
|
|
114
120
|
saveData: () => void;
|
|
115
121
|
};
|
|
116
122
|
/**
|
package/dist/data.js
CHANGED
|
@@ -4,6 +4,7 @@ var _chunkRT2VSMJLjs = require('./chunk-RT2VSMJL.js');
|
|
|
4
4
|
|
|
5
5
|
// src/data.tsx
|
|
6
6
|
var _fs = require('fs');
|
|
7
|
+
var _crypto = require('crypto');
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
|
|
@@ -28,13 +29,27 @@ function useDataFile(dataFile, usage) {
|
|
|
28
29
|
return dataFile.useDataFile(usage);
|
|
29
30
|
}
|
|
30
31
|
var ArcaneDataFileError = class extends Error {
|
|
31
|
-
constructor(message, operation, path, cause) {
|
|
32
|
+
constructor(message, operation, path, contents, cause) {
|
|
32
33
|
super(message, { cause });
|
|
33
34
|
this.operation = operation;
|
|
34
35
|
this.path = path;
|
|
36
|
+
this.contents = contents;
|
|
35
37
|
this.name = `ArcaneDataFileError(${operation})`;
|
|
36
38
|
}
|
|
37
39
|
};
|
|
40
|
+
function stripUtf8Bom(data) {
|
|
41
|
+
return data.charCodeAt(0) === 65279 ? data.slice(1) : data;
|
|
42
|
+
}
|
|
43
|
+
async function writeFileAtomically(path, data) {
|
|
44
|
+
const tempPath = `${path}.${process.pid}.${_crypto.randomUUID.call(void 0, )}.tmp`;
|
|
45
|
+
try {
|
|
46
|
+
await _fs.promises.writeFile(tempPath, data, "utf8");
|
|
47
|
+
await _fs.promises.rename(tempPath, path);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
await _fs.promises.rm(tempPath, { force: true }).catch(() => void 0);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
38
53
|
function useDataFileCore({
|
|
39
54
|
schema,
|
|
40
55
|
defaultValue,
|
|
@@ -45,6 +60,7 @@ function useDataFileCore({
|
|
|
45
60
|
const log = _chunkRT2VSMJLjs.useLogger.call(void 0, );
|
|
46
61
|
const state = _react.useRef.call(void 0, {
|
|
47
62
|
initialized: false,
|
|
63
|
+
saveChain: Promise.resolve(),
|
|
48
64
|
path: null,
|
|
49
65
|
data: void 0,
|
|
50
66
|
previousData: void 0,
|
|
@@ -60,6 +76,7 @@ function useDataFileCore({
|
|
|
60
76
|
const error = new ArcaneDataFileError(
|
|
61
77
|
"Cannot change schema or defaultValue after initialization",
|
|
62
78
|
"usage",
|
|
79
|
+
null,
|
|
63
80
|
null
|
|
64
81
|
);
|
|
65
82
|
_optionalChain([onError, 'optionalCall', _ => _(error)]);
|
|
@@ -110,11 +127,15 @@ function useDataFileCore({
|
|
|
110
127
|
if (!currentPath || currentData === void 0) {
|
|
111
128
|
return;
|
|
112
129
|
}
|
|
113
|
-
|
|
114
|
-
|
|
130
|
+
const json = JSON.stringify(currentData, null, 2);
|
|
131
|
+
const queuedSave = state.current.saveChain.catch(() => void 0).then(async () => {
|
|
115
132
|
await _fs.promises.mkdir(_path.dirname.call(void 0, currentPath), { recursive: true });
|
|
116
|
-
await
|
|
117
|
-
|
|
133
|
+
await writeFileAtomically(currentPath, json);
|
|
134
|
+
});
|
|
135
|
+
try {
|
|
136
|
+
state.current.saveChain = queuedSave;
|
|
137
|
+
await queuedSave;
|
|
138
|
+
if (state.current.path === currentPath && state.current.saveChain === queuedSave) {
|
|
118
139
|
state.current.state = { state: "saved" };
|
|
119
140
|
}
|
|
120
141
|
} catch (cause) {
|
|
@@ -122,10 +143,11 @@ function useDataFileCore({
|
|
|
122
143
|
`Error saving data file to path: ${currentPath}`,
|
|
123
144
|
"save",
|
|
124
145
|
currentPath,
|
|
146
|
+
null,
|
|
125
147
|
cause
|
|
126
148
|
);
|
|
127
149
|
_optionalChain([onError, 'optionalCall', _2 => _2(error)]);
|
|
128
|
-
if (state.current.path === currentPath && state.current.
|
|
150
|
+
if (state.current.path === currentPath && state.current.saveChain === queuedSave) {
|
|
129
151
|
state.current.state = { state: "error", error };
|
|
130
152
|
updateDataFromState();
|
|
131
153
|
}
|
|
@@ -152,8 +174,10 @@ function useDataFileCore({
|
|
|
152
174
|
state: "saved"
|
|
153
175
|
}
|
|
154
176
|
};
|
|
177
|
+
let contents = null;
|
|
155
178
|
_fs.promises.readFile(path, "utf8").then((data2) => {
|
|
156
|
-
|
|
179
|
+
contents = data2;
|
|
180
|
+
const parsedData = schema.parse(JSON.parse(stripUtf8Bom(data2)));
|
|
157
181
|
if (state.current.path === path) {
|
|
158
182
|
state.current.data = parsedData;
|
|
159
183
|
state.current.lastUpdatedMillis = Date.now();
|
|
@@ -182,6 +206,7 @@ function useDataFileCore({
|
|
|
182
206
|
`Error loading data file at path: ${path}`,
|
|
183
207
|
"load",
|
|
184
208
|
path,
|
|
209
|
+
contents,
|
|
185
210
|
err
|
|
186
211
|
);
|
|
187
212
|
_optionalChain([onError, 'optionalCall', _5 => _5(error)]);
|
|
@@ -190,25 +215,36 @@ function useDataFileCore({
|
|
|
190
215
|
updateDataFromState();
|
|
191
216
|
});
|
|
192
217
|
}, [path, onPathChange]);
|
|
193
|
-
const
|
|
194
|
-
() => (
|
|
218
|
+
const setDataTo = _react.useMemo.call(void 0,
|
|
219
|
+
() => (data2) => {
|
|
195
220
|
if (state.current.path !== path) {
|
|
196
221
|
return;
|
|
197
222
|
}
|
|
198
|
-
|
|
199
|
-
throw new Error("Attempt to update data before it has been loaded");
|
|
200
|
-
}
|
|
201
|
-
state.current.data = update(state.current.data);
|
|
223
|
+
state.current.data = data2;
|
|
202
224
|
state.current.lastUpdatedMillis = Date.now();
|
|
203
225
|
state.current.state = { state: "dirty" };
|
|
204
226
|
saveData();
|
|
205
227
|
updateDataFromState();
|
|
206
228
|
},
|
|
207
|
-
[path]
|
|
229
|
+
[saveData, path]
|
|
230
|
+
);
|
|
231
|
+
const updateData = _react.useMemo.call(void 0,
|
|
232
|
+
() => (update) => {
|
|
233
|
+
if (state.current.data === void 0) {
|
|
234
|
+
throw new Error("Attempt to update data before it has been loaded");
|
|
235
|
+
}
|
|
236
|
+
setDataTo(update(state.current.data));
|
|
237
|
+
},
|
|
238
|
+
[setDataTo]
|
|
239
|
+
);
|
|
240
|
+
const resetData = _react.useMemo.call(void 0,
|
|
241
|
+
() => () => setDataTo(defaultValue),
|
|
242
|
+
[setDataTo, defaultValue]
|
|
208
243
|
);
|
|
209
244
|
return {
|
|
210
245
|
data,
|
|
211
246
|
updateData,
|
|
247
|
+
resetData,
|
|
212
248
|
saveData
|
|
213
249
|
};
|
|
214
250
|
}
|
|
@@ -223,6 +259,9 @@ function createDataFileDefinition({
|
|
|
223
259
|
updateData: () => {
|
|
224
260
|
throw new Error("Data file provider not used");
|
|
225
261
|
},
|
|
262
|
+
resetData: () => {
|
|
263
|
+
throw new Error("Data file provider not used");
|
|
264
|
+
},
|
|
226
265
|
saveData: () => {
|
|
227
266
|
throw new Error("Data file provider not used");
|
|
228
267
|
},
|
|
@@ -245,7 +284,7 @@ function createDataFileDefinition({
|
|
|
245
284
|
onError,
|
|
246
285
|
children
|
|
247
286
|
}) => {
|
|
248
|
-
const { data, updateData, saveData } = useDataFile2({
|
|
287
|
+
const { data, updateData, resetData, saveData } = useDataFile2({
|
|
249
288
|
path,
|
|
250
289
|
onPathChange,
|
|
251
290
|
onError
|
|
@@ -256,6 +295,7 @@ function createDataFileDefinition({
|
|
|
256
295
|
status: data.status,
|
|
257
296
|
lastUpdatedMillis: data.lastUpdatedMillis,
|
|
258
297
|
updateData,
|
|
298
|
+
resetData,
|
|
259
299
|
saveData,
|
|
260
300
|
error: data.status === "error" ? data.error : void 0
|
|
261
301
|
}),
|
package/dist/data.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/data.tsx
|
|
6
6
|
import { promises as fs } from "fs";
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
7
8
|
import {
|
|
8
9
|
useMemo,
|
|
9
10
|
useContext,
|
|
@@ -28,13 +29,27 @@ function useDataFile(dataFile, usage) {
|
|
|
28
29
|
return dataFile.useDataFile(usage);
|
|
29
30
|
}
|
|
30
31
|
var ArcaneDataFileError = class extends Error {
|
|
31
|
-
constructor(message, operation, path, cause) {
|
|
32
|
+
constructor(message, operation, path, contents, cause) {
|
|
32
33
|
super(message, { cause });
|
|
33
34
|
this.operation = operation;
|
|
34
35
|
this.path = path;
|
|
36
|
+
this.contents = contents;
|
|
35
37
|
this.name = `ArcaneDataFileError(${operation})`;
|
|
36
38
|
}
|
|
37
39
|
};
|
|
40
|
+
function stripUtf8Bom(data) {
|
|
41
|
+
return data.charCodeAt(0) === 65279 ? data.slice(1) : data;
|
|
42
|
+
}
|
|
43
|
+
async function writeFileAtomically(path, data) {
|
|
44
|
+
const tempPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
45
|
+
try {
|
|
46
|
+
await fs.writeFile(tempPath, data, "utf8");
|
|
47
|
+
await fs.rename(tempPath, path);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
await fs.rm(tempPath, { force: true }).catch(() => void 0);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
38
53
|
function useDataFileCore({
|
|
39
54
|
schema,
|
|
40
55
|
defaultValue,
|
|
@@ -45,6 +60,7 @@ function useDataFileCore({
|
|
|
45
60
|
const log = useLogger();
|
|
46
61
|
const state = useRef({
|
|
47
62
|
initialized: false,
|
|
63
|
+
saveChain: Promise.resolve(),
|
|
48
64
|
path: null,
|
|
49
65
|
data: void 0,
|
|
50
66
|
previousData: void 0,
|
|
@@ -60,6 +76,7 @@ function useDataFileCore({
|
|
|
60
76
|
const error = new ArcaneDataFileError(
|
|
61
77
|
"Cannot change schema or defaultValue after initialization",
|
|
62
78
|
"usage",
|
|
79
|
+
null,
|
|
63
80
|
null
|
|
64
81
|
);
|
|
65
82
|
onError?.(error);
|
|
@@ -110,11 +127,15 @@ function useDataFileCore({
|
|
|
110
127
|
if (!currentPath || currentData === void 0) {
|
|
111
128
|
return;
|
|
112
129
|
}
|
|
113
|
-
|
|
114
|
-
|
|
130
|
+
const json = JSON.stringify(currentData, null, 2);
|
|
131
|
+
const queuedSave = state.current.saveChain.catch(() => void 0).then(async () => {
|
|
115
132
|
await fs.mkdir(dirname(currentPath), { recursive: true });
|
|
116
|
-
await
|
|
117
|
-
|
|
133
|
+
await writeFileAtomically(currentPath, json);
|
|
134
|
+
});
|
|
135
|
+
try {
|
|
136
|
+
state.current.saveChain = queuedSave;
|
|
137
|
+
await queuedSave;
|
|
138
|
+
if (state.current.path === currentPath && state.current.saveChain === queuedSave) {
|
|
118
139
|
state.current.state = { state: "saved" };
|
|
119
140
|
}
|
|
120
141
|
} catch (cause) {
|
|
@@ -122,10 +143,11 @@ function useDataFileCore({
|
|
|
122
143
|
`Error saving data file to path: ${currentPath}`,
|
|
123
144
|
"save",
|
|
124
145
|
currentPath,
|
|
146
|
+
null,
|
|
125
147
|
cause
|
|
126
148
|
);
|
|
127
149
|
onError?.(error);
|
|
128
|
-
if (state.current.path === currentPath && state.current.
|
|
150
|
+
if (state.current.path === currentPath && state.current.saveChain === queuedSave) {
|
|
129
151
|
state.current.state = { state: "error", error };
|
|
130
152
|
updateDataFromState();
|
|
131
153
|
}
|
|
@@ -152,8 +174,10 @@ function useDataFileCore({
|
|
|
152
174
|
state: "saved"
|
|
153
175
|
}
|
|
154
176
|
};
|
|
177
|
+
let contents = null;
|
|
155
178
|
fs.readFile(path, "utf8").then((data2) => {
|
|
156
|
-
|
|
179
|
+
contents = data2;
|
|
180
|
+
const parsedData = schema.parse(JSON.parse(stripUtf8Bom(data2)));
|
|
157
181
|
if (state.current.path === path) {
|
|
158
182
|
state.current.data = parsedData;
|
|
159
183
|
state.current.lastUpdatedMillis = Date.now();
|
|
@@ -182,6 +206,7 @@ function useDataFileCore({
|
|
|
182
206
|
`Error loading data file at path: ${path}`,
|
|
183
207
|
"load",
|
|
184
208
|
path,
|
|
209
|
+
contents,
|
|
185
210
|
err
|
|
186
211
|
);
|
|
187
212
|
onError?.(error);
|
|
@@ -190,25 +215,36 @@ function useDataFileCore({
|
|
|
190
215
|
updateDataFromState();
|
|
191
216
|
});
|
|
192
217
|
}, [path, onPathChange]);
|
|
193
|
-
const
|
|
194
|
-
() => (
|
|
218
|
+
const setDataTo = useMemo(
|
|
219
|
+
() => (data2) => {
|
|
195
220
|
if (state.current.path !== path) {
|
|
196
221
|
return;
|
|
197
222
|
}
|
|
198
|
-
|
|
199
|
-
throw new Error("Attempt to update data before it has been loaded");
|
|
200
|
-
}
|
|
201
|
-
state.current.data = update(state.current.data);
|
|
223
|
+
state.current.data = data2;
|
|
202
224
|
state.current.lastUpdatedMillis = Date.now();
|
|
203
225
|
state.current.state = { state: "dirty" };
|
|
204
226
|
saveData();
|
|
205
227
|
updateDataFromState();
|
|
206
228
|
},
|
|
207
|
-
[path]
|
|
229
|
+
[saveData, path]
|
|
230
|
+
);
|
|
231
|
+
const updateData = useMemo(
|
|
232
|
+
() => (update) => {
|
|
233
|
+
if (state.current.data === void 0) {
|
|
234
|
+
throw new Error("Attempt to update data before it has been loaded");
|
|
235
|
+
}
|
|
236
|
+
setDataTo(update(state.current.data));
|
|
237
|
+
},
|
|
238
|
+
[setDataTo]
|
|
239
|
+
);
|
|
240
|
+
const resetData = useMemo(
|
|
241
|
+
() => () => setDataTo(defaultValue),
|
|
242
|
+
[setDataTo, defaultValue]
|
|
208
243
|
);
|
|
209
244
|
return {
|
|
210
245
|
data,
|
|
211
246
|
updateData,
|
|
247
|
+
resetData,
|
|
212
248
|
saveData
|
|
213
249
|
};
|
|
214
250
|
}
|
|
@@ -223,6 +259,9 @@ function createDataFileDefinition({
|
|
|
223
259
|
updateData: () => {
|
|
224
260
|
throw new Error("Data file provider not used");
|
|
225
261
|
},
|
|
262
|
+
resetData: () => {
|
|
263
|
+
throw new Error("Data file provider not used");
|
|
264
|
+
},
|
|
226
265
|
saveData: () => {
|
|
227
266
|
throw new Error("Data file provider not used");
|
|
228
267
|
},
|
|
@@ -245,7 +284,7 @@ function createDataFileDefinition({
|
|
|
245
284
|
onError,
|
|
246
285
|
children
|
|
247
286
|
}) => {
|
|
248
|
-
const { data, updateData, saveData } = useDataFile2({
|
|
287
|
+
const { data, updateData, resetData, saveData } = useDataFile2({
|
|
249
288
|
path,
|
|
250
289
|
onPathChange,
|
|
251
290
|
onError
|
|
@@ -256,6 +295,7 @@ function createDataFileDefinition({
|
|
|
256
295
|
status: data.status,
|
|
257
296
|
lastUpdatedMillis: data.lastUpdatedMillis,
|
|
258
297
|
updateData,
|
|
298
|
+
resetData,
|
|
259
299
|
saveData,
|
|
260
300
|
error: data.status === "error" ? data.error : void 0
|
|
261
301
|
}),
|