@nano-app/backend 0.8.3 → 0.9.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
@@ -46,7 +46,7 @@ export default createRoute({
46
46
 
47
47
  ## Routes
48
48
 
49
- Each `*.route.ts` under `mainPlugin.routesDir` default-exports an object with the HTTP methods it implements. **The file path is the URL** (`index.route.ts` → `/`, `api/users.route.ts` → `/api/users`). `expression` is appended to that path as is, in Express syntax (`/:id`, `{/:id}` for optional):
49
+ Each `*.route.ts` under `mainPlugin.routesDir` default-exports an object with the HTTP methods it implements (`GET`, `POST`, `PUT`, `DELETE`, `QUERY`). **The file path is the URL** (`index.route.ts` → `/`, `api/users.route.ts` → `/api/users`). `expression` is appended to that path as is, in Express syntax (`/:id`, `{/:id}` for optional):
50
50
 
51
51
  ```ts
52
52
  export default createRoute({
@@ -98,11 +98,10 @@ createNanoApp({
98
98
 
99
99
  ## Database
100
100
 
101
- Off until enabled: it loads the Prisma client you generated and passes it to your handlers as their second parameter. **It never modifies your database**: generating the client and migrating are up to you, with the Prisma CLI.
101
+ Off until enabled: at startup nano-app generates the Prisma client of your schema, checks that its tables can be read and passes it to your handlers as their second parameter. **It never modifies your database**: applying the schema (`prisma db push`, `prisma migrate dev`, `prisma migrate deploy`) is up to you, with Prisma.
102
102
 
103
103
  ```bash
104
- npm install @prisma/client @prisma/adapter-pg pg
105
- npm install -D prisma
104
+ npm install prisma @prisma/client @prisma/adapter-pg pg # prisma too: the startup generates the client
106
105
  ```
107
106
 
108
107
  ```prisma
@@ -113,7 +112,7 @@ datasource db {
113
112
 
114
113
  generator client {
115
114
  provider = "prisma-client"
116
- output = "./client"
115
+ output = "./client" // any value: nano-app generates in .nano-app/prisma-clients/app
117
116
  }
118
117
 
119
118
  model User {
@@ -122,11 +121,6 @@ model User {
122
121
  }
123
122
  ```
124
123
 
125
- ```bash
126
- npx nano-app generate # client in .nano-app/prisma-clients/app (add .nano-app/ to .gitignore)
127
- npx nano-app push # generate + prisma db push; or any Prisma command: npx nano-app migrate dev …
128
- ```
129
-
130
124
  ```ts
131
125
  // src/app.ts — DATABASE_URL comes from the environment (or a .env file)
132
126
  createNanoApp({ mainPlugin: { database: { enabled: true } } });
@@ -144,18 +138,21 @@ export default createRoute<PrismaClient>({
144
138
 
145
139
  | Option | Default | Description |
146
140
  |---|---|---|
147
- | `mainPlugin.database.enabled` | `false` | Mounts your client at startup. |
148
- | `mainPlugin.database.schemaPath` | `"src/prisma/schema.prisma"` | The single `.prisma` file the client was generated from; its datasource must be `postgresql`. |
149
-
150
- - Each key falls back to its default, so `{ enabled: true }` is enough with the default layout. The client is always read from `.nano-app/prisma-clients/app/`, where `nano-app generate` writes it.
151
- - Startup fails if `DATABASE_URL` is missing or the client cannot be loaded or is not a valid PostgreSQL Prisma client. The connection opens on the first query.
152
- - The `nano_` prefix is reserved for the framework's plugins: a datasource schema starting with it fails the startup.
141
+ | `mainPlugin.database.enabled` | `false` | Generates and mounts your client at startup. |
142
+ | `mainPlugin.database.schemaPath` | `"src/prisma/schema.prisma"` | Your single `.prisma` file; its datasource must be `postgresql`. |
143
+
144
+ - Each key falls back to its default, so `{ enabled: true }` is enough with the default layout.
145
+ - The client lives in `.nano-app/prisma-clients/app/` (add `.nano-app/` to `.gitignore`). It is generated again only when the schema changes. Do not run `prisma generate`: a second client would be out of sync.
146
+ - `npx nano-app generate` does what the startup does, without starting: for your editor's types, or for the build when the filesystem is read-only in production.
147
+ - Startup fails if `DATABASE_URL` is missing, the schema is not a valid PostgreSQL schema, or a table or column of a model cannot be read. The message says which: apply your schema with Prisma and start again.
148
+ - The `nano_` prefix is reserved for the official plugins (`@nano-app/plugins`): a datasource schema of yours starting with it fails the startup.
153
149
  - Pass your generated `PrismaClient` to `createRoute<PrismaClient>` to type `db`; otherwise it is `any`, and without an enabled database `undefined`.
154
150
  - For several PostgreSQL schemas, declare `schemas = ["public", "billing"]` in the datasource and `@@schema("billing")` on each model; one client covers them all.
151
+ - Prisma is loaded only when your database or a mounted plugin's is enabled.
155
152
 
156
153
  ## Plugins
157
154
 
158
- A plugin is a group of endpoints mounted under a base path. Your routes are mounted as one (named `app`). The official plugins are in [`@nano-app/plugins`](https://github.com/Nano-Nathan/nano-app/blob/main/plugins/README.md). You write one with `createPlugin` and list it in `plugins`. Nothing is mounted unless you list it.
155
+ A plugin is a group of endpoints mounted under a base path. Your routes are mounted as one (named `app`). The official plugins are in [`@nano-app/plugins`](https://github.com/Nano-Nathan/nano-app/blob/main/plugins/README.md). You write one with `createPlugin` and list it in `plugins`. Nothing is mounted unless you list it. Official plugins that have tables (like `auth`) ship their own migrations: nano-app applies them at startup, each in its `nano_*` schema. That is the only change it makes to your database.
159
156
 
160
157
  ```ts
161
158
  // src/plugins/hello/index.ts
@@ -216,12 +213,7 @@ The endpoints of one plugin have no guaranteed order: one route's `onInit` must
216
213
 
217
214
  Everything is mounted on one Express application, in this order: base middlewares → `before` plugins → your routes → `after` plugins → 404 → error handler.
218
215
 
219
- A plugin with its own Prisma schema is generated by name. `nano-app generate <name>` reads `plugins/<name>/schema.prisma` from this package when the plugin ships inside it. A plugin you keep in your app points `database.schemaPath` at your own file and you generate that client yourself. See [PLUGINS.md](https://github.com/Nano-Nathan/nano-app/blob/main/PLUGINS.md).
220
-
221
- ```bash
222
- npx nano-app generate # the app schema (src/prisma/schema.prisma) → .nano-app/prisma-clients/app
223
- npx nano-app push # generate + prisma db push
224
- ```
216
+ A plugin with a database follows the same rules, with its client in `.nano-app/prisma-clients/<name>`: a plugin of yours points `database.schemaPath` at its schema and you apply it with Prisma. If that folder holds the client of another schema (another plugin with the same name), the startup warns and replaces it. An official plugin of `@nano-app/plugins` needs nothing from you: at startup nano-app applies its migrations to its own `nano_*` schema. See [PLUGINS.md](https://github.com/Nano-Nathan/nano-app/blob/main/PLUGINS.md).
225
217
 
226
218
  ## Errors
227
219
 
@@ -248,8 +240,8 @@ Also exported: the configuration types (`NanoAppConfig`, `PackagesConfig`, `Plug
248
240
  ## Development
249
241
 
250
242
  ```bash
251
- pnpm build # tsup → build/ (ESM + .d.ts)
252
- pnpm dev # tsup --watch
243
+ pnpm build # tsdown → build/ (ESM + .d.ts)
244
+ pnpm dev # tsdown --watch
253
245
  pnpm test # node:test against the real server (tsx --test)
254
246
  DATABASE_URL=postgres://… pnpm test # also runs the PostgreSQL suite
255
247
  ```
@@ -0,0 +1,221 @@
1
+ import { createRequire } from "node:module";
2
+ import { glob } from "glob";
3
+ import { dirname, isAbsolute, join, resolve } from "node:path";
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { createJiti } from "jiti";
6
+ import { execFileSync } from "node:child_process";
7
+ import { createHash } from "node:crypto";
8
+ //#region ../core/loader/Validator.ts
9
+ const HTTP_METHODS = [
10
+ "GET",
11
+ "POST",
12
+ "PUT",
13
+ "DELETE",
14
+ "QUERY"
15
+ ];
16
+ /** Checks the consumer's files: invalid ones are warned about and discarded */
17
+ var Validator = class Validator {
18
+ static oValidators = {
19
+ route: () => this.isRoute,
20
+ prisma: () => this.isPrisma
21
+ };
22
+ /** Warns about an invalid file and returns `false`, to discard it */
23
+ static warn(sFilePath, sMessage) {
24
+ console.warn(`[nano-app] [WARNING] ${sFilePath}: ${sMessage}`);
25
+ return false;
26
+ }
27
+ /** Checks that the default export is an object */
28
+ static isObject(oEntry, sFilePath) {
29
+ if (oEntry && typeof oEntry === "object") return true;
30
+ return Validator.warn(sFilePath, "the default export is not an object.");
31
+ }
32
+ /**
33
+ * Type guard for a `*.route.ts` entry.
34
+ */
35
+ static isRoute(oEntry, sFilePath) {
36
+ if (!Validator.isObject(oEntry, sFilePath)) return false;
37
+ const oRoute = oEntry;
38
+ if (oRoute.expression && typeof oRoute.expression !== "string") return Validator.warn(sFilePath, "\"expression\" must be a string.");
39
+ for (const sMethod of HTTP_METHODS) if (oRoute[sMethod] && typeof oRoute[sMethod] !== "function") return Validator.warn(sFilePath, `"${String(sMethod)}" must be a function.`);
40
+ if (!HTTP_METHODS.some((sMethod) => oRoute[sMethod])) return Validator.warn(sFilePath, "defines no HTTP method.");
41
+ return true;
42
+ }
43
+ /**
44
+ * Type guard for a `.prisma` entry: the contents of the file, without comments.
45
+ */
46
+ static isPrisma(oEntry, sFilePath) {
47
+ if (typeof oEntry !== "string") return Validator.warn(sFilePath, "the contents are not a string.");
48
+ const sProvider = oEntry.match(/datasource\s+\w+\s*\{[^}]*provider\s*=\s*["']([^"']+)["']/s)?.[1];
49
+ if (sProvider !== "postgresql") return Validator.warn(sFilePath, `the datasource provider must be "postgresql"${sProvider ? `, not "${sProvider}"` : ""}.`);
50
+ return true;
51
+ }
52
+ /** Validates a full map of entries and returns only the valid ones */
53
+ static validate(oEntries, sExtension) {
54
+ const oValid = {};
55
+ for (const [sFilePath, oEntry] of Object.entries(oEntries)) {
56
+ const oValidated = this.validateUnique(oEntry, sExtension, sFilePath);
57
+ if (oValidated !== void 0) oValid[sFilePath] = oValidated;
58
+ }
59
+ return oValid;
60
+ }
61
+ /** Validates one entry: returns it when valid, `undefined` otherwise */
62
+ static validateUnique(oEntry, sExtension, sFilePath = "") {
63
+ return this.oValidators[sExtension]?.()(oEntry, sFilePath) ? oEntry : void 0;
64
+ }
65
+ };
66
+ //#endregion
67
+ //#region ../core/loader/FileReader.ts
68
+ /** Reads and loads files from disk, resolved from `cwd` */
69
+ var FileReader = class {
70
+ static oJiti = createJiti(import.meta.url, { nativeModules: ["@prisma/client"] });
71
+ /** Loads a module via jiti */
72
+ static async loadModule(sFilePath, oOptions = {}) {
73
+ return await this.oJiti.import(this.getAbsolutePath(sFilePath), oOptions);
74
+ }
75
+ /** Reads the contents of a file */
76
+ static loadFile(sFilePath) {
77
+ const sContent = readFileSync(this.getAbsolutePath(sFilePath), "utf-8");
78
+ if (!sContent) throw new Error(`[nano-app] [ERROR] The file ${sFilePath} does not have valid content.`);
79
+ return sContent;
80
+ }
81
+ /** Reads a Prisma schema without its `//` comments */
82
+ static loadSchema(sFilePath) {
83
+ return this.loadFile(sFilePath).replace(/\/\/.*$/gm, "");
84
+ }
85
+ /** Resolves the absolute path of a file */
86
+ static getAbsolutePath(sFilePath) {
87
+ const sAbsolutePath = isAbsolute(sFilePath) ? sFilePath : resolve(process.cwd(), sFilePath);
88
+ if (!existsSync(sAbsolutePath)) throw new Error(`[nano-app] [ERROR] ${sFilePath} not found.`);
89
+ return sAbsolutePath;
90
+ }
91
+ };
92
+ //#endregion
93
+ //#region ../core/utils/Formatter.ts
94
+ /** Formats values for logs and messages */
95
+ var Formatter = class {
96
+ /** Message of anything thrown: its `message` when it has one, else the value itself */
97
+ static error(oError) {
98
+ return String(oError?.message ?? oError);
99
+ }
100
+ /** Formats a date for the request log, in UTC: `2026-10-19 12:03:10` */
101
+ static logDate(oDate = /* @__PURE__ */ new Date()) {
102
+ return oDate.toISOString().slice(0, 19).replace("T", " ");
103
+ }
104
+ };
105
+ //#endregion
106
+ //#region ../core/loader/FileLoader.ts
107
+ /** Finds, loads and validates the consumer's files */
108
+ var FileLoader = class {
109
+ /**
110
+ * 1. Validates a `.prisma` file.
111
+ * 2. Loads a generated Prisma client module (`client.ts`, or `client.js`)
112
+ */
113
+ static async findPrismaClient(sClientPath, sSchemaPath) {
114
+ const sContent = FileReader.loadSchema(sSchemaPath);
115
+ if (!Validator.validateUnique(sContent, "prisma", sSchemaPath)) throw new Error(`[nano-app] [ERROR] ${sSchemaPath} is not a valid Prisma schema.`);
116
+ try {
117
+ return (await FileReader.loadModule(sClientPath))?.PrismaClient;
118
+ } catch (oError) {
119
+ throw new Error(`[nano-app] [ERROR] The Prisma client ${sClientPath} could not be loaded: ${Formatter.error(oError)}`);
120
+ }
121
+ }
122
+ /** Finds `*.route.ts` (or built `.js`) files and returns `{ [subPath]: defaultExport }` */
123
+ static async findMany({ extension, sourceDir }) {
124
+ const sSrcDir = (sourceDir ?? `src/${extension}s`).replace(/^\.\//, "").replace(/\/$/, "");
125
+ const aFilePaths = await glob(`${sSrcDir}/**/*.${extension}.{ts,js}`, {
126
+ posix: true,
127
+ ignore: "**/*.d.ts"
128
+ });
129
+ const oEntries = {};
130
+ for (const sFilePath of aFilePaths) {
131
+ const sFileSubPath = sFilePath.slice(sSrcDir.length).replace(new RegExp(`\\.${extension}\\.(ts|js)$`), "");
132
+ oEntries[sFileSubPath] = await FileReader.loadModule(sFilePath, { default: true });
133
+ }
134
+ return Validator.validate(oEntries, extension);
135
+ }
136
+ };
137
+ //#endregion
138
+ //#region const.ts
139
+ /** Package of the official plugins: only its schemas may declare nano_* */
140
+ const OFFICIAL_PLUGINS = "@nano-app/plugins";
141
+ /** Error when the database URL is missing, at startup and on `nano-app push` */
142
+ const MISSING_DATABASE_URL = "[nano-app] [ERROR] The DATABASE_URL environment variable is not defined.";
143
+ //#endregion
144
+ //#region src/database/Prisma.ts
145
+ /** Runs the consumer's Prisma CLI: generates clients and applies official plugins' migrations */
146
+ var Prisma = class {
147
+ /** Folder of a client and its files, in the project */
148
+ static dir(sName) {
149
+ return join(process.cwd(), ".nano-app", "prisma-clients", sName);
150
+ }
151
+ /** Generates the client of a schema in `.nano-app/prisma-clients/<name>`, unless it is up to date */
152
+ static generate(sName, sSchemaPath) {
153
+ const sDir = this.dir(sName);
154
+ const sSource = FileReader.getAbsolutePath(sSchemaPath);
155
+ const sHash = createHash("sha256").update(readFileSync(sSource)).digest("hex");
156
+ const sSourceFile = join(sDir, "source.json");
157
+ const oCurrent = existsSync(sSourceFile) ? JSON.parse(readFileSync(sSourceFile, "utf-8")) : void 0;
158
+ if (oCurrent && oCurrent.schemaPath !== sSource) console.warn(`[nano-app] [WARNING] .nano-app/prisma-clients/${sName} has the client of ${oCurrent.schemaPath}: it is replaced by the one of ${sSource}.`);
159
+ if (oCurrent?.schemaPath === sSource && oCurrent.hash === sHash && existsSync(join(sDir, "generated", "client.ts"))) return;
160
+ mkdirSync(sDir, { recursive: true });
161
+ const sSchema = readFileSync(sSource, "utf-8");
162
+ writeFileSync(join(sDir, "schema.prisma"), sSchema.replace(/output\s*=\s*"[^"]*"/, "output = \"./generated\""));
163
+ this.run(sName, [
164
+ "generate",
165
+ "--schema",
166
+ join(sDir, "schema.prisma")
167
+ ]);
168
+ writeFileSync(sSourceFile, JSON.stringify({
169
+ schemaPath: sSource,
170
+ hash: sHash
171
+ }));
172
+ console.log(`[nano-app] [INFO] Prisma client "${sName}" generated`);
173
+ }
174
+ /** Applies the pending migrations an official plugin ships next to its schema */
175
+ static migrate(sName, sSchemaPath, sDbSchema) {
176
+ const sMigrations = join(dirname(FileReader.getAbsolutePath(sSchemaPath)), "migrations");
177
+ if (!existsSync(sMigrations)) throw new Error(`[nano-app] [ERROR] The plugin "${sName}" ships no migrations: ${sMigrations}`);
178
+ if (!process.env.DATABASE_URL) throw new Error(MISSING_DATABASE_URL);
179
+ const oUrl = new URL(process.env.DATABASE_URL);
180
+ oUrl.searchParams.set("schema", sDbSchema);
181
+ const sConfig = join(this.dir(sName), "prisma.config.mjs");
182
+ writeFileSync(sConfig, [
183
+ "export default {",
184
+ ` schema: ${JSON.stringify(join(this.dir(sName), "schema.prisma"))},`,
185
+ ` migrations: { path: ${JSON.stringify(sMigrations)} },`,
186
+ " datasource: { url: process.env.NANO_APP_MIGRATION_URL },",
187
+ "};",
188
+ ""
189
+ ].join("\n"));
190
+ this.run(sName, [
191
+ "migrate",
192
+ "deploy",
193
+ "--config",
194
+ sConfig
195
+ ], { NANO_APP_MIGRATION_URL: oUrl.toString() });
196
+ }
197
+ /** Runs a Prisma command of the consumer's `prisma` package */
198
+ static run(sName, aArgs, oEnv = {}) {
199
+ let sCli;
200
+ try {
201
+ sCli = createRequire(`${process.cwd()}/`).resolve("prisma/build/index.js");
202
+ } catch {
203
+ throw new Error(`[nano-app] [ERROR] The database "${sName}" needs Prisma: install the prisma package.`);
204
+ }
205
+ try {
206
+ execFileSync(process.execPath, [sCli, ...aArgs], {
207
+ env: {
208
+ ...process.env,
209
+ ...oEnv
210
+ },
211
+ stdio: "pipe"
212
+ });
213
+ } catch (oError) {
214
+ const oFailed = oError;
215
+ const sOutput = `${oFailed.stderr ?? ""}${oFailed.stdout ?? ""}`.trim() || Formatter.error(oError);
216
+ throw new Error(`[nano-app] [ERROR] prisma ${aArgs[0]} failed for "${sName}": ${sOutput}`);
217
+ }
218
+ }
219
+ };
220
+ //#endregion
221
+ export { Formatter as a, FileLoader as i, MISSING_DATABASE_URL as n, FileReader as o, OFFICIAL_PLUGINS as r, Prisma as t };
package/build/index.d.ts CHANGED
@@ -1,175 +1,183 @@
1
- import express, { Request, Express } from 'express';
2
- export { Request } from 'express';
3
- import { Options } from 'express-rate-limit';
4
- import { CorsOptions } from 'cors';
5
-
6
- /** Database client configuration. */
7
- type DatabaseConfig = {
8
- /** Whether the database is enabled. Defaults to false */
9
- enabled?: boolean;
10
- /** Prisma schema file (`.prisma`), relative to `cwd` */
11
- schemaPath: string;
12
- };
13
-
1
+ import express, { Express, Request, Request as Request$1 } from "express";
2
+ import { CorsOptions } from "cors";
3
+ import { Options } from "express-rate-limit";
4
+ import "dotenv/config";
5
+ import "jiti";
6
+ //#region ../core/types/api.d.ts
14
7
  /** API response */
15
8
  type ApiResponse<T = any> = {
16
- success?: boolean;
17
- result?: T;
18
- message?: string;
19
- warning?: boolean;
9
+ success?: boolean;
10
+ result?: T;
11
+ message?: string;
12
+ warning?: boolean;
20
13
  };
21
- type ApiPromise<T = any> = Promise<{
22
- success?: boolean;
23
- result?: T;
24
- message?: string;
25
- warning?: boolean;
26
- }>;
14
+ /** Promise of an API response, what a handler returns */
15
+ type ApiPromise<T = any> = Promise<ApiResponse<T>>;
27
16
  /** Query parameters sent in the `query` header (JSON). */
28
17
  type QueryParams = {
29
- skip?: number;
30
- top?: number;
31
- filter?: NanoObject;
32
- orderby?: Record<string, "asc" | "desc">;
33
- select?: string;
18
+ skip?: number;
19
+ top?: number;
20
+ filter?: NanoObject;
21
+ orderby?: Record<string, 'asc' | 'desc'>;
22
+ select?: string;
34
23
  };
35
-
24
+ //#endregion
25
+ //#region ../core/types/objects.d.ts
36
26
  /** Extra params of handlers and callbacks */
37
27
  type Params<DB = any> = Partial<NanoObject & {
38
- db?: DB;
39
- onBeforeResult?: NanoObject;
28
+ db?: DB;
29
+ onBeforeResult?: NanoObject;
40
30
  }>;
41
31
  /** Endpoint handlers */
42
- type Handler<DB> = (req: Request, params: Params<DB>) => ApiPromise;
32
+ type Handler<DB> = (req: Request$1, params: Params<DB>) => ApiPromise;
43
33
  type Handlers<DB = any> = {
44
- GET?: Handler<DB>;
45
- POST?: Handler<DB>;
46
- PUT?: Handler<DB>;
47
- DELETE?: Handler<DB>;
34
+ GET?: Handler<DB>;
35
+ POST?: Handler<DB>;
36
+ PUT?: Handler<DB>;
37
+ DELETE?: Handler<DB>;
38
+ QUERY?: Handler<DB>;
48
39
  };
49
40
  /** Callbacks */
50
41
  type Callback<DB> = (params: Params<DB>) => Promise<void>;
51
- type CallbackMethod<DB> = (req: Request, method: string, params: Params<DB>) => Promise<NanoObject>;
42
+ type CallbackMethod<DB> = (req: Request$1, method: string, params: Params<DB>) => Promise<NanoObject>;
52
43
  type CallbackMount<DB> = (app: Express, params: Params<DB>) => Promise<void>;
53
44
  /** Callbacks of an endpoint. They never receive the app: what affects the whole app goes in the plugin's onMount */
54
45
  type Callbacks<DB = any> = {
55
- onInit?: Callback<DB>;
56
- onBefore?: CallbackMethod<DB>;
46
+ /** Run once, when the endpoint is mounted */
47
+ onInit?: Callback<DB>;
48
+ /** Run before the endpoint is executed */
49
+ onBefore?: CallbackMethod<DB>;
57
50
  };
58
51
  /** Callbacks of a plugin */
59
52
  type PluginCallbacks<DB = any> = {
60
- onMount?: CallbackMount<DB>;
53
+ /** Run once with the Express app, after its database and before its endpoints (e.g. app-level middlewares) */
54
+ onMount?: CallbackMount<DB>;
61
55
  };
62
56
  /** Entry of a route file (`*.route.ts`) */
63
57
  type RouteEntry<DB = any> = {
64
- expression?: string;
65
- callbacks?: Callbacks<DB>;
58
+ /** Express route expression appended to the path (e.g. `/:id`) */
59
+ expression?: string;
60
+ callbacks?: Callbacks<DB>;
66
61
  } & Handlers<DB>;
62
+ /** Generic object */
67
63
  type NanoObject<T = any> = Record<string, T>;
68
-
64
+ //#endregion
65
+ //#region src/types/database.d.ts
66
+ /** Database client configuration. */
67
+ type DatabaseConfig = {
68
+ /** Whether the database is enabled. Defaults to false */
69
+ enabled?: boolean;
70
+ /** Prisma schema file (`.prisma`), relative to `cwd` */
71
+ schemaPath: string;
72
+ };
73
+ //#endregion
74
+ //#region src/types/plugin.d.ts
69
75
  /** When a plugin is mounted, relative to the consumer routes */
70
- type PluginStage = "before" | "after";
76
+ type PluginStage = 'before' | 'after';
71
77
  /** Plugin configuration: a group of endpoints */
72
78
  type PluginConfig<Options = NanoObject> = {
73
- /** Name of the plugin, same as his database (If have one) */
74
- name: string;
75
- /** Directory used to discover the `*.route.ts` files */
76
- routesDir: string;
77
- /** Base path prepended to every endpoint, e.g. "/auth" (no trailing slash). Defaults to none */
78
- basePath?: string;
79
- /** Mount point relative to the consumer routes. Defaults to "before" */
80
- stage?: PluginStage;
81
- /** Prisma client the plugin needs, mounted before its endpoints */
82
- database?: DatabaseConfig;
83
- /** Plugin options */
84
- options?: Options;
85
- /** Plugin callbacks */
86
- callbacks?: PluginCallbacks;
79
+ /** Name of the plugin, same as his database (If have one) */
80
+ name: string;
81
+ /** Directory used to discover the `*.route.ts` files */
82
+ routesDir: string;
83
+ /** Base path prepended to every endpoint, e.g. "/auth" (no trailing slash). Defaults to none */
84
+ basePath?: string;
85
+ /** Mount point relative to the consumer routes. Defaults to "before" */
86
+ stage?: PluginStage;
87
+ /** Prisma client the plugin needs, mounted before its endpoints */
88
+ database?: DatabaseConfig;
89
+ /** Plugin options */
90
+ options?: Options;
91
+ /** Plugin callbacks */
92
+ callbacks?: PluginCallbacks;
87
93
  };
88
94
  /** The project's plugin: nano-app names it "app" and mounts it between the "before" and "after" plugins */
89
95
  type MainPluginConfig<Options = NanoObject> = {
90
- /** Directory of the project's `*.route.ts` files. Defaults to "src/routes" */
91
- routesDir?: string;
92
- /** Base path prepended to every route, e.g. "/api" (no trailing slash). Defaults to none */
93
- basePath?: string;
94
- /** The project's database */
95
- database?: {
96
- /** Whether the database is enabled. Defaults to false */
97
- enabled?: boolean;
98
- /** Prisma schema file, relative to `cwd`. Defaults to "src/prisma/schema.prisma" */
99
- schemaPath?: string;
100
- };
101
- /** Plugin options */
102
- options?: Options;
103
- /** Plugin callbacks */
104
- callbacks?: PluginCallbacks;
96
+ /** Directory of the project's `*.route.ts` files. Defaults to "src/routes" */
97
+ routesDir?: string;
98
+ /** Base path prepended to every route, e.g. "/api" (no trailing slash). Defaults to none */
99
+ basePath?: string;
100
+ /** The project's database */
101
+ database?: {
102
+ /** Whether the database is enabled. Defaults to false */
103
+ enabled?: boolean;
104
+ /** Prisma schema file, relative to `cwd`. Defaults to "src/prisma/schema.prisma" */
105
+ schemaPath?: string;
106
+ };
107
+ /** Plugin options */
108
+ options?: Options;
109
+ /** Plugin callbacks */
110
+ callbacks?: PluginCallbacks;
105
111
  };
106
-
112
+ //#endregion
113
+ //#region src/types/app.d.ts
107
114
  /** Configuration of the framework's built-in packages */
108
115
  type PackagesConfig = {
109
- /** Morgan configuration */
110
- morgan?: {
111
- /** Morgan tokens */
112
- tokens?: Record<string, any>;
113
- /** Morgan format */
114
- format: string;
115
- };
116
- /** CORS configuration */
117
- cors?: CorsOptions;
118
- /** Helmet configuration */
119
- helmet?: boolean;
120
- /** Compression configuration */
121
- compression?: boolean;
122
- /** Rate-limit configuration */
123
- rateLimit?: Partial<Options>;
124
- /** express.json configuration */
125
- express?: Partial<Parameters<typeof express.json>[0]>;
126
- /** urlencoded configuration */
127
- urlencoded?: Partial<Parameters<typeof express.urlencoded>[0]>;
116
+ /** Morgan configuration */
117
+ morgan?: false | {
118
+ /** Morgan tokens */
119
+ tokens?: Record<string, any>;
120
+ /** Morgan format */
121
+ format: string;
122
+ };
123
+ /** CORS configuration */
124
+ cors?: CorsOptions | false;
125
+ /** Helmet configuration */
126
+ helmet?: boolean;
127
+ /** Compression configuration */
128
+ compression?: boolean;
129
+ /** Rate-limit configuration */
130
+ rateLimit?: Partial<Options> | false;
131
+ /** express.json configuration */
132
+ express?: Partial<Parameters<typeof express.json>[0]> | false;
133
+ /** urlencoded configuration */
134
+ urlencoded?: Partial<Parameters<typeof express.urlencoded>[0]> | false;
128
135
  };
129
136
  /** Application configuration */
130
137
  type NanoAppConfig = {
131
- /** Listener run once the server is listening */
132
- listener?: () => void;
133
- /** Application port. Defaults to 3000 */
134
- port?: number;
135
- /** Values applied with Express `app.set()` (e.g. `{ "trust proxy": 1 }`) */
136
- settings?: Record<string, unknown>;
137
- /** Default middlewares configuration */
138
- packages?: PackagesConfig;
139
- /** Plugins to mount. Defaults to [] */
140
- plugins?: PluginConfig[];
141
- /** Main plugin to mount. */
142
- mainPlugin?: MainPluginConfig;
138
+ /** Listener run once the server is listening */
139
+ listener?: () => void;
140
+ /** Application port. Defaults to 3000 */
141
+ port?: number;
142
+ /** Values applied with Express `app.set()` (e.g. `{ "trust proxy": 1 }`) */
143
+ settings?: Record<string, unknown>;
144
+ /** Default middlewares configuration */
145
+ packages?: PackagesConfig;
146
+ /** Plugins to mount. Defaults to [] */
147
+ plugins?: PluginConfig[];
148
+ /** Main plugin to mount. */
149
+ mainPlugin?: MainPluginConfig;
143
150
  };
144
-
145
- /**
146
- * Merges the consumer configuration with the initial one.
147
- */
148
- declare function mergeConfig(config?: NanoAppConfig): NanoAppConfig & {
149
- mainPlugin: PluginConfig;
151
+ //#endregion
152
+ //#region src/app/config.d.ts
153
+ /** Merges the consumer configuration over the initial one; the main plugin is always named "app" */
154
+ declare function mergeConfig(oConfig?: NanoAppConfig): NanoAppConfig & Required<Pick<NanoAppConfig, 'settings' | 'plugins' | 'packages'>> & {
155
+ mainPlugin: PluginConfig;
150
156
  };
151
-
157
+ //#endregion
158
+ //#region src/app/NanoApp.d.ts
152
159
  /** Main NanoApp class */
153
160
  declare class NanoApp {
154
- protected app: Express;
155
- protected config: ReturnType<typeof mergeConfig>;
156
- constructor(config?: NanoAppConfig);
157
- /** Starts the application */
158
- private start;
159
- /** Initializes the application in the right order */
160
- private initialize;
161
- private setPackages;
161
+ protected oApp: Express;
162
+ protected oConfig: ReturnType<typeof mergeConfig>;
163
+ constructor(oConfig?: NanoAppConfig);
164
+ /** Starts the application */
165
+ private start;
166
+ /** Initializes the application in the right order */
167
+ private initialize;
168
+ /** Adds the packages according to the configuration */
169
+ private setPackages;
162
170
  }
163
-
171
+ //#endregion
172
+ //#region src/app/creators.d.ts
164
173
  /** Creates a new NanoApp instance */
165
- declare function createNanoApp(config?: NanoAppConfig): NanoApp;
174
+ export declare function createNanoApp(oConfig?: NanoAppConfig): NanoApp;
166
175
  /** Creates and types a route object. */
167
- declare function createRoute<DB>(route: RouteEntry<DB>): RouteEntry<DB>;
176
+ export declare function createRoute<DB>(oRoute: RouteEntry<DB>): RouteEntry<DB>;
168
177
  /** Creates a plugin: a function of the plugin's own options that returns its configuration. */
169
178
  type Creator<Options> = (options?: Options) => PluginConfig<Options>;
170
- declare function createPlugin<Options>(creator: Creator<Options>): Creator<Options>;
179
+ export declare function createPlugin<Options>(fnCreator: Creator<Options>): Creator<Options>;
171
180
  /** Creates and types the callbacks of a plugin. */
172
- declare function createPluginCallbacks<DB>(callbacks: PluginCallbacks<DB>): PluginCallbacks<DB>;
173
-
174
- export { NanoApp, createNanoApp, createPlugin, createPluginCallbacks, createRoute };
175
- export type { ApiPromise, ApiResponse, Callbacks, DatabaseConfig, Handler, Handlers, MainPluginConfig, NanoAppConfig, NanoObject, PackagesConfig, Params, PluginCallbacks, PluginConfig, PluginStage, QueryParams, RouteEntry };
181
+ export declare function createPluginCallbacks<DB>(oCallbacks: PluginCallbacks<DB>): PluginCallbacks<DB>;
182
+ //#endregion
183
+ export { type ApiPromise, type ApiResponse, type Callbacks, type DatabaseConfig, type Handler, type Handlers, type MainPluginConfig, NanoApp, type NanoAppConfig, type NanoObject, type PackagesConfig, type Params, type PluginCallbacks, type PluginConfig, type PluginStage, type QueryParams, type Request, type RouteEntry };