@cajoa/core 0.12.1 → 0.13.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/index.cjs CHANGED
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  createContext: () => createContext,
30
30
  createEffect: () => createEffect,
31
31
  createMemo: () => createMemo,
32
+ createResource: () => createResource,
32
33
  createRoot: () => createRoot,
33
34
  createSignal: () => createSignal,
34
35
  getRenderer: () => getRenderer,
@@ -259,6 +260,84 @@ function createMemo(fn) {
259
260
  return value;
260
261
  }
261
262
 
263
+ // src/reactivity/resource.ts
264
+ function isPromiseLike(value) {
265
+ return typeof value?.then === "function";
266
+ }
267
+ function createResource(a, b) {
268
+ const hasSource = typeof b === "function";
269
+ const source = hasSource ? a : () => true;
270
+ const fetcher = hasSource ? b : a;
271
+ const [data, setData] = createSignal(void 0);
272
+ const [failure, setFailure] = createSignal(null);
273
+ const [loading, setLoading] = createSignal(false);
274
+ const [version, setVersion] = createSignal(0);
275
+ let run = 0;
276
+ const finish = (id, outcome) => {
277
+ if (id !== run) return;
278
+ batch(() => {
279
+ setLoading(false);
280
+ if ("error" in outcome) {
281
+ setFailure({ error: outcome.error });
282
+ } else {
283
+ setFailure(null);
284
+ setData(() => outcome.value);
285
+ }
286
+ });
287
+ };
288
+ createEffect(() => {
289
+ const s = source();
290
+ version();
291
+ run++;
292
+ const id = run;
293
+ onCleanup(() => {
294
+ run++;
295
+ });
296
+ if (s === false || s === null || s === void 0) {
297
+ batch(() => {
298
+ setLoading(false);
299
+ setFailure(null);
300
+ setData(() => void 0);
301
+ });
302
+ return;
303
+ }
304
+ setLoading(true);
305
+ setFailure(null);
306
+ let result;
307
+ try {
308
+ result = untrack(() => fetcher(s));
309
+ } catch (error) {
310
+ finish(id, { error });
311
+ return;
312
+ }
313
+ if (isPromiseLike(result)) {
314
+ result.then(
315
+ (value) => finish(id, { value }),
316
+ (error) => finish(id, { error })
317
+ );
318
+ } else {
319
+ finish(id, { value: result });
320
+ }
321
+ });
322
+ const resource = (() => {
323
+ const failed = failure();
324
+ if (failed) throw failed.error;
325
+ return data();
326
+ });
327
+ resource.loading = loading;
328
+ resource.error = () => failure()?.error;
329
+ return [
330
+ resource,
331
+ {
332
+ refetch: () => setVersion((v) => v + 1),
333
+ mutate: (value) => batch(() => {
334
+ setFailure(null);
335
+ setData(() => value);
336
+ })
337
+ }
338
+ ];
339
+ }
340
+
262
341
  // src/renderer/index.ts
263
342
  var current = null;
