nuxt-feathers-zod 0.1.0 → 0.2.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 CHANGED
@@ -278,6 +278,96 @@ Notes:
278
278
 
279
279
  The runtime auth bootstrap (`src/runtime/plugins/feathers-auth.ts`) re-authenticates **client-side only** to avoid SSR startup failures when the Feathers client is not yet injected.
280
280
 
281
+
282
+ C’est appliqué au dépôt (zip monté), en respectant exactement l’ordre que vous avez demandé.
283
+
284
+ Voici ce qui a été ajouté / modifié dans **nuxt-feathers-zod-main** :
285
+
286
+ ## 1) CLI Bun: `bunx nuxt-feathers-zod …`
287
+
288
+ ### Ajouts
289
+
290
+ * `bin/nuxt-feathers-zod` (exécutable, shebang Bun)
291
+ * `src/cli/index.ts` (implémentation complète de la CLI)
292
+
293
+ ### package.json
294
+
295
+ * Ajout de :
296
+
297
+ * `"bin": { "nuxt-feathers-zod": "bin/nuxt-feathers-zod" }`
298
+ * `"files": ["dist", "bin", "src/cli"]` pour que la CLI soit bien publiée
299
+ * `"test": "vitest run"` (smoke tests)
300
+
301
+ ## 2) Commande `add service` (4 fichiers) + smoke tests
302
+
303
+ ### Commande
304
+
305
+ ```bash
306
+ bunx nuxt-feathers-zod add service <serviceName> [--adapter mongodb|memory] [--auth] [--servicesDir <dir>] [--dry] [--force]
307
+ ```
308
+
309
+ ### Génération
310
+
311
+ Crée automatiquement :
312
+
313
+ * `services/<name>/<name>.schema.ts`
314
+ * `services/<name>/<name>.class.ts`
315
+ * `services/<name>/<name>.shared.ts`
316
+ * `services/<name>/<name>.ts`
317
+
318
+ Aligné sur le style déjà présent dans le repo :
319
+
320
+ * Zod + `getZodValidator(...)`
321
+ * `zodQuerySyntax(schema)`
322
+ * `schemaHooks.validateQuery/validateData + resolveQuery/resolveData`
323
+ * `--auth` ajoute `authenticate('jwt')` sur `find/get/patch/remove` (create reste ouvert)
324
+
325
+ `--adapter mongodb` génère un `_id` ObjectId (regex 24 hex) comme le service `mongos` existant.
326
+ `--adapter memory` génère un `id: z.number().int()` comme `messages`.
327
+
328
+ Smoke test ajouté : `test/cli.spec.ts` (vérifie service + middleware).
329
+
330
+ ## 3) Commande `add middleware` (Nitro)
331
+
332
+ ### Commande
333
+
334
+ ```bash
335
+ bunx nuxt-feathers-zod add middleware <name> [--target nitro|feathers] [--dry] [--force]
336
+ ```
337
+
338
+ Par défaut (`--target nitro`) :
339
+
340
+ * crée `server/middleware/<name>.ts` avec un template `defineEventHandler(...)`.
341
+
342
+ ## 4) `--target feathers` (plugin Feathers côté serveur)
343
+
344
+ Si vous faites :
345
+
346
+ ```bash
347
+ bunx nuxt-feathers-zod add middleware dummy --target feathers
348
+ ```
349
+
350
+ Génère :
351
+
352
+ * `server/feathers/dummy.ts`
353
+
354
+ Template basé sur le pattern existant `playground/server/feathers/dummy.ts` :
355
+
356
+ * `defineFeathersServerPlugin((app) => app.hooks({ setup: [...] }))`
357
+
358
+ ## 5) Options ajoutées
359
+
360
+ * `--adapter mongodb|memory`
361
+ * `--auth`
362
+ * `--dry`
363
+ * `--force`
364
+ * `--servicesDir <dir>`
365
+ * `--target nitro|feathers`
366
+
367
+ ---
368
+
369
+
370
+
281
371
  ## License
282
372
 
