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 +90 -0
- package/bin/nuxt-feathers-zod +5 -0
- package/dist/module.json +1 -1
- package/package.json +19 -17
- package/src/cli/index.ts +522 -0
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
|
package/dist/module.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-feathers-zod",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "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
|
-
".": [
|
|
28
|
-
|
|
29
|
-
|
|
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": [
|
|
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": "
|
|
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
|
}
|
package/src/cli/index.ts
ADDED
|
@@ -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
|
+
}
|