@arcanejs/react-toolkit 0.13.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 CHANGED
@@ -17,6 +17,8 @@ type DataFileUsage = WithPathChange & {
17
17
  * this will be relative to the current working directory.
18
18
  */
19
19
  path: string;
20
+ /** Callback that will be called if any error occurs during loading or saving the file */
21
+ onError?: (error: ArcaneDataFileError) => void;
20
22
  };
21
23
  type ProviderProps = DataFileUsage & {
22
24
  children: ReactNode;
@@ -43,6 +45,10 @@ type DataFileContext<T> = {
43
45
  */
44
46
  lastUpdatedMillis: number;
45
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;
46
52
  /**
47
53
  * Can be called to force an attempt to re-save the data to disk
48
54
  */
@@ -93,24 +99,34 @@ type DataState<T> = {
93
99
  status: 'ready';
94
100
  data: T;
95
101
  });
102
+ type DataFileOperation = 'load' | 'save' | 'usage';
103
+ declare class ArcaneDataFileError extends Error {
104
+ readonly operation: DataFileOperation;
105
+ readonly path: string | null;
106
+ readonly contents: string | null;
107
+ constructor(message: string, operation: DataFileOperation, path: string | null, contents: string | null, cause?: unknown);
108
+ }
109
+ type ErrorListener = (error: ArcaneDataFileError) => void;
96
110
  type UseDataFileCoreProps<T> = WithPathChange & {
97
111
  schema: ZodType<T>;
98
112
  defaultValue: T;
99
113
  path: string;
114
+ onError?: (error: ArcaneDataFileError) => void;
100
115
  };
101
116
  type DataFileCore<T> = {
102
117
  data: DataState<T>;
103
118
  updateData: DataFileUpdater<T>;
119
+ resetData: () => void;
104
120
  saveData: () => void;
105
121
  };
106
122
  /**
107
123
  * Primary hook for & logic for using data files.
108
124
  */
109
- declare function useDataFileCore<T>({ schema, defaultValue, path, onPathChange, }: UseDataFileCoreProps<T>): DataFileCore<T>;
125
+ declare function useDataFileCore<T>({ schema, defaultValue, path, onPathChange, onError, }: UseDataFileCoreProps<T>): DataFileCore<T>;
110
126
  type CreateDataFileDefinitionProps<T extends ZodType> = {
111
127
  schema: T;
112
128
  defaultValue: z.infer<T>;
113
129
  };
114
130
  declare function createDataFileDefinition<T extends ZodType>({ schema, defaultValue, }: CreateDataFileDefinitionProps<T>): DataFileDefinition<z.infer<T>>;
115
131
 
116
- export { type CreateDataFileDefinitionProps, type DataFileContext, type DataFileCore, type DataFileDefinition, type DataFileUpdater, type DataState, type ProviderProps, type UseDataFileCoreProps, createDataFileDefinition, useDataFile, useDataFileContext, useDataFileCore, useDataFileData, useDataFileUpdater };
132
+ export { ArcaneDataFileError, type CreateDataFileDefinitionProps, type DataFileContext, type DataFileCore, type DataFileDefinition, type DataFileOperation, type DataFileUpdater, type DataState, type ErrorListener, type ProviderProps, type UseDataFileCoreProps, createDataFileDefinition, useDataFile, useDataFileContext, useDataFileCore, useDataFileData, useDataFileUpdater };
package/dist/data.d.ts CHANGED
@@ -17,6 +17,8 @@ type DataFileUsage = WithPathChange & {
17
17
  * this will be relative to the current working directory.
18
18
  */
19
19
  path: string;
20
+ /** Callback that will be called if any error occurs during loading or saving the file */
21
+ onError?: (error: ArcaneDataFileError) => void;
20
22
  };
21
23
  type ProviderProps = DataFileUsage & {
22
24
  children: ReactNode;
@@ -43,6 +45,10 @@ type DataFileContext<T> = {
43
45
  */
44
46
  lastUpdatedMillis: number;
45
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;
46
52
  /**
47
53
  * Can be called to force an attempt to re-save the data to disk
48
54
  */
@@ -93,24 +99,34 @@ type DataState<T> = {
93
99
  status: 'ready';
94
100
  data: T;
95
101
  });
102
+ type DataFileOperation = 'load' | 'save' | 'usage';
103
+ declare class ArcaneDataFileError extends Error {
104
+ readonly operation: DataFileOperation;
105
+ readonly path: string | null;
106
+ readonly contents: string | null;
107
+ constructor(message: string, operation: DataFileOperation, path: string | null, contents: string | null, cause?: unknown);
108
+ }
109
+ type ErrorListener = (error: ArcaneDataFileError) => void;
96
110
  type UseDataFileCoreProps<T> = WithPathChange & {
97
111
  schema: ZodType<T>;
98
112
  defaultValue: T;
99
113
  path: string;
114
+ onError?: (error: ArcaneDataFileError) => void;
100
115
  };
101
116
  type DataFileCore<T> = {
102
117
  data: DataState<T>;
103
118
  updateData: DataFileUpdater<T>;
119
+ resetData: () => void;
104
120
  saveData: () => void;
105
121
  };
106
122
  /**
107
123
  * Primary hook for & logic for using data files.
108
124
  */
109
- declare function useDataFileCore<T>({ schema, defaultValue, path, onPathChange, }: UseDataFileCoreProps<T>): DataFileCore<T>;
125
+ declare function useDataFileCore<T>({ schema, defaultValue, path, onPathChange, onError, }: UseDataFileCoreProps<T>): DataFileCore<T>;
110
126
  type CreateDataFileDefinitionProps<T extends ZodType> = {
111
127
  schema: T;
112
128
  defaultValue: z.infer<T>;
113
129
  };
114
130
  declare function createDataFileDefinition<T extends ZodType>({ schema, defaultValue, }: CreateDataFileDefinitionProps<T>): DataFileDefinition<z.infer<T>>;
115
131
 
116
- export { type CreateDataFileDefinitionProps, type DataFileContext, type DataFileCore, type DataFileDefinition, type DataFileUpdater, type DataState, type ProviderProps, type UseDataFileCoreProps, createDataFileDefinition, useDataFile, useDataFileContext, useDataFileCore, useDataFileData, useDataFileUpdater };
132
+ export { ArcaneDataFileError, type CreateDataFileDefinitionProps, type DataFileContext, type DataFileCore, type DataFileDefinition, type DataFileOperation, type DataFileUpdater, type DataState, type ErrorListener, type ProviderProps, type UseDataFileCoreProps, createDataFileDefinition, useDataFile, useDataFileContext, useDataFileCore, useDataFileData, useDataFileUpdater };
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
 
@@ -27,15 +28,39 @@ function useDataFileContext(dataFile) {
27
28
  function useDataFile(dataFile, usage) {
28
29
  return dataFile.useDataFile(usage);
29
30
  }
31
+ var ArcaneDataFileError = class extends Error {
32
+ constructor(message, operation, path, contents, cause) {
33
+ super(message, { cause });
34
+ this.operation = operation;
35
+ this.path = path;
36
+ this.contents = contents;
37
+ this.name = `ArcaneDataFileError(${operation})`;
38
+ }
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
+ }
30
53
  function useDataFileCore({
31
54
  schema,
32
55
  defaultValue,
33
56
  path,
34
- onPathChange = "defaultValue"
57
+ onPathChange = "defaultValue",
58
+ onError
35
59
  }) {
36
60
  const log = _chunkRT2VSMJLjs.useLogger.call(void 0, );
37
61
  const state = _react.useRef.call(void 0, {
38
62
  initialized: false,
63
+ saveChain: Promise.resolve(),
39
64
  path: null,
40
65
  data: void 0,
41
66
  previousData: void 0,
@@ -48,9 +73,14 @@ function useDataFileCore({
48
73
  if (!state.current.initialized) {
49
74
  state.current.initialized = true;
50
75
  } else {
51
- throw new Error(
52
- "Cannot change schema or defaultValue after initialization"
76
+ const error = new ArcaneDataFileError(
77
+ "Cannot change schema or defaultValue after initialization",
78
+ "usage",
79
+ null,
80
+ null
53
81
  );
82
+ _optionalChain([onError, 'optionalCall', _ => _(error)]);
83
+ throw error;
54
84
  }
55
85
  }, [schema, defaultValue]);
56
86
  const [data, setData] = _react.useState.call(void 0, {
@@ -97,15 +127,27 @@ function useDataFileCore({
97
127
  if (!currentPath || currentData === void 0) {
98
128
  return;
99
129
  }
100
- try {
101
- const json = JSON.stringify(currentData, null, 2);
130
+ const json = JSON.stringify(currentData, null, 2);
131
+ const queuedSave = state.current.saveChain.catch(() => void 0).then(async () => {
102
132
  await _fs.promises.mkdir(_path.dirname.call(void 0, currentPath), { recursive: true });
103
- await _fs.promises.writeFile(currentPath, json, "utf8");
104
- if (state.current.path === currentPath && state.current.data === currentData) {
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) {
105
139
  state.current.state = { state: "saved" };
106
140
  }
107
- } catch (error) {
108
- if (state.current.path === currentPath && state.current.data === currentData) {
141
+ } catch (cause) {
142
+ const error = new ArcaneDataFileError(
143
+ `Error saving data file to path: ${currentPath}`,
144
+ "save",
145
+ currentPath,
146
+ null,
147
+ cause
148
+ );
149
+ _optionalChain([onError, 'optionalCall', _2 => _2(error)]);
150
+ if (state.current.path === currentPath && state.current.saveChain === queuedSave) {
109
151
  state.current.state = { state: "error", error };
110
152
  updateDataFromState();
111
153
  }
@@ -132,24 +174,26 @@ function useDataFileCore({
132
174
  state: "saved"
133
175
  }
134
176
  };
177
+ let contents = null;
135
178
  _fs.promises.readFile(path, "utf8").then((data2) => {
136
- const parsedData = schema.parse(JSON.parse(data2));
179
+ contents = data2;
180
+ const parsedData = schema.parse(JSON.parse(stripUtf8Bom(data2)));
137
181
  if (state.current.path === path) {
138
182
  state.current.data = parsedData;
139
183
  state.current.lastUpdatedMillis = Date.now();
140
184
  state.current.state = { state: "saved" };
141
185
  updateDataFromState();
142
186
  }
143
- }).catch((error) => {
187
+ }).catch((err) => {
144
188
  if (state.current.path !== path) {
145
189
  return;
146
190
  }
147
- if (error.code === "ENOENT") {
191
+ if (err.code === "ENOENT") {
148
192
  const initialData = onPathChange === "transfer" && state.current.previousData !== void 0 ? state.current.previousData : defaultValue;
149
193
  state.current.data = initialData;
150
194
  state.current.lastUpdatedMillis = Date.now();
151
195
  state.current.state = { state: "dirty" };
152
- _optionalChain([log, 'optionalAccess', _ => _.info, 'call', _2 => _2(
196
+ _optionalChain([log, 'optionalAccess', _3 => _3.info, 'call', _4 => _4(
153
197
  "Creating a new file at %s with initial data %o",
154
198
  path,
155
199
  initialData
@@ -158,29 +202,49 @@ function useDataFileCore({
158
202
  updateDataFromState();
159
203
  return;
160
204
  }
205
+ const error = new ArcaneDataFileError(
206
+ `Error loading data file at path: ${path}`,
207
+ "load",
208
+ path,
209
+ contents,
210
+ err
211
+ );
212
+ _optionalChain([onError, 'optionalCall', _5 => _5(error)]);
213
+ _optionalChain([log, 'optionalAccess', _6 => _6.error, 'call', _7 => _7(error)]);
161
214
  state.current.state = { state: "error", error };
162
215
  updateDataFromState();
163
216
  });
164
217
  }, [path, onPathChange]);
165
- const updateData = _react.useMemo.call(void 0,
166
- () => (update) => {
218
+ const setDataTo = _react.useMemo.call(void 0,
219
+ () => (data2) => {
167
220
  if (state.current.path !== path) {
168
221
  return;
169
222
  }
170
- if (state.current.data === void 0) {
171
- throw new Error("Attempt to update data before it has been loaded");
172
- }
173
- state.current.data = update(state.current.data);
223
+ state.current.data = data2;
174
224
  state.current.lastUpdatedMillis = Date.now();
175
225
  state.current.state = { state: "dirty" };
176
226
  saveData();
177
227
  updateDataFromState();
178
228
  },
179
- [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]
180
243
  );
181
244
  return {
182
245
  data,
183
246
  updateData,
247
+ resetData,
184
248
  saveData
185
249
  };
186
250
  }
@@ -195,6 +259,9 @@ function createDataFileDefinition({
195
259
  updateData: () => {
196
260
  throw new Error("Data file provider not used");
197
261
  },
262
+ resetData: () => {
263
+ throw new Error("Data file provider not used");
264
+ },
198
265
  saveData: () => {
199
266
  throw new Error("Data file provider not used");
200
267
  },
@@ -202,17 +269,25 @@ function createDataFileDefinition({
202
269
  });
203
270
  const useDataFile2 = ({
204
271
  path,
205
- onPathChange
272
+ onPathChange,
273
+ onError
206
274
  }) => useDataFileCore({
207
275
  schema,
208
276
  defaultValue,
209
277
  path,
210
- onPathChange
278
+ onPathChange,
279
+ onError
211
280
  });
212
- const Provider = ({ path, onPathChange, children }) => {
213
- const { data, updateData, saveData } = useDataFile2({
281
+ const Provider = ({
282
+ path,
283
+ onPathChange,
284
+ onError,
285
+ children
286
+ }) => {
287
+ const { data, updateData, resetData, saveData } = useDataFile2({
214
288
  path,
215
- onPathChange
289
+ onPathChange,
290
+ onError
216
291
  });
217
292
  const providedContext = _react.useMemo.call(void 0,
218
293
  () => ({
@@ -220,6 +295,7 @@ function createDataFileDefinition({
220
295
  status: data.status,
221
296
  lastUpdatedMillis: data.lastUpdatedMillis,
222
297
  updateData,
298
+ resetData,
223
299
  saveData,
224
300
  error: data.status === "error" ? data.error : void 0
225
301
  }),
@@ -243,4 +319,5 @@ function createDataFileDefinition({
243
319
 
244
320
 
245
321
 
246
- exports.createDataFileDefinition = createDataFileDefinition; exports.useDataFile = useDataFile; exports.useDataFileContext = useDataFileContext; exports.useDataFileCore = useDataFileCore; exports.useDataFileData = useDataFileData; exports.useDataFileUpdater = useDataFileUpdater;
322
+
323
+ exports.ArcaneDataFileError = ArcaneDataFileError; exports.createDataFileDefinition = createDataFileDefinition; exports.useDataFile = useDataFile; exports.useDataFileContext = useDataFileContext; exports.useDataFileCore = useDataFileCore; exports.useDataFileData = useDataFileData; exports.useDataFileUpdater = useDataFileUpdater;
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,
@@ -27,15 +28,39 @@ function useDataFileContext(dataFile) {
27
28
  function useDataFile(dataFile, usage) {
28
29
  return dataFile.useDataFile(usage);
29
30
  }
31
+ var ArcaneDataFileError = class extends Error {
32
+ constructor(message, operation, path, contents, cause) {
33
+ super(message, { cause });
34
+ this.operation = operation;
35
+ this.path = path;
36
+ this.contents = contents;
37
+ this.name = `ArcaneDataFileError(${operation})`;
38
+ }
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
+ }
30
53
  function useDataFileCore({
31
54
  schema,
32
55
  defaultValue,
33
56
  path,
34
- onPathChange = "defaultValue"
57
+ onPathChange = "defaultValue",
58
+ onError
35
59
  }) {
36
60
  const log = useLogger();
37
61
  const state = useRef({
38
62
  initialized: false,
63
+ saveChain: Promise.resolve(),
39
64
  path: null,
40
65
  data: void 0,
41
66
  previousData: void 0,
@@ -48,9 +73,14 @@ function useDataFileCore({
48
73
  if (!state.current.initialized) {
49
74
  state.current.initialized = true;
50
75
  } else {
51
- throw new Error(
52
- "Cannot change schema or defaultValue after initialization"
76
+ const error = new ArcaneDataFileError(
77
+ "Cannot change schema or defaultValue after initialization",
78
+ "usage",
79
+ null,
80
+ null
53
81
  );
82
+ onError?.(error);
83
+ throw error;
54
84
  }
55
85
  }, [schema, defaultValue]);
56
86
  const [data, setData] = useState({
@@ -97,15 +127,27 @@ function useDataFileCore({
97
127
  if (!currentPath || currentData === void 0) {
98
128
  return;
99
129
  }
100
- try {
101
- const json = JSON.stringify(currentData, null, 2);
130
+ const json = JSON.stringify(currentData, null, 2);
131
+ const queuedSave = state.current.saveChain.catch(() => void 0).then(async () => {
102
132
  await fs.mkdir(dirname(currentPath), { recursive: true });
103
- await fs.writeFile(currentPath, json, "utf8");
104
- if (state.current.path === currentPath && state.current.data === currentData) {
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) {
105
139
  state.current.state = { state: "saved" };
106
140
  }
107
- } catch (error) {
108
- if (state.current.path === currentPath && state.current.data === currentData) {
141
+ } catch (cause) {
142
+ const error = new ArcaneDataFileError(
143
+ `Error saving data file to path: ${currentPath}`,
144
+ "save",
145
+ currentPath,
146
+ null,
147
+ cause
148
+ );
149
+ onError?.(error);
150
+ if (state.current.path === currentPath && state.current.saveChain === queuedSave) {
109
151
  state.current.state = { state: "error", error };
110
152
  updateDataFromState();
111
153
  }
@@ -132,19 +174,21 @@ function useDataFileCore({
132
174
  state: "saved"
133
175
  }
134
176
  };
177
+ let contents = null;
135
178
  fs.readFile(path, "utf8").then((data2) => {
136
- const parsedData = schema.parse(JSON.parse(data2));
179
+ contents = data2;
180
+ const parsedData = schema.parse(JSON.parse(stripUtf8Bom(data2)));
137
181
  if (state.current.path === path) {
138
182
  state.current.data = parsedData;
139
183
  state.current.lastUpdatedMillis = Date.now();
140
184
  state.current.state = { state: "saved" };
141
185
  updateDataFromState();
142
186
  }
143
- }).catch((error) => {
187
+ }).catch((err) => {
144
188
  if (state.current.path !== path) {
145
189
  return;
146
190
  }
147
- if (error.code === "ENOENT") {
191
+ if (err.code === "ENOENT") {
148
192
  const initialData = onPathChange === "transfer" && state.current.previousData !== void 0 ? state.current.previousData : defaultValue;
149
193
  state.current.data = initialData;
150
194
  state.current.lastUpdatedMillis = Date.now();
@@ -158,29 +202,49 @@ function useDataFileCore({
158
202
  updateDataFromState();
159
203
  return;
160
204
  }
205
+ const error = new ArcaneDataFileError(
206
+ `Error loading data file at path: ${path}`,
207
+ "load",
208
+ path,
209
+ contents,
210
+ err
211
+ );
212
+ onError?.(error);
213
+ log?.error(error);
161
214
  state.current.state = { state: "error", error };
162
215
  updateDataFromState();
163
216
  });
164
217
  }, [path, onPathChange]);
165
- const updateData = useMemo(
166
- () => (update) => {
218
+ const setDataTo = useMemo(
219
+ () => (data2) => {
167
220
  if (state.current.path !== path) {
168
221
  return;
169
222
  }
170
- if (state.current.data === void 0) {
171
- throw new Error("Attempt to update data before it has been loaded");
172
- }
173
- state.current.data = update(state.current.data);
223
+ state.current.data = data2;
174
224
  state.current.lastUpdatedMillis = Date.now();
175
225
  state.current.state = { state: "dirty" };
176
226
  saveData();
177
227
  updateDataFromState();
178
228
  },
179
- [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]
180
243
  );
181
244
  return {
182
245
  data,
183
246
  updateData,
247
+ resetData,
184
248
  saveData
185
249
  };
186
250
  }
@@ -195,6 +259,9 @@ function createDataFileDefinition({
195
259
  updateData: () => {
196
260
  throw new Error("Data file provider not used");
197
261
  },
262
+ resetData: () => {
263
+ throw new Error("Data file provider not used");
264
+ },
198
265
  saveData: () => {
199
266
  throw new Error("Data file provider not used");
200
267
  },
@@ -202,17 +269,25 @@ function createDataFileDefinition({
202
269
  });
203
270
  const useDataFile2 = ({
204
271
  path,
205
- onPathChange
272
+ onPathChange,
273
+ onError
206
274
  }) => useDataFileCore({
207
275
  schema,
208
276
  defaultValue,
209
277
  path,
210
- onPathChange
278
+ onPathChange,
279
+ onError
211
280
  });
212
- const Provider = ({ path, onPathChange, children }) => {
213
- const { data, updateData, saveData } = useDataFile2({
281
+ const Provider = ({
282
+ path,
283
+ onPathChange,
284
+ onError,
285
+ children
286
+ }) => {
287
+ const { data, updateData, resetData, saveData } = useDataFile2({
214
288
  path,
215
- onPathChange
289
+ onPathChange,
290
+ onError
216
291
  });
217
292
  const providedContext = useMemo(
218
293
  () => ({
@@ -220,6 +295,7 @@ function createDataFileDefinition({
220
295
  status: data.status,
221
296
  lastUpdatedMillis: data.lastUpdatedMillis,
222
297
  updateData,
298
+ resetData,
223
299
  saveData,
224
300
  error: data.status === "error" ? data.error : void 0
225
301
  }),
@@ -237,6 +313,7 @@ function createDataFileDefinition({
237
313
  };
238
314
  }
239
315
  export {
316
+ ArcaneDataFileError,
240
317
  createDataFileDefinition,
241
318
  useDataFile,
242
319
  useDataFileContext,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcanejs/react-toolkit",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "private": false,
5
5
  "description": "Build web-accessible control interfaces for your long-running Node.js processes",
6
6
  "keywords": [