@plastic-js/plastic 1.0.1 → 1.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plastic-js/plastic",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "main": "src/index.js",
5
5
  "access": "public",
6
6
  "sideEffects": false,
@@ -57,7 +57,7 @@
57
57
  "@babel/core": "^7.29.0",
58
58
  "@babel/preset-react": "^7.28.5",
59
59
  "@testing-library/jest-dom": "^6.9.1",
60
- "babel-preset-plastic": "^0.1.0",
60
+ "babel-preset-plastic": "^0.1.1",
61
61
  "@vitejs/plugin-react": "^6.0.2",
62
62
  "@vitejs/plugin-vue": "^6.0.7",
63
63
  "eslint": "^9.39.2",
package/src/async.js ADDED
@@ -0,0 +1,88 @@
1
+ import { createSignal } from './reactivity.js'
2
+
3
+ const normalizeService = (service)=> {
4
+ if (typeof service === 'function'){
5
+ return service
6
+ }
7
+
8
+ if (service instanceof Promise){
9
+ return ()=> service
10
+ }
11
+
12
+ throw new TypeError('createAsync expects a Promise or a function returning a Promise')
13
+ }
14
+
15
+ const createAsync = (service)=> {
16
+ const runner = normalizeService(service)
17
+ const isLoading = createSignal(false)
18
+ const data = createSignal(undefined)
19
+ const error = createSignal(null)
20
+ let latestRunId = 0
21
+
22
+ const run = (...args)=> {
23
+ const runId = ++latestRunId
24
+ isLoading(true)
25
+ error(null)
26
+
27
+ let promise
28
+ try {
29
+ promise = runner(...args)
30
+ } catch(err){
31
+ error(err)
32
+ isLoading(false)
33
+ return Promise.reject(err)
34
+ }
35
+
36
+ if (!(promise instanceof Promise)){
37
+ const typeError = new TypeError('createAsync runner must return a Promise')
38
+ error(typeError)
39
+ isLoading(false)
40
+ return Promise.reject(typeError)
41
+ }
42
+
43
+ return promise
44
+ .then((value)=> {
45
+ if (runId !== latestRunId){
46
+ return value
47
+ }
48
+
49
+ data(value)
50
+ error(null)
51
+ return value
52
+ })
53
+ .catch((err)=> {
54
+ if (runId === latestRunId){
55
+ error(err)
56
+ }
57
+ throw err
58
+ })
59
+ .finally(()=> {
60
+ if (runId === latestRunId){
61
+ isLoading(false)
62
+ }
63
+ })
64
+ }
65
+
66
+ const cancel = ()=> {
67
+ if (!isLoading()){
68
+ return
69
+ }
70
+ latestRunId++
71
+ isLoading(false)
72
+ }
73
+
74
+ // Default behavior: trigger once on creation for immediate data fetch.
75
+ run().catch(()=> {})
76
+
77
+ return {
78
+ isLoading,
79
+ data,
80
+ error,
81
+ run,
82
+ cancel,
83
+ }
84
+ }
85
+
86
+ export {
87
+ createAsync,
88
+ }
@@ -1,3 +1,9 @@
1
+ // Shared module-level state for the currently running computation.
2
+ // Extracted into its own module so the two consumers can share the same
3
+ // reference without forming a circular import between them:
4
+ // - the computation runner writes it before/after executing a tracked fn
5
+ // - the signal/state layer reads it when a signal is invoked (e.g.
6
+ // `signal()`) to register the running computation as a dependency
1
7
  let currentComputation = null
2
8
 
3
9
  const getCurrentComputation = ()=> currentComputation
package/src/index.js CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  useSearchParams,
40
40
  } from './router.js'
41
41
  import { batch } from './reactivity.js'
42
+ import { createAsync } from './async.js'
42
43
  import { mergeProps } from './merge-props.js'
43
44
  import { createSplitProps, splitProps } from './split-props.js'
44
45
 
@@ -80,6 +81,7 @@ export {
80
81
  createContext,
81
82
  useContext,
82
83
  batch,
84
+ createAsync,
83
85
  mergeProps,
84
86
  splitProps,
85
87
  createSplitProps,