@appweaver/cli 1.5.0 → 1.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appweaver/cli",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Appweaver - the backend framework for AI-first development (@cli)",
5
5
  "author": "Luka Matosevic",
6
6
  "license": "MIT",
package/skill/SKILL.md CHANGED
@@ -66,10 +66,10 @@ create-weaver-app <name> [description] [options]
66
66
  | `--bun` | Use Bun as application runtime. (default is node and npm) | false |
67
67
  | `--skipInstall` | Skip all dependencies installation. | false |
68
68
  | `--noDocker` | Skip Dockerfile, Dockerfile.bun and docker-compose.yml files | false |
69
- | `--noRedis` | Skip ioredis | false |
70
- | `--noQueue` | Skip bullmq | false |
71
- | `--noMailer` | Skip nodemailer | false |
72
- | `--noCron` | Skip cron | false |
69
+ | `--noRedis` | Skip ioredis, use in-memory cache, rate limit and queue | false |
70
+ | `--noQueue` | Skip bullmq, use in-memory queue | false |
71
+ | `--noMailer` | Skip nodemailer, disable mailer (email features respond 501) | false |
72
+ | `--noCron` | Skip cron, disable scheduler | false |
73
73
 
74
74
  **Example — PostgreSQL project without queue:**
75
75
 
@@ -15,6 +15,18 @@ import { Cache } from '@appweaver/common';
15
15
  const cache = inject(Cache);
16
16
  ```
17
17
 
18
+ ### Unavailable backend
19
+
20
+ With `CACHE_SKIP_ON_ERROR` enabled (the default) the cache never fails its callers. While the backing memory (e.g.
21
+ Redis) is down or a command fails, every method returns its empty result (`get` → `null`, `has`/`set`/`evict` →
22
+ `false`, `expire` → `0`, `keys` → `[]`), so the callers fall back to the database. The outage is logged once with an
23
+ error, and the recovery once with info. Entries written before an outage may have missed invalidations made during it,
24
+ so the whole cache is cleared once the memory is available again. Calling code does not need its own `try`/`catch`
25
+ around cache access.
26
+
27
+ Set `CACHE_SKIP_ON_ERROR` to `false` to make every method throw the memory error instead, so requests that read or
28
+ write the cache fail while its backend is down. Resource cache invalidation still only logs its errors.
29
+
18
30
  #### `cache.get<T>(key)`
19
31
 
20
32
  Retrieves a cached value. Returns `null` if the key does not exist.
@@ -167,6 +179,7 @@ const key = cacheService.buildCacheKey({
167
179
  | `CACHE_EVICTION_STRATEGY` | `enum` | `'lru'` | `lru`, `lfu`, or `fifo` |
168
180
  | `CACHE_INVALIDATION_STRATEGY` | `enum` | `'expire-related'` | `expire-related`, `expire-all`, or `none` |
169
181
  | `CACHE_INVALIDATION_DEFERRED` | `bool` | `false` | Fire invalidation in the background (non-blocking) |
182
+ | `CACHE_SKIP_ON_ERROR` | `bool` | `true` | Return empty results instead of failing on errors |
170
183
 
171
184
  Switch to the in-memory implementation for local development or tests:
172
185
 
@@ -46,10 +46,13 @@ weaver openapi|oa [options]
46
46
 
47
47
  Generate application OpenAPI specification schema.
48
48
 
49
- | Option | Description | Default |
50
- |---------------------------|------------------------------------------------------------------|-----------------|
51
- | `-o, --outputPath [path]` | Output path for generated OpenAPI specification | `./schema.json` |
52
- | `-f, --format [format]` | Output format for generated OpenAPI specification (json or yaml) | `json` |
49
+ | Option | Description | Default |
50
+ |---------------------------|------------------------------------------------------------------|------------------|
51
+ | `-o, --outputPath [path]` | Output path for generated OpenAPI specification | `./openapi.json` |
52
+ | `-f, --format [format]` | Output format for generated OpenAPI specification (json or yaml) | `json` |
53
+
54
+ When `--format yaml` is used and `--outputPath` is left at its default, the output path becomes `./openapi.yaml`.
55
+ Missing output directories are created automatically.
53
56
 
54
57
  ---
55
58
 
@@ -80,9 +83,12 @@ Per model, the type file holds `<Model>`, `<Model>Single`, `<Model>Multiple`, `<
80
83
  ## `weaver migrate`
81
84
 
82
85
  ```
83
- weaver migrate|mge [options]
86
+ weaver migrate|mge
84
87
  ```
