@zeus-js/resource 0.0.0-stage → 0.1.1-beta.14
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/resource.cjs +151 -0
- package/dist/resource.d.ts +45 -0
- package/dist/resource.esm-bundler.js +237 -0
- package/dist/resource.prod.cjs +151 -0
- package/index.cjs +7 -0
- package/package.json +43 -3
- package/README.md +0 -3
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resource v0.1.1-beta.14
|
|
3
|
+
* (c) 2026 baicie
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
**/
|
|
6
|
+
Object.defineProperties(exports, {
|
|
7
|
+
__esModule: { value: true },
|
|
8
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
9
|
+
});
|
|
10
|
+
let _zeus_js_signal = require("@zeus-js/signal");
|
|
11
|
+
let _zeus_js_signal_internal = require("@zeus-js/signal/internal");
|
|
12
|
+
//#region packages/core/resource/src/index.ts
|
|
13
|
+
const primitiveKey = (value) => {
|
|
14
|
+
if (typeof value === "string") return `string:${value}`;
|
|
15
|
+
if (typeof value === "number") return `number:${value}`;
|
|
16
|
+
if (typeof value === "boolean") return `boolean:${value}`;
|
|
17
|
+
if (value === null) return "null";
|
|
18
|
+
if (value === void 0) return "undefined";
|
|
19
|
+
throw new Error("Resource source objects require an explicit options.key");
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Creates a single-owner async resource. Every request has a generation and
|
|
23
|
+
* AbortSignal, so a late response cannot overwrite a newer result.
|
|
24
|
+
*/
|
|
25
|
+
function createResource(source, fetcher, options = {}) {
|
|
26
|
+
var _options$key;
|
|
27
|
+
const keyOf = (_options$key = options.key) !== null && _options$key !== void 0 ? _options$key : primitiveKey;
|
|
28
|
+
const cache = /* @__PURE__ */ new Map();
|
|
29
|
+
const [state, setState] = (0, _zeus_js_signal.createSignal)({
|
|
30
|
+
status: "pending",
|
|
31
|
+
pending: true,
|
|
32
|
+
success: false,
|
|
33
|
+
error: void 0,
|
|
34
|
+
data: options.initialData,
|
|
35
|
+
stale: options.initialData !== void 0,
|
|
36
|
+
updatedAt: void 0
|
|
37
|
+
});
|
|
38
|
+
let controller;
|
|
39
|
+
let generation = 0;
|
|
40
|
+
let disposed = false;
|
|
41
|
+
let lastSource;
|
|
42
|
+
const cancel = () => {
|
|
43
|
+
generation++;
|
|
44
|
+
controller === null || controller === void 0 || controller.abort();
|
|
45
|
+
controller = void 0;
|
|
46
|
+
};
|
|
47
|
+
const refetch = async () => {
|
|
48
|
+
if (disposed) throw new Error("Resource has been disposed");
|
|
49
|
+
const currentSource = lastSource;
|
|
50
|
+
const key = keyOf(currentSource);
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
cancel();
|
|
53
|
+
const requestGeneration = ++generation;
|
|
54
|
+
const cached = cache.get(key);
|
|
55
|
+
if (cached && options.staleTime !== void 0 && now - cached.updatedAt < options.staleTime) {
|
|
56
|
+
setState({
|
|
57
|
+
status: "success",
|
|
58
|
+
pending: false,
|
|
59
|
+
success: true,
|
|
60
|
+
error: void 0,
|
|
61
|
+
data: cached.data,
|
|
62
|
+
stale: false,
|
|
63
|
+
updatedAt: cached.updatedAt
|
|
64
|
+
});
|
|
65
|
+
return cached.data;
|
|
66
|
+
}
|
|
67
|
+
const requestController = new AbortController();
|
|
68
|
+
controller = requestController;
|
|
69
|
+
const previous = state().data;
|
|
70
|
+
setState({
|
|
71
|
+
status: "pending",
|
|
72
|
+
pending: true,
|
|
73
|
+
success: false,
|
|
74
|
+
error: void 0,
|
|
75
|
+
data: previous,
|
|
76
|
+
stale: previous !== void 0,
|
|
77
|
+
updatedAt: state().updatedAt
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
const data = await fetcher(currentSource, { signal: requestController.signal });
|
|
81
|
+
if (disposed || requestGeneration !== generation) throw new Error("Resource request became stale");
|
|
82
|
+
const updatedAt = Date.now();
|
|
83
|
+
cache.set(key, {
|
|
84
|
+
data,
|
|
85
|
+
updatedAt
|
|
86
|
+
});
|
|
87
|
+
setState({
|
|
88
|
+
status: "success",
|
|
89
|
+
pending: false,
|
|
90
|
+
success: true,
|
|
91
|
+
error: void 0,
|
|
92
|
+
data,
|
|
93
|
+
stale: false,
|
|
94
|
+
updatedAt
|
|
95
|
+
});
|
|
96
|
+
return data;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (disposed || requestGeneration !== generation) throw error;
|
|
99
|
+
if (requestController.signal.aborted) setState({
|
|
100
|
+
...state(),
|
|
101
|
+
status: "pending",
|
|
102
|
+
pending: true,
|
|
103
|
+
success: false,
|
|
104
|
+
stale: state().data !== void 0
|
|
105
|
+
});
|
|
106
|
+
else setState({
|
|
107
|
+
...state(),
|
|
108
|
+
status: "error",
|
|
109
|
+
pending: false,
|
|
110
|
+
success: false,
|
|
111
|
+
error,
|
|
112
|
+
stale: state().data !== void 0
|
|
113
|
+
});
|
|
114
|
+
throw error;
|
|
115
|
+
} finally {
|
|
116
|
+
if (controller === requestController) controller = void 0;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const dispose = () => {
|
|
120
|
+
if (disposed) return;
|
|
121
|
+
disposed = true;
|
|
122
|
+
cancel();
|
|
123
|
+
cache.clear();
|
|
124
|
+
};
|
|
125
|
+
const readSource = () => typeof source === "function" ? source() : source;
|
|
126
|
+
const load = () => {
|
|
127
|
+
lastSource = readSource();
|
|
128
|
+
refetch().catch(() => void 0);
|
|
129
|
+
};
|
|
130
|
+
if (typeof source === "function" && (0, _zeus_js_signal_internal.getCurrentScope)()) (0, _zeus_js_signal.createEffect)(load);
|
|
131
|
+
else {
|
|
132
|
+
lastSource = readSource();
|
|
133
|
+
refetch().catch(() => void 0);
|
|
134
|
+
}
|
|
135
|
+
if ((0, _zeus_js_signal_internal.getCurrentScope)()) (0, _zeus_js_signal.onCleanup)(dispose);
|
|
136
|
+
return {
|
|
137
|
+
state,
|
|
138
|
+
status: () => state().status,
|
|
139
|
+
pending: () => state().pending,
|
|
140
|
+
success: () => state().success,
|
|
141
|
+
error: () => state().error,
|
|
142
|
+
data: () => state().data,
|
|
143
|
+
stale: () => state().stale,
|
|
144
|
+
updatedAt: () => state().updatedAt,
|
|
145
|
+
refetch,
|
|
146
|
+
cancel,
|
|
147
|
+
dispose
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
exports.createResource = createResource;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Accessor } from '@zeus-js/signal';
|
|
2
|
+
|
|
3
|
+
export type ResourceStatus = 'pending' | 'success' | 'error';
|
|
4
|
+
export interface ResourceState<T> {
|
|
5
|
+
readonly status: ResourceStatus;
|
|
6
|
+
readonly pending: boolean;
|
|
7
|
+
readonly success: boolean;
|
|
8
|
+
readonly error: unknown;
|
|
9
|
+
readonly data: T | undefined;
|
|
10
|
+
readonly stale: boolean;
|
|
11
|
+
readonly updatedAt: number | undefined;
|
|
12
|
+
}
|
|
13
|
+
export interface ResourceContext {
|
|
14
|
+
readonly signal: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export interface ResourceOptions<K, T> {
|
|
17
|
+
/** Converts a source value into a cache key. Primitive keys are supported by default. */
|
|
18
|
+
key?: (source: K) => string;
|
|
19
|
+
/** Fresh results are reused until this many milliseconds have elapsed. */
|
|
20
|
+
staleTime?: number;
|
|
21
|
+
/** Optional value shown before the first successful request. */
|
|
22
|
+
initialData?: T;
|
|
23
|
+
}
|
|
24
|
+
export interface Resource<T> {
|
|
25
|
+
readonly state: Accessor<ResourceState<T>>;
|
|
26
|
+
readonly status: Accessor<ResourceStatus>;
|
|
27
|
+
readonly pending: Accessor<boolean>;
|
|
28
|
+
readonly success: Accessor<boolean>;
|
|
29
|
+
readonly error: Accessor<unknown>;
|
|
30
|
+
readonly data: Accessor<T | undefined>;
|
|
31
|
+
readonly stale: Accessor<boolean>;
|
|
32
|
+
readonly updatedAt: Accessor<number | undefined>;
|
|
33
|
+
/** Starts a request for the current source and cancels the previous one. */
|
|
34
|
+
refetch(): Promise<T>;
|
|
35
|
+
cancel(): void;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
}
|
|
38
|
+
type Source<K> = K | Accessor<K>;
|
|
39
|
+
/**
|
|
40
|
+
* Creates a single-owner async resource. Every request has a generation and
|
|
41
|
+
* AbortSignal, so a late response cannot overwrite a newer result.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createResource<T, K>(source: Source<K>, fetcher: (source: K, context: ResourceContext) => Promise<T> | T, options?: ResourceOptions<K, T>): Resource<T>;
|
|
44
|
+
|
|
45
|
+
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resource v0.1.1-beta.14
|
|
3
|
+
* (c) 2026 baicie
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
**/
|
|
6
|
+
import { createEffect, createSignal, onCleanup } from "@zeus-js/signal";
|
|
7
|
+
import { getCurrentScope } from "@zeus-js/signal/internal";
|
|
8
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/typeof.js
|
|
9
|
+
function _typeof(o) {
|
|
10
|
+
"@babel/helpers - typeof";
|
|
11
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
12
|
+
return typeof o;
|
|
13
|
+
} : function(o) {
|
|
14
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
15
|
+
}, _typeof(o);
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPrimitive.js
|
|
19
|
+
function toPrimitive(t, r) {
|
|
20
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
21
|
+
var e = t[Symbol.toPrimitive];
|
|
22
|
+
if (void 0 !== e) {
|
|
23
|
+
var i = e.call(t, r || "default");
|
|
24
|
+
if ("object" != _typeof(i)) return i;
|
|
25
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
26
|
+
}
|
|
27
|
+
return ("string" === r ? String : Number)(t);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/toPropertyKey.js
|
|
31
|
+
function toPropertyKey(t) {
|
|
32
|
+
var i = toPrimitive(t, "string");
|
|
33
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/defineProperty.js
|
|
37
|
+
function _defineProperty(e, r, t) {
|
|
38
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
39
|
+
value: t,
|
|
40
|
+
enumerable: !0,
|
|
41
|
+
configurable: !0,
|
|
42
|
+
writable: !0
|
|
43
|
+
}) : e[r] = t, e;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/objectSpread2.js
|
|
47
|
+
function ownKeys(e, r) {
|
|
48
|
+
var t = Object.keys(e);
|
|
49
|
+
if (Object.getOwnPropertySymbols) {
|
|
50
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
51
|
+
r && (o = o.filter(function(r) {
|
|
52
|
+
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
|
53
|
+
})), t.push.apply(t, o);
|
|
54
|
+
}
|
|
55
|
+
return t;
|
|
56
|
+
}
|
|
57
|
+
function _objectSpread2(e) {
|
|
58
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
59
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
60
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
|
|
61
|
+
_defineProperty(e, r, t[r]);
|
|
62
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
|
|
63
|
+
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return e;
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region \0@oxc-project+runtime@0.137.0/helpers/esm/asyncToGenerator.js
|
|
70
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
71
|
+
try {
|
|
72
|
+
var i = n[a](c), u = i.value;
|
|
73
|
+
} catch (n) {
|
|
74
|
+
e(n);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
78
|
+
}
|
|
79
|
+
function _asyncToGenerator(n) {
|
|
80
|
+
return function() {
|
|
81
|
+
var t = this, e = arguments;
|
|
82
|
+
return new Promise(function(r, o) {
|
|
83
|
+
var a = n.apply(t, e);
|
|
84
|
+
function _next(n) {
|
|
85
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
|
|
86
|
+
}
|
|
87
|
+
function _throw(n) {
|
|
88
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
|
|
89
|
+
}
|
|
90
|
+
_next(void 0);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region packages/core/resource/src/index.ts
|
|
96
|
+
const primitiveKey = (value) => {
|
|
97
|
+
if (typeof value === "string") return `string:${value}`;
|
|
98
|
+
if (typeof value === "number") return `number:${value}`;
|
|
99
|
+
if (typeof value === "boolean") return `boolean:${value}`;
|
|
100
|
+
if (value === null) return "null";
|
|
101
|
+
if (value === void 0) return "undefined";
|
|
102
|
+
throw new Error("Resource source objects require an explicit options.key");
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Creates a single-owner async resource. Every request has a generation and
|
|
106
|
+
* AbortSignal, so a late response cannot overwrite a newer result.
|
|
107
|
+
*/
|
|
108
|
+
function createResource(source, fetcher, options = {}) {
|
|
109
|
+
var _options$key;
|
|
110
|
+
const keyOf = (_options$key = options.key) !== null && _options$key !== void 0 ? _options$key : primitiveKey;
|
|
111
|
+
const cache = /* @__PURE__ */ new Map();
|
|
112
|
+
const [state, setState] = createSignal({
|
|
113
|
+
status: "pending",
|
|
114
|
+
pending: true,
|
|
115
|
+
success: false,
|
|
116
|
+
error: void 0,
|
|
117
|
+
data: options.initialData,
|
|
118
|
+
stale: options.initialData !== void 0,
|
|
119
|
+
updatedAt: void 0
|
|
120
|
+
});
|
|
121
|
+
let controller;
|
|
122
|
+
let generation = 0;
|
|
123
|
+
let disposed = false;
|
|
124
|
+
let lastSource;
|
|
125
|
+
const cancel = () => {
|
|
126
|
+
generation++;
|
|
127
|
+
controller === null || controller === void 0 || controller.abort();
|
|
128
|
+
controller = void 0;
|
|
129
|
+
};
|
|
130
|
+
const refetch = function() {
|
|
131
|
+
var _ref = _asyncToGenerator(function* () {
|
|
132
|
+
if (disposed) throw new Error("Resource has been disposed");
|
|
133
|
+
const currentSource = lastSource;
|
|
134
|
+
const key = keyOf(currentSource);
|
|
135
|
+
const now = Date.now();
|
|
136
|
+
cancel();
|
|
137
|
+
const requestGeneration = ++generation;
|
|
138
|
+
const cached = cache.get(key);
|
|
139
|
+
if (cached && options.staleTime !== void 0 && now - cached.updatedAt < options.staleTime) {
|
|
140
|
+
setState({
|
|
141
|
+
status: "success",
|
|
142
|
+
pending: false,
|
|
143
|
+
success: true,
|
|
144
|
+
error: void 0,
|
|
145
|
+
data: cached.data,
|
|
146
|
+
stale: false,
|
|
147
|
+
updatedAt: cached.updatedAt
|
|
148
|
+
});
|
|
149
|
+
return cached.data;
|
|
150
|
+
}
|
|
151
|
+
const requestController = new AbortController();
|
|
152
|
+
controller = requestController;
|
|
153
|
+
const previous = state().data;
|
|
154
|
+
setState({
|
|
155
|
+
status: "pending",
|
|
156
|
+
pending: true,
|
|
157
|
+
success: false,
|
|
158
|
+
error: void 0,
|
|
159
|
+
data: previous,
|
|
160
|
+
stale: previous !== void 0,
|
|
161
|
+
updatedAt: state().updatedAt
|
|
162
|
+
});
|
|
163
|
+
try {
|
|
164
|
+
const data = yield fetcher(currentSource, { signal: requestController.signal });
|
|
165
|
+
if (disposed || requestGeneration !== generation) throw new Error("Resource request became stale");
|
|
166
|
+
const updatedAt = Date.now();
|
|
167
|
+
cache.set(key, {
|
|
168
|
+
data,
|
|
169
|
+
updatedAt
|
|
170
|
+
});
|
|
171
|
+
setState({
|
|
172
|
+
status: "success",
|
|
173
|
+
pending: false,
|
|
174
|
+
success: true,
|
|
175
|
+
error: void 0,
|
|
176
|
+
data,
|
|
177
|
+
stale: false,
|
|
178
|
+
updatedAt
|
|
179
|
+
});
|
|
180
|
+
return data;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (disposed || requestGeneration !== generation) throw error;
|
|
183
|
+
if (requestController.signal.aborted) setState(_objectSpread2(_objectSpread2({}, state()), {}, {
|
|
184
|
+
status: "pending",
|
|
185
|
+
pending: true,
|
|
186
|
+
success: false,
|
|
187
|
+
stale: state().data !== void 0
|
|
188
|
+
}));
|
|
189
|
+
else setState(_objectSpread2(_objectSpread2({}, state()), {}, {
|
|
190
|
+
status: "error",
|
|
191
|
+
pending: false,
|
|
192
|
+
success: false,
|
|
193
|
+
error,
|
|
194
|
+
stale: state().data !== void 0
|
|
195
|
+
}));
|
|
196
|
+
throw error;
|
|
197
|
+
} finally {
|
|
198
|
+
if (controller === requestController) controller = void 0;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return function refetch() {
|
|
202
|
+
return _ref.apply(this, arguments);
|
|
203
|
+
};
|
|
204
|
+
}();
|
|
205
|
+
const dispose = () => {
|
|
206
|
+
if (disposed) return;
|
|
207
|
+
disposed = true;
|
|
208
|
+
cancel();
|
|
209
|
+
cache.clear();
|
|
210
|
+
};
|
|
211
|
+
const readSource = () => typeof source === "function" ? source() : source;
|
|
212
|
+
const load = () => {
|
|
213
|
+
lastSource = readSource();
|
|
214
|
+
refetch().catch(() => void 0);
|
|
215
|
+
};
|
|
216
|
+
if (typeof source === "function" && getCurrentScope()) createEffect(load);
|
|
217
|
+
else {
|
|
218
|
+
lastSource = readSource();
|
|
219
|
+
refetch().catch(() => void 0);
|
|
220
|
+
}
|
|
221
|
+
if (getCurrentScope()) onCleanup(dispose);
|
|
222
|
+
return {
|
|
223
|
+
state,
|
|
224
|
+
status: () => state().status,
|
|
225
|
+
pending: () => state().pending,
|
|
226
|
+
success: () => state().success,
|
|
227
|
+
error: () => state().error,
|
|
228
|
+
data: () => state().data,
|
|
229
|
+
stale: () => state().stale,
|
|
230
|
+
updatedAt: () => state().updatedAt,
|
|
231
|
+
refetch,
|
|
232
|
+
cancel,
|
|
233
|
+
dispose
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
export { createResource };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resource v0.1.1-beta.14
|
|
3
|
+
* (c) 2026 baicie
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
**/
|
|
6
|
+
Object.defineProperties(exports, {
|
|
7
|
+
__esModule: { value: true },
|
|
8
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
9
|
+
});
|
|
10
|
+
let _zeus_js_signal = require("@zeus-js/signal");
|
|
11
|
+
let _zeus_js_signal_internal = require("@zeus-js/signal/internal");
|
|
12
|
+
//#region packages/core/resource/src/index.ts
|
|
13
|
+
const primitiveKey = (value) => {
|
|
14
|
+
if (typeof value === "string") return `string:${value}`;
|
|
15
|
+
if (typeof value === "number") return `number:${value}`;
|
|
16
|
+
if (typeof value === "boolean") return `boolean:${value}`;
|
|
17
|
+
if (value === null) return "null";
|
|
18
|
+
if (value === void 0) return "undefined";
|
|
19
|
+
throw new Error("Resource source objects require an explicit options.key");
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Creates a single-owner async resource. Every request has a generation and
|
|
23
|
+
* AbortSignal, so a late response cannot overwrite a newer result.
|
|
24
|
+
*/
|
|
25
|
+
function createResource(source, fetcher, options = {}) {
|
|
26
|
+
var _options$key;
|
|
27
|
+
const keyOf = (_options$key = options.key) !== null && _options$key !== void 0 ? _options$key : primitiveKey;
|
|
28
|
+
const cache = /* @__PURE__ */ new Map();
|
|
29
|
+
const [state, setState] = (0, _zeus_js_signal.createSignal)({
|
|
30
|
+
status: "pending",
|
|
31
|
+
pending: true,
|
|
32
|
+
success: false,
|
|
33
|
+
error: void 0,
|
|
34
|
+
data: options.initialData,
|
|
35
|
+
stale: options.initialData !== void 0,
|
|
36
|
+
updatedAt: void 0
|
|
37
|
+
});
|
|
38
|
+
let controller;
|
|
39
|
+
let generation = 0;
|
|
40
|
+
let disposed = false;
|
|
41
|
+
let lastSource;
|
|
42
|
+
const cancel = () => {
|
|
43
|
+
generation++;
|
|
44
|
+
controller === null || controller === void 0 || controller.abort();
|
|
45
|
+
controller = void 0;
|
|
46
|
+
};
|
|
47
|
+
const refetch = async () => {
|
|
48
|
+
if (disposed) throw new Error("Resource has been disposed");
|
|
49
|
+
const currentSource = lastSource;
|
|
50
|
+
const key = keyOf(currentSource);
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
cancel();
|
|
53
|
+
const requestGeneration = ++generation;
|
|
54
|
+
const cached = cache.get(key);
|
|
55
|
+
if (cached && options.staleTime !== void 0 && now - cached.updatedAt < options.staleTime) {
|
|
56
|
+
setState({
|
|
57
|
+
status: "success",
|
|
58
|
+
pending: false,
|
|
59
|
+
success: true,
|
|
60
|
+
error: void 0,
|
|
61
|
+
data: cached.data,
|
|
62
|
+
stale: false,
|
|
63
|
+
updatedAt: cached.updatedAt
|
|
64
|
+
});
|
|
65
|
+
return cached.data;
|
|
66
|
+
}
|
|
67
|
+
const requestController = new AbortController();
|
|
68
|
+
controller = requestController;
|
|
69
|
+
const previous = state().data;
|
|
70
|
+
setState({
|
|
71
|
+
status: "pending",
|
|
72
|
+
pending: true,
|
|
73
|
+
success: false,
|
|
74
|
+
error: void 0,
|
|
75
|
+
data: previous,
|
|
76
|
+
stale: previous !== void 0,
|
|
77
|
+
updatedAt: state().updatedAt
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
const data = await fetcher(currentSource, { signal: requestController.signal });
|
|
81
|
+
if (disposed || requestGeneration !== generation) throw new Error("Resource request became stale");
|
|
82
|
+
const updatedAt = Date.now();
|
|
83
|
+
cache.set(key, {
|
|
84
|
+
data,
|
|
85
|
+
updatedAt
|
|
86
|
+
});
|
|
87
|
+
setState({
|
|
88
|
+
status: "success",
|
|
89
|
+
pending: false,
|
|
90
|
+
success: true,
|
|
91
|
+
error: void 0,
|
|
92
|
+
data,
|
|
93
|
+
stale: false,
|
|
94
|
+
updatedAt
|
|
95
|
+
});
|
|
96
|
+
return data;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (disposed || requestGeneration !== generation) throw error;
|
|
99
|
+
if (requestController.signal.aborted) setState({
|
|
100
|
+
...state(),
|
|
101
|
+
status: "pending",
|
|
102
|
+
pending: true,
|
|
103
|
+
success: false,
|
|
104
|
+
stale: state().data !== void 0
|
|
105
|
+
});
|
|
106
|
+
else setState({
|
|
107
|
+
...state(),
|
|
108
|
+
status: "error",
|
|
109
|
+
pending: false,
|
|
110
|
+
success: false,
|
|
111
|
+
error,
|
|
112
|
+
stale: state().data !== void 0
|
|
113
|
+
});
|
|
114
|
+
throw error;
|
|
115
|
+
} finally {
|
|
116
|
+
if (controller === requestController) controller = void 0;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const dispose = () => {
|
|
120
|
+
if (disposed) return;
|
|
121
|
+
disposed = true;
|
|
122
|
+
cancel();
|
|
123
|
+
cache.clear();
|
|
124
|
+
};
|
|
125
|
+
const readSource = () => typeof source === "function" ? source() : source;
|
|
126
|
+
const load = () => {
|
|
127
|
+
lastSource = readSource();
|
|
128
|
+
refetch().catch(() => void 0);
|
|
129
|
+
};
|
|
130
|
+
if (typeof source === "function" && (0, _zeus_js_signal_internal.getCurrentScope)()) (0, _zeus_js_signal.createEffect)(load);
|
|
131
|
+
else {
|
|
132
|
+
lastSource = readSource();
|
|
133
|
+
refetch().catch(() => void 0);
|
|
134
|
+
}
|
|
135
|
+
if ((0, _zeus_js_signal_internal.getCurrentScope)()) (0, _zeus_js_signal.onCleanup)(dispose);
|
|
136
|
+
return {
|
|
137
|
+
state,
|
|
138
|
+
status: () => state().status,
|
|
139
|
+
pending: () => state().pending,
|
|
140
|
+
success: () => state().success,
|
|
141
|
+
error: () => state().error,
|
|
142
|
+
data: () => state().data,
|
|
143
|
+
stale: () => state().stale,
|
|
144
|
+
updatedAt: () => state().updatedAt,
|
|
145
|
+
refetch,
|
|
146
|
+
cancel,
|
|
147
|
+
dispose
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
exports.createResource = createResource;
|
package/index.cjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeus-js/resource",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.1-beta.14",
|
|
5
|
+
"description": "Fine-grained async resource primitives for Zeus",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/baicie/zeus.git",
|
|
10
|
+
"directory": "packages/core/resource"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"provenance": true
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/resource.d.ts",
|
|
20
|
+
"module": "./dist/resource.esm-bundler.js",
|
|
21
|
+
"import": "./dist/resource.esm-bundler.js",
|
|
22
|
+
"require": {
|
|
23
|
+
"production": "./dist/resource.prod.cjs",
|
|
24
|
+
"development": "./dist/resource.cjs",
|
|
25
|
+
"default": "./index.cjs"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"index.cjs",
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@zeus-js/signal": "0.1.1-beta.14"
|
|
35
|
+
},
|
|
36
|
+
"buildOptions": {
|
|
37
|
+
"name": "ZeusResource",
|
|
38
|
+
"formats": [
|
|
39
|
+
"esm-bundler",
|
|
40
|
+
"cjs"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"main": "index.cjs",
|
|
44
|
+
"module": "dist/resource.esm-bundler.js",
|
|
45
|
+
"types": "dist/resource.d.ts"
|
|
6
46
|
}
|
package/README.md
DELETED