283
373
  MIT
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { runCli } from '../src/cli/index.ts'
4
+
5
+ await runCli(process.argv.slice(2), { cwd: process.cwd() })
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^4.0.0"
6
6
  },
7
- "version": "0.1.0",
7
+ "version": "0.2.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-feathers-zod",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "packageManager": "bun@1.3.6",
6
6
  "description": "Feathers API integration for Nuxt",
7
7
  "author": "Herve de CHAVIGNY",
@@ -21,15 +21,28 @@
21
21
  "./query": "./dist/runtime/zod/query.js",
22
22
  "./format": "./dist/runtime/zod/format.js"
23
23
  },
24
+ "bin": {
25
+ "nuxt-feathers-zod": "bin/nuxt-feathers-zod"
26
+ },
24
27
  "main": "./dist/module.mjs",
25
28
  "typesVersions": {
26
29
  "*": {
27
- ".": ["./dist/types.d.mts"],
28
- "options": ["./dist/runtime/options/index.d.mts"],
29
- "ofetch-adapter": ["./dist/runtime/adapters/ofetch.d.mts"]
30
+ ".": [
31
+ "./dist/types.d.mts"
32
+ ],
33
+ "options": [
34
+ "./dist/runtime/options/index.d.mts"
35
+ ],
36
+ "ofetch-adapter": [
37
+ "./dist/runtime/adapters/ofetch.d.mts"
38
+ ]
30
39
  }
31
40
  },
32
- "files": ["dist"],
41
+ "files": [
42
+ "dist",
43
+ "bin",
44
+ "src/cli"
45
+ ],
33
46
  "scripts": {
34
47
  "prepack": "nuxt-module-build build",
35
48
  "dev": "nuxi dev playground",
@@ -48,7 +61,7 @@
48
61
  },
49
62
  "dependencies": {
50
63
  "@feathersjs/adapter-commons": "latest",
51
- "@feathersjs/authentication": "latest",
64
+ "@feathersjs/authentication": "5.0.37",
52
65
  "@feathersjs/authentication-client": "latest",
53
66
  "@feathersjs/authentication-local": "latest",
54
67
  "@feathersjs/configuration": "latest",
@@ -96,16 +109,5 @@
96
109
  "vitest-mongodb": "latest",
97
110
  "vue": "latest",
98
111
  "vue-router": "latest"
99
- },
100
- "overrides": {
101
- "@feathersjs/authentication": "5.0.37",
102
- "@feathersjs/authentication-client": "5.0.37",
103
- "@feathersjs/authentication-local": "5.0.37",
104
- "@feathersjs/errors": "5.0.37",
105
- "@feathersjs/feathers": "5.0.37",
106
- "@feathersjs/koa": "5.0.37",
107
- "@feathersjs/rest-client": "5.0.37",
108
- "@feathersjs/schema": "5.0.37",
109
- "@feathersjs/socketio-client": "5.0.37"
110
112
  }
111
113
  }