85
88
 
89
+ Apply all pending database migrations (`prisma migrate deploy`). Takes no options, creates nothing, and never
90
+ prompts, which makes it the command to run in CI, in containers, and in production.
91
+
86
92
  ---
87
93
 
88
94
  ## `weaver migration`
@@ -102,10 +108,15 @@ Database migration commands.
102
108
 
103
109
  ### `weaver migration reset`
104
110
 
105
- | Option | Description | Default |
106
- |---------------|----------------------------------------------|---------|
107
- | `-f, --force` | Force reset for non-development environments | `false` |
108
- | `-y, --yes` | Skip confirmation prompt | `false` |
111
+ Drops the database, recreates it, and re-applies every migration (`prisma migrate reset`). **All data is lost.**
112
+
113
+ | Option | Description | Default |
114
+ |---------------|-----------------------------------------------------------|---------|
115
+ | `-f, --force` | Allow the reset outside the `dev` and `test` environments | `false` |
116
+ | `-y, --yes` | Skip confirmation prompt | `false` |
117
+
118
+ Without `--force` the command runs only when `NODE_ENV` resolves to `dev` or `test`, and aborts otherwise. Either
119
+ `--force` or `--yes` skips the Prisma confirmation prompt.
109
120
 
110
121
  ---
111
122
 
@@ -117,13 +128,13 @@ weaver seed|sd [options]
117
128
 
118
129
  Seed the database.
119
130
 
120
- | Option | Description | Default |
121
- |-------------------------|---------------------------------------------------------------------|-----------------------|
122
- | `--seedersPath [path]` | Seeders directory path | `config.SEEDERS_PATH` |
123
- | `-b, --buildProject` | Build the project before seeding | `false` |
124
- | `-p, --project` | TypeScript project build config file (used when `-b` is set) | `tsconfig.build.json` |
125
- | `-c, --continueOnError` | Continue seeder execution if error is thrown | `false` |
126
- | `-f, --fixWarnings` | Fix all seeder warnings like wrong checksum or deleted seeder files | `false` |
131
+ | Option | Description | Default |
132
+ |-------------------------|---------------------------------------------------------------------|------------------------------------|
133
+ | `--seedersPath [path]` | Seeders directory path | `config.DATABASE_SEEDERS_DIR_PATH` |
134
+ | `-b, --buildProject` | Build the project before seeding | `false` |
135
+ | `-p, --project` | TypeScript project build config file (used when `-b` is set) | `tsconfig.build.json` |
136
+ | `-c, --continueOnError` | Continue seeder execution if error is thrown | `false` |
137
+ | `-f, --fixWarnings` | Fix all seeder warnings like wrong checksum or deleted seeder files | `false` |
127
138
 
128
139
  ---
129
140
 
@@ -135,10 +146,10 @@ weaver start|s [options]
135
146
 
136
147
  Start the application.
137
148
 
138
- | Option | Description | Default |
139
- |-----------------|-------------------------------------------------------------|-----------------|
140
- | `-p, --project` | TypeScript project config file | `tsconfig.json` |
141
- | `-w, --watch` | Run in watch mode (recompiles and restarts on file changes) | `false` |
149
+ | Option | Description | Default |
150
+ |-----------------|-------------------------------------------------------------|-----------------------|
151
+ | `-p, --project` | TypeScript project config file | `tsconfig.build.json` |
152
+ | `-w, --watch` | Run in watch mode (recompiles and restarts on file changes) | `false` |
142
153
 
143
154
  ---
144
155
 
@@ -171,6 +182,9 @@ Requires `NODE_ENV=test`.
171
182
  | `--migrationName [name]` | Name for the initial migration | `init_test` |
172
183
  | `--verbose` | Print verbose output | `false` |
173
184
 
185
+ Aborts unless the storage, schema and client paths all resolve inside `--dir`, so a misconfigured test run cannot
186
+ touch the development database or uploads.
187
+
174
188
  ### `weaver test reset`
175
189
 
176
190
  Requires `NODE_ENV=test`. With no flags, resets both database and storage.
@@ -120,13 +120,14 @@ The config object is frozen with `Object.freeze()` after loading to prevent runt
120
120
 
121
121
  ### Rate limiting (RATE_LIMIT_\*)
122
122
 
