@nuxtagentic/kit 0.0.0-stage → 0.1.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/LICENSE.md +9 -0
- package/README.md +46 -2
- package/package.json +31 -3
- package/registration.ts +194 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Proprietary software. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Use of Nuxt Agentic packages and starter materials is governed by the applicable
|
|
4
|
+
commercial agreement supplied with a purchase. This repository does not grant
|
|
5
|
+
an open-source license or permission to redistribute the packages as a competing
|
|
6
|
+
template or module library.
|
|
7
|
+
|
|
8
|
+
Third-party dependencies retain their respective licenses. This notice does not
|
|
9
|
+
replace or modify those licenses.
|
package/README.md
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Module registration
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@nuxtagentic/kit` is the build-time helper shared by the local Nuxt modules.
|
|
4
|
+
It exports `registerWorkspaceModule` and `getWorkspaceModules`; it is not itself a
|
|
5
|
+
Nuxt module to add to the host's `modules` array.
|
|
6
|
+
|
|
7
|
+
`externalizeSsrDependencies(names, nuxt)` lets Nitro bundle shared server
|
|
8
|
+
libraries once when both SSR and server routes import the same dependency. It resolves from the importing module,
|
|
9
|
+
including unhoisted pnpm dependencies. Browser builds and development retain
|
|
10
|
+
their existing bundling behavior.
|
|
11
|
+
|
|
12
|
+
A module passes its ID, runtime directory, optional catalog entry, feature, metadata, and
|
|
13
|
+
server contribution paths to `registerWorkspaceModule`. Kit registers its
|
|
14
|
+
composable and server directories and creates four generated manifests:
|
|
15
|
+
|
|
16
|
+
| Alias | Contains |
|
|
17
|
+
| --------------------- | -------------------------------------------------------------- |
|
|
18
|
+
| `#workspace/modules` | Installable catalog entries, defaults, and dependencies |
|
|
19
|
+
| `#workspace/features` | UI and workspace contributions tagged with their owning module |
|
|
20
|
+
| `#workspace/client` | Browser-safe model, tool, and integration metadata |
|
|
21
|
+
| `#workspace/server` | Server resource factories and integration hooks |
|
|
22
|
+
|
|
23
|
+
Use `catalog` for business features that users can install. It provides a label,
|
|
24
|
+
description, icon, icon classes, optional opening URL, dependencies, and an optional
|
|
25
|
+
`defaultInstalled` flag. Shared services and core features omit the catalog entry
|
|
26
|
+
and remain active. A package can
|
|
27
|
+
own multiple contributions.
|
|
28
|
+
Client and server manifests expose `getWorkspaceMetadata(installedIds)` to select
|
|
29
|
+
active contributions. The no-argument metadata exports contain all available entries.
|
|
30
|
+
|
|
31
|
+
Each consuming module still owns its page, layout, component, configuration, and
|
|
32
|
+
worker registration in `module.ts`. Registration is scoped to the Nuxt instance
|
|
33
|
+
and rejects duplicate IDs.
|
|
34
|
+
|
|
35
|
+
Kit does not mutate `cloud.config.ts` or run migrations. The host statically
|
|
36
|
+
composes schema exports so CloudConfig's CLI can read them outside Nuxt.
|
|
37
|
+
Feature packages declare schema exports in their `cloudconfig.models` metadata.
|
|
38
|
+
See [the package map and setup guide](../README.md).
|
|
39
|
+
|
|
40
|
+
The portable fixture installs one module at a time without the host app:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
NUXT_TEST_MODULE=agents pnpm exec nuxt typecheck packages/kit/tests/fixture
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Replace `agents` with another module name. `pnpm test:modules` checks import
|
|
47
|
+
boundaries, dependency cycles, exported sources, and fixture isolation.
|
package/package.json
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtagentic/kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"registration.ts",
|
|
7
|
+
"README.md",
|
|
8
|
+
"LICENSE.md"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./registration.ts",
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@nuxt/kit": "4.6.1",
|
|
16
|
+
"@nuxt/schema": "4.6.1"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"nuxt": "^4.6.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"tsx": "^4.23.15",
|
|
23
|
+
"typescript": "^5.9.3"
|
|
24
|
+
},
|
|
25
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
26
|
+
"description": "Nuxt module registration and runtime discovery for Nuxt Agentic.",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"registry": "https://registry.npmjs.org/"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": "^22.22.3 || ^24.15.0 || >=26.0.0"
|
|
33
|
+
}
|
|
6
34
|
}
|
package/registration.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { addImportsDir, addServerScanDir, addTemplate, useNuxt } from '@nuxt/kit'
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { dirname } from 'node:path'
|
|
4
|
+
import type { Nuxt } from '@nuxt/schema'
|
|
5
|
+
|
|
6
|
+
const ssrDependenciesKey = Symbol.for('nuxt-agentic.ssrDependencies')
|
|
7
|
+
|
|
8
|
+
/** Let Nitro bundle shared server dependencies once, instead of rebundling Vite's copies. */
|
|
9
|
+
export function externalizeSsrDependencies(dependencies: readonly string[], nuxt = useNuxt()) {
|
|
10
|
+
if (nuxt.options.dev) {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
const app = nuxt as Nuxt & { [ssrDependenciesKey]?: Set<string> }
|
|
14
|
+
if (app[ssrDependenciesKey]) {
|
|
15
|
+
for (const dependency of dependencies) {
|
|
16
|
+
app[ssrDependenciesKey].add(dependency)
|
|
17
|
+
}
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
const packages = new Set(dependencies)
|
|
21
|
+
app[ssrDependenciesKey] = packages
|
|
22
|
+
nuxt.hook('vite:extendConfig', (config, { isServer }) => {
|
|
23
|
+
if (!isServer) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
const viteConfig = config as { plugins?: NonNullable<typeof config.plugins> }
|
|
27
|
+
viteConfig.plugins ??= []
|
|
28
|
+
viteConfig.plugins.unshift({
|
|
29
|
+
name: 'nuxt-agentic:ssr-dependencies',
|
|
30
|
+
enforce: 'pre',
|
|
31
|
+
async resolveId(source, importer) {
|
|
32
|
+
if (!packages.has(source)) {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
// Resolve from the importing module so pnpm's unhoisted dependencies work.
|
|
36
|
+
const resolved = await this.resolve(source, importer, { skipSelf: true })
|
|
37
|
+
if (!resolved) {
|
|
38
|
+
throw new Error(`Cannot resolve SSR dependency ${source}`)
|
|
39
|
+
}
|
|
40
|
+
return { ...resolved, external: true }
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface FeatureRegistration {
|
|
47
|
+
id: string
|
|
48
|
+
runtime: string
|
|
49
|
+
feature?: { from: string; name: string }
|
|
50
|
+
metadata?: string
|
|
51
|
+
server?: string
|
|
52
|
+
catalog?: Omit<WorkspaceModuleDefinition, 'id'>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface WorkspaceModuleDefinition {
|
|
56
|
+
id: string
|
|
57
|
+
label: string
|
|
58
|
+
description: string
|
|
59
|
+
icon: string
|
|
60
|
+
iconClass: string
|
|
61
|
+
to?: string
|
|
62
|
+
dependencies?: string[]
|
|
63
|
+
defaultInstalled?: boolean
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Nuxt can load this helper in separate module contexts; keep registration on its instance.
|
|
67
|
+
const registryKey = Symbol.for('nuxt-agentic.modules')
|
|
68
|
+
type RegisteredNuxt = Nuxt & { [registryKey]?: FeatureRegistration[] }
|
|
69
|
+
|
|
70
|
+
export function getWorkspaceModules(nuxt = useNuxt()) {
|
|
71
|
+
return (nuxt as RegisteredNuxt)[registryKey] ?? []
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Include registered package UI in Tailwind's scan, including packages in node_modules. */
|
|
75
|
+
export function registerWorkspaceStyles(stylesheet: string, nuxt = useNuxt()) {
|
|
76
|
+
const sources = addTemplate({
|
|
77
|
+
filename: 'workspace/styles.css',
|
|
78
|
+
getContents: () =>
|
|
79
|
+
[
|
|
80
|
+
`@import ${JSON.stringify(stylesheet)};`,
|
|
81
|
+
...getWorkspaceModules(nuxt).map(
|
|
82
|
+
({ runtime }) => `@source ${JSON.stringify(dirname(runtime))};`,
|
|
83
|
+
),
|
|
84
|
+
].join('\n'),
|
|
85
|
+
write: true,
|
|
86
|
+
})
|
|
87
|
+
nuxt.options.css.push(sources.dst)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function registerWorkspaceModule(entry: FeatureRegistration, nuxt = useNuxt()) {
|
|
91
|
+
const app = nuxt as RegisteredNuxt
|
|
92
|
+
let entries = app[registryKey]
|
|
93
|
+
if (!entries) {
|
|
94
|
+
entries = []
|
|
95
|
+
app[registryKey] = entries
|
|
96
|
+
const modules = entries
|
|
97
|
+
nuxt.hook('modules:done', () => {
|
|
98
|
+
if (modules.some((entry) => entry.id === 'workspace')) {
|
|
99
|
+
registerManifest(nuxt, '#workspace/features', 'features', () => featureManifest(modules))
|
|
100
|
+
registerManifest(nuxt, '#workspace/modules', 'modules', () =>
|
|
101
|
+
[
|
|
102
|
+
"import type { WorkspaceModuleDefinition } from '@nuxtagentic/kit'",
|
|
103
|
+
`export const MODULE_CATALOG: readonly WorkspaceModuleDefinition[] = ${JSON.stringify(
|
|
104
|
+
modules.flatMap((entry) =>
|
|
105
|
+
entry.catalog ? [{ id: entry.id, ...entry.catalog }] : [],
|
|
106
|
+
),
|
|
107
|
+
)}`,
|
|
108
|
+
].join('\n'),
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
if (modules.some((entry) => entry.id === 'prompt')) {
|
|
112
|
+
registerManifest(nuxt, '#workspace/client', 'client', () =>
|
|
113
|
+
metadataManifest(modules, false),
|
|
114
|
+
)
|
|
115
|
+
registerManifest(nuxt, '#workspace/server', 'server', () => metadataManifest(modules, true))
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
if (entries.some((module) => module.id === entry.id)) {
|
|
120
|
+
throw new Error(`Workspace module "${entry.id}" was registered twice.`)
|
|
121
|
+
}
|
|
122
|
+
entries.push(entry)
|
|
123
|
+
// Nuxt excludes node_modules from auto-import injection unless the runtime is opted in.
|
|
124
|
+
const runtimePath = entry.runtime.replaceAll('\\', '/').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
125
|
+
nuxt.options.imports.transform ??= {}
|
|
126
|
+
nuxt.options.imports.transform.include ??= []
|
|
127
|
+
nuxt.options.imports.transform.include.push(new RegExp(`^${runtimePath}/`))
|
|
128
|
+
nuxt.options.build.transpile.push(entry.runtime)
|
|
129
|
+
if (existsSync(`${entry.runtime}/composables`)) {
|
|
130
|
+
addImportsDir(`${entry.runtime}/composables`)
|
|
131
|
+
}
|
|
132
|
+
if (existsSync(`${entry.runtime}/server`)) {
|
|
133
|
+
addServerScanDir(`${entry.runtime}/server`)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function registerManifest(nuxt: Nuxt, alias: string, name: string, getContents: () => string) {
|
|
138
|
+
const template = addTemplate({ filename: `workspace/${name}.ts`, getContents, write: true })
|
|
139
|
+
nuxt.options.alias[alias] = template.dst
|
|
140
|
+
nuxt.hook('nitro:config', (config) => {
|
|
141
|
+
config.alias ??= {}
|
|
142
|
+
config.alias[alias] = template.dst
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function featureManifest(entries: FeatureRegistration[]) {
|
|
147
|
+
const features = entries.flatMap((entry) =>
|
|
148
|
+
entry.feature ? [{ ...entry.feature, id: entry.id }] : [],
|
|
149
|
+
)
|
|
150
|
+
return [
|
|
151
|
+
"import type { WorkspaceContribution, WorkspaceFeature } from '@nuxtagentic/workspace/runtime/types'",
|
|
152
|
+
...features.map(
|
|
153
|
+
(feature, index) =>
|
|
154
|
+
`import { ${feature.name} as feature${index} } from ${JSON.stringify(feature.from.replace(/\.ts$/, ''))}`,
|
|
155
|
+
),
|
|
156
|
+
`export const WORKSPACE_CONTRIBUTIONS: readonly WorkspaceContribution[] = [${features.map((feature, index) => `[feature${index}].flat().map(entry => ({ ...entry, moduleId: ${JSON.stringify(feature.id)} }))`).join(', ')}].flat()`,
|
|
157
|
+
'export const WORKSPACE_FEATURES = WORKSPACE_CONTRIBUTIONS.filter((entry): entry is WorkspaceFeature => "component" in entry && !!entry.component)',
|
|
158
|
+
'export type WorkspaceFeatureId = string',
|
|
159
|
+
].join('\n')
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function metadataManifest(entries: FeatureRegistration[], server: boolean) {
|
|
163
|
+
const sources = entries.flatMap((entry) => {
|
|
164
|
+
const path = server ? (entry.server ?? entry.metadata) : entry.metadata
|
|
165
|
+
return path ? [{ path, id: entry.id, optional: !!entry.catalog }] : []
|
|
166
|
+
})
|
|
167
|
+
const type = server ? 'ServerContribution' : 'WorkspaceMetadata'
|
|
168
|
+
return [
|
|
169
|
+
`import type { ${type} } from '@nuxtagentic/prompt/server-contributions'`,
|
|
170
|
+
...sources.map(
|
|
171
|
+
(source, index) =>
|
|
172
|
+
`import entry${index} from ${JSON.stringify(source.path.replace(/\.ts$/, ''))}`,
|
|
173
|
+
),
|
|
174
|
+
`const contributions: (${type} & { moduleId: string; optional: boolean })[] = [${sources.map((source, index) => `({ ...entry${index}, moduleId: ${JSON.stringify(source.id)}, optional: ${source.optional} })`).join(', ')}]`,
|
|
175
|
+
'export function getWorkspaceMetadata(installed?: readonly string[]) {',
|
|
176
|
+
' const entries = contributions.filter(entry => !entry.optional || !installed || installed.includes(entry.moduleId))',
|
|
177
|
+
' return {',
|
|
178
|
+
' MODEL_VIEWS: entries.flatMap(entry => entry.views ?? []),',
|
|
179
|
+
` AGENT_TOOLS: Object.assign({}, ...entries.map(entry => entry.tools ?? {})) as NonNullable<${type}['tools']>,`,
|
|
180
|
+
` AGENT_RESOURCE_ALIASES: Object.assign({}, ...entries.map(entry => entry.resourceAliases ?? {})) as NonNullable<${type}['resourceAliases']>,`,
|
|
181
|
+
` GOOGLE_SERVICES: Object.assign({}, ...entries.map(entry => entry.googleServices ?? {})) as NonNullable<${type}['googleServices']>,`,
|
|
182
|
+
' AGENT_EXCLUDED_MODELS: entries.flatMap(entry => entry.excludedModels ?? []),',
|
|
183
|
+
...(server
|
|
184
|
+
? [
|
|
185
|
+
' resourceFactories: entries.flatMap(entry => entry.resourceFactories ?? []),',
|
|
186
|
+
' googleDisconnectHooks: entries.flatMap(entry => entry.googleDisconnectHooks ?? []),',
|
|
187
|
+
' googleAvailabilityHooks: entries.flatMap(entry => entry.googleAvailabilityHooks ?? []),',
|
|
188
|
+
]
|
|
189
|
+
: []),
|
|
190
|
+
' }',
|
|
191
|
+
'}',
|
|
192
|
+
`export const { MODEL_VIEWS, AGENT_TOOLS, AGENT_RESOURCE_ALIASES, GOOGLE_SERVICES, AGENT_EXCLUDED_MODELS${server ? ', resourceFactories, googleDisconnectHooks, googleAvailabilityHooks' : ''} } = getWorkspaceMetadata()`,
|
|
193
|
+
].join('\n')
|
|
194
|
+
}
|