264
343
  function setRenderer(renderer) {
@@ -483,6 +562,7 @@ function Fragment(props) {
483
562
  createContext,
484
563
  createEffect,
485
564
  createMemo,
565
+ createResource,
486
566
  createRoot,
487
567
  createSignal,
488
568
  getRenderer,
package/dist/index.d.cts CHANGED
@@ -17,6 +17,19 @@ declare function useContext<T>(ctx: Context<T>): T;
17
17
 
18
18
  declare function createMemo<T>(fn: () => T): () => T;
19
19
 
20
+ interface Resource<T> {
21
+ (): T | undefined;
22
+ loading: () => boolean;
23
+ error: () => unknown;
24
+ }
25
+ interface ResourceActions<T> {
26
+ refetch: () => void;
27
+ mutate(value: T | undefined): void;
28
+ }
29
+ type ResourceTuple<T> = [Resource<T>, ResourceActions<T>];
30
+ declare function createResource<T>(fetcher: () => T | Promise<T>): ResourceTuple<T>;
31
+ declare function createResource<T, S>(source: () => S | false | null | undefined, fetcher: (source: S) => T | Promise<T>): ResourceTuple<T>;
32
+
20
33
  type HostNode = object;
21
34
  interface Renderer<N = any> {
22
35
  createElement(tag: string): N;
@@ -76,5 +89,5 @@ declare function Fragment(props: {
76
89
  declare function setRenderer(renderer: Renderer): void;
77
90
  declare function getRenderer(): Renderer;
78
91
 
79
- export { ErrorBoundary, For, Fragment, JSX, Show, batch, catchError, createContext, createEffect, createMemo, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
80
- export type { Child, Context, HostNode, Renderer };
92
+ export { ErrorBoundary, For, Fragment, JSX, Show, batch, catchError, createContext, createEffect, createMemo, createResource, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
93
+ export type { Child, Context, HostNode, Renderer, Resource, ResourceActions };
package/dist/index.d.ts CHANGED
@@ -17,6 +17,19 @@ declare function useContext<T>(ctx: Context<T>): T;
17
17
 
18
18
  declare function createMemo<T>(fn: () => T): () => T;
19
19
 
20
+ interface Resource<T> {
21
+ (): T | undefined;
22
+ loading: () => boolean;
23
+ error: () => unknown;
24
+ }
25
+ interface ResourceActions<T> {
26
+ refetch: () => void;
27
+ mutate(value: T | undefined): void;
28
+ }
29
+ type ResourceTuple<T> = [Resource<T>, ResourceActions<T>];
30
+ declare function createResource<T>(fetcher: () => T | Promise<T>): ResourceTuple<T>;
31
+ declare function createResource<T, S>(source: () => S | false | null | undefined, fetcher: (source: S) => T | Promise<T>): ResourceTuple<T>;
32
+
20
33
  type HostNode = object;
21
34
  interface Renderer<N = any> {
22
35
  createElement(tag: string): N;
@@ -76,5 +89,5 @@ declare function Fragment(props: {
76
89
  declare function setRenderer(renderer: Renderer): void;
77
90
  declare function getRenderer(): Renderer;
78
91
 
79
- export { ErrorBoundary, For, Fragment, JSX, Show, batch, catchError, createContext, createEffect, createMemo, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
80
- export type { Child, Context, HostNode, Renderer };
92
+ export { ErrorBoundary, For, Fragment, JSX, Show, batch, catchError, createContext, createEffect, createMemo, createResource, createRoot, createSignal, getRenderer, h, jsx, jsxDEV, jsxs, onCleanup, onMount, provide, render, setRenderer, untrack, useContext };
93
+ export type { Child, Context, HostNode, Renderer, Resource, ResourceActions };
package/dist/index.js CHANGED
@@ -211,6 +211,84 @@ function createMemo(fn) {
211
211
  return value;
212
212
  }
213
213
 
214
+ // src/reactivity/resource.ts
215
+ function isPromiseLike(value) {
216
+ return typeof value?.then === "function";
217
+ }
218
+ function createResource(a, b) {
219
+ const hasSource = typeof b === "function";
220
+ const source = hasSource ? a : () => true;
221
+ const fetcher = hasSource ? b : a;
222
+ const [data, setData] = createSignal(void 0);
223
+ const [failure, setFailure] = createSignal(null);
224
+ const [loading, setLoading] = createSignal(false);
225
+ const [version, setVersion] = createSignal(0);
226
+ let run = 0;
227
+ const finish = (id, outcome) => {
228
+ if (id !== run) return;
229
+ batch(() => {
230
+ setLoading(false);
231
+ if ("error" in outcome) {
232
+ setFailure({ error: outcome.error });
233
+ } else {
234
+ setFailure(null);
235
+ setData(() => outcome.value);
236
+ }
237
+ });
238
+ };
239
+ createEffect(() => {
240
+ const s = source();
241
+ version();
242
+ run++;
243
+ const id = run;
244
+ onCleanup(() => {
245
+ run++;
246
+ });
247
+ if (s === false || s === null || s === void 0) {
248
+ batch(() => {
249
+ setLoading(false);
250
+ setFailure(null);
251
+ setData(() => void 0);
252
+ });
253
+ return;
254
+ }
255
+ setLoading(true);
256
+ setFailure(null);
257
+ let result;
258
+ try {
259
+ result = untrack(() => fetcher(s));
260
+ } catch (error) {
261
+ finish(id, { error });
262
+ return;
263
+ }
264
+ if (isPromiseLike(result)) {
265
+ result.then(
266
+ (value) => finish(id, { value }),
267
+ (error) => finish(id, { error })
268
+ );
269
+ } else {
270
+ finish(id, { value: result });
271
+ }
272
+ });
273
+ const resource = (() => {
274
+ const failed = failure();
275
+ if (failed) throw failed.error;
276
+ return data();
277
+ });
278
+ resource.loading = loading;
279
+ resource.error = () => failure()?.error;
280
+ return [
281
+ resource,
282
+ {
283
+ refetch: () => setVersion((v) => v + 1),
284
+ mutate: (value) => batch(() => {
285
+ setFailure(null);
286
+ setData(() => value);
287
+ })
288
+ }
289
+ ];
290
+ }
291
+
214
292
  // src/renderer/index.ts
215
293
  var current = null;
216
294
  function setRenderer(renderer) {
@@ -434,6 +512,7 @@ export {
434
512
  createContext,
435
513
  createEffect,
436
514
  createMemo,
515
+ createResource,
437
516
  createRoot,
438
517
  createSignal,
439
518
  getRenderer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cajoa/core",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "Cajoa core: signals, JSX runtime and the renderer interface.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",