nuxt-feathers-zod 0.2.0 → 0.2.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/README.md CHANGED
@@ -364,8 +364,59 @@ Template basé sur le pattern existant `playground/server/feathers/dummy.ts` :
364
364
  * `--servicesDir <dir>`
365
365
  * `--target nitro|feathers`
366
366
 
367
+
368
+ ## Changements du 25 janvier 2026
369
+
370
+ ### 1) `add service` : nouvelles options ROI
371
+
372
+ La commande supporte désormais :
373
+
374
+ ```bash
375
+ bunx nuxt-feathers-zod add service <serviceName> \
376
+ [--idField id|_id] \
377
+ [--path <customPath>] \
378
+ [--docs] \
379
+ [--adapter mongodb|memory] \
380
+ [--auth] \
381
+ [--servicesDir <dir>] \
382
+ [--dry] \
383
+ [--force]
384
+ ```
385
+
386
+ #### `--idField id|_id`
387
+
388
+ * Détermine le champ d’identifiant dans le schéma Zod généré.
389
+ * Compatible avec `mongodb` (ObjectId en string 24 hex) et `memory` (number int).
390
+ * Défauts :
391
+
392
+ * `mongodb` → `_id`
393
+ * `memory` → `id`
394
+
395
+ #### `--path <customPath>`
396
+
397
+ * Découple le **dossier service** du **path Feathers exposé**.
398
+ * Normalise automatiquement les `/` de tête/fin (ex: `/accounts/` → `accounts`).
399
+ * Impacte : `*.shared.ts` (path const), et donc tous les usages de `service(path)`.
400
+
401
+ #### `--docs`
402
+
403
+ * Injecte un bloc `docs:` (Swagger legacy) dans le `app.use(...)`.
404
+ * Ajoute `securities: ['jwt']` si `--auth` est activé.
405
+
406
+ ### 2) Tests smoke mis à jour
407
+
408
+ * Ajout d’un test couvrant `--path`, `--idField`, `--docs`.
409
+
367
410
  ---
368
411
 
412
+ ## Exemple rapide (valide)
413
+
414
+ ```bash
415
+ bunx nuxt-feathers-zod add service users --adapter mongodb --auth --idField id --path accounts --docs
416
+ ```
417
+
418
+ Génère `services/users/*` mais expose le service sur `accounts` avec schéma `id: objectIdSchema()` + bloc `docs:`.
419
+
369
420
 
370
421
 
371
422
  ## License
File without changes
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^4.0.0"
6
6
  },
7
- "version": "0.2.0",
7
+ "version": "0.2.1",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { defineNuxtModule, createResolver, addImportsDir, addTemplate, addServerPlugin, addImports, addPlugin, hasNuxtModule, installModule } from '@nuxt/kit';
2
2
  import { consola } from 'consola';
3
3
  import defu from 'defu';
4
+ import { createRequire } from 'node:module';
4
5
  import { resolveOptions, resolveRuntimeConfig, resolvePublicRuntimeConfig } from '../dist/runtime/options/index.js';
5
6
  import { serverDefaults } from '../dist/runtime/options/server.js';
6
7
  import { getServicesImports, addServicesImports } from '../dist/runtime/services.js';
@@ -64,11 +65,22 @@ const module$1 = defineNuxtModule({
64
65
  extendDefaults: true
65
66
  },
66
67
  loadFeathersConfig: false,
67
- auth: true
68
+ auth: true,
69
+ swagger: false
68
70
  },
