nuxt-state 0.0.1
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/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/module.d.mts +7 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +22 -0
- package/dist/runtime/app/composables/defineState.d.ts +6 -0
- package/dist/runtime/app/composables/defineState.js +21 -0
- package/dist/types.d.mts +3 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Navid Talebian
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# nuxt-state
|
|
2
|
+
|
|
3
|
+
> Define shared Nuxt state using the same Composition API you already use in composables.
|
|
4
|
+
|
|
5
|
+
`nuxt-state` is an experimental Nuxt 4 module that explores one small primitive:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
defineState(() => {
|
|
9
|
+
// standard Vue Composition API
|
|
10
|
+
return {
|
|
11
|
+
/* public state */
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
A regular composable runs its factory for every invocation. A composable created by
|
|
17
|
+
`defineState` runs its factory lazily, once for the current Nuxt application instance,
|
|
18
|
+
and returns that exact result to every caller in that app.
|
|
19
|
+
|
|
20
|
+
This is a working prototype for discussion and possible future contribution to Nuxt.
|
|
21
|
+
It is not yet presented as production-ready.
|
|
22
|
+
|
|
23
|
+
## Why
|
|
24
|
+
|
|
25
|
+
Nuxt's `useState()` is excellent for simple SSR-aware values. Pinia is a strong choice
|
|
26
|
+
when an application wants a dedicated state-management library and its ecosystem.
|
|
27
|
+
This project does not replace either one. It explores a lightweight native abstraction
|
|
28
|
+
between a raw `useState()` value and a full state-management library.
|
|
29
|
+
|
|
30
|
+
There are no stores, actions, getters, mutations, IDs, configuration objects, or special
|
|
31
|
+
wrappers. The factory and its return value use normal Vue semantics.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm add nuxt-state
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// nuxt.config.ts
|
|
41
|
+
export default defineNuxtConfig({
|
|
42
|
+
modules: ["nuxt-state"],
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The package has not been published yet; during development, use this repository as a
|
|
47
|
+
workspace dependency.
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
Create a state in Nuxt 4's application source directory:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// app/states/counter.ts
|
|
55
|
+
export const useCounter = defineState(() => {
|
|
56
|
+
const count = ref(0);
|
|
57
|
+
const double = computed(() => count.value * 2);
|
|
58
|
+
|
|
59
|
+
function increment() {
|
|
60
|
+
count.value++;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
count,
|
|
65
|
+
double,
|
|
66
|
+
increment,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Exports from `app/states/`, including nested directories and multiple exports per file,
|
|
72
|
+
are auto-imported. `defineState` is also auto-imported and can be used elsewhere, such
|
|
73
|
+
as in `app/composables/`.
|
|
74
|
+
|
|
75
|
+
```vue
|
|
76
|
+
<script setup lang="ts">
|
|
77
|
+
const { count, double, increment } = useCounter();
|
|
78
|
+
|
|
79
|
+
count.value++;
|
|
80
|
+
increment();
|
|
81
|
+
console.log(double.value);
|
|
82
|
+
</script>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Calling `useCounter()` in ten components returns the same object for that Nuxt app.
|
|
86
|
+
Different server requests and different client apps receive different objects.
|
|
87
|
+
|
|
88
|
+
The returned object is not cloned, wrapped, or transformed. Refs remain refs, computed
|
|
89
|
+
refs remain computed refs, reactive objects remain reactive, and functions are unchanged.
|
|
90
|
+
|
|
91
|
+
## Semantics
|
|
92
|
+
|
|
93
|
+
- The factory is synchronous and takes no arguments.
|
|
94
|
+
- The generated composable takes no arguments.
|
|
95
|
+
- The factory is lazy and runs at first use.
|
|
96
|
+
- It runs once per Nuxt app instance.
|
|
97
|
+
- The exact factory result is returned to all callers in that app.
|
|
98
|
+
- A module-local `WeakMap` keys instances by `NuxtApp`, providing request isolation while
|
|
99
|
+
allowing old application instances to be garbage-collected.
|
|
100
|
+
- Separate `defineState()` calls have separate closure-owned caches, including calls in
|
|
101
|
+
the same file.
|
|
102
|
+
|
|
103
|
+
Async factories are rejected by TypeScript and guarded at runtime for JavaScript users.
|
|
104
|
+
Expose an async function from synchronous state or use Nuxt's data-fetching APIs instead.
|
|
105
|
+
|
|
106
|
+
## SSR scope
|
|
107
|
+
|
|
108
|
+
v0 guarantees isolation between SSR requests: module-level state does not become a
|
|
109
|
+
process-wide user-state singleton. This is intentionally different from hydration.
|
|
110
|
+
|
|
111
|
+
v0 does **not** serialize or hydrate arbitrary factory results. A result may contain
|
|
112
|
+
functions, computed refs, class instances, and other runtime-only values. If a state is
|
|
113
|
+
mutated during SSR, the client may recreate the factory's initial state during hydration.
|
|
114
|
+
Do not rely on server mutations transferring to the client yet.
|
|
115
|
+
|
|
116
|
+
## Current limitations
|
|
117
|
+
|
|
118
|
+
- Nuxt 4 and Vue 3 only.
|
|
119
|
+
- Synchronous factories only.
|
|
120
|
+
- No persistence or browser-storage integration.
|
|
121
|
+
- No arbitrary-state payload serialization or hydration yet.
|
|
122
|
+
- No Nuxt Layers support yet.
|
|
123
|
+
- State resets when its module is hot-reloaded; HMR preservation is not implemented.
|
|
124
|
+
- Compatibility with every context-sensitive Nuxt composable inside a factory is not
|
|
125
|
+
guaranteed yet.
|
|
126
|
+
- There is no reset API, keyed/multi-instance state, DevTools integration, or central
|
|
127
|
+
registry.
|
|
128
|
+
|
|
129
|
+
See [ROADMAP.md](./ROADMAP.md) for the technical questions behind future hydration and
|
|
130
|
+
context compatibility.
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
Requires Node.js 22+ and pnpm.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pnpm install
|
|
138
|
+
pnpm dev
|
|
139
|
+
pnpm lint
|
|
140
|
+
pnpm test:types
|
|
141
|
+
pnpm test
|
|
142
|
+
pnpm prepack
|
|
143
|
+
pnpm dev:build
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The playground contains two counter components, a reactive object example, a nested
|
|
147
|
+
state, and two independent states exported from one file.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
[MIT](./LICENSE)
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { defineNuxtModule, createResolver, addImports, addImportsDir } from '@nuxt/kit';
|
|
3
|
+
|
|
4
|
+
const module$1 = defineNuxtModule({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "nuxt-state",
|
|
7
|
+
compatibility: {
|
|
8
|
+
nuxt: "^4.0.0"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
defaults: {},
|
|
12
|
+
setup(_options, nuxt) {
|
|
13
|
+
const resolver = createResolver(import.meta.url);
|
|
14
|
+
addImports({
|
|
15
|
+
name: "defineState",
|
|
16
|
+
from: resolver.resolve("./runtime/app/composables/defineState")
|
|
17
|
+
});
|
|
18
|
+
addImportsDir(resolve(nuxt.options.srcDir, "states/**"));
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useNuxtApp } from "#app";
|
|
2
|
+
function isPromiseLike(value) {
|
|
3
|
+
return (typeof value === "object" && value !== null || typeof value === "function") && "then" in value && typeof value.then === "function";
|
|
4
|
+
}
|
|
5
|
+
export function defineState(factory) {
|
|
6
|
+
const instances = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
return function useDefinedState() {
|
|
8
|
+
const nuxtApp = useNuxtApp();
|
|
9
|
+
if (instances.has(nuxtApp)) {
|
|
10
|
+
return instances.get(nuxtApp);
|
|
11
|
+
}
|
|
12
|
+
const instance = factory();
|
|
13
|
+
if (isPromiseLike(instance)) {
|
|
14
|
+
throw new TypeError(
|
|
15
|
+
"[nuxt-state] State factories must be synchronous. Expose an async function from the state or use Nuxt data-fetching composables instead."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
instances.set(nuxtApp, instance);
|
|
19
|
+
return instance;
|
|
20
|
+
};
|
|
21
|
+
}
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nuxt-state",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Define shared Nuxt state using the same Composition API you already use in composables.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nuxt",
|
|
7
|
+
"nuxt-module",
|
|
8
|
+
"vue",
|
|
9
|
+
"state",
|
|
10
|
+
"composable"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Navid Talebian",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/navidtm/nuxt-state.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/navidtm/nuxt-state#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/navidtm/nuxt-state/issues"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/types.d.mts",
|
|
26
|
+
"import": "./dist/module.mjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"main": "./dist/module.mjs",
|
|
30
|
+
"typesVersions": {
|
|
31
|
+
"*": {
|
|
32
|
+
".": [
|
|
33
|
+
"./dist/types.d.mts"
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"workspaces": [
|
|
41
|
+
"playground"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@nuxt/kit": "^4.5.2"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"nuxt": "^4.0.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@nuxt/devtools": "^3.4.2",
|
|
57
|
+
"@nuxt/eslint-config": "^1.17.0",
|
|
58
|
+
"@nuxt/module-builder": "^1.0.3",
|
|
59
|
+
"@nuxt/schema": "^4.5.2",
|
|
60
|
+
"@nuxt/test-utils": "^4.1.0",
|
|
61
|
+
"@types/node": "latest",
|
|
62
|
+
"changelogen": "^0.6.2",
|
|
63
|
+
"eslint": "^10.9.0",
|
|
64
|
+
"happy-dom": "^20.8.3",
|
|
65
|
+
"nuxt": "^4.5.2",
|
|
66
|
+
"typescript": "^6.0.3",
|
|
67
|
+
"vitest": "^4.1.11",
|
|
68
|
+
"vue": "^3.5.31",
|
|
69
|
+
"vue-tsc": "^3.3.11"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"dev": "pnpm dev:prepare && nuxt dev playground",
|
|
73
|
+
"dev:build": "nuxt build playground",
|
|
74
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
75
|
+
"lint": "eslint .",
|
|
76
|
+
"test": "vitest run",
|
|
77
|
+
"test:watch": "vitest",
|
|
78
|
+
"test:types": "vue-tsc --noEmit && pnpm --dir playground exec vue-tsc --noEmit"
|
|
79
|
+
}
|
|
80
|
+
}
|