@appweaver/create-weaver-app 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.
Files changed (64) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +7 -0
  3. package/create-weaver-app.d.ts +2 -0
  4. package/create-weaver-app.js +266 -0
  5. package/package.json +37 -0
  6. package/skill/GUIDELINES.md +298 -0
  7. package/skill/SKILL.md +593 -0
  8. package/skill/references/cache.md +207 -0
  9. package/skill/references/cli.md +213 -0
  10. package/skill/references/client.md +507 -0
  11. package/skill/references/configuration.md +402 -0
  12. package/skill/references/database.md +134 -0
  13. package/skill/references/dependency-injection.md +214 -0
  14. package/skill/references/events.md +152 -0
  15. package/skill/references/mailer.md +235 -0
  16. package/skill/references/queue.md +196 -0
  17. package/skill/references/resources.md +961 -0
  18. package/skill/references/scheduler.md +184 -0
  19. package/skill/references/security.md +694 -0
  20. package/skill/references/storage.md +251 -0
  21. package/templates/default/.dockerignore +5 -0
  22. package/templates/default/.env.tpl +1 -0
  23. package/templates/default/.prettierignore +3 -0
  24. package/templates/default/.prettierrc +7 -0
  25. package/templates/default/Dockerfile +56 -0
  26. package/templates/default/Dockerfile.bun +56 -0
  27. package/templates/default/README.md.tpl +7 -0
  28. package/templates/default/appweaver.dev.json.tpl +9 -0
  29. package/templates/default/appweaver.json.bun.tpl +17 -0
  30. package/templates/default/appweaver.json.tpl +16 -0
  31. package/templates/default/appweaver.test.json.tpl +30 -0
  32. package/templates/default/bunfig.toml.bun +8 -0
  33. package/templates/default/database/client.ts.tpl +7 -0
  34. package/templates/default/database/schema.prisma +13 -0
  35. package/templates/default/database/seeders/001-create-admin-user.ts.tpl +39 -0
  36. package/templates/default/eslint.config.mjs +55 -0
  37. package/templates/default/eslint.config.mjs.bun +53 -0
  38. package/templates/default/jest.config.json.node +23 -0
  39. package/templates/default/package.json.bun.tpl +39 -0
  40. package/templates/default/package.json.tpl +44 -0
  41. package/templates/default/prisma.config.ts.tpl +14 -0
  42. package/templates/default/public/favicon.ico +0 -0
  43. package/templates/default/public/robots.txt +2 -0
  44. package/templates/default/src/features/index.ts.tpl +0 -0
  45. package/templates/default/src/main.ts.tpl +7 -0
  46. package/templates/default/src/resources/user/model.ts.tpl +28 -0
  47. package/templates/default/src/resources/user/policy.ts.tpl +3 -0
  48. package/templates/default/src/resources/user/routes.ts.tpl +3 -0
  49. package/templates/default/src/resources/user/service.ts.tpl +16 -0
  50. package/templates/default/src/types/generated.ts.tpl +1 -0
  51. package/templates/default/src/types/index.ts.tpl +1 -0
  52. package/templates/default/start.sh +26 -0
  53. package/templates/default/start.sh.bun +26 -0
  54. package/templates/default/swc.config.json.node +13 -0
  55. package/templates/default/test/e2e/jest.e2e-config.json.node +22 -0
  56. package/templates/default/test/e2e/main.test.ts.tpl +24 -0
  57. package/templates/default/test/e2e/support/each.ts.tpl +13 -0
  58. package/templates/default/test/e2e/support/preload.ts.bun +13 -0
  59. package/templates/default/test/e2e/support/setup.ts.tpl +13 -0
  60. package/templates/default/test/e2e/support/teardown.ts.tpl +13 -0
  61. package/templates/default/test/unit/sample.test.ts.tpl +5 -0
  62. package/templates/default/tsconfig.build.json +10 -0
  63. package/templates/default/tsconfig.json +27 -0
  64. package/templates/default/tsconfig.json.bun +28 -0