69
71
  async setup(options, nuxt) {
70
72
  const resolver = createResolver(import.meta.url);
71
73
  const resolvedOptions = await resolveOptions(options, nuxt);
74
+ if (resolvedOptions.swagger) {
75
+ const require = createRequire(import.meta.url);
76
+ try {
77
+ require.resolve("feathers-swagger", { paths: [nuxt.options.rootDir] });
78
+ } catch {
79
+ consola.warn(
80
+ "feathers.swagger is enabled but 'feathers-swagger' could not be resolved from this Nuxt project. Install it in your app (root) dependencies: bun add feathers-swagger swagger-ui-dist"
81
+ );
82
+ }
83
+ }
72
84
  nuxt.options.runtimeConfig._feathers = resolveRuntimeConfig(resolvedOptions);
73
85
  nuxt.options.runtimeConfig.public._feathers = resolvePublicRuntimeConfig(resolvedOptions);
74
86
  const servicesImports = await getServicesImports(resolvedOptions.servicesDirs);
@@ -7,6 +7,7 @@ import type { ResolvedServerOptions, ServerOptions } from './server.js';
7
7
  import type { ServicesDir, ServicesDirs } from './services.js';
8
8
  import type { ResolvedTransportsOptions, TransportsOptions } from './transports/index.js';
9
9
  import type { ResolvedValidatorOptions, ValidatorOptions } from './validator.js';
10
+ import type { ResolvedSwaggerOptionsOrDisabled, SwaggerOptionsOrDisabled } from './swagger.js';
10
11
  export interface ModuleOptions {
11
12
  transports: TransportsOptions;
12
13
  database: DataBaseOptions;
@@ -16,6 +17,7 @@ export interface ModuleOptions {
16
17
  client: ClientOptions | boolean;
17
18
  validator: ValidatorOptions;
18
19
  loadFeathersConfig: boolean;
20
+ swagger?: SwaggerOptionsOrDisabled;
19
21
  }
20
22
  export interface ResolvedOptions {
21
23
  templateDir: string;
@@ -27,6 +29,7 @@ export interface ResolvedOptions {
27
29
  client: ResolvedClientOptionsOrDisabled;
28
30
  validator: ResolvedValidatorOptions;
29
31
  loadFeathersConfig: boolean;
32
+ swagger?: ResolvedSwaggerOptionsOrDisabled;
30
33
  }
31
34
  export interface FeathersRuntimeConfig {
32
35
  auth?: ResolvedAuthOptions;
@@ -7,6 +7,7 @@ import { resolveServerOptions } from "./server.js";
7
7
  import { resolveServicesDirs } from "./services.js";
8
8
  import { resolveTransportsOptions } from "./transports/index.js";
9
9
  import { resolveValidatorOptions } from "./validator.js";
10
+ import { resolveSwaggerOptions } from "./swagger.js";
10
11
  export async function resolveOptions(options, nuxt) {
11
12
  const { rootDir, srcDir, serverDir, appDir, buildDir, ssr } = nuxt.options;
12
13
  const resolver = createResolver(import.meta.url);
@@ -17,6 +18,7 @@ export async function resolveOptions(options, nuxt) {
17
18
  const server = await resolveServerOptions(options.server, rootDir, serverDir);
18
19
  const client = await resolveClientOptions(options.client, !!database.mongo, rootDir, srcDir);
19
20
  const validator = resolveValidatorOptions(options.validator);
21
+ const swagger = resolveSwaggerOptions(options.swagger, transports);
20
22
  const servicesImports = await getServicesImports(servicesDirs);
21
23
  const auth = resolveAuthOptions(options.auth, !!client, servicesImports, appDir);
22
24
  const loadFeathersConfig = options.loadFeathersConfig;
@@ -29,7 +31,8 @@ export async function resolveOptions(options, nuxt) {
29
31
  client,
30
32
  validator,
31
33
  auth,
32
- loadFeathersConfig
34
+ loadFeathersConfig,
35
+ swagger
33
36
  };
34
37
  console.dir(resolvedOptions, { depth: null });
35
38
  return resolvedOptions;
@@ -0,0 +1,34 @@
1
+ import type { ResolvedTransportsOptions } from './transports/index.js';
2
+ export interface SwaggerOptions {
3
+ /**
4
+ * Enable Swagger/OpenAPI documentation generation via feathers-swagger (legacy).
5
+ * When `true`, defaults are applied.
6
+ */
7
+ enabled?: boolean;
8
+ /** Swagger UI base path (e.g. /docs) */
9
+ docsPath?: string;
10
+ /** OpenAPI JSON path (e.g. /docs.json) */
11
+ docsJsonPath?: string;
12
+ /** OpenAPI version (2 or 3). Default: 3 */
13
+ openApiVersion?: 2 | 3;
14
+ /** OpenAPI info */
15
+ info?: {
16
+ title: string;
17
+ description?: string;
18
+ version: string;
19
+ };
20
+ }
21
+ export type SwaggerOptionsOrDisabled = SwaggerOptions | boolean;
22
+ export interface ResolvedSwaggerOptions {
23
+ enabled: true;
24
+ docsPath: string;
25
+ docsJsonPath: string;
26
+ openApiVersion: 2 | 3;
27
+ info: {
28
+ title: string;
29
+ description?: string;
30
+ version: string;
31
+ };
32
+ }
33
+ export type ResolvedSwaggerOptionsOrDisabled = ResolvedSwaggerOptions | false;
34
+ export declare function resolveSwaggerOptions(options: SwaggerOptionsOrDisabled | undefined, transports: ResolvedTransportsOptions): ResolvedSwaggerOptionsOrDisabled;
@@ -0,0 +1,16 @@
1
+ export function resolveSwaggerOptions(options, transports) {
2
+ if (options == null || options === false)
3
+ return false;
4
+ if (options === true)
5
+ options = {};
6
+ const enabled = options.enabled ?? true;
7
+ if (!enabled)
8
+ return false;
9
+ return {
10
+ enabled: true,
11
+ docsPath: options.docsPath || "/docs",
12
+ docsJsonPath: options.docsJsonPath || "/docs.json",
13
+ openApiVersion: options.openApiVersion || 3,
14
+ info: options.info || { title: "API Docs", version: "1.0.0" }
15
+ };
16
+ }
@@ -26,6 +26,46 @@ export function getServerPluginContents(options) {
26
26
  const authService = options?.auth?.service;
27
27
  const restPath = transports?.rest?.path;
28
28
  const websocketPath = transports?.websocket?.path;
29
+ const swaggerEnabled = !!options.swagger;
30
+ const swagger = options.swagger;
31
+ const normalizePath = (p) => {
32
+ if (!p)
33
+ return "";
34
+ return p.startsWith("/") ? p : `/${p}`;
35
+ };
36
+ const trimTrailingSlash = (p) => {
37
+ if (!p)
38
+ return "";
39
+ return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
40
+ };
41
+ const docsPath = trimTrailingSlash(normalizePath(swagger?.docsPath ?? "/docs"));
42
+ const docsPathSlash = `${docsPath}/`;
43
+ const docsJsonPath = trimTrailingSlash(normalizePath(swagger?.docsJsonPath ?? "/docs.json"));
44
+ const swaggerInitBlock = swaggerEnabled ? ` // Init Swagger (feathers-swagger legacy)
45
+
46
+
47
+ app.configure(swagger.customMethodsHandler)
48
+ app.configure(swagger({
49
+ docsPath: '${docsPath}',
50
+ docsJsonPath: '/swagger.json',
51
+ specs: {
52
+ info: ${JSON.stringify(swagger?.info ?? { title: "API Docs", description: "Feathers API", version: "1.0.0" })},
53
+ components: {
54
+ securitySchemes: {
55
+ BearerAuth: {
56
+ type: 'http',
57
+ scheme: 'bearer',
58
+ bearerFormat: 'JWT',
59
+ },
60
+ },
61
+ },
62
+ security: [
63
+ { BearerAuth: [] },
64
+ ],
65
+ },
66
+ ui: swagger.swaggerUI({ docsPath: '${docsPath}' }),
67
+ }))
68
+ ` : "";
29
69
  return `// ! Generated by nuxt-feathers-zod - do not change manually
30
70
  import type { NitroApp } from 'nitropack'
31
71
  import type { Application } from './server.js'
@@ -36,6 +76,7 @@ ${puts([
36
76
  [exp, `import feathersExpress, { json, rest, urlencoded } from '@feathersjs/express'`],
37
77
  [sio, `import socketio from '@feathersjs/socketio'`]
38
78
  ])}
79
+ ${put(swaggerEnabled, `import swagger from 'feathers-swagger'`)}
39
80
  ${put(rest, `import { ${framework}ErrorHandler } from '@gabortorma/feathers-nitro-adapter/handlers'`)}
40
81
  ${put(auth, `import authentication from './authentication.js'`)}
41
82
  import { ${routers.join(", ")} } from '@gabortorma/feathers-nitro-adapter/routers'
@@ -52,6 +93,7 @@ export default defineNitroPlugin((nitroApp: NitroApp) => {
52
93
  ${put(options.loadFeathersConfig, `
53
94
  app.configure(configuration())
54
95
  `)}
96
+ ${put(swaggerEnabled, swaggerInitBlock)}
55
97
  // Add nitroApp to feathers app
56
98
  app.nitroApp = nitroApp;
57
99
  ${put(rest, `${put(koa, `
@@ -70,6 +112,7 @@ ${put(exp, ` // Set up Express middleware
70
112
  },
71
113
  }`)}))
72
114
  `)}`)}
115
+
73
116
  // Init socket.io server for real-time functionality
74
117
  app.set('websocket', ${!!sio})${put(sio, `
75
118
  app.configure(socketio({
@@ -83,11 +126,13 @@ ${put(exp, ` // Set up Express middleware
83
126
  ${put(mongo, `// Init mongodb
84
127
  app.configure(mongodb)
85
128
  `)}
129
+
86
130
  // Init services
87
131
  ${services.map((service) => `app.configure(${service.meta.importId})`).join("\n ")}
88
132
 
89
133
  // Init plugins
90
134
  ${plugins.map((plugin) => `app.configure(${plugin.meta.importId})`).join("\n ")}
135
+
91
136
  ${put(exp, `
92
137
  // Set up Express middleware for 404s and the error handler
93
138
  app.configure(expressErrorHandler)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-feathers-zod",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "packageManager": "bun@1.3.6",
6
6
  "description": "Feathers API integration for Nuxt",
7
7
  "author": "Herve de CHAVIGNY",
package/src/cli/index.ts CHANGED
@@ -7,6 +7,8 @@ import consola from 'consola'
7
7
 
8
8
  type Adapter = 'mongodb' | 'memory'
9
9
  type MiddlewareTarget = 'nitro' | 'feathers'
10
+ type IdField = 'id' | '_id'
11
+ type CollectionName = string
10
12
 
11
13
  export type RunCliOptions = {
12
14
  cwd: string
@@ -48,6 +50,10 @@ export async function runCli(argv: string[], opts: RunCliOptions) {
48
50
  if (subcmd === 'service') {
49
51
  const adapter = (flags.adapter as Adapter | undefined) ?? 'mongodb'
50
52
  const auth = Boolean(flags.auth)
53
+ const idField = (flags.idField as IdField | undefined) ?? (adapter === 'mongodb' ? '_id' : 'id')
54
+ const servicePath = typeof flags.path === 'string' ? String(flags.path) : undefined
55
+ const collectionName = typeof flags.collection === 'string' ? String(flags.collection) : undefined
56
+ const docs = Boolean(flags.docs)
51
57
  const dry = Boolean(flags.dry)
52
58
  const force = Boolean(flags.force)
53
59
 
@@ -60,6 +66,10 @@ export async function runCli(argv: string[], opts: RunCliOptions) {
60
66
  name,
61
67
  adapter,
62
68
  auth,
69
+ idField,
70
+ servicePath,
71
+ collectionName,
72
+ docs,
63
73
  dry,
64
74
  force,
65
75
  })
@@ -86,7 +96,7 @@ export async function runCli(argv: string[], opts: RunCliOptions) {
86
96
  function printHelp() {
87
97
  // Keep output short; this is a CLI entrypoint.
88
98
  // eslint-disable-next-line no-console
89
- console.log(`\nnuxt-feathers-zod CLI\n\nUsage:\n nuxt-feathers-zod add service <serviceName> [--adapter mongodb|memory] [--auth] [--servicesDir <dir>] [--dry] [--force]\n nuxt-feathers-zod add middleware <name> [--target nitro|feathers] [--dry] [--force]\n\nExamples:\n bunx nuxt-feathers-zod add service posts --adapter mongodb --auth\n bunx nuxt-feathers-zod add middleware session\n bunx nuxt-feathers-zod add middleware dummy --target feathers\n`)
99
+ console.log(`\nnuxt-feathers-zod CLI\n\nUsage:\n nuxt-feathers-zod add service <serviceName> [--adapter mongodb|memory] [--auth] [--idField id|_id] [--path <customPath>] [--collection <mongoCollection>] [--docs] [--servicesDir <dir>] [--dry] [--force]\n nuxt-feathers-zod add middleware <name> [--target nitro|feathers] [--dry] [--force]\n\nExamples:\n bunx nuxt-feathers-zod add service posts --adapter mongodb --auth\n bunx nuxt-feathers-zod add service users --adapter mongodb --idField _id --path accounts --docs\n bunx nuxt-feathers-zod add service haproxy-domains --path haproxy/domains --collection haproxy-domains --auth --docs\n bunx nuxt-feathers-zod add middleware session\n bunx nuxt-feathers-zod add middleware dummy --target feathers\n`)
90
100
  }
91
101
 
92
102
  function parseFlags(argv: string[]) {
@@ -132,6 +142,28 @@ function normalizeServiceName(raw: string) {
132
142
  return kebabCase(raw)
133
143
  }
134
144
 
145
+ function normalizeServicePath(raw: string) {
146
+ // Feathers service paths are usually kebab-case and can include slashes.
147
+ // We keep user intent, but normalize leading/trailing slashes.
148
+ const cleaned = String(raw).trim().replace(/^\/+/, '').replace(/\/+$/, '')
149
+ if (!cleaned) throw new Error('Invalid --path: path cannot be empty')
150
+ return cleaned
151
+ }
152
+
153
+ function normalizeCollectionName(raw: string) {
154
+ // MongoDB collection names are strings, but in practice should not include path separators.
155
+ // We keep it permissive while preventing common foot-guns.
156
+ const cleaned = String(raw).trim()
157
+ if (!cleaned) throw new Error('Invalid --collection: collection name cannot be empty')
158
+ if (cleaned.includes('/') || cleaned.includes('\\')) {
159
+ throw new Error('Invalid --collection: collection name must not include \/ or \\')
160
+ }
161
+ if (cleaned.includes('\u0000')) {
162
+ throw new Error('Invalid --collection: collection name must not include null characters')
163
+ }
164
+ return cleaned
165
+ }
166
+
135
167
  function createServiceIds(serviceNameKebab: string) {
136
168
  const baseKebab = singularize(serviceNameKebab)
137
169
  const basePascal = pascalCase(baseKebab)
@@ -151,6 +183,10 @@ type GenerateServiceOptions = {
151
183
  name: string
152
184
  adapter: Adapter
153
185
  auth: boolean
186
+ idField: IdField
187
+ servicePath?: string
188
+ collectionName?: CollectionName
189
+ docs: boolean
154
190
  dry: boolean
155
191
  force: boolean
156
192
  }
@@ -159,6 +195,12 @@ export async function generateService(opts: GenerateServiceOptions) {
159
195
  const serviceNameKebab = normalizeServiceName(opts.name)
160
196
  const ids = createServiceIds(serviceNameKebab)
161
197
 
198
+ const servicePath = normalizeServicePath(opts.servicePath ?? serviceNameKebab)
199
+ const collectionName = normalizeCollectionName(
200
+ opts.collectionName
201
+ ?? (servicePath.includes('/') ? serviceNameKebab : servicePath),
202
+ )
203
+
162
204
  const dir = join(opts.servicesDir, serviceNameKebab)
163
205
  const schemaFile = join(dir, `${serviceNameKebab}.schema.ts`)
164
206
  const classFile = join(dir, `${serviceNameKebab}.class.ts`)
@@ -166,10 +208,10 @@ export async function generateService(opts: GenerateServiceOptions) {
166
208
  const serviceFile = join(dir, `${serviceNameKebab}.ts`)
167
209
 
168
210
  const files: Array<{ path: string; content: string }> = [
169
- { path: schemaFile, content: renderSchema(ids, opts.adapter) },
170
- { path: classFile, content: renderClass(ids, opts.adapter) },
171
- { path: sharedFile, content: renderShared(ids) },
172
- { path: serviceFile, content: renderService(ids, opts.auth) },
211
+ { path: schemaFile, content: renderSchema(ids, opts.adapter, opts.idField) },
212
+ { path: classFile, content: renderClass(ids, opts.adapter, collectionName) },
213
+ { path: sharedFile, content: renderShared(ids, servicePath) },
214
+ { path: serviceFile, content: renderService(ids, opts.auth, opts.docs) },
173
215
  ]
174
216
 
175
217
  await ensureDir(dir, opts.dry)
@@ -178,11 +220,41 @@ export async function generateService(opts: GenerateServiceOptions) {
178
220
  await writeFileSafe(f.path, f.content, { dry: opts.dry, force: opts.force })
179
221
  }
180
222
 
223
+ if (opts.docs) {
224
+ await ensureFeathersSwaggerSupport(opts.projectRoot, { dry: opts.dry, force: opts.force })
225
+ }
226
+
181
227
  if (!opts.dry) {
182
228
  consola.success(`Generated service '${serviceNameKebab}' in ${relativeToCwd(dir)}`)
183
229
  }
184
230
  }
185
231
 
232
+ async function ensureFeathersSwaggerSupport(projectRoot: string, io: { dry: boolean; force: boolean }) {
233
+ // 1) Ensure TS sees `ServiceOptions.docs` (required for feathers-swagger in TS projects)
234
+ const typesDir = join(projectRoot, 'types')
235
+ const typesFile = join(typesDir, 'feathers-swagger.d.ts')
236
+ const typesContent = `// Auto-generated by nuxt-feathers-zod CLI (required when using feathers-swagger in TypeScript)\n\nimport type { ServiceSwaggerOptions } from 'feathers-swagger'\n\ndeclare module '@feathersjs/feathers' {\n interface ServiceOptions {\n docs?: ServiceSwaggerOptions\n }\n}\n`
237
+
238
+ await ensureDir(typesDir, io.dry)
239
+ await writeFileSafe(typesFile, typesContent, { dry: io.dry, force: io.force })
240
+
241
+ // 2) Best-effort dependency hint (we do not auto-install dependencies)
242
+ try {
243
+ const pkgPath = join(projectRoot, 'package.json')
244
+ if (!existsSync(pkgPath)) return
245
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8')) as any
246
+ const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) }
247
+ if (!deps['feathers-swagger']) {
248
+ consola.warn(
249
+ `--docs was used but 'feathers-swagger' is not listed in package.json. Install it (and swagger UI deps if needed): bun add feathers-swagger swagger-ui-dist`,
250
+ )
251
+ }
252
+ }
253
+ catch {
254
+ // ignore
255
+ }
256
+ }
257
+
186
258
  type GenerateMiddlewareOptions = {
187
259
  projectRoot: string
188
260
  name: string
@@ -238,12 +310,12 @@ function relativeToCwd(p: string) {
238
310
  }
239
311
  }
240
312
 
241
- function renderSchema(ids: ReturnType<typeof createServiceIds>, adapter: Adapter) {
313
+ function renderSchema(ids: ReturnType<typeof createServiceIds>, adapter: Adapter, idField: IdField) {
242
314
  const base = ids.baseCamel
243
315
  const Base = ids.basePascal
244
316
  const serviceClass = `${Base}Service`
245
317
 
246
- const idField = adapter === 'mongodb' ? '_id' : 'id'
318
+ const idSchemaField = idField
247
319
  const idSchema = adapter === 'mongodb'
248
320
  ? `
249
321
  const objectIdRegex = /^[0-9a-f]{24}$/i
@@ -253,11 +325,11 @@ export const objectIdSchema = () => z.string().regex(objectIdRegex, 'Invalid Obj
253
325
 
254
326
  const mainSchema = adapter === 'mongodb'
255
327
  ? `export const ${base}Schema = z.object({
256
- ${idField}: objectIdSchema(),
328
+ ${idSchemaField}: objectIdSchema(),
257
329
  text: z.string(),
258
330
  })`
259
331
  : `export const ${base}Schema = z.object({
260
- ${idField}: z.number().int(),
332
+ ${idSchemaField}: z.number().int(),
261
333
  text: z.string(),
262
334
  })`
263
335
 
@@ -303,8 +375,7 @@ export const ${base}QueryResolver = resolve<${Base}Query, HookContext<${serviceC
303
375
  `
304
376
  }
305
377
 
306
- function renderClass(ids: ReturnType<typeof createServiceIds>, adapter: Adapter) {
307
- const base = ids.baseCamel
378
+ function renderClass(ids: ReturnType<typeof createServiceIds>, adapter: Adapter, collectionName: string) {
308
379
  const Base = ids.basePascal
309
380
  const serviceName = ids.serviceNameKebab
310
381
  const serviceClass = `${Base}Service`
@@ -364,13 +435,13 @@ export function getOptions(app: Application): MongoDBAdapterOptions {
364
435
  max: 100,
365
436
  },
366
437
  multi: true,
367
- Model: mongoClient.then(db => db.collection('${serviceName}')),
438
+ Model: mongoClient.then(db => db.collection('${collectionName}')),
368
439
  }
369
440
  }
370
441
  `
371
442
  }
372
443
 
373
- function renderShared(ids: ReturnType<typeof createServiceIds>) {
444
+ function renderShared(ids: ReturnType<typeof createServiceIds>, servicePath: string) {
374
445
  const base = ids.baseCamel
375
446
  const Base = ids.basePascal
376
447
  const serviceName = ids.serviceNameKebab
@@ -390,7 +461,7 @@ export type { ${Base}, ${Base}Data, ${Base}Patch, ${Base}Query }
390
461
 
391
462
  export type ${clientServiceType} = Pick<${serviceClass}<Params<${Base}Query>>, (typeof ${methodsConst})[number]>
392
463
 
393
- export const ${pathConst} = '${serviceName}'
464
+ export const ${pathConst} = '${servicePath}'
394
465
 
395
466
  export const ${methodsConst}: Array<keyof ${serviceClass}> = ['find', 'get', 'create', 'patch', 'remove']
396
467
 
@@ -410,13 +481,39 @@ declare module 'nuxt-feathers-zod/client' {
410
481
  `
411
482
  }
412
483
 
413
- function renderService(ids: ReturnType<typeof createServiceIds>, auth: boolean) {
484
+ function renderService(ids: ReturnType<typeof createServiceIds>, auth: boolean, docs: boolean) {
414
485
  const base = ids.baseCamel
415
486
  const Base = ids.basePascal
416
487
  const serviceName = ids.serviceNameKebab
417
488
  const serviceClass = `${Base}Service`
418
489
  const authImports = auth ? "import { authenticate } from '@feathersjs/authentication'\n" : ''
419
490
 
491
+ const swaggerImports = ''
492
+
493
+ const swaggerSchemaImports = ''
494
+ const docsBlock = docs
495
+ ? `
496
+ docs: {
497
+ description: '${Base} service',
498
+ idType: 'string',
499
+ ${auth ? ` securities: ${base}Methods,
500
+ ` : ''} definitions: {
501
+ ${base}: { type: 'object', properties: {} },
502
+ ${base}Data: { type: 'object', properties: {} },
503
+ ${base}Patch: { type: 'object', properties: {} },
504
+ ${base}Query: {
505
+ type: 'object',
506
+ properties: {
507
+ $limit: { type: 'number' },
508
+ $skip: { type: 'number' },
509
+ $sort: { type: 'object', additionalProperties: { type: 'number' } },
510
+ },
511
+ },
512
+ },
513
+ },
514
+ `
515
+ : ''
516
+
420
517
  const authAround = auth
421
518
  ? `
422
519
  find: [authenticate('jwt')],
@@ -436,10 +533,10 @@ function renderService(ids: ReturnType<typeof createServiceIds>, auth: boolean)
436
533
  return `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.html
437
534
 
438
535
  import type { Application } from 'nuxt-feathers-zod/server'
439
- ${authImports}import { hooks as schemaHooks } from '@feathersjs/schema'
536
+ ${authImports}${swaggerImports}import { hooks as schemaHooks } from '@feathersjs/schema'
440
537
  import { getOptions, ${serviceClass} } from './${serviceName}.class'
441
538
  import {
442
- ${base}DataResolver,
539
+ ${swaggerSchemaImports} ${base}DataResolver,
443
540
  ${base}DataValidator,
444
541
  ${base}ExternalResolver,
445
542
  ${base}PatchResolver,
@@ -456,7 +553,7 @@ export * from './${serviceName}.schema'
456
553
  export function ${base}(app: Application) {
457
554
  app.use(${base}Path, new ${serviceClass}(getOptions(app)), {
458
555
  methods: ${base}Methods,
459
- events: [],
556
+ events: [],${docsBlock}
460
557
  })
461
558
 
462
559
  app.service(${base}Path).hooks({