fusee-framework 1.7.0 → 1.9.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/README.md +206 -139
- package/app/benchmark/benchmark.js +2 -2
- package/app/benchmark/vite.config.js +23 -0
- package/app/main.js +24 -3
- package/app/main.ts +7 -1
- package/app/pages/_layout.js +27 -0
- package/app/pages/async-work.js +65 -0
- package/app/pages/index.js +25 -5
- package/app/pages/index.ts +9 -0
- package/app/pages/router-example.js +24 -1
- package/app/pages/scroll-demo.ts +102 -0
- package/app/providers/ChildComponent.js +8 -2
- package/app/providers/ChildComponent.ts +47 -47
- package/app/providers/GrandchildComponent.js +19 -0
- package/app/providers/ParentComponent.js +5 -1
- package/app/routes/RouterExample.js +23 -1
- package/app/routes/TestResource.js +47 -0
- package/app/services.js +15 -0
- package/auto-imports.d.ts +12 -0
- package/framework/auto-import-preset.js +15 -9
- package/framework/core/async-context.js +27 -0
- package/framework/core/compiler/ast.js +167 -0
- package/framework/core/compiler/errors-list.js +47 -0
- package/framework/core/compiler/errors.js +107 -0
- package/framework/core/compiler/generator.js +363 -0
- package/framework/core/compiler/lexer.js +347 -0
- package/framework/core/compiler/main-compiler.js +115 -0
- package/framework/core/compiler/parser.js +414 -0
- package/framework/core/compiler/plugins/actions-plugin.js +263 -0
- package/framework/core/compiler/plugins/compiler-plugin.d.ts +5 -0
- package/framework/core/compiler/plugins/compiler-plugin.js +45 -0
- package/framework/core/compiler/plugins/file-router-plugin.js +301 -0
- package/framework/core/compiler/plugins/router-plugin.js +196 -0
- package/framework/core/compiler/transformer.js +398 -0
- package/framework/core/component.js +61 -23
- package/framework/core/di.js +187 -0
- package/framework/core/directives.js +40 -96
- package/framework/core/evaluator.js +1 -1
- package/framework/core/h.js +430 -0
- package/framework/core/hydration.js +83 -0
- package/framework/core/reconcile.js +151 -0
- package/framework/core/signal.js +269 -0
- package/framework/core/store.js +322 -81
- package/framework/index.js +5 -3
- package/framework/router/file-router.js +1 -1
- package/framework/router/router.js +113 -21
- package/framework/server/actions.js +173 -0
- package/framework/server/actions.server.js +88 -0
- package/framework/server/index.js +18 -0
- package/framework/types/actions.d.ts +50 -0
- package/framework/types/actions.server.d.ts +36 -0
- package/framework/types/compiler/ast.d.ts +166 -0
- package/framework/types/compiler/errors-codes.d.ts +23 -0
- package/framework/types/compiler/errors.d.ts +43 -0
- package/framework/types/compiler/generator.d.ts +8 -0
- package/framework/types/compiler/h.d.ts +46 -0
- package/framework/types/compiler/lexer.d.ts +27 -0
- package/framework/types/compiler/main-compiler.d.ts +60 -0
- package/framework/types/compiler/parser.d.ts +4 -0
- package/framework/types/compiler/plugins/actions.d.ts +30 -0
- package/framework/types/compiler/plugins/compiler-plugin.d.ts +5 -0
- package/framework/types/compiler/plugins/file-router-plugins.d.ts +32 -0
- package/framework/types/compiler/plugins/router-plugin.d.ts +24 -0
- package/framework/types/compiler/transformer.d.ts +28 -0
- package/framework/types/component.d.ts +89 -88
- package/framework/types/di.d.ts +43 -0
- package/framework/types/hydration.d.ts +30 -0
- package/framework/types/index.d.ts +38 -22
- package/framework/types/reconcile.d.ts +7 -0
- package/framework/types/router.d.ts +11 -1
- package/framework/types/server.d.ts +11 -0
- package/framework/types/signal.d.ts +142 -79
- package/framework/types/store.d.ts +66 -8
- package/ghid_go_fusee_integrat_go1_26.txt +276 -0
- package/ghid_rust_fusee_rust_1_95_0.txt +1118 -0
- package/package.json +44 -36
- package/vite.config.js +65 -47
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
import { currentRoute, navigate, routeParams, computed } from '../../framework/index.js'
|
|
1
|
+
import { currentRoute, navigate, routeParams, routeQuery, computed } from '../../framework/index.js'
|
|
2
2
|
|
|
3
3
|
export const RouterExample = defineComponent({
|
|
4
4
|
setup() {
|
|
5
5
|
const path = currentRoute
|
|
6
6
|
const params = routeParams
|
|
7
|
+
const query = routeQuery
|
|
7
8
|
const paramsJson = computed(() => JSON.stringify(params()))
|
|
9
|
+
const queryJson = computed(() => JSON.stringify(query()))
|
|
10
|
+
|
|
11
|
+
const updateQuery = () => {
|
|
12
|
+
const now = new Date().toLocaleTimeString()
|
|
13
|
+
const basePath = path().split('?')[0]
|
|
14
|
+
navigate(`${basePath}?t=${encodeURIComponent(now)}&user=guest`)
|
|
15
|
+
}
|
|
8
16
|
|
|
9
17
|
return {
|
|
10
18
|
path,
|
|
11
19
|
paramsJson,
|
|
20
|
+
queryJson,
|
|
12
21
|
navigate,
|
|
22
|
+
updateQuery,
|
|
13
23
|
template: `
|
|
14
24
|
<div class="page">
|
|
15
25
|
<h1>🔗 Router API Demo</h1>
|
|
@@ -19,6 +29,19 @@ export const RouterExample = defineComponent({
|
|
|
19
29
|
<code style="color: #8b8bff; font-size: 1.5rem;">{{ path }}</code>
|
|
20
30
|
</div>
|
|
21
31
|
|
|
32
|
+
<div class="demo-card">
|
|
33
|
+
<h2>routeQuery Signal (Query Params)</h2>
|
|
34
|
+
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
35
|
+
<a href="/router-example?search=framework&page=1" f-link class="cmp-btn" style="text-decoration: none;">?search=framework&page=1</a>
|
|
36
|
+
<a href="/router-example?filter=active&sort=desc" f-link class="cmp-btn" style="text-decoration: none;">?filter=active&sort=desc</a>
|
|
37
|
+
<button @click="updateQuery" class="cmp-btn">Update Time via Query</button>
|
|
38
|
+
</div>
|
|
39
|
+
<code style="color: #f59e0b; font-size: 1.2rem;">{{ queryJson }}</code>
|
|
40
|
+
<p style="margin-top: 10px; color: #888; font-size: 0.9rem;">
|
|
41
|
+
Query parameters are automatically parsed into the routeQuery signal.
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
22
45
|
<div class="demo-card">
|
|
23
46
|
<h2>routeParams Signal (Dynamic Routes)</h2>
|
|
24
47
|
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { defineComponent, currentRoute } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Scroll Demo - demonstrates router scroll behavior
|
|
5
|
+
*/
|
|
6
|
+
export default defineComponent({
|
|
7
|
+
setup() {
|
|
8
|
+
const route = currentRoute
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
template: `
|
|
12
|
+
<div class="content-wrap">
|
|
13
|
+
<h1>🔽 Scroll Behavior Demo</h1>
|
|
14
|
+
<p>This page demonstrates the router's scroll behavior. Click the navigation links to jump to sections.</p>
|
|
15
|
+
|
|
16
|
+
<!-- Navigation -->
|
|
17
|
+
<div class="demo-section">
|
|
18
|
+
<h2>Navigation</h2>
|
|
19
|
+
<div class="controls">
|
|
20
|
+
<a href="/scroll-demo#intro" f-link class="cmp-btn">Introduction</a>
|
|
21
|
+
<a href="/scroll-demo#features" f-link class="cmp-btn">Features</a>
|
|
22
|
+
<a href="/scroll-demo#examples" f-link class="cmp-btn">Examples</a>
|
|
23
|
+
<a href="/scroll-demo#api" f-link class="cmp-btn">API</a>
|
|
24
|
+
<a href="/scroll-demo#bottom" f-link class="cmp-btn">Bottom</a>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Scroll indicator -->
|
|
29
|
+
<div class="info-section">
|
|
30
|
+
<h2>Current Route</h2>
|
|
31
|
+
<p><strong>Scroll Features:</strong></p>
|
|
32
|
+
<ul>
|
|
33
|
+
<li>✓ scrollToTop</li>
|
|
34
|
+
<li>✓ saveScrollPosition</li>
|
|
35
|
+
<li>✓ custom scroll function</li>
|
|
36
|
+
</ul>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- Sections -->
|
|
40
|
+
<div style="display: flex; flex-direction: column; gap: 2rem; margin-top: 2rem;">
|
|
41
|
+
<section id="intro" class="demo-section" style="min-height: 40vh; scroll-margin-top: 100px;">
|
|
42
|
+
<h2>Introduction</h2>
|
|
43
|
+
<p>Welcome to the scroll behavior demo! Navigate between sections using the links below.</p>
|
|
44
|
+
<div style="opacity: 0.5; margin-top: 1rem;">
|
|
45
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
46
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
47
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
48
|
+
</div>
|
|
49
|
+
</section>
|
|
50
|
+
|
|
51
|
+
<section id="features" class="demo-section" style="min-height: 40vh; scroll-margin-top: 100px;">
|
|
52
|
+
<h2>Features</h2>
|
|
53
|
+
<p>Smooth scrolling, anchor navigation, and scroll position restoration are all supported by the Fusee router.</p>
|
|
54
|
+
<div style="opacity: 0.5; margin-top: 1rem;">
|
|
55
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
56
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
57
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
58
|
+
</div>
|
|
59
|
+
</section>
|
|
60
|
+
|
|
61
|
+
<section id="examples" class="demo-section" style="min-height: 40vh; scroll-margin-top: 100px;">
|
|
62
|
+
<h2>Examples</h2>
|
|
63
|
+
<p>Click on any section link to see smooth scroll to anchor. Use browser back/forward to see scroll position restore.</p>
|
|
64
|
+
<div style="opacity: 0.5; margin-top: 1rem;">
|
|
65
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
66
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
67
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
68
|
+
</div>
|
|
69
|
+
</section>
|
|
70
|
+
|
|
71
|
+
<section id="api" class="demo-section" style="min-height: 40vh; scroll-margin-top: 100px;">
|
|
72
|
+
<h2>API</h2>
|
|
73
|
+
<p>Configure scroll behavior in createRouter(): scrollToTop, saveScrollPosition, or custom function.</p>
|
|
74
|
+
<div style="opacity: 0.5; margin-top: 1rem;">
|
|
75
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
76
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
77
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
78
|
+
</div>
|
|
79
|
+
</section>
|
|
80
|
+
|
|
81
|
+
<section id="bottom" class="demo-section" style="min-height: 40vh; scroll-margin-top: 100px;">
|
|
82
|
+
<h2>Bottom Section</h2>
|
|
83
|
+
<p>You made it to the bottom! The scroll behavior automatically scrolls to anchors when navigating with #hash URLs.</p>
|
|
84
|
+
<div style="opacity: 0.5; margin-top: 1rem;">
|
|
85
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
86
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
87
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
|
88
|
+
</div>
|
|
89
|
+
</section>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<!-- Back to top -->
|
|
93
|
+
<div style="text-align: center; margin-top: 3rem; padding: 2rem;">
|
|
94
|
+
<a href="/scroll-demo" f-link class="action-btn btn-primary">
|
|
95
|
+
⬆️ Back to Top
|
|
96
|
+
</a>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
`
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inject } from '../../framework/index.js'
|
|
2
|
+
import { LoggerService } from '../services.js'
|
|
2
3
|
import { GrandchildComponent } from './GrandchildComponent.js'
|
|
3
4
|
|
|
4
5
|
// ChildComponent - injects values from parent (ParentComponent)
|
|
@@ -9,8 +10,11 @@ export const ChildComponent = defineComponent({
|
|
|
9
10
|
const count = inject('count')
|
|
10
11
|
const message = inject('message')
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const logger = inject(LoggerService)
|
|
14
|
+
logger.log('ChildComponent initialized!')
|
|
15
|
+
|
|
16
|
+
// These are NOT provided by parent, so we use optional: true to avoid throwing NullInjectorError
|
|
17
|
+
const nonExistent = inject('nonExistent', { optional: true })
|
|
14
18
|
|
|
15
19
|
function updateMessage() {
|
|
16
20
|
message('Message updated by Child!')
|
|
@@ -20,6 +24,7 @@ export const ChildComponent = defineComponent({
|
|
|
20
24
|
count,
|
|
21
25
|
message,
|
|
22
26
|
nonExistent,
|
|
27
|
+
logger,
|
|
23
28
|
updateMessage,
|
|
24
29
|
template: `
|
|
25
30
|
<div style="padding: 15px; border: 2px solid #2196F3; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
|
|
@@ -29,6 +34,7 @@ export const ChildComponent = defineComponent({
|
|
|
29
34
|
<li><strong>count:</strong> {{ count }}</li>
|
|
30
35
|
<li><strong>message:</strong> {{ message }}</li>
|
|
31
36
|
<li><strong>nonExistent:</strong> {{ nonExistent || 'undefined' }}</li>
|
|
37
|
+
<li><strong>Logger ID:</strong> {{ logger.id }} <em>(Singleton!)</em></li>
|
|
32
38
|
</ul>
|
|
33
39
|
<button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
34
40
|
Update Message
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { inject, provide } from '../../framework/index.js'
|
|
2
|
-
import { GrandchildComponent } from './GrandchildComponent.js'
|
|
3
|
-
|
|
4
|
-
// ChildComponent - injects values from parent (ParentComponent)
|
|
5
|
-
export const ChildComponent = defineComponent({
|
|
6
|
-
components: { GrandchildComponent },
|
|
7
|
-
setup() {
|
|
8
|
-
// Inject values provided by the parent
|
|
9
|
-
const count = inject('count')
|
|
10
|
-
const message = inject('message')
|
|
11
|
-
|
|
12
|
-
// These are NOT provided by parent, so
|
|
13
|
-
const nonExistent = inject('nonExistent')
|
|
14
|
-
|
|
15
|
-
function updateMessage(): void {
|
|
16
|
-
const msg = message as any
|
|
17
|
-
if (msg) {
|
|
18
|
-
msg('Message updated by Child!')
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
count,
|
|
24
|
-
message,
|
|
25
|
-
nonExistent,
|
|
26
|
-
updateMessage,
|
|
27
|
-
template: `
|
|
28
|
-
<div style="padding: 15px; border: 2px solid #2196F3; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
|
|
29
|
-
<h3 style="color: #2196F3;">Child Component (direct child of Parent)</h3>
|
|
30
|
-
<p>I'm injecting these values from my parent:</p>
|
|
31
|
-
<ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
|
|
32
|
-
<li><strong>count:</strong> {{ count }}</li>
|
|
33
|
-
<li><strong>message:</strong> {{ message }}</li>
|
|
34
|
-
<li><strong>nonExistent:</strong> {{ nonExistent || 'undefined' }}</li>
|
|
35
|
-
</ul>
|
|
36
|
-
<button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
37
|
-
Update Message
|
|
38
|
-
</button>
|
|
39
|
-
|
|
40
|
-
<div style="margin-top: 15px;">
|
|
41
|
-
{{ GrandchildComponent }}
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
`
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
})
|
|
1
|
+
import { inject, provide } from '../../framework/index.js'
|
|
2
|
+
import { GrandchildComponent } from './GrandchildComponent.js'
|
|
3
|
+
|
|
4
|
+
// ChildComponent - injects values from parent (ParentComponent)
|
|
5
|
+
export const ChildComponent = defineComponent({
|
|
6
|
+
components: { GrandchildComponent },
|
|
7
|
+
setup() {
|
|
8
|
+
// Inject values provided by the parent
|
|
9
|
+
const count = inject('count')
|
|
10
|
+
const message = inject('message')
|
|
11
|
+
|
|
12
|
+
// These are NOT provided by parent, so we use optional: true to avoid throwing NullInjectorError
|
|
13
|
+
const nonExistent = inject('nonExistent', { optional: true })
|
|
14
|
+
|
|
15
|
+
function updateMessage(): void {
|
|
16
|
+
const msg = message as any
|
|
17
|
+
if (msg) {
|
|
18
|
+
msg('Message updated by Child!')
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
count,
|
|
24
|
+
message,
|
|
25
|
+
nonExistent,
|
|
26
|
+
updateMessage,
|
|
27
|
+
template: `
|
|
28
|
+
<div style="padding: 15px; border: 2px solid #2196F3; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
|
|
29
|
+
<h3 style="color: #2196F3;">Child Component (direct child of Parent)</h3>
|
|
30
|
+
<p>I'm injecting these values from my parent:</p>
|
|
31
|
+
<ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
|
|
32
|
+
<li><strong>count:</strong> {{ count }}</li>
|
|
33
|
+
<li><strong>message:</strong> {{ message }}</li>
|
|
34
|
+
<li><strong>nonExistent:</strong> {{ nonExistent || 'undefined' }}</li>
|
|
35
|
+
</ul>
|
|
36
|
+
<button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
37
|
+
Update Message
|
|
38
|
+
</button>
|
|
39
|
+
|
|
40
|
+
<div style="margin-top: 15px;">
|
|
41
|
+
{{ GrandchildComponent }}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
`
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inject } from '../../framework/index.js'
|
|
2
|
+
import { LoggerService, APP_CONFIG } from '../services.js'
|
|
2
3
|
|
|
3
4
|
// GrandchildComponent - injects values from grandparent (ParentComponent)
|
|
4
5
|
// This demonstrates that inject() searches the entire ancestor chain
|
|
@@ -10,6 +11,11 @@ export const GrandchildComponent = defineComponent({
|
|
|
10
11
|
const userData = inject('userData')
|
|
11
12
|
const appVersion = inject('appVersion')
|
|
12
13
|
|
|
14
|
+
const logger = inject(LoggerService)
|
|
15
|
+
const config = inject(APP_CONFIG)
|
|
16
|
+
|
|
17
|
+
logger.log('GrandchildComponent initialized!')
|
|
18
|
+
|
|
13
19
|
function updateMessage() {
|
|
14
20
|
// message is a signal, so we can update it from grandchild!
|
|
15
21
|
message('Updated from Grandchild!')
|
|
@@ -20,6 +26,8 @@ export const GrandchildComponent = defineComponent({
|
|
|
20
26
|
message,
|
|
21
27
|
userData,
|
|
22
28
|
appVersion,
|
|
29
|
+
logger,
|
|
30
|
+
config,
|
|
23
31
|
updateMessage,
|
|
24
32
|
template: `
|
|
25
33
|
<div style="padding: 15px; border: 2px solid #9C27B0; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
|
|
@@ -30,7 +38,18 @@ export const GrandchildComponent = defineComponent({
|
|
|
30
38
|
<li><strong>message:</strong> {{ message }} <em>(from ParentComponent)</em></li>
|
|
31
39
|
<li><strong>userData:</strong> {{ userData.name }}, {{ userData.age }} <em>(from ParentComponent)</em></li>
|
|
32
40
|
<li><strong>appVersion:</strong> {{ appVersion }} <em>(from ParentComponent)</em></li>
|
|
41
|
+
<li><strong>Logger ID:</strong> {{ logger.id }} <em>(Singleton!)</em></li>
|
|
42
|
+
<li><strong>App Title:</strong> {{ config.title }} <em>(Global Config)</em></li>
|
|
33
43
|
</ul>
|
|
44
|
+
|
|
45
|
+
<div style="margin-top: 15px; padding: 10px; background: #2a2a2a; border-radius: 4px;">
|
|
46
|
+
<h5 style="margin-bottom: 5px; color: #34d399;">Service Logs:</h5>
|
|
47
|
+
<ul style="font-size: 0.8rem; list-style: none;">
|
|
48
|
+
<li f-for="log in logger.logs" style="border-bottom: 1px solid #444; padding: 2px 0;">
|
|
49
|
+
<span style="color: #888;">[{{ log.time }}]</span> {{ log.message }}
|
|
50
|
+
</li>
|
|
51
|
+
</ul>
|
|
52
|
+
</div>
|
|
34
53
|
<button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #9C27B0; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
35
54
|
Update Message from Grandchild
|
|
36
55
|
</button>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { provide } from '../../framework/index.js'
|
|
1
|
+
import { provide, inject } from '../../framework/index.js'
|
|
2
|
+
import { LoggerService } from '../services.js'
|
|
2
3
|
import { ChildComponent } from './ChildComponent.js'
|
|
3
4
|
|
|
4
5
|
// ParentComponent - provides values to child components
|
|
@@ -9,6 +10,9 @@ export const ParentComponent = defineComponent({
|
|
|
9
10
|
const message = signal('Hello from Parent!')
|
|
10
11
|
const userData = { name: 'John', age: 30 }
|
|
11
12
|
|
|
13
|
+
const logger = inject(LoggerService)
|
|
14
|
+
logger.log('ParentComponent initialized!')
|
|
15
|
+
|
|
12
16
|
// Provide values to descendant components
|
|
13
17
|
provide('count', count)
|
|
14
18
|
provide('message', message)
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import { currentRoute, navigate, routeParams, computed } from '../../framework/index.js'
|
|
1
|
+
import { currentRoute, navigate, routeParams, routeQuery, computed } from '../../framework/index.js'
|
|
2
2
|
|
|
3
3
|
export const RouterExample = defineComponent({
|
|
4
4
|
setup() {
|
|
5
5
|
const path = currentRoute
|
|
6
6
|
const params = routeParams
|
|
7
|
+
const query = routeQuery
|
|
7
8
|
const paramsJson = computed(() => JSON.stringify(params()))
|
|
9
|
+
const queryJson = computed(() => JSON.stringify(query()))
|
|
10
|
+
|
|
11
|
+
const updateQuery = () => {
|
|
12
|
+
const now = new Date().toLocaleTimeString()
|
|
13
|
+
navigate(`${path()}?t=${encodeURIComponent(now)}&user=guest`)
|
|
14
|
+
}
|
|
8
15
|
|
|
9
16
|
return {
|
|
10
17
|
path,
|
|
11
18
|
paramsJson,
|
|
19
|
+
queryJson,
|
|
12
20
|
navigate,
|
|
21
|
+
updateQuery,
|
|
13
22
|
template: `
|
|
14
23
|
<div class="page">
|
|
15
24
|
<h1>🔗 Router API Demo</h1>
|
|
@@ -19,6 +28,19 @@ export const RouterExample = defineComponent({
|
|
|
19
28
|
<code style="color: #8b8bff; font-size: 1.5rem;">{{ path }}</code>
|
|
20
29
|
</div>
|
|
21
30
|
|
|
31
|
+
<div class="demo-card">
|
|
32
|
+
<h2>routeQuery Signal (Query Params)</h2>
|
|
33
|
+
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
34
|
+
<a href="/router?search=framework&page=1" f-link class="cmp-btn" style="text-decoration: none;">?search=framework&page=1</a>
|
|
35
|
+
<a href="/router?filter=active&sort=desc" f-link class="cmp-btn" style="text-decoration: none;">?filter=active&sort=desc</a>
|
|
36
|
+
<button @click="updateQuery" class="cmp-btn">Update Time via Query</button>
|
|
37
|
+
</div>
|
|
38
|
+
<code style="color: #f59e0b; font-size: 1.2rem;">{{ queryJson }}</code>
|
|
39
|
+
<p style="margin-top: 10px; color: #888; font-size: 0.9rem;">
|
|
40
|
+
Query parameters are automatically parsed into the routeQuery signal.
|
|
41
|
+
</p>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
22
44
|
<div class="demo-card">
|
|
23
45
|
<h2>routeParams Signal (Dynamic Routes)</h2>
|
|
24
46
|
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineComponent, signal, resource } from '../../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export default defineComponent({
|
|
4
|
+
setup() {
|
|
5
|
+
const userId = signal(1)
|
|
6
|
+
|
|
7
|
+
const [user] = resource(userId, async (id) => {
|
|
8
|
+
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
|
|
9
|
+
if (!response.ok) throw new Error('User not found')
|
|
10
|
+
return response.json()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
userId,
|
|
15
|
+
user,
|
|
16
|
+
next: () => userId(userId() + 1),
|
|
17
|
+
prev: () => userId() > 1 && userId(userId() - 1),
|
|
18
|
+
template: `
|
|
19
|
+
<div class="p-4">
|
|
20
|
+
<h1 class="text-2xl font-bold mb-4">Resource Demo</h1>
|
|
21
|
+
|
|
22
|
+
<div class="flex gap-2 mb-4">
|
|
23
|
+
<button @click="prev" class="px-4 py-2 bg-blue-500 text-white rounded">Prev User</button>
|
|
24
|
+
<button @click="next" class="px-4 py-2 bg-blue-500 text-white rounded">Next User</button>
|
|
25
|
+
<span class="py-2">User ID: {{ userId }}</span>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div f-if="user.loading()">
|
|
29
|
+
<p class="text-gray-500 italic">Loading user data...</p>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div f-if="user.error()">
|
|
33
|
+
<p class="text-red-500">Error: {{ user.error().message }}</p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div f-if="user() && !user.loading()">
|
|
37
|
+
<div class="card border p-4 rounded shadow-lg bg-white">
|
|
38
|
+
<h2 class="text-xl font-semibold">{{ user().name }}</h2>
|
|
39
|
+
<p class="text-gray-600">Email: {{ user().email }}</p>
|
|
40
|
+
<p class="text-gray-600">Company: {{ user().company.name }}</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
`
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
package/app/services.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { InjectionToken, signal } from '../framework/index.js'
|
|
2
|
+
|
|
3
|
+
export class LoggerService {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.id = Math.random().toString(36).substr(2, 9);
|
|
6
|
+
this.logs = signal([]);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
log(message) {
|
|
10
|
+
console.log(`[Logger ${this.id}] ${message}`);
|
|
11
|
+
this.logs([...this.logs(), { time: new Date().toLocaleTimeString(), message }]);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const APP_CONFIG = new InjectionToken('app-config');
|
package/auto-imports.d.ts
CHANGED
|
@@ -6,9 +6,14 @@
|
|
|
6
6
|
// biome-ignore lint: disable
|
|
7
7
|
export {}
|
|
8
8
|
declare global {
|
|
9
|
+
const InjectionToken: typeof import('fusee-framework').InjectionToken
|
|
10
|
+
const MutationType: typeof import('fusee-framework').MutationType
|
|
11
|
+
const assertSetupContext: typeof import('fusee-framework').assertSetupContext
|
|
9
12
|
const batch: typeof import('fusee-framework').batch
|
|
13
|
+
const clearStores: typeof import('fusee-framework').clearStores
|
|
10
14
|
const computed: typeof import('fusee-framework').computed
|
|
11
15
|
const defineComponent: typeof import('fusee-framework').defineComponent
|
|
16
|
+
const defineComposable: typeof import('fusee-framework').defineComposable
|
|
12
17
|
const defineStore: typeof import('fusee-framework').defineStore
|
|
13
18
|
const directive: typeof import('fusee-framework').directive
|
|
14
19
|
const effect: typeof import('fusee-framework').effect
|
|
@@ -21,7 +26,14 @@ declare global {
|
|
|
21
26
|
const onMount: typeof import('fusee-framework').onMount
|
|
22
27
|
const onUnmount: typeof import('fusee-framework').onUnmount
|
|
23
28
|
const provide: typeof import('fusee-framework').provide
|
|
29
|
+
const provideGlobal: typeof import('fusee-framework').provideGlobal
|
|
30
|
+
const registerStorePlugin: typeof import('fusee-framework').registerStorePlugin
|
|
31
|
+
const resetStore: typeof import('fusee-framework').resetStore
|
|
24
32
|
const signal: typeof import('fusee-framework').signal
|
|
33
|
+
const storeToGetters: typeof import('fusee-framework').storeToGetters
|
|
34
|
+
const storeToRefs: typeof import('fusee-framework').storeToRefs
|
|
35
|
+
const storeToState: typeof import('fusee-framework').storeToState
|
|
25
36
|
const untrack: typeof import('fusee-framework').untrack
|
|
37
|
+
const useNestedStore: typeof import('fusee-framework').useNestedStore
|
|
26
38
|
const watch: typeof import('fusee-framework').watch
|
|
27
39
|
}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
export const FuseePreset = {
|
|
2
|
-
'fusee-framework': [
|
|
3
|
-
'signal', 'computed', 'effect', 'watch',
|
|
4
|
-
'batch', 'untrack', 'inspect', 'onCleanup',
|
|
5
|
-
'defineComponent', 'onMount', 'onUnmount',
|
|
6
|
-
'directive',
|
|
7
|
-
'mountOutlet', 'mountTemplate', 'emit'
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
export const FuseePreset = {
|
|
2
|
+
'fusee-framework': [
|
|
3
|
+
'signal', 'computed', 'effect', 'watch',
|
|
4
|
+
'batch', 'untrack', 'inspect', 'onCleanup',
|
|
5
|
+
'defineComponent', 'onMount', 'onUnmount',
|
|
6
|
+
'directive',
|
|
7
|
+
'mountOutlet', 'mountTemplate', 'emit',
|
|
8
|
+
'defineStore', 'resetStore', 'clearStores',
|
|
9
|
+
'registerStorePlugin', 'useNestedStore',
|
|
10
|
+
'storeToRefs', 'storeToState', 'storeToGetters',
|
|
11
|
+
'MutationType',
|
|
12
|
+
'provide', 'inject', 'provideGlobal', 'InjectionToken',
|
|
13
|
+
'defineComposable', 'assertSetupContext'
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
let _als = null
|
|
2
|
+
|
|
3
|
+
export function _registerALS(als) {
|
|
4
|
+
_als = als
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function _getALS() {
|
|
8
|
+
return _als
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const _globalInFlightMaps = {
|
|
12
|
+
byKey: new Map(),
|
|
13
|
+
byFetcher: new WeakMap()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getInFlightStore() {
|
|
17
|
+
if (_als) {
|
|
18
|
+
const ctx = _als.getStore()
|
|
19
|
+
if (ctx) return ctx.inFlightMaps
|
|
20
|
+
}
|
|
21
|
+
return _globalInFlightMaps
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isSSRContext() {
|
|
25
|
+
if (!_als) return false
|
|
26
|
+
return _als.getStore() !== undefined
|
|
27
|
+
}
|