123
- | Property | Type | Default | Description |
124
- |-------------------------|-----------|-----------|------------------------------------------------------------------|
125
- | `RATE_LIMIT_ENABLED` | boolean | `true` | Enable global rate limiting middleware. |
126
- | `RATE_LIMIT_MAX` | integer | `1000` | Maximum requests allowed per time window. |
127
- | `RATE_LIMIT_WINDOW` | integer | `60000` | Rate limit window in milliseconds. |
128
- | `RATE_LIMIT_ALLOW_LIST` | string[]? | - | IP addresses/patterns exempt from rate limiting. |
129
- | `RATE_LIMIT_STORE` | enum | `'redis'` | Store backend for tracking limits. Values: `redis`, `in-memory`. |
123
+ | Property | Type | Default | Description |
124
+ |----------------------------|-----------|-----------|------------------------------------------------------------------------------------------------------------|
125
+ | `RATE_LIMIT_ENABLED` | boolean | `true` | Enable global rate limiting middleware. |
126
+ | `RATE_LIMIT_MAX` | integer | `1000` | Maximum requests allowed per time window. |
127
+ | `RATE_LIMIT_WINDOW` | integer | `60000` | Rate limit window in milliseconds. |
128
+ | `RATE_LIMIT_ALLOW_LIST` | string[]? | - | IP addresses/patterns exempt from rate limiting. |
129
+ | `RATE_LIMIT_STORE` | enum | `'redis'` | Store backend for tracking limits. Values: `redis`, `in-memory`. |
130
+ | `RATE_LIMIT_SKIP_ON_ERROR` | boolean | `true` | Skip rate limiting instead of failing requests with `500` when the store errors, e.g. while Redis is down. |
130
131
 
131
132
  ### Swagger / OpenAPI (SWAGGER\_\*)
132
133
 
@@ -182,14 +183,14 @@ The config object is frozen with `Object.freeze()` after loading to prevent runt
182
183
 
183
184
  #### General
184
185
 
185
- | Property | Type | Default | Description |
186
- |--------------------------------------|----------|---------------------------------------------------------|--------------------------------------------------|
187
- | `SECURITY_ROUTE_PREFIX` | string | `'/auth'` | Base path for authentication routes. |
188
- | `SECURITY_CACHE_TTL` | integer | `300000` | Security cache TTL in milliseconds. |
189
- | `SECURITY_AUTH_OTT_TTL` | integer | `120000` | One-time token TTL for authentication (ms). |
190
- | `SECURITY_ALLOWED_REDIRECT_HOSTS` | string[] | `['*']` | Allowed hosts for post-authentication redirects. |
191
- | `SECURITY_STORE_PROVIDER` | string | `'@appweaver/core/security/store/redis-security-store'` | Security store implementation path. |
192
- | `SECURITY_STORE_KEEP_DATABASE_TABLE` | boolean | `false` | Keep database table after migrations. |
186
+ | Property | Type | Default | Description |
187
+ |--------------------------------------|----------|------------------------------------------------------------|-----------------------------------------------------------|
188
+ | `SECURITY_ROUTE_PREFIX` | string | `'/auth'` | Base path for authentication routes. |
189
+ | `SECURITY_CACHE_TTL` | integer | `300000` | Security cache TTL in milliseconds. |
190
+ | `SECURITY_AUTH_OTT_TTL` | integer | `120000` | One-time token TTL for authentication (ms). |
191
+ | `SECURITY_ALLOWED_REDIRECT_HOSTS` | string[] | `['*']` | Allowed hosts for post-authentication redirects. |
192
+ | `SECURITY_STORE_PROVIDER` | string | `'@appweaver/core/security/store/database-security-store'` | Security store implementation path. |
193
+ | `SECURITY_STORE_KEEP_DATABASE_TABLE` | boolean | `false` | Keep the one-time token table when another store is used. |
193
194
 
194
195
  #### Password policy
195
196
 
@@ -390,6 +391,12 @@ you generated yourself, or provide the team ID, key ID and `.p8` private key and
390
391
  | `REDIS_URL` | string | `'redis://localhost:6379/0'` | Redis connection URL. |
391
392
  | `REDIS_PROVIDER` | string | `'@appweaver/core/memory/redis'` | Redis provider implementation path. |
392
393
 
394
+ The application starts and keeps running while Redis is unreachable. Connections reconnect in the background, each
395
+ outage and recovery is logged once, and commands fail right away instead of waiting for the reconnection, so by default
396
+ the
397
+ cache falls back to the database (see `CACHE_SKIP_ON_ERROR`), rate limiting is skipped (see
398
+ `RATE_LIMIT_SKIP_ON_ERROR`) and queues throw (see `queue.md`).
399
+
393
400
  ### In-memory store (MEMORY\_\*)
