@appweaver/cli 1.0.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 +1 -0
- package/README.md +7 -0
- package/build/build-command.d.ts +2 -0
- package/build/build-command.js +15 -0
- package/build/build-project.d.ts +8 -0
- package/build/build-project.js +19 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +18 -0
- package/generate/generate-command.d.ts +2 -0
- package/generate/generate-command.js +38 -0
- package/generate/generate-schema.d.ts +12 -0
- package/generate/generate-schema.js +475 -0
- package/generate/generate-types.d.ts +10 -0
- package/generate/generate-types.js +86 -0
- package/generate/index.d.ts +3 -0
- package/generate/index.js +19 -0
- package/migrate/index.d.ts +1 -0
- package/migrate/index.js +17 -0
- package/migrate/migrate-command.d.ts +2 -0
- package/migrate/migrate-command.js +13 -0
- package/migration/index.d.ts +1 -0
- package/migration/index.js +17 -0
- package/migration/migration-command.d.ts +2 -0
- package/migration/migration-command.js +34 -0
- package/openapi/index.d.ts +1 -0
- package/openapi/index.js +17 -0
- package/openapi/openapi-command.d.ts +2 -0
- package/openapi/openapi-command.js +46 -0
- package/package.json +56 -0
- package/seed/index.d.ts +1 -0
- package/seed/index.js +17 -0
- package/seed/seed-command.d.ts +2 -0
- package/seed/seed-command.js +33 -0
- package/skill/GUIDELINES.md +298 -0
- package/skill/SKILL.md +593 -0
- package/skill/references/cache.md +207 -0
- package/skill/references/cli.md +213 -0
- package/skill/references/client.md +507 -0
- package/skill/references/configuration.md +402 -0
- package/skill/references/database.md +134 -0
- package/skill/references/dependency-injection.md +214 -0
- package/skill/references/events.md +152 -0
- package/skill/references/mailer.md +235 -0
- package/skill/references/queue.md +196 -0
- package/skill/references/resources.md +961 -0
- package/skill/references/scheduler.md +184 -0
- package/skill/references/security.md +694 -0
- package/skill/references/storage.md +251 -0
- package/start/index.d.ts +2 -0
- package/start/index.js +18 -0
- package/start/start-command.d.ts +2 -0
- package/start/start-command.js +17 -0
- package/start/start-project.d.ts +8 -0
- package/start/start-project.js +147 -0
- package/testing/index.d.ts +1 -0
- package/testing/index.js +17 -0
- package/testing/testing-command.d.ts +2 -0
- package/testing/testing-command.js +96 -0
- package/update/index.d.ts +2 -0
- package/update/index.js +18 -0
- package/update/update-command.d.ts +2 -0
- package/update/update-command.js +84 -0
- package/update/update-packages.d.ts +10 -0
- package/update/update-packages.js +45 -0
- package/update/update-skill.d.ts +8 -0
- package/update/update-skill.js +93 -0
- package/utils/index.d.ts +3 -0
- package/utils/index.js +19 -0
- package/utils/loader-util.d.ts +29 -0
- package/utils/loader-util.js +132 -0
- package/utils/path-util.d.ts +41 -0
- package/utils/path-util.js +98 -0
- package/utils/process-util.d.ts +39 -0
- package/utils/process-util.js +92 -0
- package/weaver.d.ts +2 -0
- package/weaver.js +53 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# Appweaver Guidelines
|
|
2
|
+
|
|
3
|
+
Appweaver is a TypeScript/Node.js library for building web applications. Built on Fastify (HTTP) and Prisma (ORM), it
|
|
4
|
+
provides factory methods for creating resource models, services, policies, and routes with sensible defaults.
|
|
5
|
+
|
|
6
|
+
## Project structure
|
|
7
|
+
|
|
8
|
+
- `database/` - migrations, seeders, generated Prisma client
|
|
9
|
+
- `dist/` - transpiled JavaScript output
|
|
10
|
+
- `public/` - static files (if enabled)
|
|
11
|
+
- `src/features/` - application logic (vertical slice architecture)
|
|
12
|
+
- `src/resources/` - resources (models, services, policies, routes)
|
|
13
|
+
- `src/types/` - generated and manual types
|
|
14
|
+
- `src/main.ts` - application entrypoint
|
|
15
|
+
- `test/e2e/` - end-to-end tests
|
|
16
|
+
- `test/unit/` - unit tests
|
|
17
|
+
- `.env` / `.env.{env}` - environment variable overrides (optional)
|
|
18
|
+
- `appweaver.json` / `appweaver.{env}.json` - central configuration
|
|
19
|
+
- `Dockerfile` - Docker image definition
|
|
20
|
+
|
|
21
|
+
**IMPORTANT:** `{env}` is controlled by `NODE_ENV` evironment variable.
|
|
22
|
+
|
|
23
|
+
## Application entrypoint
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
// src/main.ts
|
|
27
|
+
import { createApp } from '@appweaver/core';
|
|
28
|
+
import { logger } from '@appweaver/common';
|
|
29
|
+
|
|
30
|
+
createApp().catch((err) => logger.error(err));
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Creating resources
|
|
34
|
+
|
|
35
|
+
Resources are the core building blocks. There are four types: **model**, **service**, **routes**, and **policy**.
|
|
36
|
+
Exported resources are loaded automatically on application start.
|
|
37
|
+
|
|
38
|
+
Dependency chain: **model** → **service** → **routes** → **policy**
|
|
39
|
+
|
|
40
|
+
Only a model is required. If a service exists, a model must exist. If routes exist, a service must exist. Policy is
|
|
41
|
+
independent.
|
|
42
|
+
|
|
43
|
+
**DOS:**
|
|
44
|
+
|
|
45
|
+
- Use default configuration values whenever possible
|
|
46
|
+
- Rely on library defaults for `omit`/`pick`, and `input`/`output` settings
|
|
47
|
+
- Use default `mimeType` and `namePattern` patterns in file configurations unless specifically requested
|
|
48
|
+
- Prefer storing configuration in JSON file (`appweaver.json`) over environment (`.env`) file, but prefer it for secrets
|
|
49
|
+
- Always create all four resource configs (model, service, routes, and policy) unless specified otherwise
|
|
50
|
+
|
|
51
|
+
**DON'TS:**
|
|
52
|
+
|
|
53
|
+
- Don't explicitly set default values in configuration unless specifically requested
|
|
54
|
+
- Don't override `omit`/`pick` for `read`, `create` and `update` settings unnecessarily
|
|
55
|
+
- Don't specify `input`/`output` configurations if defaults suffice
|
|
56
|
+
- Don't modify file's `mimeType` and `namePattern` patterns unless specifically instructed
|
|
57
|
+
- Don't customize index arrays without an explicit requirement
|
|
58
|
+
|
|
59
|
+
### Model
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// src/resources/product/model.ts
|
|
63
|
+
import { createModel } from '@appweaver/core';
|
|
64
|
+
|
|
65
|
+
export default createModel({
|
|
66
|
+
name: 'Product',
|
|
67
|
+
scalars: {
|
|
68
|
+
title: { type: 'string', minLength: 1, maxLength: 200 },
|
|
69
|
+
price: { type: 'float', minimum: 0 },
|
|
70
|
+
status: { type: 'enum', default: 'Draft', values: ['Draft', 'Active', 'Sold'] },
|
|
71
|
+
description: { type: 'string', required: false },
|
|
72
|
+
lastViewedAt: { type: 'dateTime', defaultGenerator: 'now()' },
|
|
73
|
+
enabled: { type: 'boolean', default: true }
|
|
74
|
+
},
|
|
75
|
+
relations: {
|
|
76
|
+
category: { model: 'Category', mappedBy: 'products', owner: true, output: { type: 'always' } }
|
|
77
|
+
},
|
|
78
|
+
files: {
|
|
79
|
+
photo: { mimeType: 'image/*', maxSize: '2 MB' }
|
|
80
|
+
},
|
|
81
|
+
create: { omit: ['status'] },
|
|
82
|
+
update: { pick: ['title', 'price', 'status', 'description'] },
|
|
83
|
+
index: ['title']
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Service
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// src/resources/product/service.ts
|
|
91
|
+
import { createService } from '@appweaver/core';
|
|
92
|
+
|
|
93
|
+
export default createService({
|
|
94
|
+
modelName: 'Product',
|
|
95
|
+
afterCreate: (resource) => {
|
|
96
|
+
console.log('Product created:', resource.id);
|
|
97
|
+
},
|
|
98
|
+
textSearch: {
|
|
99
|
+
title: { contains: '{input}', mode: 'insensitive' }
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Routes
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
// src/resources/product/routes.ts
|
|
108
|
+
import { createRoutes } from '@appweaver/core';
|
|
109
|
+
|
|
110
|
+
export default createRoutes({
|
|
111
|
+
modelName: 'Product',
|
|
112
|
+
find: { cache: true, roles: ['Admin', 'User'], rateLimit: { max: 100 } },
|
|
113
|
+
query: { cacheTTL: 5000 },
|
|
114
|
+
create: { permissions: ['product:create'] },
|
|
115
|
+
delete: { exclude: true }
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Policy
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// src/resources/product/policy.ts
|
|
123
|
+
import { createPolicy } from '@appweaver/core';
|
|
124
|
+
|
|
125
|
+
export default createPolicy({
|
|
126
|
+
modelName: 'Product',
|
|
127
|
+
checkAccess: (action, resource) => resource.status === 'Draft',
|
|
128
|
+
readRestrictions: (action, resource) => {
|
|
129
|
+
enabled: true;
|
|
130
|
+
},
|
|
131
|
+
files: {
|
|
132
|
+
photo: { accessType: 'public' }
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Auth model and service
|
|
138
|
+
|
|
139
|
+
Use `createAuthModel` and `createAuthService` for authenticatable users. They must be used together.
|
|
140
|
+
|
|
141
|
+
`createAuthModel` adds: `email`, `passwordHash`, `verifiedEmail`, `twoFactorAuth`, `enabled`, `logoutAt` scalars; a
|
|
142
|
+
virtual `password` field; a `roles` relation; and optional `apiKeys` relation.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
// src/resources/user/model.ts
|
|
146
|
+
import { createAuthModel } from '@appweaver/core';
|
|
147
|
+
|
|
148
|
+
export default createAuthModel({
|
|
149
|
+
name: 'User',
|
|
150
|
+
scalars: { name: { type: 'string', maxLength: 100 } },
|
|
151
|
+
files: { avatar: { mimeType: 'image/(png|jpeg|gif)', maxSize: '2 MB' } }
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
// src/resources/user/service.ts
|
|
157
|
+
import { createAuthService } from '@appweaver/core';
|
|
158
|
+
|
|
159
|
+
export default createAuthService({
|
|
160
|
+
modelName: 'User',
|
|
161
|
+
registrationData: (_, email, password) => ({ email, password, roles: [1, 2] })
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Custom routes, models, and plugins
|
|
166
|
+
|
|
167
|
+
### Custom route
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
// src/features/custom-route.ts
|
|
171
|
+
import { registerRoute, Router } from '@appweaver/core';
|
|
172
|
+
import { Type } from '@sinclair/typebox';
|
|
173
|
+
|
|
174
|
+
registerRoute(
|
|
175
|
+
async function (router: Router) {
|
|
176
|
+
router.get('/search-result', {
|
|
177
|
+
schema: { summary: 'Search result', response: { 200: Type.Ref('SearchResult') } },
|
|
178
|
+
handler: async () => ({ message: 'Hello, world!' })
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
{ public: true, cacheTTL: 15000 }
|
|
182
|
+
);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Custom model
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
// src/features/custom-model.ts
|
|
189
|
+
import { registerModel } from '@appweaver/core';
|
|
190
|
+
import { Type } from '@sinclair/typebox';
|
|
191
|
+
|
|
192
|
+
registerModel(
|
|
193
|
+
Type.Object(
|
|
194
|
+
{ id: Type.Number(), title: Type.String(), score: Type.Number({ minimum: 0, maximum: 1 }) },
|
|
195
|
+
{ $id: 'SearchResult' }
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Plugin
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
// src/plugins/audit-log.ts
|
|
204
|
+
import { registerPlugin } from '@appweaver/core';
|
|
205
|
+
|
|
206
|
+
registerPlugin('audit-log', async (server) => {
|
|
207
|
+
server.addHook('onResponse', async (request, reply) => {
|
|
208
|
+
console.log(`${request.method} ${request.url} → ${reply.statusCode}`);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Dependency injection
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { Cache } from '@appweaver/common';
|
|
217
|
+
import { define, inject, loadProvider } from '@appweaver/core';
|
|
218
|
+
|
|
219
|
+
define(RedisCacheService, Cache); // register class under abstract token
|
|
220
|
+
define('https://api.example.com', 'ApiBaseUrl'); // register plain value
|
|
221
|
+
|
|
222
|
+
const cache = inject(Cache); // resolve singleton
|
|
223
|
+
const url = inject<string>('ApiBaseUrl'); // resolve by string token
|
|
224
|
+
|
|
225
|
+
// Dynamic provider loading (typical in main.ts)
|
|
226
|
+
loadProvider(__dirname, config.CACHE_PROVIDER, Cache);
|
|
227
|
+
loadProvider(__dirname, config.MAILER_PROVIDER, Mailer, false); // optional
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Seeders
|
|
231
|
+
|
|
232
|
+
Seeder files export async functions and run in alphabetical order. Prefix filenames with ordinal numbers.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
// database/seeders/001-create-admin-user.ts
|
|
236
|
+
import { hashPassword } from '@appweaver/core';
|
|
237
|
+
import { db } from '@db/client';
|
|
238
|
+
|
|
239
|
+
export async function createAdminUser(): Promise<void> {
|
|
240
|
+
await db.user.create({
|
|
241
|
+
data: {
|
|
242
|
+
firstName: 'Admin',
|
|
243
|
+
lastName: 'Admin',
|
|
244
|
+
email: 'admin@appweaver.com',
|
|
245
|
+
roles: {
|
|
246
|
+
connectOrCreate: [
|
|
247
|
+
{
|
|
248
|
+
where: { name: 'Admin' },
|
|
249
|
+
create: {
|
|
250
|
+
name: 'Admin',
|
|
251
|
+
permissions: {
|
|
252
|
+
connectOrCreate: [
|
|
253
|
+
{ where: { name: '*.read' }, create: { name: '*.read' } },
|
|
254
|
+
{ where: { name: '*.write' }, create: { name: '*.write' } }
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Common commands
|
|
267
|
+
|
|
268
|
+
| Command | Description |
|
|
269
|
+
|-------------------------------|-------------------------------------------|
|
|
270
|
+
| `npm run generate` | Generate TypeScript types + Prisma schema |
|
|
271
|
+
| `npm run build` | Build the application |
|
|
272
|
+
| `npm run start` | Start in production mode |
|
|
273
|
+
| `npm run dev` | Start in development (watch) mode |
|
|
274
|
+
| `npm run seed` | Seed the database |
|
|
275
|
+
| `npm run migrate` | Apply pending database migrations |
|
|
276
|
+
| `npm run test` | Run unit tests |
|
|
277
|
+
| `npm run e2e` | Run end-to-end tests |
|
|
278
|
+
| `npm run format` | Format code with Prettier |
|
|
279
|
+
| `npm run lint` | Lint code with ESLint |
|
|
280
|
+
| `weaver migration new <name>` | Create a new database migration |
|
|
281
|
+
| `weaver update` | Update all @appweaver/* packages |
|
|
282
|
+
| `weaver openapi` | Generate OpenAPI specification |
|
|
283
|
+
|
|
284
|
+
## References
|
|
285
|
+
|
|
286
|
+
- Application CLI (weaver): [cli.md](references/cli.md)
|
|
287
|
+
- Application configuration: [configuration.md](references/configuration.md)
|
|
288
|
+
- Application resources: [resources.md](references/resources.md)
|
|
289
|
+
- Dependency injection: [dependency-injection.md](references/dependency-injection.md)
|
|
290
|
+
- Security details: [security.md](references/security.md)
|
|
291
|
+
- Storage & File management: [storage.md](references/storage.md)
|
|
292
|
+
- Database & Migrations: [database.md](references/database.md)
|
|
293
|
+
- Events & Hooks: [events.md](references/events.md)
|
|
294
|
+
- Cache management: [cache.md](references/cache.md)
|
|
295
|
+
- Queue jobs: [queue.md](references/queue.md)
|
|
296
|
+
- Scheduling jobs: [scheduler.md](references/scheduler.md)
|
|
297
|
+
- Sending emails: [mailer.md](references/mailer.md)
|
|
298
|
+
- Generating an HTTP client for using API: [client.md](references/client.md)
|