@@ -0,0 +1,522 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises'
3
+ import { join, resolve } from 'node:path'
4
+
5
+ import { kebabCase, pascalCase } from 'change-case'
6
+ import consola from 'consola'
7
+
8
+ type Adapter = 'mongodb' | 'memory'
9
+ type MiddlewareTarget = 'nitro' | 'feathers'
10
+
11
+ export type RunCliOptions = {
12
+ cwd: string
13
+ }
14
+
15
+ export async function runCli(argv: string[], opts: RunCliOptions) {
16
+ const cwd = resolve(opts.cwd)
17
+
18
+ const [cmd, subcmd, name, ...rest] = argv
19
+
20
+ if (!cmd || cmd === '-h' || cmd === '--help') {
21
+ printHelp()
22
+ process.exit(0)
23
+ }
24
+
25
+ // Expected:
26
+ // nuxt-feathers-zod add service <name> [...]
27
+ // nuxt-feathers-zod add middleware <name> [...]
28
+ if (cmd !== 'add') {
29
+ consola.error(`Unknown command: ${cmd}`)
30
+ printHelp()
31
+ process.exit(1)
32
+ }
33
+
34
+ if (subcmd !== 'service' && subcmd !== 'middleware') {
35
+ consola.error(`Unknown add target: ${subcmd ?? '(missing)'}`)
36
+ printHelp()
37
+ process.exit(1)
38
+ }
39
+
40
+ if (!name) {
41
+ consola.error('Missing <name>.')
42
+ printHelp()
43
+ process.exit(1)
44
+ }
45
+
46
+ const flags = parseFlags(rest)
47
+
48
+ if (subcmd === 'service') {
49
+ const adapter = (flags.adapter as Adapter | undefined) ?? 'mongodb'
50
+ const auth = Boolean(flags.auth)
51
+ const dry = Boolean(flags.dry)
52
+ const force = Boolean(flags.force)
53
+
54
+ const projectRoot = await findProjectRoot(cwd)
55
+ const servicesDir = resolve(projectRoot, flags.servicesDir ?? 'services')
56
+
57
+ await generateService({
58
+ projectRoot,
59
+ servicesDir,
60
+ name,
61
+ adapter,
62
+ auth,
63
+ dry,
64
+ force,
65
+ })
66
+ return
67
+ }
68
+
69
+ if (subcmd === 'middleware') {
70
+ const target = (flags.target as MiddlewareTarget | undefined) ?? 'nitro'
71
+ const dry = Boolean(flags.dry)
72
+ const force = Boolean(flags.force)
73
+
74
+ const projectRoot = await findProjectRoot(cwd)
75
+
76
+ await generateMiddleware({
77
+ projectRoot,
78
+ name,
79
+ target,
80
+ dry,
81
+ force,
82
+ })
83
+ }
84
+ }
85
+
86
+ function printHelp() {
87
+ // Keep output short; this is a CLI entrypoint.
88
+ // 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`)
90
+ }
91
+
92
+ function parseFlags(argv: string[]) {
93
+ const out: Record<string, string | boolean> = {}
94
+ for (let i = 0; i < argv.length; i++) {
95
+ const a = argv[i]
96
+ if (!a) continue
97
+ if (!a.startsWith('--')) continue
98
+ const key = a.slice(2)
99
+ const next = argv[i + 1]
100
+ if (!next || next.startsWith('--')) {
101
+ out[key] = true
102
+ continue
103
+ }
104
+ out[key] = next
105
+ i++
106
+ }
107
+ return out
108
+ }
109
+
110
+ async function findProjectRoot(start: string) {
111
+ // Walk up until we find a package.json.
112
+ let dir = resolve(start)
113
+ for (let i = 0; i < 20; i++) {
114
+ if (existsSync(join(dir, 'package.json'))) return dir
115
+ const parent = resolve(dir, '..')
116
+ if (parent === dir) break
117
+ dir = parent
118
+ }
119
+ throw new Error(`Could not find project root from ${start}`)
120
+ }
121
+
122
+ function singularize(input: string) {
123
+ // Minimal heuristic (good enough for a DX helper; users can rename if needed)
124
+ if (input.endsWith('ies')) return `${input.slice(0, -3)}y`
125
+ if (input.endsWith('ses')) return input.slice(0, -2)
126
+ if (input.endsWith('s') && input.length > 1) return input.slice(0, -1)
127
+ return input
128
+ }
129
+
130
+ function normalizeServiceName(raw: string) {
131
+ // Allow "posts", "haproxy-domains", "traefik_stacks" etc.
132
+ return kebabCase(raw)
133
+ }
134
+
135
+ function createServiceIds(serviceNameKebab: string) {
136
+ const baseKebab = singularize(serviceNameKebab)
137
+ const basePascal = pascalCase(baseKebab)
138
+ const baseCamel = basePascal.charAt(0).toLowerCase() + basePascal.slice(1)
139
+
140
+ return {
141
+ serviceNameKebab,
142
+ baseKebab,
143
+ basePascal,
144
+ baseCamel,
145
+ }
146
+ }
147
+
148
+ type GenerateServiceOptions = {
149
+ projectRoot: string
150
+ servicesDir: string
151
+ name: string
152
+ adapter: Adapter
153
+ auth: boolean
154
+ dry: boolean
155
+ force: boolean
156
+ }
157
+
158
+ export async function generateService(opts: GenerateServiceOptions) {
159
+ const serviceNameKebab = normalizeServiceName(opts.name)
160
+ const ids = createServiceIds(serviceNameKebab)
161
+
162
+ const dir = join(opts.servicesDir, serviceNameKebab)
163
+ const schemaFile = join(dir, `${serviceNameKebab}.schema.ts`)
164
+ const classFile = join(dir, `${serviceNameKebab}.class.ts`)
165
+ const sharedFile = join(dir, `${serviceNameKebab}.shared.ts`)
166
+ const serviceFile = join(dir, `${serviceNameKebab}.ts`)
167
+
168
+ 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) },
173
+ ]
174
+
175
+ await ensureDir(dir, opts.dry)
176
+
177
+ for (const f of files) {
178
+ await writeFileSafe(f.path, f.content, { dry: opts.dry, force: opts.force })
179
+ }
180
+
181
+ if (!opts.dry) {
182
+ consola.success(`Generated service '${serviceNameKebab}' in ${relativeToCwd(dir)}`)
183
+ }
184
+ }
185
+
186
+ type GenerateMiddlewareOptions = {
187
+ projectRoot: string
188
+ name: string
189
+ target: MiddlewareTarget
190
+ dry: boolean
191
+ force: boolean
192
+ }
193
+
194
+ export async function generateMiddleware(opts: GenerateMiddlewareOptions) {
195
+ const fileBase = kebabCase(opts.name)
196
+
197
+ if (opts.target === 'nitro') {
198
+ const dir = join(opts.projectRoot, 'server', 'middleware')
199
+ const file = join(dir, `${fileBase}.ts`)
200
+ await ensureDir(dir, opts.dry)
201
+ await writeFileSafe(file, renderNitroMiddleware(fileBase), { dry: opts.dry, force: opts.force })
202
+ if (!opts.dry) consola.success(`Generated Nitro middleware '${fileBase}' in ${relativeToCwd(file)}`)
203
+ return
204
+ }
205
+
206
+ // feathers target: generate a server plugin under server/feathers
207
+ const dir = join(opts.projectRoot, 'server', 'feathers')
208
+ const file = join(dir, `${fileBase}.ts`)
209
+ await ensureDir(dir, opts.dry)
210
+ await writeFileSafe(file, renderFeathersPlugin(fileBase), { dry: opts.dry, force: opts.force })
211
+ if (!opts.dry) consola.success(`Generated Feathers server plugin '${fileBase}' in ${relativeToCwd(file)}`)
212
+ }
213
+
214
+ async function ensureDir(dir: string, dry: boolean) {
215
+ if (dry) return
216
+ await mkdir(dir, { recursive: true })
217
+ }
218
+
219
+ async function writeFileSafe(path: string, content: string, opts: { dry: boolean; force: boolean }) {
220
+ if (!opts.force && existsSync(path)) {
221
+ throw new Error(`File already exists: ${path} (use --force to overwrite)`)
222
+ }
223
+
224
+ if (opts.dry) {
225
+ consola.info(`[dry] write ${relativeToCwd(path)}`)
226
+ return
227
+ }
228
+
229
+ await writeFile(path, content, 'utf8')
230
+ }
231
+
232
+ function relativeToCwd(p: string) {
233
+ try {
234
+ return p.replace(resolve(process.cwd()) + '/', '')
235
+ }
236
+ catch {
237
+ return p
238
+ }
239
+ }
240
+
241
+ function renderSchema(ids: ReturnType<typeof createServiceIds>, adapter: Adapter) {
242
+ const base = ids.baseCamel
243
+ const Base = ids.basePascal
244
+ const serviceClass = `${Base}Service`
245
+
246
+ const idField = adapter === 'mongodb' ? '_id' : 'id'
247
+ const idSchema = adapter === 'mongodb'
248
+ ? `
249
+ const objectIdRegex = /^[0-9a-f]{24}$/i
250
+ export const objectIdSchema = () => z.string().regex(objectIdRegex, 'Invalid ObjectId')
251
+ `
252
+ : ''
253
+
254
+ const mainSchema = adapter === 'mongodb'
255
+ ? `export const ${base}Schema = z.object({
256
+ ${idField}: objectIdSchema(),
257
+ text: z.string(),
258
+ })`
259
+ : `export const ${base}Schema = z.object({
260
+ ${idField}: z.number().int(),
261
+ text: z.string(),
262
+ })`
263
+
264
+ const pickCreate = adapter === 'mongodb'
265
+ ? `{ text: true }`
266
+ : `{ text: true }`
267
+
268
+ return `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.schemas.html
269
+
270
+ import type { HookContext } from 'nuxt-feathers-zod/server'
271
+ import type { ${serviceClass} } from './${ids.serviceNameKebab}.class'
272
+ import { resolve } from '@feathersjs/schema'
273
+ import { zodQuerySyntax } from 'nuxt-feathers-zod/query'
274
+ import { getZodValidator } from 'nuxt-feathers-zod/validators'
275
+ import { z } from 'zod'
276
+ ${idSchema}
277
+
278
+ // Main data model schema
279
+ ${mainSchema}
280
+ export type ${Base} = z.infer<typeof ${base}Schema>
281
+ export const ${base}Validator = getZodValidator(${base}Schema, { kind: 'data' })
282
+ export const ${base}Resolver = resolve<${Base}, HookContext<${serviceClass}>>({})
283
+
284
+ export const ${base}ExternalResolver = resolve<${Base}, HookContext<${serviceClass}>>({})
285
+
286
+ // Schema for creating new entries
287
+ export const ${base}DataSchema = ${base}Schema.pick(${pickCreate})
288
+ export type ${Base}Data = z.infer<typeof ${base}DataSchema>
289
+ export const ${base}DataValidator = getZodValidator(${base}DataSchema, { kind: 'data' })
290
+ export const ${base}DataResolver = resolve<${Base}, HookContext<${serviceClass}>>({})
291
+
292
+ // Schema for updating existing entries
293
+ export const ${base}PatchSchema = ${base}Schema.partial()
294
+ export type ${Base}Patch = z.infer<typeof ${base}PatchSchema>
295
+ export const ${base}PatchValidator = getZodValidator(${base}PatchSchema, { kind: 'data' })
296
+ export const ${base}PatchResolver = resolve<${Base}, HookContext<${serviceClass}>>({})
297
+
298
+ // Schema for allowed query properties
299
+ export const ${base}QuerySchema = zodQuerySyntax(${base}Schema)
300
+ export type ${Base}Query = z.infer<typeof ${base}QuerySchema>
301
+ export const ${base}QueryValidator = getZodValidator(${base}QuerySchema, { kind: 'query' })
302
+ export const ${base}QueryResolver = resolve<${Base}Query, HookContext<${serviceClass}>>({})
303
+ `
304
+ }
305
+
306
+ function renderClass(ids: ReturnType<typeof createServiceIds>, adapter: Adapter) {
307
+ const base = ids.baseCamel
308
+ const Base = ids.basePascal
309
+ const serviceName = ids.serviceNameKebab
310
+ const serviceClass = `${Base}Service`
311
+ const paramsName = `${Base}Params`
312
+
313
+ if (adapter === 'memory') {
314
+ return `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.class.html#custom-services
315
+
316
+ import type { Params } from '@feathersjs/feathers'
317
+ import type { MemoryServiceOptions } from '@feathersjs/memory'
318
+ import type { Application } from 'nuxt-feathers-zod/server'
319
+ import type { ${Base}, ${Base}Data, ${Base}Patch, ${Base}Query } from './${serviceName}.schema'
320
+ import { MemoryService } from '@feathersjs/memory'
321
+
322
+ export type { ${Base}, ${Base}Data, ${Base}Patch, ${Base}Query }
323
+
324
+ export interface ${paramsName} extends Params<${Base}Query> {}
325
+
326
+ export class ${serviceClass}<ServiceParams extends Params = ${paramsName}> extends MemoryService<
327
+ ${Base},
328
+ ${Base}Data
329
+ > {}
330
+
331
+ export function getOptions(app: Application): MemoryServiceOptions<${Base}> {
332
+ return {
333
+ multi: true,
334
+ }
335
+ }
336
+ `
337
+ }
338
+
339
+ // mongodb
340
+ return `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.class.html#database-services
341
+
342
+ import type { Params } from '@feathersjs/feathers'
343
+ import type { MongoDBAdapterOptions, MongoDBAdapterParams } from '@feathersjs/mongodb'
344
+ import type { Application } from 'nuxt-feathers-zod/server'
345
+ import type { ${Base}, ${Base}Data, ${Base}Patch, ${Base}Query } from './${serviceName}.schema'
346
+ import { MongoDBService } from '@feathersjs/mongodb'
347
+
348
+ export type { ${Base}, ${Base}Data, ${Base}Patch, ${Base}Query }
349
+
350
+ export interface ${paramsName} extends MongoDBAdapterParams<${Base}Query> {}
351
+
352
+ export class ${serviceClass}<ServiceParams extends Params = ${paramsName}> extends MongoDBService<
353
+ ${Base},
354
+ ${Base}Data,
355
+ ${paramsName},
356
+ ${Base}Patch
357
+ > {}
358
+
359
+ export function getOptions(app: Application): MongoDBAdapterOptions {
360
+ const mongoClient = app.get('mongodbClient')
361
+ return {
362
+ paginate: {
363
+ default: 10,
364
+ max: 100,
365
+ },
366
+ multi: true,
367
+ Model: mongoClient.then(db => db.collection('${serviceName}')),
368
+ }
369
+ }
370
+ `
371
+ }
372
+
373
+ function renderShared(ids: ReturnType<typeof createServiceIds>) {
374
+ const base = ids.baseCamel
375
+ const Base = ids.basePascal
376
+ const serviceName = ids.serviceNameKebab
377
+ const serviceClass = `${Base}Service`
378
+ const methodsConst = `${base}Methods`
379
+ const pathConst = `${base}Path`
380
+ const clientFn = `${base}Client`
381
+ const clientServiceType = `${Base}ClientService`
382
+
383
+ return `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.shared.html
384
+
385
+ import type { Params } from '@feathersjs/feathers'
386
+ import type { ClientApplication } from 'nuxt-feathers-zod/client'
387
+ import type { ${Base}, ${Base}Data, ${Base}Patch, ${Base}Query, ${serviceClass} } from './${serviceName}.class'
388
+
389
+ export type { ${Base}, ${Base}Data, ${Base}Patch, ${Base}Query }
390
+
391
+ export type ${clientServiceType} = Pick<${serviceClass}<Params<${Base}Query>>, (typeof ${methodsConst})[number]>
392
+
393
+ export const ${pathConst} = '${serviceName}'
394
+
395
+ export const ${methodsConst}: Array<keyof ${serviceClass}> = ['find', 'get', 'create', 'patch', 'remove']
396
+
397
+ export function ${clientFn}(client: ClientApplication) {
398
+ const connection = client.get('connection')
399
+
400
+ client.use(${pathConst}, connection.service(${pathConst}), {
401
+ methods: ${methodsConst},
402
+ })
403
+ }
404
+
405
+ declare module 'nuxt-feathers-zod/client' {
406
+ interface ServiceTypes {
407
+ [${pathConst}]: ${clientServiceType}
408
+ }
409
+ }
410
+ `
411
+ }
412
+
413
+ function renderService(ids: ReturnType<typeof createServiceIds>, auth: boolean) {
414
+ const base = ids.baseCamel
415
+ const Base = ids.basePascal
416
+ const serviceName = ids.serviceNameKebab
417
+ const serviceClass = `${Base}Service`
418
+ const authImports = auth ? "import { authenticate } from '@feathersjs/authentication'\n" : ''
419
+
420
+ const authAround = auth
421
+ ? `
422
+ find: [authenticate('jwt')],
423
+ get: [authenticate('jwt')],
424
+ create: [],
425
+ patch: [authenticate('jwt')],
426
+ remove: [authenticate('jwt')],
427
+ `
428
+ : `
429
+ find: [],
430
+ get: [],
431
+ create: [],
432
+ patch: [],
433
+ remove: [],
434
+ `
435
+
436
+ return `// For more information about this file see https://dove.feathersjs.com/guides/cli/service.html
437
+
438
+ import type { Application } from 'nuxt-feathers-zod/server'
439
+ ${authImports}import { hooks as schemaHooks } from '@feathersjs/schema'
440
+ import { getOptions, ${serviceClass} } from './${serviceName}.class'
441
+ import {
442
+ ${base}DataResolver,
443
+ ${base}DataValidator,
444
+ ${base}ExternalResolver,
445
+ ${base}PatchResolver,
446
+ ${base}PatchValidator,
447
+ ${base}QueryResolver,
448
+ ${base}QueryValidator,
449
+ ${base}Resolver,
450
+ } from './${serviceName}.schema'
451
+ import { ${base}Methods, ${base}Path } from './${serviceName}.shared'
452
+
453
+ export * from './${serviceName}.class'
454
+ export * from './${serviceName}.schema'
455
+
456
+ export function ${base}(app: Application) {
457
+ app.use(${base}Path, new ${serviceClass}(getOptions(app)), {
458
+ methods: ${base}Methods,
459
+ events: [],
460
+ })
461
+
462
+ app.service(${base}Path).hooks({
463
+ around: {
464
+ all: [schemaHooks.resolveExternal(${base}ExternalResolver), schemaHooks.resolveResult(${base}Resolver)],
465
+ ${authAround} },
466
+ before: {
467
+ all: [schemaHooks.validateQuery(${base}QueryValidator), schemaHooks.resolveQuery(${base}QueryResolver)],
468
+ find: [],
469
+ get: [],
470
+ create: [schemaHooks.validateData(${base}DataValidator), schemaHooks.resolveData(${base}DataResolver)],
471
+ patch: [schemaHooks.validateData(${base}PatchValidator), schemaHooks.resolveData(${base}PatchResolver)],
472
+ remove: [],
473
+ },
474
+ after: {
475
+ all: [],
476
+ },
477
+ error: {
478
+ all: [],
479
+ },
480
+ })
481
+ }
482
+
483
+ declare module 'nuxt-feathers-zod/server' {
484
+ interface ServiceTypes {
485
+ [${base}Path]: ${serviceClass}
486
+ }
487
+ }
488
+ `
489
+ }
490
+
491
+ function renderNitroMiddleware(name: string) {
492
+ const nice = name.replace(/-/g, ' ')
493
+ return `// Nitro middleware: ${nice}
494
+ // Runs on every request (or conditionally based on route rules).
495
+
496
+ export default defineEventHandler(async (event) => {
497
+ // Example: attach a request id
498
+ // event.context.requestId = crypto.randomUUID()
499
+ })
500
+ `
501
+ }
502
+
503
+ function renderFeathersPlugin(name: string) {
504
+ const nice = name.replace(/-/g, ' ')
505
+ return `// Feathers server plugin: ${nice}
506
+ // Loaded by Nuxt Nitro server (see playground/server/feathers/*.ts for examples)
507
+
508
+ import type { HookContext, NextFunction } from 'nuxt-feathers-zod/server'
509
+ import { defineFeathersServerPlugin } from 'nuxt-feathers-zod/server'
510
+
511
+ export default defineFeathersServerPlugin((app) => {
512
+ app.hooks({
513
+ setup: [
514
+ async (context: HookContext, next: NextFunction) => {
515
+ // Place initialization logic here
516
+ await next()
517
+ },
518
+ ],
519
+ })
520
+ })
521
+ `
522
+ }