394
401
 
395
402
  | Property | Type | Default | Description |
@@ -412,6 +419,7 @@ you generated yourself, or provide the team ID, key ID and `.p8` private key and
412
419
  | `CACHE_EVICTION_DEFERRED` | boolean | `false` | Defer eviction to a background process. |
413
420
  | `CACHE_INVALIDATION_STRATEGY` | enum | `'expire-related'` | Invalidation strategy. Values: `expire-related`, `expire-all`, `none`. |
414
421
  | `CACHE_INVALIDATION_DEFERRED` | boolean | `false` | Defer invalidation to a background process. |
422
+ | `CACHE_SKIP_ON_ERROR` | boolean | `true` | Return empty results instead of failing when the cache backend errors. |
415
423
  | `CACHE_PROVIDER` | string | `'@appweaver/core/cache/redis-cache'` | Cache provider implementation path. |
416
424
 
417
425
  ### Job queue (QUEUE\_\*)
@@ -429,10 +437,11 @@ you generated yourself, or provide the team ID, key ID and `.p8` private key and
429
437
 
430
438
  ### Scheduler (SCHEDULER\_\*)
431
439
 
432
- | Property | Type | Default | Description |
433
- |----------------------------|---------|----------------------------------------------|---------------------------------------------------|
434
- | `SCHEDULER_AUTO_START_JOB` | boolean | `true` | Auto-start scheduled jobs on application startup. |
435
- | `SCHEDULER_PROVIDER` | string | `'@appweaver/core/scheduler/cron-scheduler'` | Scheduler provider implementation path. |
440
+ | Property | Type | Default | Description |
441
+ |----------------------------|---------|----------------------------------------------|----------------------------------------------------------------------------|
442
+ | `SCHEDULER_ENABLED` | boolean | `true` | Enable the scheduler. Disable it when the `cron` package is not installed. |
443
+ | `SCHEDULER_AUTO_START_JOB` | boolean | `true` | Auto-start scheduled jobs on application startup. |
444
+ | `SCHEDULER_PROVIDER` | string | `'@appweaver/core/scheduler/cron-scheduler'` | Scheduler provider implementation path. |
436
445
 
437
446
  ### Events (EVENTS\_\*)
438
447
 
@@ -443,16 +452,17 @@ you generated yourself, or provide the team ID, key ID and `.p8` private key and
443
452
 
444
453
  ### Mailer (MAILER\_\*)
445
454
 
446
- | Property | Type | Default | Description |
447
- |-------------------------|---------|----------------------------------------|------------------------------------------|
448
- | `MAILER_SENDER_NAME` | string? | - | Default sender name for outgoing emails. |
449
- | `MAILER_SENDER_ADDRESS` | string? | - | Default sender email address. |
450
- | `MAILER_PROVIDER` | string | `'@appweaver/core/mailer/smtp-mailer'` | Mailer provider implementation path. |
451
- | `MAILER_SMTP_HOST` | string | `'127.0.0.1'` | SMTP server hostname. |
452
- | `MAILER_SMTP_PORT` | integer | `587` | SMTP server port. |
453
- | `MAILER_SMTP_SECURE` | boolean | `false` | Use TLS/SSL for SMTP connections. |
454
- | `MAILER_SMTP_USER` | string? | - | SMTP authentication username. |
455
- | `MAILER_SMTP_PASSWORD` | string? | - | SMTP authentication password. |
455
+ | Property | Type | Default | Description |
456
+ |-------------------------|---------|----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
457
+ | `MAILER_ENABLED` | boolean | `true` | Enable the mailer. Disable it when the `nodemailer` package is not installed. Without it the email features (email verification, password reset, 2FA) respond with `501`. |
458
+ | `MAILER_SENDER_NAME` | string? | - | Default sender name for outgoing emails. |
459
+ | `MAILER_SENDER_ADDRESS` | string? | - | Default sender email address. |
460
+ | `MAILER_PROVIDER` | string | `'@appweaver/core/mailer/smtp-mailer'` | Mailer provider implementation path. |
461
+ | `MAILER_SMTP_HOST` | string | `'127.0.0.1'` | SMTP server hostname. |
462
+ | `MAILER_SMTP_PORT` | integer | `587` | SMTP server port. |
463
+ | `MAILER_SMTP_SECURE` | boolean | `false` | Use TLS/SSL for SMTP connections. |
464
+ | `MAILER_SMTP_USER` | string? | - | SMTP authentication username. |
465
+ | `MAILER_SMTP_PASSWORD` | string? | - | SMTP authentication password. |
456
466
 