@@ -0,0 +1,251 @@
1
+ # Storage
2
+
3
+ The storage module handles file persistence: storing, streaming, and deleting binary content. The default
4
+ implementation (`FilesystemStorage`) writes files to a local directory using a configurable name pattern. The module
5
+ supports range-based streaming for efficient large-file delivery (e.g., video, audio).
6
+
7
+ ## Injecting Storage
8
+
9
+ ```ts
10
+ import { inject } from '@appweaver/core';
11
+ import { Storage } from '@appweaver/common';
12
+
13
+ const storage = inject(Storage);
14
+ ```
15
+
16
+ ---
17
+
18
+ #### `storage.store(fileName, data)`
19
+
20
+ Persists a readable stream under the given file name. Returns the stored file's path/ID (derived from
21
+ `STORAGE_NAME_PATTERN`), or `null` on failure.
22
+
23
+ | Parameter | Type | Description |
24
+ |------------|------------|--------------------------------------------|
25
+ | `fileName` | `string` | Original file name (used for name pattern) |
26
+ | `data` | `Readable` | Node.js readable stream with the content |
27
+
28
+ ```ts
29
+ import { createReadStream } from 'node:fs';
30
+
31
+ const fileId = await storage.store('avatar.png', createReadStream('/tmp/upload.png'));
32
+ // fileId e.g. "avatar-a3f8bc12.png"
33
+ ```
34
+
35
+ **From a multipart upload (Fastify example):**
36
+
37
+ ```ts
38
+ const upload = await req.file();
39
+ const fileId = await storage.store(upload.filename, upload.file);
40
+ ```
41
+
42
+ ---
43
+
44
+ #### `storage.stream(fileName, start, end?)`
45
+
46
+ Returns a `ContentStream` for the stored file, or `null` if the file does not exist. Supports range requests.
47
+
48
+ | Parameter | Type | Description |
49
+ |------------|----------|--------------------------------------------|
50
+ | `fileName` | `string` | File ID returned by `store` |
51
+ | `start` | `number` | Byte offset to start reading from |
52
+ | `end` | `number` | Byte offset to stop reading at (inclusive) |
53
+
54
+ ```ts
55
+ type ContentStream = {
56
+ stream: Readable;
57
+ size: number; // total file size in bytes
58
+ };
59
+ ```
60
+
61
+ **Full file download:**
62
+
63
+ ```ts
64
+ const result = await storage.stream('avatar-a3f8bc12.png', 0);
65
+ if (!result) throw new Error('File not found');
66
+
67
+ reply.header('Content-Length', result.size);
68
+ reply.send(result.stream);
69
+ ```
70
+
71
+ **Range request (e.g., video seeking):**
72
+
73
+ ```ts
74
+ const { start, end } = parseRangeHeader(req.headers.range, fileSize);
75
+ const result = await storage.stream(fileId, start, end);
76
+
77
+ reply
78
+ .status(206)
79
+ .header('Content-Range', `bytes ${start}-${end}/${fileSize}`)
80
+ .send(result.stream);
81
+ ```
82
+
83
+ ---
84
+
85
+ #### `storage.exists(fileName)`
86
+
87
+ Returns `true` if the file exists in storage.
88
+
89
+ ```ts
90
+ if (await storage.exists('avatar-a3f8bc12.png')) {
91
+ // safe to stream
92
+ }
93
+ ```
94
+
95
+ ---
96
+
97
+ #### `storage.delete(fileName)`
98
+
99
+ Deletes the stored file. Returns `true` if the file existed and was removed.
100
+
101
+ ```ts
102
+ await storage.delete('avatar-a3f8bc12.png');
103
+ ```
104
+
105
+ ---
106
+
107
+ #### `storage.checkHealth()`
108
+
109
+ Verifies that the storage directory is readable and writable. Returns a `HealthCheckResult`.
110
+
111
+ ---
112
+
113
+ ## File name pattern
114
+
115
+ Stored files are renamed according to `STORAGE_NAME_PATTERN`. The default pattern is `{name}-{hash}.{extension}`, which
116
+ produces collision-resistant names while preserving the original extension.
117
+
118
+ Available placeholders:
119
+
120
+ | Placeholder | Resolved to |
121
+ |---------------|-----------------------------|
122
+ | `{name}` | Base name without extension |
123
+ | `{hash}` | Short content hash |
124
+ | `{extension}` | Original file extension |
125
+
126
+ Example: storing `photo.jpg` produces `photo-c7d3a1f2.jpg`.
127
+
128
+ ---
129
+
130
+ ## Configuration
131
+
132
+ | Key | Type | Default | Description |
133
+ |------------------------------|----------|------------------------------------------------|-------------------------------------------------------|
134
+ | `STORAGE_PATH` | `string` | `'./storage'` | Root directory where files are stored |
135
+ | `STORAGE_NAME_PATTERN` | `string` | `'{name}-{hash}.{extension}'` | Template for deriving the stored file name |
136
+ | `STORAGE_CACHE_TTL` | `int` | `86400000` | Cache TTL for storage responses in milliseconds (24h) |
137
+ | `STORAGE_FILES_ROUTE_PREFIX` | `string` | `/files` | URL prefix for file access routes. |
138
+ | `STORAGE_PROVIDER` | `string` | `'@appweaver/core/storage/filesystem-storage'` | Path to the Storage implementation |
139
+
140
+ **`appweaver.json` example:**
141
+
142
+ ```json
143
+ {
144
+ "STORAGE_PATH": "./uploads",
145
+ "STORAGE_NAME_PATTERN": "{name}-{hash}.{extension}"
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Real-world example
152
+
153
+ ```ts
154
+ import { inject } from '@appweaver/core';
155
+ import { Storage } from '@appweaver/common';
156
+
157
+ export class AvatarService {
158
+ private readonly _storage = inject(Storage);
159
+
160
+ async upload(userId: number, stream: Readable, originalName: string): Promise<string> {
161
+ const fileId = await this._storage.store(originalName, stream);
162
+ if (!fileId) throw new Error('Upload failed');
163
+
164
+ await saveAvatarRecord(userId, fileId);
165
+ return fileId;
166
+ }
167
+
168
+ async download(fileId: string): Promise<{ stream: Readable; size: number }> {
169
+ const result = await this._storage.stream(fileId, 0);
170
+ if (!result) throw new Error('File not found');
171
+ return result;
172
+ }
173
+
174
+ async remove(fileId: string): Promise<void> {
175
+ await this._storage.delete(fileId);
176
+ await deleteAvatarRecord(fileId);
177
+ }
178
+ }
179
+ ```
180
+
181
+ ---
182
+
183
+ ## FileService usage
184
+
185
+ The `FileService` provides higher-level file management specifically for **Resource Models**. Unlike the raw `Storage`
186
+ module, it automatically manages the database records (`File` entries) and enforces security policies defined on your
187
+ models.
188
+
189
+ ### Injecting FileService
190
+
191
+ ```ts
192
+ import { inject } from '@appweaver/core';
193
+ import { FileService } from '@appweaver/core/storage';
194
+
195
+ const fileService = inject(FileService);
196
+ ```
197
+
198
+ ### Saving a file
199
+
200
+ When saving a file via `FileService`, you must provide the multipart data, the resource instance it belongs to, and its
201
+ `ResourceClient`.
202
+
203
+ ```ts
204
+ import { inject, injectService, injectModel } from '@appweaver/core';
205
+ import { FileService } from '@appweaver/core/storage';
206
+
207
+ export class PostService {
208
+ private readonly _fileService = inject(FileService);
209
+ private readonly _postService = injectService('Post');
210
+ private readonly _postClient = injectModel('Post');
211
+
212
+ async uploadImage(postId: number, data: MultipartFile) {
213
+ const post = await this._postService.find(postId);
214
+
215
+ // saveFile stores the file in Storage AND creates a File record in the DB
216
+ // linked to the 'image' field of the 'post' resource.
217
+ const file = await this._fileService.saveFile(data, post, this._postClient);
218
+
219
+ return file;
220
+ }
221
+ }
222
+ ```
223
+
224
+ ### Streaming a file
225
+
226
+ `FileService.stream()` handles authorization checks (public, private, or protected) and range-based requests
227
+ automatically.
228
+
229
+ ```ts
230
+ // Streams a file while checking if the current user has access
231
+ const fileStream = await fileService.stream('photo-c7d3a1f2.jpg', req.headers.range);
232
+
233
+ reply
234
+ .status(fileStream.start > 0 ? 206 : 200)
235
+ .header('Content-Type', fileStream.mimeType)
236
+ .header('Content-Disposition', `attachment; filename="${fileStream.fileName}"`)
237
+ .send(fileStream.content.stream);
238
+ ```
239
+
240
+ ### Deleting a file
241
+
242
+ Deleting a file via `FileService` removes it from both the storage backend and the database.
243
+
244
+ ```ts
245
+ await fileService.deleteFile(
246
+ 'photo-c7d3a1f2.jpg',
247
+ 'image', // resource field name
248
+ post, // resource instance
249
+ postClient // ResourceClient
250
+ );
251
+ ```
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ storage
4
+ logs
5
+ .env
@@ -0,0 +1 @@
1
+ NODE_ENV=dev
@@ -0,0 +1,3 @@
1
+ **/dist
2
+ **/node_modules
3
+ **/database/client
@@ -0,0 +1,7 @@
1
+ {
2
+ "endOfLine": "lf",
3
+ "singleQuote": true,
4
+ "trailingComma": "none",
5
+ "tabWidth": 2,
6
+ "printWidth": 80
7
+ }
@@ -0,0 +1,56 @@
1
+ ## 1. Install dependencies and build the application
2
+ FROM node:24.13.0-alpine AS build
3
+
4
+ # Install tools for building libraries on other platforms
5
+ RUN apk add build-base python3 py3-pip make gcc
6
+
7
+ # Create app directory
8
+ WORKDIR /usr/app
9
+
10
+ # Copy project files
11
+ COPY ../.. .
12
+
13
+ # Install NPM dependencies
14
+ RUN npm ci
15
+
16
+ # Build project
17
+ RUN npm run build
18
+
19
+
20
+ ## 2. Setup server environment, copy build output and start the application script
21
+ FROM node:24.13.0-alpine AS deploy
22
+
23
+ # Install tools for building libraries on other platforms
24
+ RUN apk add build-base python3 py3-pip make gcc
25
+
26
+ # Create app directory
27
+ WORKDIR /usr/app
28
+
29
+ # Copy NPM package
30
+ COPY --from=build /usr/app/package*.json /usr/app/
31
+
32
+ # Install NPM dependencies for production
33
+ RUN npm ci --omit=dev
34
+
35
+ # Copy build files
36
+ COPY --from=build /usr/app/dist /usr/app/dist
37
+
38
+ # Copy Prisma config, database migrations, and schema file
39
+ COPY --from=build /usr/app/prisma.config.ts /usr/app/prisma.config.ts
40
+ COPY --from=build /usr/app/database/migrations /usr/app/database/migrations
41
+ COPY --from=build /usr/app/database/schema.prisma /usr/app/database/schema.prisma
42
+
43
+ # Copy config file/s
44
+ COPY --from=build /usr/app/appweaver*.json /usr/app/
45
+
46
+ # Copy start script
47
+ COPY --from=build /usr/app/start.sh /usr/app/start.sh
48
+
49
+ # Make start script executable
50
+ RUN chmod +x /usr/app/start.sh
51
+
52
+ # Create storage and logs directories
53
+ RUN mkdir storage logs
54
+
55
+ # Start container
56
+ ENTRYPOINT ["/usr/app/start.sh"]
@@ -0,0 +1,56 @@
1
+ ## 1. Install dependencies and build the application
2
+ FROM oven/bun:1.3.11-alpine AS build
3
+
4
+ # Install tools for building libraries on other platforms
5
+ RUN apk add build-base python3 py3-pip make gcc
6
+
7
+ # Create app directory
8
+ WORKDIR /usr/app
9
+
10
+ # Copy project files
11
+ COPY ../.. .
12
+
13
+ # Install NPM dependencies
14
+ RUN bun install --frozen-lockfile
15
+
16
+ # Build project
17
+ RUN bun run build
18
+
19
+
20
+ ## 2. Setup server environment, copy build output and start the application script
21
+ FROM oven/bun:1.3.11-alpine AS deploy
22
+
23
+ # Install tools for building libraries on other platforms
24
+ RUN apk add build-base python3 py3-pip make gcc
25
+
26
+ # Create app directory
27
+ WORKDIR /usr/app
28
+
29
+ # Copy NPM package
30
+ COPY --from=build /usr/app/package*.json /usr/app/
31
+
32
+ # Install NPM dependencies for production
33
+ RUN bun install --frozen-lockfile --production
34
+
35
+ # Copy build files
36
+ COPY --from=build /usr/app/dist /usr/app/dist
37
+
38
+ # Copy Prisma config, database migrations, and schema file
39
+ COPY --from=build /usr/app/prisma.config.ts /usr/app/prisma.config.ts
40
+ COPY --from=build /usr/app/database/migrations /usr/app/database/migrations
41
+ COPY --from=build /usr/app/database/schema.prisma /usr/app/database/schema.prisma
42
+
43
+ # Copy config file/s
44
+ COPY --from=build /usr/app/appweaver*.json /usr/app/
45
+
46
+ # Copy start script
47
+ COPY --from=build /usr/app/start.sh /usr/app/start.sh
48
+
49
+ # Make start script executable
50
+ RUN chmod +x /usr/app/start.sh
51
+
52
+ # Create storage and logs directories
53
+ RUN mkdir storage logs
54
+
55
+ # Start container
56
+ ENTRYPOINT ["/usr/app/start.sh"]
@@ -0,0 +1,7 @@
1
+ # {{NAME}}
2
+
3
+ > {{DESCRIPTION}}
4
+
5
+ ## License
6
+
7
+ UNLICENSED
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "./node_modules/@appweaver/common/config/schema.json",
3
+ "config": {
4
+ "log": {
5
+ "level": "debug",
6
+ "pretty": true
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "./node_modules/@appweaver/common/config/schema.json",
3
+ "config": {
4
+ "app": {
5
+ "name": "{{NAME}}",
6
+ "description": "{{DESCRIPTION}}",
7
+ "runtime": "bun"
8
+ },
9
+ "server": {
10
+ "host": "{{HOST}}",
11
+ "port": {{PORT}}
12
+ },
13
+ "database": {
14
+ "url": "{{DATABASE_URL}}"
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "./node_modules/@appweaver/common/config/schema.json",
3
+ "config": {
4
+ "app": {
5
+ "name": "{{NAME}}",
6
+ "description": "{{DESCRIPTION}}"
7
+ },
8
+ "server": {
9
+ "host": "{{HOST}}",
10
+ "port": {{PORT}}
11
+ },
12
+ "database": {
13
+ "url": "{{DATABASE_URL}}"
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "./node_modules/@appweaver/common/config/schema.json",
3
+ "config": {
4
+ "log": {
5
+ "level": "error",
6
+ "pretty": true
7
+ },
8
+ "database": {
9
+ "url": "{{DATABASE_TEST_URL}}",
10
+ "schemaPath": "./temp/database/schema.prisma",
11
+ "migrationsDirPath": "./temp/database/migrations",
12
+ "clientOutputDirPath": "./temp/database/client"
13
+ },
14
+ "storage": {
15
+ "path": "./temp/storage"
16
+ },
17
+ "redis": {
18
+ "provider": "@appweaver/core/memory/in-memory"
19
+ },
20
+ "queue": {
21
+ "provider": "@appweaver/core/queue/memory-queue"
22
+ },
23
+ "mailer": {
24
+ "provider": "@appweaver/core/mailer/json-mailer"
25
+ },
26
+ "scheduler": {
27
+ "autoStartJob": false
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,8 @@
1
+ [install]
2
+ linker = "isolated"
3
+
4
+ [test]
5
+ root = "test"
6
+ coverageReporter = ["text", "lcov"]
7
+ coverageSkipTestFiles = true
8
+ coverageDir = "./reports"
@@ -0,0 +1,7 @@
1
+ import { inject } from '@appweaver/core';
2
+ import { Database } from '@appweaver/common';
3
+ import { PrismaClient } from '@db/client/client';
4
+
5
+ export const db = inject(Database).client<PrismaClient>();
6
+
7
+ export default db;
@@ -0,0 +1,13 @@
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ // Generated by Appweaver. Please do not edit this file manually.
5
+
6
+ datasource db {
7
+ provider = "sqlite"
8
+ }
9
+
10
+ generator client {
11
+ provider = "prisma-client"
12
+ output = "./client"
13
+ }
@@ -0,0 +1,39 @@
1
+ import { hashPassword } from '@appweaver/core';
2
+ import { config, randomString } from '@appweaver/common';
3
+ import { db } from '@db/client';
4
+
5
+ export async function createAdminUser(): Promise<void> {
6
+ let password = config.SYSTEM_ADMIN_INITIAL_PASSWORD;
7
+
8
+ if (!password) {
9
+ password = randomString(16, { extra: false });
10
+ console.log(`Generated admin password: ${password}`);
11
+ }
12
+
13
+ const passwordHash = await hashPassword(password);
14
+
15
+ await db.user.create({
16
+ data: {
17
+ firstName: 'Admin',
18
+ lastName: 'Admin',
19
+ email: config.SYSTEM_ADMIN_INITIAL_EMAIL,
20
+ passwordHash,
21
+ roles: {
22
+ connectOrCreate: [
23
+ {
24
+ where: { name: 'Admin' },
25
+ create: {
26
+ name: 'Admin',
27
+ permissions: {
28
+ connectOrCreate: [
29
+ { where: { name: '*.read' }, create: { name: '*.read' } },
30
+ { where: { name: '*.write' }, create: { name: '*.write' } }
31
+ ]
32
+ }
33
+ }
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ });
39
+ }
@@ -0,0 +1,55 @@
1
+ import eslint from '@eslint/js';
2
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
3
+ import eslintPluginJest from 'eslint-plugin-jest';
4
+ import globals from 'globals';
5
+ import tsEslint from 'typescript-eslint';
6
+
7
+ export default tsEslint.config(
8
+ {
9
+ ignores: [
10
+ 'node_modules',
11
+ 'dist',
12
+ 'database/client',
13
+ '**/*.js',
14
+ '**/*.d.ts'
15
+ ]
16
+ },
17
+ eslint.configs.recommended,
18
+ ...tsEslint.configs.recommendedTypeChecked.map((config) => ({ ...config })),
19
+ eslintPluginPrettierRecommended,
20
+ eslintPluginJest.configs['flat/recommended'],
21
+ {
22
+ languageOptions: {
23
+ globals: {
24
+ ...globals.node,
25
+ ...globals.jest
26
+ },
27
+ ecmaVersion: 5,
28
+ sourceType: 'module',
29
+ parserOptions: {
30
+ projectService: true,
31
+ tsconfigRootDir: import.meta.dirname
32
+ }
33
+ }
34
+ },
35
+ {
36
+ rules: {
37
+ '@typescript-eslint/interface-name-prefix': 'off',
38
+ '@typescript-eslint/explicit-function-return-type': 'off',
39
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
40
+ '@typescript-eslint/restrict-template-expressions': 'off',
41
+ '@typescript-eslint/require-await': 'off',
42
+ '@typescript-eslint/unbound-method': 'off',
43
+ '@typescript-eslint/no-redundant-type-constituents': 'off',
44
+ '@typescript-eslint/no-misused-promises': 'off',
45
+ '@typescript-eslint/no-explicit-any': 'off',
46
+ '@typescript-eslint/no-empty-object-type': 'off',
47
+ '@typescript-eslint/no-unused-vars': 'off',
48
+ '@typescript-eslint/no-unsafe-member-access': 'off',
49
+ '@typescript-eslint/no-unsafe-assignment': 'off',
50
+ '@typescript-eslint/no-unsafe-argument': 'off',
51
+ '@typescript-eslint/no-unsafe-call': 'off',
52
+ '@typescript-eslint/no-unsafe-return': 'off'
53
+ }
54
+ }
55
+ );
@@ -0,0 +1,53 @@
1
+ import eslint from '@eslint/js';
2
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
3
+ import globals from 'globals';
4
+ import tsEslint from 'typescript-eslint';
5
+
6
+ export default tsEslint.config(
7
+ {
8
+ ignores: [
9
+ 'node_modules',
10
+ 'dist',
11
+ 'database/client',
12
+ '**/*.js',
13
+ '**/*.d.ts'
14
+ ]
15
+ },
16
+ eslint.configs.recommended,
17
+ ...tsEslint.configs.recommendedTypeChecked.map((config) => ({ ...config })),
18
+ eslintPluginPrettierRecommended,
19
+ {
20
+ languageOptions: {
21
+ globals: {
22
+ ...globals.node,
23
+ ...globals.bunBuiltin
24
+ },
25
+ ecmaVersion: 5,
26
+ sourceType: 'module',
27
+ parserOptions: {
28
+ projectService: true,
29
+ tsconfigRootDir: import.meta.dirname
30
+ }
31
+ }
32
+ },
33
+ {
34
+ rules: {
35
+ '@typescript-eslint/interface-name-prefix': 'off',
36
+ '@typescript-eslint/explicit-function-return-type': 'off',
37
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
38
+ '@typescript-eslint/restrict-template-expressions': 'off',
39
+ '@typescript-eslint/require-await': 'off',
40
+ '@typescript-eslint/unbound-method': 'off',
41
+ '@typescript-eslint/no-redundant-type-constituents': 'off',
42
+ '@typescript-eslint/no-misused-promises': 'off',
43
+ '@typescript-eslint/no-explicit-any': 'off',
44
+ '@typescript-eslint/no-empty-object-type': 'off',
45
+ '@typescript-eslint/no-unused-vars': 'off',
46
+ '@typescript-eslint/no-unsafe-member-access': 'off',
47
+ '@typescript-eslint/no-unsafe-assignment': 'off',
48
+ '@typescript-eslint/no-unsafe-argument': 'off',
49
+ '@typescript-eslint/no-unsafe-call': 'off',
50
+ '@typescript-eslint/no-unsafe-return': 'off'
51
+ }
52
+ }
53
+ );
@@ -0,0 +1,23 @@
1
+ {
2
+ "moduleFileExtensions": ["js", "json", "ts"],
3
+ "rootDir": ".",
4
+ "testEnvironment": "node",
5
+ "testMatch": ["**/?(*.)+(spec|test).ts"],
6
+ "testPathIgnorePatterns": ["/test/e2e/"],
7
+ "transform": {
8
+ "^.+\\.(t|j)s$": ["@swc/jest"]
9
+ },
10
+ "reporters": [
11
+ "default",
12
+ [
13
+ "jest-junit",
14
+ {
15
+ "outputDirectory": "./reports",
16
+ "outputName": "junit.xml"
17
+ }
18
+ ]
19
+ ],
20
+ "coverageReporters": ["cobertura", "text"],
21
+ "collectCoverageFrom": ["./src/**/*.(t|j)s"],
22
+ "coverageDirectory": "./reports"
23
+ }