457
467
  ### System (SYSTEM\_\*)
458
468
 
@@ -45,6 +45,12 @@ await queue.closeAll();
45
45
 
46
46
  Returns a `HealthCheckResult` indicating whether the underlying queue backend is reachable.
47
47
 
48
+ ### Unavailable backend
49
+
50
+ `BullQueue` needs Redis and has no fallback. The application still starts while Redis is down, but `sendJob` and
51
+ `sendBulkJobs` throw `Queue '<name>' is unavailable, Redis connection is not ready` right away instead of waiting for
52
+ the reconnection, so callers that must not fail should catch it. Workers pick jobs up again once Redis reconnects.
53
+
48
54
  ---
49
55
 
50
56
  ## `QueueProcessor` — per-queue API
@@ -76,7 +76,8 @@ if `SECURITY_JWT_SECRET` is set.
76
76
  "source": "password | apiKey | basic | oauth2Google | oauth2Facebook | oauth2X | oauth2Github | oauth2Gitlab | oauth2Linkedin | oauth2Apple | oauth2Microsoft | oauth2Custom",
77
77
  "username": "User email (e.g. admin@example.com)",
78
78
  "sub": "User ID (e.g. 123)",
79
- "iat": "Issued at timestamp (e.g. 1774623924234)"
79
+ "iat": "Issued at, in seconds (e.g. 1774623924)",
80
+ "exp": "Expiration, in seconds (e.g. 1777215924)"
80
81
  }
81
82
  ```
82
83
 
@@ -85,9 +86,11 @@ if `SECURITY_JWT_SECRET` is set.
85
86
  On every authenticated request, the server:
86
87
 
87
88
  1. Verifies the JWT signature
88
- 2. Loads the user from the database by `sub` (user ID)
89
+ 2. Loads the user by `sub` (user ID), from the cache when possible. Every update or delete of the auth user, through
90
+ the auth service (logout, password change or reset) or the resource routes, evicts the cached user regardless of
91
+ the cache invalidation strategy
89
92
  3. Checks that the user is enabled
90
- 4. Validates `logoutAt` is before the token's `iat` (tokens issued before logout are rejected)
93
+ 4. Rejects tokens issued before the user's `logoutAt` (compared in whole seconds)
91
94
  5. Checks that the token scope allows access to the requested URL
92
95
 
93
96
  ### Auth routes
@@ -428,7 +431,7 @@ update: {
428
431
  On every authenticated request:
429
432
 
430
433
  1. Verify user exists and is enabled
431
- 2. Verify `logoutAt` is before token `iat`
434
+ 2. Verify the token was not issued before `logoutAt`
432
435
  3. Verify JWT scope allows access to the URL
433
436
  4. Verify the user has required roles (if configured)
434
437
  5. Verify the user has required permissions (if configured)
@@ -629,10 +632,24 @@ time-limited.
629
632
 
630
633
  ### Storage
631
634
 
632
- OTTs are stored in the configured security store:
635
+ OTTs are stored in the security store set by `SECURITY_STORE_PROVIDER`. Only a hash of each token is stored.
633
636
 
634
- - **Redis** (default): `@appweaver/core/security/store/redis-security-store`
635
- - **Database**: `@appweaver/core/security/store/database-security-store`
637
+ - **Database** (default): `@appweaver/core/security/store/database-security-store`. Stores the tokens in the
638
+ `OneTimeToken` model, which is added to the schema only while this store is configured (or
639
+ `SECURITY_STORE_KEEP_DATABASE_TABLE` is set). The model has no routes.
640
+ - **Redis**: `@appweaver/core/security/store/redis-security-store`. Stores the tokens as Redis keys with a TTL, so the
641
+ one-time token flows (OAuth2 login, 2FA, email verification, password reset) fail while Redis is unavailable.
642
+
643
+ Switching the store adds or removes the `OneTimeToken` model, so run `weaver generate` and create a migration
644
+ afterwards.
645
+
646
+ Both stores behave the same way:
647
+
648
+ - A token is consumed by its first successful use. Of concurrent uses of the same token only one succeeds, the others
649
+ are rejected with `401`.
650
+ - A token failing the content validation (e.g. a mistyped 2FA code) is kept, so it can be used again until it expires.
651
+ - Expired tokens are rejected. The database store deletes them whenever a new token is created, Redis expires them on
652
+ its own.
636
653
 
637
654
  ---
638
655