tempest-express-sdk 0.8.0 → 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 +4 -3
- package/dist/chunk-4QZZGHGV.js +6 -0
- package/dist/{chunk-JWJJAIXV.js.map → chunk-4QZZGHGV.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +60 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -7
- package/dist/index.d.ts +55 -7
- package/dist/index.js +60 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-JWJJAIXV.js +0 -6
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ npm install tempest-express-sdk tempest-db-js express zod
|
|
|
37
37
|
| **sse / websockets** | `SSEBroker`/`sseResponse` (+ `RedisSSEBroker`), transport-agnostic `WebSocketHub` + `attachWebSocketHub` |
|
|
38
38
|
| **flags / storage** | `FeatureFlags` (+ guard), `UploadStorage`/`LocalUploadStorage` |
|
|
39
39
|
| **webpush / email** | `WebPushDispatcher` (VAPID), `EmailUtils` (SMTP) |
|
|
40
|
-
| **server utils** | `TOTPHelper` (MFA), `HTTPClient` (retry + circuit breaker), `MetricsUtils` (+ Prometheus), `getClientIp` |
|
|
40
|
+
| **server utils** | `TOTPHelper` (MFA), `HTTPClient` (retry + circuit breaker), `MetricsUtils` (+ Prometheus, GPU), `makeMetricsRouter`, `getClientIp` |
|
|
41
41
|
| **integrations** | `MessagingProvider` contract; `WhatsAppProvider` (zap-api), `TelegramProvider` (Bot API), `TwilioSmsProvider` (SMS) + webhook receivers |
|
|
42
42
|
| **admin** | `AdminSite` + `makeAdminRouter` — JSON admin with auto-derived CRUD + introspection |
|
|
43
43
|
| **api** | `createApp`, `runServer`, `registerExceptionHandlers`, `createOpenApiRegistry`, `generateOpenApiDocument`, `mountSwaggerUi`, `mountRedoc`, `makeHealthRouter` |
|
|
@@ -98,8 +98,9 @@ Also shipped: the `integrations/` module — a typed WhatsApp client over
|
|
|
98
98
|
[`zap-api`](https://github.com/mauriciobenjamin700) behind a shared
|
|
99
99
|
`MessagingProvider` contract.
|
|
100
100
|
|
|
101
|
-
Planned (see [ROADMAP.md](./ROADMAP.md)):
|
|
102
|
-
|
|
101
|
+
Planned (see [ROADMAP.md](./ROADMAP.md)): additional `MessagingProvider`
|
|
102
|
+
channels (more SMS vendors, a transactional-email provider) under the shared
|
|
103
|
+
contract.
|
|
103
104
|
|
|
104
105
|
## License
|
|
105
106
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts"],"names":[],"mappings":";AACO,IAAM,OAAA,GAAU","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/version.ts"],"names":[],"mappings":";AACO,IAAM,OAAA,GAAU","file":"chunk-4QZZGHGV.js","sourcesContent":["/** The installed SDK version. Single source of truth for the barrel + CLI. */\nexport const VERSION = \"0.9.0\";\n"]}
|
package/dist/cli.cjs
CHANGED
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/template.ts","../src/version.ts","../src/cli/index.ts"],"names":["join","mkdir","dirname","writeFile","resolve","parseArgs","randomBytes"],"mappings":"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","file":"cli.cjs","sourcesContent":["/**\n * Project-template generator for the `new` command, mirroring `cli.new`.\n *\n * Produces a complete, runnable Express service that follows the SDK's layered\n * architecture (router → controller → service → repository → model) and is\n * pre-wired with `createApp`, native Swagger + Redoc, Zod validation, and a\n * `tempest-db-js` model. {@link projectFiles} returns a `path → contents` map\n * the CLI writes to disk.\n */\n\n/** SDK version the generated project depends on (kept in sync at release). */\nconst SDK_VERSION = \"^0.1.0\";\n\n/**\n * Build the file map for a new service named `name`.\n *\n * @param name - The project (and package) name.\n * @returns A map of relative file path to file contents.\n */\nexport function projectFiles(name: string): Record<string, string> {\n return {\n \"package.json\": `${JSON.stringify(\n {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: {\n dev: \"tsx watch main.ts\",\n start: \"tsx main.ts\",\n build: \"tsc -p tsconfig.json\",\n typecheck: \"tsc --noEmit\",\n },\n dependencies: {\n express: \"^5.1.0\",\n \"tempest-db-js\": \"^0.3.0\",\n \"tempest-express-sdk\": SDK_VERSION,\n zod: \"^3.24.1\",\n },\n devDependencies: {\n \"@types/express\": \"^5.0.0\",\n \"@types/node\": \"^22.10.0\",\n tsx: \"^4.19.2\",\n typescript: \"^5.7.0\",\n },\n },\n null,\n 2,\n )}\\n`,\n\n \"tsconfig.json\": `${JSON.stringify(\n {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"Bundler\",\n lib: [\"ES2022\"],\n strict: true,\n noUncheckedIndexedAccess: true,\n verbatimModuleSyntax: true,\n esModuleInterop: true,\n skipLibCheck: true,\n forceConsistentCasingInFileNames: true,\n outDir: \"dist\",\n paths: { \"@/*\": [\"./src/*\"] },\n },\n include: [\"src\", \"main.ts\"],\n },\n null,\n 2,\n )}\\n`,\n\n \".gitignore\": \"node_modules\\ndist\\n.env\\n*.log\\n\",\n\n \".env.example\":\n \"HOST=127.0.0.1\\nPORT=8000\\nDEBUG=false\\nDATABASE_URL=sqlite://./app.db\\nCORS_ORIGINS=*\\n\",\n\n \"main.ts\": `import { run } from \"@/index\";\n\nrun();\n`,\n\n \"src/index.ts\": `import { run } from \"@/server\";\n\nexport { run };\n`,\n\n \"src/core/settings.ts\": `import { baseAppSettingsShape, loadSettings, z } from \"tempest-express-sdk\";\n\n/** Application settings — extend the SDK base shape with project fields. */\nexport const settingsSchema = z.object({\n ...baseAppSettingsShape,\n});\n\nexport const settings = loadSettings(settingsSchema);\n`,\n\n \"src/db/models/itemModel.ts\": `import { BaseModel, column, tableNameFor } from \"tempest-express-sdk\";\n\n/** A sample domain model. Replace with your own. */\nexport class ItemModel extends BaseModel {\n static tablename = tableNameFor(\"ItemModel\");\n name = column.text().notNull();\n price = column.integer().notNull();\n}\n`,\n\n \"src/schemas/item.ts\": `import { baseResponseSchema, z } from \"tempest-express-sdk\";\n\n/** Request payload to create an item. */\nexport const itemCreateSchema = z\n .object({\n name: z.string().min(1).openapi({ description: \"The item name.\" }),\n price: z.number().int().min(0).openapi({ description: \"Price in cents.\" }),\n })\n .openapi(\"ItemCreate\");\n\n/** Response payload for an item (base columns + domain fields). */\nexport const itemResponseSchema = baseResponseSchema\n .extend({\n name: z.string(),\n price: z.number().int(),\n })\n .openapi(\"Item\");\n\nexport type ItemCreate = z.infer<typeof itemCreateSchema>;\nexport type ItemResponse = z.infer<typeof itemResponseSchema>;\n`,\n\n \"src/db/repositories/itemRepository.ts\": `import { type AsyncSession, BaseRepository } from \"tempest-express-sdk\";\nimport { ItemModel } from \"@/db/models/itemModel\";\n\n/** Data-access layer for items. */\nexport class ItemRepository extends BaseRepository<typeof ItemModel> {\n constructor(session: AsyncSession) {\n super(ItemModel, session);\n }\n}\n`,\n\n \"src/services/itemService.ts\": `import { BaseService } from \"tempest-express-sdk\";\nimport type { ItemRepository } from \"@/db/repositories/itemRepository\";\nimport type { ItemModel } from \"@/db/models/itemModel\";\nimport type { ItemResponse } from \"@/schemas/item\";\n\n/** Business logic for items. Maps raw rows to the response shape. */\nexport class ItemService extends BaseService<typeof ItemModel, ItemResponse> {\n constructor(repository: ItemRepository) {\n super(repository, (row) => ({\n id: row.id,\n isActive: row.isActive,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n name: row.name,\n price: row.price,\n }));\n }\n}\n`,\n\n \"src/controllers/itemController.ts\": `import { BaseController } from \"tempest-express-sdk\";\nimport type { ItemModel } from \"@/db/models/itemModel\";\nimport type { ItemResponse } from \"@/schemas/item\";\nimport type { ItemService } from \"@/services/itemService\";\n\n/** Orchestration boundary between the router and the item service. */\nexport class ItemController extends BaseController<typeof ItemModel, ItemResponse> {\n constructor(service: ItemService) {\n super(service);\n }\n}\n`,\n\n \"src/api/routers/items.ts\": `import { Router } from \"express\";\nimport type { OpenAPIRegistry } from \"tempest-express-sdk\";\nimport { itemCreateSchema, itemResponseSchema } from \"@/schemas/item\";\n\n/**\n * Build the items router and register its OpenAPI paths.\n *\n * NOTE: wire a real controller (with a DB session) here. This stub returns\n * static data so a freshly generated project boots without a database.\n */\nexport function makeItemsRouter(registry: OpenAPIRegistry): Router {\n const router = Router();\n\n registry.registerPath({\n method: \"get\",\n path: \"/api/items\",\n summary: \"List items\",\n responses: {\n 200: {\n description: \"The items\",\n content: { \"application/json\": { schema: itemResponseSchema.array() } },\n },\n },\n });\n\n registry.registerPath({\n method: \"post\",\n path: \"/api/items\",\n summary: \"Create an item\",\n request: {\n body: { content: { \"application/json\": { schema: itemCreateSchema } } },\n },\n responses: {\n 201: {\n description: \"The created item\",\n content: { \"application/json\": { schema: itemResponseSchema } },\n },\n },\n });\n\n router.get(\"/api/items\", (_req, res) => {\n res.json([]);\n });\n\n router.post(\"/api/items\", (req, res) => {\n const data = itemCreateSchema.parse(req.body);\n res.status(201).json({\n id: \"00000000-0000-0000-0000-000000000000\",\n isActive: true,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n ...data,\n });\n });\n\n return router;\n}\n`,\n\n \"src/api/app.ts\": `import type { Express } from \"express\";\nimport { createApp, createOpenApiRegistry } from \"tempest-express-sdk\";\nimport { settings } from \"@/core/settings\";\nimport { makeItemsRouter } from \"@/api/routers/items\";\n\n/** Build the configured Express application. */\nexport async function makeApp(): Promise<Express> {\n const registry = createOpenApiRegistry();\n\n return createApp({\n corsOrigins: settings.CORS_ORIGINS,\n openapi: {\n registry,\n info: { title: \"${name}\", version: \"0.1.0\", description: \"Powered by tempest-express-sdk.\" },\n },\n configure: (app) => {\n app.use(makeItemsRouter(registry));\n },\n });\n}\n`,\n\n \"src/server.ts\": `import { runServer } from \"tempest-express-sdk\";\nimport { settings } from \"@/core/settings\";\nimport { makeApp } from \"@/api/app\";\n\n/** Build the app and start listening. */\nexport async function run(): Promise<void> {\n const app = await makeApp();\n await runServer(app, { host: settings.HOST, port: settings.PORT });\n}\n`,\n\n \"docker-compose.yml\": dockerComposeFile(name),\n\n \"README.md\": `# ${name}\n\nGenerated with \\`tempest-express new\\` — an Express + Zod + tempest-db-js service.\n\n## Develop\n\n\\`\\`\\`bash\nnpm install\ncp .env.example .env\nnpm run dev\n\\`\\`\\`\n\n- API: http://127.0.0.1:8000/api/items\n- Swagger UI: http://127.0.0.1:8000/docs\n- Redoc: http://127.0.0.1:8000/redoc\n- Health: http://127.0.0.1:8000/health\n\\`\\`\\`\n`,\n };\n}\n\n/** Lowercase the first letter (PascalCase → camelCase). */\nfunction toCamel(name: string): string {\n return name.charAt(0).toLowerCase() + name.slice(1);\n}\n\n/** Convert `CamelCase` to `snake_case`. */\nfunction toSnake(name: string): string {\n return name.replace(/(?<!^)(?=[A-Z])/g, \"_\").toLowerCase();\n}\n\n/**\n * Build a full CRUD resource file map for a PascalCase resource name.\n *\n * Generates model + schema + repository + service + controller + router under\n * `src/`, mirroring the layered slice the `new` template ships with.\n *\n * @param pascal - The resource name in PascalCase (e.g. `Product`).\n * @returns A map of relative file path to file contents.\n */\nexport function resourceFiles(pascal: string): Record<string, string> {\n const camel = toCamel(pascal);\n const table = toSnake(pascal);\n\n return {\n [`src/db/models/${camel}Model.ts`]: `import { BaseModel, column, tableNameFor } from \"tempest-express-sdk\";\n\n/** The ${pascal} domain model. */\nexport class ${pascal}Model extends BaseModel {\n static tablename = tableNameFor(\"${pascal}Model\"); // \"${table}\"\n name = column.text().notNull();\n}\n`,\n\n [`src/schemas/${camel}.ts`]: `import { baseResponseSchema, z } from \"tempest-express-sdk\";\n\n/** Request payload to create a ${pascal}. */\nexport const ${camel}CreateSchema = z\n .object({ name: z.string().min(1) })\n .openapi(\"${pascal}Create\");\n\n/** Response payload for a ${pascal}. */\nexport const ${camel}ResponseSchema = baseResponseSchema\n .extend({ name: z.string() })\n .openapi(\"${pascal}\");\n\nexport type ${pascal}Create = z.infer<typeof ${camel}CreateSchema>;\nexport type ${pascal}Response = z.infer<typeof ${camel}ResponseSchema>;\n`,\n\n [`src/db/repositories/${camel}Repository.ts`]: `import { type AsyncSession, BaseRepository } from \"tempest-express-sdk\";\nimport { ${pascal}Model } from \"@/db/models/${camel}Model\";\n\n/** Data-access layer for ${pascal}. */\nexport class ${pascal}Repository extends BaseRepository<typeof ${pascal}Model> {\n constructor(session: AsyncSession) {\n super(${pascal}Model, session);\n }\n}\n`,\n\n [`src/services/${camel}Service.ts`]: `import { BaseService } from \"tempest-express-sdk\";\nimport type { ${pascal}Model } from \"@/db/models/${camel}Model\";\nimport type { ${pascal}Repository } from \"@/db/repositories/${camel}Repository\";\nimport type { ${pascal}Response } from \"@/schemas/${camel}\";\n\n/** Business logic for ${pascal}. */\nexport class ${pascal}Service extends BaseService<typeof ${pascal}Model, ${pascal}Response> {\n constructor(repository: ${pascal}Repository) {\n super(repository, (row) => ({\n id: row.id,\n isActive: row.isActive,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n name: row.name,\n }));\n }\n}\n`,\n\n [`src/controllers/${camel}Controller.ts`]: `import { BaseController } from \"tempest-express-sdk\";\nimport type { ${pascal}Model } from \"@/db/models/${camel}Model\";\nimport type { ${pascal}Response } from \"@/schemas/${camel}\";\nimport type { ${pascal}Service } from \"@/services/${camel}Service\";\n\n/** Orchestration boundary for ${pascal}. */\nexport class ${pascal}Controller extends BaseController<typeof ${pascal}Model, ${pascal}Response> {\n constructor(service: ${pascal}Service) {\n super(service);\n }\n}\n`,\n\n [`src/api/routers/${camel}s.ts`]: `import { Router } from \"express\";\nimport type { OpenAPIRegistry } from \"tempest-express-sdk\";\nimport { ${camel}CreateSchema, ${camel}ResponseSchema } from \"@/schemas/${camel}\";\n\n/** Build the ${pascal} router and register its OpenAPI paths. */\nexport function make${pascal}sRouter(registry: OpenAPIRegistry): Router {\n const router = Router();\n\n registry.registerPath({\n method: \"get\",\n path: \"/api/${camel}s\",\n summary: \"List ${camel}s\",\n responses: {\n 200: {\n description: \"OK\",\n content: { \"application/json\": { schema: ${camel}ResponseSchema.array() } },\n },\n },\n });\n registry.registerPath({\n method: \"post\",\n path: \"/api/${camel}s\",\n summary: \"Create a ${camel}\",\n request: {\n body: { content: { \"application/json\": { schema: ${camel}CreateSchema } } },\n },\n responses: {\n 201: {\n description: \"Created\",\n content: { \"application/json\": { schema: ${camel}ResponseSchema } },\n },\n },\n });\n\n router.get(\"/api/${camel}s\", (_req, res) => res.json([]));\n router.post(\"/api/${camel}s\", (req, res) => {\n const data = ${camel}CreateSchema.parse(req.body);\n res.status(201).json({\n id: \"00000000-0000-0000-0000-000000000000\",\n isActive: true,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n ...data,\n });\n });\n\n return router;\n}\n`,\n };\n}\n\n/** Build a `docker-compose.yml` with Postgres + Redis for local dev. */\nexport function dockerComposeFile(name: string): string {\n return `services:\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: ${name}\n POSTGRES_PASSWORD: ${name}\n POSTGRES_DB: ${name}\n ports:\n - \"5432:5432\"\n volumes:\n - pgdata:/var/lib/postgresql/data\n redis:\n image: redis:7-alpine\n ports:\n - \"6379:6379\"\n\nvolumes:\n pgdata:\n`;\n}\n","/** The installed SDK version. Single source of truth for the barrel + CLI. */\nexport const VERSION = \"0.8.0\";\n","#!/usr/bin/env node\n/**\n * `tempest-express` command-line interface, mirroring `cli.main`.\n *\n * Commands:\n * - `new <name>` — scaffold a runnable Express service from the SDK template.\n * - `--version` — print the CLI/SDK version.\n * - `--help` — print usage.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { dockerComposeFile, projectFiles, resourceFiles } from \"@/cli/template\";\nimport { VERSION } from \"@/version\";\n\nconst USAGE = `tempest-express — Express SDK CLI\n\nUsage:\n tempest-express new <name> [--dir <path>] Scaffold a new service\n tempest-express generate <Name> [--dir <path>] Scaffold a CRUD resource\n tempest-express secret [--bytes <n>] Print a random secret\n tempest-express docker-compose [--dir <path>] Write a docker-compose.yml\n tempest-express db Migration guidance\n tempest-express --version Print the version\n tempest-express --help Show this help\n`;\n\nconst DB_HELP = `Migrations are handled by tempest-db-js (programmatic API):\n\n import { ... } from \"tempest-db-js/migrations\";\n\nSee https://www.npmjs.com/package/tempest-db-js for the migration workflow.\n`;\n\n/** Write a project file map under `root`, creating directories as needed. */\nasync function writeFiles(root: string, files: Record<string, string>): Promise<void> {\n for (const [relative, contents] of Object.entries(files)) {\n const target = join(root, relative);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, contents, \"utf8\");\n }\n}\n\n/** Scaffold a new project named `name` into `dir/name`. */\nasync function runNew(name: string, dir: string): Promise<void> {\n if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {\n throw new Error(`Invalid project name: ${JSON.stringify(name)}`);\n }\n const root = resolve(dir, name);\n await writeFiles(root, projectFiles(name));\n process.stdout.write(\n `Created ${name} at ${root}\\n\\nNext steps:\\n cd ${name}\\n npm install\\n cp .env.example .env\\n npm run dev\\n`,\n );\n}\n\n/** Scaffold a CRUD resource into `dir` (PascalCase name required). */\nasync function runGenerate(name: string, dir: string): Promise<void> {\n if (!/^[A-Z][A-Za-z0-9]*$/.test(name)) {\n throw new Error(`Resource name must be PascalCase, got ${JSON.stringify(name)}`);\n }\n const files = resourceFiles(name);\n await writeFiles(resolve(dir), files);\n const written = Object.keys(files)\n .map((path) => ` ${path}`)\n .join(\"\\n\");\n process.stdout.write(`Generated ${name} resource:\\n${written}\\n`);\n}\n\n/**\n * Parse `argv` and dispatch to the matching command.\n *\n * @param argv - Arguments after `node script` (defaults to `process.argv`).\n * @returns The process exit code.\n */\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n version: { type: \"boolean\", short: \"v\" },\n help: { type: \"boolean\", short: \"h\" },\n dir: { type: \"string\", default: \".\" },\n bytes: { type: \"string\", default: \"32\" },\n },\n });\n\n if (values.version) {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n\n const [command, ...rest] = positionals;\n\n if (values.help || command === undefined || command === \"help\") {\n process.stdout.write(USAGE);\n return 0;\n }\n\n const dir = values.dir ?? \".\";\n\n if (command === \"new\") {\n const name = rest[0];\n if (!name) {\n process.stderr.write(`error: \\`new\\` requires a project name\\n\\n${USAGE}`);\n return 1;\n }\n await runNew(name, dir);\n return 0;\n }\n\n if (command === \"generate\" || command === \"g\") {\n const name = rest[0];\n if (!name) {\n process.stderr.write(`error: \\`generate\\` requires a resource name\\n\\n${USAGE}`);\n return 1;\n }\n await runGenerate(name, dir);\n return 0;\n }\n\n if (command === \"secret\") {\n const nbytes = Number.parseInt(values.bytes ?? \"32\", 10);\n if (!Number.isInteger(nbytes) || nbytes < 16) {\n process.stderr.write(\"error: --bytes must be an integer >= 16\\n\");\n return 1;\n }\n process.stdout.write(`${randomBytes(nbytes).toString(\"base64url\")}\\n`);\n return 0;\n }\n\n if (command === \"docker-compose\") {\n await writeFiles(resolve(dir), { \"docker-compose.yml\": dockerComposeFile(\"app\") });\n process.stdout.write(`Wrote ${resolve(dir, \"docker-compose.yml\")}\\n`);\n return 0;\n }\n\n if (command === \"db\") {\n process.stdout.write(DB_HELP);\n return 0;\n }\n\n process.stderr.write(`error: unknown command ${JSON.stringify(command)}\\n\\n${USAGE}`);\n return 1;\n}\n\nmain()\n .then((code) => {\n process.exitCode = code;\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`error: ${message}\\n`);\n process.exitCode = 1;\n });\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/template.ts","../src/version.ts","../src/cli/index.ts"],"names":["join","mkdir","dirname","writeFile","resolve","parseArgs","randomBytes"],"mappings":"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","file":"cli.cjs","sourcesContent":["/**\n * Project-template generator for the `new` command, mirroring `cli.new`.\n *\n * Produces a complete, runnable Express service that follows the SDK's layered\n * architecture (router → controller → service → repository → model) and is\n * pre-wired with `createApp`, native Swagger + Redoc, Zod validation, and a\n * `tempest-db-js` model. {@link projectFiles} returns a `path → contents` map\n * the CLI writes to disk.\n */\n\n/** SDK version the generated project depends on (kept in sync at release). */\nconst SDK_VERSION = \"^0.1.0\";\n\n/**\n * Build the file map for a new service named `name`.\n *\n * @param name - The project (and package) name.\n * @returns A map of relative file path to file contents.\n */\nexport function projectFiles(name: string): Record<string, string> {\n return {\n \"package.json\": `${JSON.stringify(\n {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: {\n dev: \"tsx watch main.ts\",\n start: \"tsx main.ts\",\n build: \"tsc -p tsconfig.json\",\n typecheck: \"tsc --noEmit\",\n },\n dependencies: {\n express: \"^5.1.0\",\n \"tempest-db-js\": \"^0.3.0\",\n \"tempest-express-sdk\": SDK_VERSION,\n zod: \"^3.24.1\",\n },\n devDependencies: {\n \"@types/express\": \"^5.0.0\",\n \"@types/node\": \"^22.10.0\",\n tsx: \"^4.19.2\",\n typescript: \"^5.7.0\",\n },\n },\n null,\n 2,\n )}\\n`,\n\n \"tsconfig.json\": `${JSON.stringify(\n {\n compilerOptions: {\n target: \"ES2022\",\n module: \"ESNext\",\n moduleResolution: \"Bundler\",\n lib: [\"ES2022\"],\n strict: true,\n noUncheckedIndexedAccess: true,\n verbatimModuleSyntax: true,\n esModuleInterop: true,\n skipLibCheck: true,\n forceConsistentCasingInFileNames: true,\n outDir: \"dist\",\n paths: { \"@/*\": [\"./src/*\"] },\n },\n include: [\"src\", \"main.ts\"],\n },\n null,\n 2,\n )}\\n`,\n\n \".gitignore\": \"node_modules\\ndist\\n.env\\n*.log\\n\",\n\n \".env.example\":\n \"HOST=127.0.0.1\\nPORT=8000\\nDEBUG=false\\nDATABASE_URL=sqlite://./app.db\\nCORS_ORIGINS=*\\n\",\n\n \"main.ts\": `import { run } from \"@/index\";\n\nrun();\n`,\n\n \"src/index.ts\": `import { run } from \"@/server\";\n\nexport { run };\n`,\n\n \"src/core/settings.ts\": `import { baseAppSettingsShape, loadSettings, z } from \"tempest-express-sdk\";\n\n/** Application settings — extend the SDK base shape with project fields. */\nexport const settingsSchema = z.object({\n ...baseAppSettingsShape,\n});\n\nexport const settings = loadSettings(settingsSchema);\n`,\n\n \"src/db/models/itemModel.ts\": `import { BaseModel, column, tableNameFor } from \"tempest-express-sdk\";\n\n/** A sample domain model. Replace with your own. */\nexport class ItemModel extends BaseModel {\n static tablename = tableNameFor(\"ItemModel\");\n name = column.text().notNull();\n price = column.integer().notNull();\n}\n`,\n\n \"src/schemas/item.ts\": `import { baseResponseSchema, z } from \"tempest-express-sdk\";\n\n/** Request payload to create an item. */\nexport const itemCreateSchema = z\n .object({\n name: z.string().min(1).openapi({ description: \"The item name.\" }),\n price: z.number().int().min(0).openapi({ description: \"Price in cents.\" }),\n })\n .openapi(\"ItemCreate\");\n\n/** Response payload for an item (base columns + domain fields). */\nexport const itemResponseSchema = baseResponseSchema\n .extend({\n name: z.string(),\n price: z.number().int(),\n })\n .openapi(\"Item\");\n\nexport type ItemCreate = z.infer<typeof itemCreateSchema>;\nexport type ItemResponse = z.infer<typeof itemResponseSchema>;\n`,\n\n \"src/db/repositories/itemRepository.ts\": `import { type AsyncSession, BaseRepository } from \"tempest-express-sdk\";\nimport { ItemModel } from \"@/db/models/itemModel\";\n\n/** Data-access layer for items. */\nexport class ItemRepository extends BaseRepository<typeof ItemModel> {\n constructor(session: AsyncSession) {\n super(ItemModel, session);\n }\n}\n`,\n\n \"src/services/itemService.ts\": `import { BaseService } from \"tempest-express-sdk\";\nimport type { ItemRepository } from \"@/db/repositories/itemRepository\";\nimport type { ItemModel } from \"@/db/models/itemModel\";\nimport type { ItemResponse } from \"@/schemas/item\";\n\n/** Business logic for items. Maps raw rows to the response shape. */\nexport class ItemService extends BaseService<typeof ItemModel, ItemResponse> {\n constructor(repository: ItemRepository) {\n super(repository, (row) => ({\n id: row.id,\n isActive: row.isActive,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n name: row.name,\n price: row.price,\n }));\n }\n}\n`,\n\n \"src/controllers/itemController.ts\": `import { BaseController } from \"tempest-express-sdk\";\nimport type { ItemModel } from \"@/db/models/itemModel\";\nimport type { ItemResponse } from \"@/schemas/item\";\nimport type { ItemService } from \"@/services/itemService\";\n\n/** Orchestration boundary between the router and the item service. */\nexport class ItemController extends BaseController<typeof ItemModel, ItemResponse> {\n constructor(service: ItemService) {\n super(service);\n }\n}\n`,\n\n \"src/api/routers/items.ts\": `import { Router } from \"express\";\nimport type { OpenAPIRegistry } from \"tempest-express-sdk\";\nimport { itemCreateSchema, itemResponseSchema } from \"@/schemas/item\";\n\n/**\n * Build the items router and register its OpenAPI paths.\n *\n * NOTE: wire a real controller (with a DB session) here. This stub returns\n * static data so a freshly generated project boots without a database.\n */\nexport function makeItemsRouter(registry: OpenAPIRegistry): Router {\n const router = Router();\n\n registry.registerPath({\n method: \"get\",\n path: \"/api/items\",\n summary: \"List items\",\n responses: {\n 200: {\n description: \"The items\",\n content: { \"application/json\": { schema: itemResponseSchema.array() } },\n },\n },\n });\n\n registry.registerPath({\n method: \"post\",\n path: \"/api/items\",\n summary: \"Create an item\",\n request: {\n body: { content: { \"application/json\": { schema: itemCreateSchema } } },\n },\n responses: {\n 201: {\n description: \"The created item\",\n content: { \"application/json\": { schema: itemResponseSchema } },\n },\n },\n });\n\n router.get(\"/api/items\", (_req, res) => {\n res.json([]);\n });\n\n router.post(\"/api/items\", (req, res) => {\n const data = itemCreateSchema.parse(req.body);\n res.status(201).json({\n id: \"00000000-0000-0000-0000-000000000000\",\n isActive: true,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n ...data,\n });\n });\n\n return router;\n}\n`,\n\n \"src/api/app.ts\": `import type { Express } from \"express\";\nimport { createApp, createOpenApiRegistry } from \"tempest-express-sdk\";\nimport { settings } from \"@/core/settings\";\nimport { makeItemsRouter } from \"@/api/routers/items\";\n\n/** Build the configured Express application. */\nexport async function makeApp(): Promise<Express> {\n const registry = createOpenApiRegistry();\n\n return createApp({\n corsOrigins: settings.CORS_ORIGINS,\n openapi: {\n registry,\n info: { title: \"${name}\", version: \"0.1.0\", description: \"Powered by tempest-express-sdk.\" },\n },\n configure: (app) => {\n app.use(makeItemsRouter(registry));\n },\n });\n}\n`,\n\n \"src/server.ts\": `import { runServer } from \"tempest-express-sdk\";\nimport { settings } from \"@/core/settings\";\nimport { makeApp } from \"@/api/app\";\n\n/** Build the app and start listening. */\nexport async function run(): Promise<void> {\n const app = await makeApp();\n await runServer(app, { host: settings.HOST, port: settings.PORT });\n}\n`,\n\n \"docker-compose.yml\": dockerComposeFile(name),\n\n \"README.md\": `# ${name}\n\nGenerated with \\`tempest-express new\\` — an Express + Zod + tempest-db-js service.\n\n## Develop\n\n\\`\\`\\`bash\nnpm install\ncp .env.example .env\nnpm run dev\n\\`\\`\\`\n\n- API: http://127.0.0.1:8000/api/items\n- Swagger UI: http://127.0.0.1:8000/docs\n- Redoc: http://127.0.0.1:8000/redoc\n- Health: http://127.0.0.1:8000/health\n\\`\\`\\`\n`,\n };\n}\n\n/** Lowercase the first letter (PascalCase → camelCase). */\nfunction toCamel(name: string): string {\n return name.charAt(0).toLowerCase() + name.slice(1);\n}\n\n/** Convert `CamelCase` to `snake_case`. */\nfunction toSnake(name: string): string {\n return name.replace(/(?<!^)(?=[A-Z])/g, \"_\").toLowerCase();\n}\n\n/**\n * Build a full CRUD resource file map for a PascalCase resource name.\n *\n * Generates model + schema + repository + service + controller + router under\n * `src/`, mirroring the layered slice the `new` template ships with.\n *\n * @param pascal - The resource name in PascalCase (e.g. `Product`).\n * @returns A map of relative file path to file contents.\n */\nexport function resourceFiles(pascal: string): Record<string, string> {\n const camel = toCamel(pascal);\n const table = toSnake(pascal);\n\n return {\n [`src/db/models/${camel}Model.ts`]: `import { BaseModel, column, tableNameFor } from \"tempest-express-sdk\";\n\n/** The ${pascal} domain model. */\nexport class ${pascal}Model extends BaseModel {\n static tablename = tableNameFor(\"${pascal}Model\"); // \"${table}\"\n name = column.text().notNull();\n}\n`,\n\n [`src/schemas/${camel}.ts`]: `import { baseResponseSchema, z } from \"tempest-express-sdk\";\n\n/** Request payload to create a ${pascal}. */\nexport const ${camel}CreateSchema = z\n .object({ name: z.string().min(1) })\n .openapi(\"${pascal}Create\");\n\n/** Response payload for a ${pascal}. */\nexport const ${camel}ResponseSchema = baseResponseSchema\n .extend({ name: z.string() })\n .openapi(\"${pascal}\");\n\nexport type ${pascal}Create = z.infer<typeof ${camel}CreateSchema>;\nexport type ${pascal}Response = z.infer<typeof ${camel}ResponseSchema>;\n`,\n\n [`src/db/repositories/${camel}Repository.ts`]: `import { type AsyncSession, BaseRepository } from \"tempest-express-sdk\";\nimport { ${pascal}Model } from \"@/db/models/${camel}Model\";\n\n/** Data-access layer for ${pascal}. */\nexport class ${pascal}Repository extends BaseRepository<typeof ${pascal}Model> {\n constructor(session: AsyncSession) {\n super(${pascal}Model, session);\n }\n}\n`,\n\n [`src/services/${camel}Service.ts`]: `import { BaseService } from \"tempest-express-sdk\";\nimport type { ${pascal}Model } from \"@/db/models/${camel}Model\";\nimport type { ${pascal}Repository } from \"@/db/repositories/${camel}Repository\";\nimport type { ${pascal}Response } from \"@/schemas/${camel}\";\n\n/** Business logic for ${pascal}. */\nexport class ${pascal}Service extends BaseService<typeof ${pascal}Model, ${pascal}Response> {\n constructor(repository: ${pascal}Repository) {\n super(repository, (row) => ({\n id: row.id,\n isActive: row.isActive,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n name: row.name,\n }));\n }\n}\n`,\n\n [`src/controllers/${camel}Controller.ts`]: `import { BaseController } from \"tempest-express-sdk\";\nimport type { ${pascal}Model } from \"@/db/models/${camel}Model\";\nimport type { ${pascal}Response } from \"@/schemas/${camel}\";\nimport type { ${pascal}Service } from \"@/services/${camel}Service\";\n\n/** Orchestration boundary for ${pascal}. */\nexport class ${pascal}Controller extends BaseController<typeof ${pascal}Model, ${pascal}Response> {\n constructor(service: ${pascal}Service) {\n super(service);\n }\n}\n`,\n\n [`src/api/routers/${camel}s.ts`]: `import { Router } from \"express\";\nimport type { OpenAPIRegistry } from \"tempest-express-sdk\";\nimport { ${camel}CreateSchema, ${camel}ResponseSchema } from \"@/schemas/${camel}\";\n\n/** Build the ${pascal} router and register its OpenAPI paths. */\nexport function make${pascal}sRouter(registry: OpenAPIRegistry): Router {\n const router = Router();\n\n registry.registerPath({\n method: \"get\",\n path: \"/api/${camel}s\",\n summary: \"List ${camel}s\",\n responses: {\n 200: {\n description: \"OK\",\n content: { \"application/json\": { schema: ${camel}ResponseSchema.array() } },\n },\n },\n });\n registry.registerPath({\n method: \"post\",\n path: \"/api/${camel}s\",\n summary: \"Create a ${camel}\",\n request: {\n body: { content: { \"application/json\": { schema: ${camel}CreateSchema } } },\n },\n responses: {\n 201: {\n description: \"Created\",\n content: { \"application/json\": { schema: ${camel}ResponseSchema } },\n },\n },\n });\n\n router.get(\"/api/${camel}s\", (_req, res) => res.json([]));\n router.post(\"/api/${camel}s\", (req, res) => {\n const data = ${camel}CreateSchema.parse(req.body);\n res.status(201).json({\n id: \"00000000-0000-0000-0000-000000000000\",\n isActive: true,\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n ...data,\n });\n });\n\n return router;\n}\n`,\n };\n}\n\n/** Build a `docker-compose.yml` with Postgres + Redis for local dev. */\nexport function dockerComposeFile(name: string): string {\n return `services:\n postgres:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: ${name}\n POSTGRES_PASSWORD: ${name}\n POSTGRES_DB: ${name}\n ports:\n - \"5432:5432\"\n volumes:\n - pgdata:/var/lib/postgresql/data\n redis:\n image: redis:7-alpine\n ports:\n - \"6379:6379\"\n\nvolumes:\n pgdata:\n`;\n}\n","/** The installed SDK version. Single source of truth for the barrel + CLI. */\nexport const VERSION = \"0.9.0\";\n","#!/usr/bin/env node\n/**\n * `tempest-express` command-line interface, mirroring `cli.main`.\n *\n * Commands:\n * - `new <name>` — scaffold a runnable Express service from the SDK template.\n * - `--version` — print the CLI/SDK version.\n * - `--help` — print usage.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { dockerComposeFile, projectFiles, resourceFiles } from \"@/cli/template\";\nimport { VERSION } from \"@/version\";\n\nconst USAGE = `tempest-express — Express SDK CLI\n\nUsage:\n tempest-express new <name> [--dir <path>] Scaffold a new service\n tempest-express generate <Name> [--dir <path>] Scaffold a CRUD resource\n tempest-express secret [--bytes <n>] Print a random secret\n tempest-express docker-compose [--dir <path>] Write a docker-compose.yml\n tempest-express db Migration guidance\n tempest-express --version Print the version\n tempest-express --help Show this help\n`;\n\nconst DB_HELP = `Migrations are handled by tempest-db-js (programmatic API):\n\n import { ... } from \"tempest-db-js/migrations\";\n\nSee https://www.npmjs.com/package/tempest-db-js for the migration workflow.\n`;\n\n/** Write a project file map under `root`, creating directories as needed. */\nasync function writeFiles(root: string, files: Record<string, string>): Promise<void> {\n for (const [relative, contents] of Object.entries(files)) {\n const target = join(root, relative);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, contents, \"utf8\");\n }\n}\n\n/** Scaffold a new project named `name` into `dir/name`. */\nasync function runNew(name: string, dir: string): Promise<void> {\n if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {\n throw new Error(`Invalid project name: ${JSON.stringify(name)}`);\n }\n const root = resolve(dir, name);\n await writeFiles(root, projectFiles(name));\n process.stdout.write(\n `Created ${name} at ${root}\\n\\nNext steps:\\n cd ${name}\\n npm install\\n cp .env.example .env\\n npm run dev\\n`,\n );\n}\n\n/** Scaffold a CRUD resource into `dir` (PascalCase name required). */\nasync function runGenerate(name: string, dir: string): Promise<void> {\n if (!/^[A-Z][A-Za-z0-9]*$/.test(name)) {\n throw new Error(`Resource name must be PascalCase, got ${JSON.stringify(name)}`);\n }\n const files = resourceFiles(name);\n await writeFiles(resolve(dir), files);\n const written = Object.keys(files)\n .map((path) => ` ${path}`)\n .join(\"\\n\");\n process.stdout.write(`Generated ${name} resource:\\n${written}\\n`);\n}\n\n/**\n * Parse `argv` and dispatch to the matching command.\n *\n * @param argv - Arguments after `node script` (defaults to `process.argv`).\n * @returns The process exit code.\n */\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n version: { type: \"boolean\", short: \"v\" },\n help: { type: \"boolean\", short: \"h\" },\n dir: { type: \"string\", default: \".\" },\n bytes: { type: \"string\", default: \"32\" },\n },\n });\n\n if (values.version) {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n\n const [command, ...rest] = positionals;\n\n if (values.help || command === undefined || command === \"help\") {\n process.stdout.write(USAGE);\n return 0;\n }\n\n const dir = values.dir ?? \".\";\n\n if (command === \"new\") {\n const name = rest[0];\n if (!name) {\n process.stderr.write(`error: \\`new\\` requires a project name\\n\\n${USAGE}`);\n return 1;\n }\n await runNew(name, dir);\n return 0;\n }\n\n if (command === \"generate\" || command === \"g\") {\n const name = rest[0];\n if (!name) {\n process.stderr.write(`error: \\`generate\\` requires a resource name\\n\\n${USAGE}`);\n return 1;\n }\n await runGenerate(name, dir);\n return 0;\n }\n\n if (command === \"secret\") {\n const nbytes = Number.parseInt(values.bytes ?? \"32\", 10);\n if (!Number.isInteger(nbytes) || nbytes < 16) {\n process.stderr.write(\"error: --bytes must be an integer >= 16\\n\");\n return 1;\n }\n process.stdout.write(`${randomBytes(nbytes).toString(\"base64url\")}\\n`);\n return 0;\n }\n\n if (command === \"docker-compose\") {\n await writeFiles(resolve(dir), { \"docker-compose.yml\": dockerComposeFile(\"app\") });\n process.stdout.write(`Wrote ${resolve(dir, \"docker-compose.yml\")}\\n`);\n return 0;\n }\n\n if (command === \"db\") {\n process.stdout.write(DB_HELP);\n return 0;\n }\n\n process.stderr.write(`error: unknown command ${JSON.stringify(command)}\\n\\n${USAGE}`);\n return 1;\n}\n\nmain()\n .then((code) => {\n process.exitCode = code;\n })\n .catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`error: ${message}\\n`);\n process.exitCode = 1;\n });\n"]}
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -5,7 +5,9 @@ var zodToOpenapi = require('@asteasolutions/zod-to-openapi');
|
|
|
5
5
|
var zod = require('zod');
|
|
6
6
|
var tempestDbJs = require('tempest-db-js');
|
|
7
7
|
var crypto = require('crypto');
|
|
8
|
+
var child_process = require('child_process');
|
|
8
9
|
var os = require('os');
|
|
10
|
+
var util = require('util');
|
|
9
11
|
var promises = require('fs/promises');
|
|
10
12
|
var path = require('path');
|
|
11
13
|
var express2 = require('express');
|
|
@@ -7011,6 +7013,7 @@ var HTTPClient = class {
|
|
|
7011
7013
|
return this.request("DELETE", url, init);
|
|
7012
7014
|
}
|
|
7013
7015
|
};
|
|
7016
|
+
var execFileAsync = util.promisify(child_process.execFile);
|
|
7014
7017
|
function readCpu() {
|
|
7015
7018
|
const cores = os.cpus().length;
|
|
7016
7019
|
const load1 = os.loadavg()[0] ?? 0;
|
|
@@ -7039,7 +7042,27 @@ function readSystem() {
|
|
|
7039
7042
|
uptimeSeconds: process.uptime()
|
|
7040
7043
|
};
|
|
7041
7044
|
}
|
|
7042
|
-
function
|
|
7045
|
+
async function readGpus() {
|
|
7046
|
+
try {
|
|
7047
|
+
const { stdout } = await execFileAsync("nvidia-smi", [
|
|
7048
|
+
"--query-gpu=index,utilization.gpu,memory.used,memory.total,temperature.gpu",
|
|
7049
|
+
"--format=csv,noheader,nounits"
|
|
7050
|
+
]);
|
|
7051
|
+
return stdout.trim().split("\n").filter((line) => line.trim().length > 0).map((line) => {
|
|
7052
|
+
const [index, util, used, total, temp] = line.split(",").map((v) => Number(v.trim()));
|
|
7053
|
+
return {
|
|
7054
|
+
index: index ?? 0,
|
|
7055
|
+
utilizationPercent: util ?? 0,
|
|
7056
|
+
memoryUsedMb: used ?? 0,
|
|
7057
|
+
memoryTotalMb: total ?? 0,
|
|
7058
|
+
temperatureC: temp ?? 0
|
|
7059
|
+
};
|
|
7060
|
+
});
|
|
7061
|
+
} catch {
|
|
7062
|
+
return [];
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
function toPrometheus(snapshot = readSystem(), gpus = []) {
|
|
7043
7066
|
const lines = [
|
|
7044
7067
|
"# HELP process_cpu_load_percent 1-minute load average as percent of cores",
|
|
7045
7068
|
"# TYPE process_cpu_load_percent gauge",
|
|
@@ -7054,6 +7077,29 @@ function toPrometheus(snapshot = readSystem()) {
|
|
|
7054
7077
|
"# TYPE process_uptime_seconds counter",
|
|
7055
7078
|
`process_uptime_seconds ${snapshot.uptimeSeconds}`
|
|
7056
7079
|
];
|
|
7080
|
+
if (gpus.length > 0) {
|
|
7081
|
+
lines.push(
|
|
7082
|
+
"# HELP gpu_utilization_percent GPU utilization percent",
|
|
7083
|
+
"# TYPE gpu_utilization_percent gauge"
|
|
7084
|
+
);
|
|
7085
|
+
for (const gpu of gpus) {
|
|
7086
|
+
lines.push(`gpu_utilization_percent{gpu="${gpu.index}"} ${gpu.utilizationPercent}`);
|
|
7087
|
+
}
|
|
7088
|
+
lines.push(
|
|
7089
|
+
"# HELP gpu_memory_used_mb GPU memory used in MiB",
|
|
7090
|
+
"# TYPE gpu_memory_used_mb gauge"
|
|
7091
|
+
);
|
|
7092
|
+
for (const gpu of gpus) {
|
|
7093
|
+
lines.push(`gpu_memory_used_mb{gpu="${gpu.index}"} ${gpu.memoryUsedMb}`);
|
|
7094
|
+
}
|
|
7095
|
+
lines.push(
|
|
7096
|
+
"# HELP gpu_temperature_celsius GPU core temperature",
|
|
7097
|
+
"# TYPE gpu_temperature_celsius gauge"
|
|
7098
|
+
);
|
|
7099
|
+
for (const gpu of gpus) {
|
|
7100
|
+
lines.push(`gpu_temperature_celsius{gpu="${gpu.index}"} ${gpu.temperatureC}`);
|
|
7101
|
+
}
|
|
7102
|
+
}
|
|
7057
7103
|
return `${lines.join("\n")}
|
|
7058
7104
|
`;
|
|
7059
7105
|
}
|
|
@@ -7061,6 +7107,7 @@ var MetricsUtils = {
|
|
|
7061
7107
|
cpu: readCpu,
|
|
7062
7108
|
memory: readMemory,
|
|
7063
7109
|
system: readSystem,
|
|
7110
|
+
gpus: readGpus,
|
|
7064
7111
|
toPrometheus
|
|
7065
7112
|
};
|
|
7066
7113
|
|
|
@@ -9317,6 +9364,16 @@ function makeHealthRouter(options = {}) {
|
|
|
9317
9364
|
});
|
|
9318
9365
|
return router;
|
|
9319
9366
|
}
|
|
9367
|
+
function makeMetricsRouter(options = {}) {
|
|
9368
|
+
const path = options.path ?? "/metrics";
|
|
9369
|
+
const router = express2.Router();
|
|
9370
|
+
if (options.guard) router.use(path, options.guard);
|
|
9371
|
+
router.get(path, async (_req, res) => {
|
|
9372
|
+
const gpus = options.includeGpu ? await MetricsUtils.gpus() : [];
|
|
9373
|
+
res.type("text/plain").send(MetricsUtils.toPrometheus(MetricsUtils.system(), gpus));
|
|
9374
|
+
});
|
|
9375
|
+
return router;
|
|
9376
|
+
}
|
|
9320
9377
|
var logger3 = new JSONLogger("tempest_express_sdk.api.server");
|
|
9321
9378
|
function corsMiddleware(origins) {
|
|
9322
9379
|
const allowAll = origins === "*";
|
|
@@ -9383,7 +9440,7 @@ function runServer(app, options = {}) {
|
|
|
9383
9440
|
}
|
|
9384
9441
|
|
|
9385
9442
|
// src/version.ts
|
|
9386
|
-
var VERSION = "0.
|
|
9443
|
+
var VERSION = "0.9.0";
|
|
9387
9444
|
|
|
9388
9445
|
Object.defineProperty(exports, "OpenAPIRegistry", {
|
|
9389
9446
|
enumerable: true,
|
|
@@ -9654,6 +9711,7 @@ exports.makeAuthRouter = makeAuthRouter;
|
|
|
9654
9711
|
exports.makeFlagGuard = makeFlagGuard;
|
|
9655
9712
|
exports.makeHealthRouter = makeHealthRouter;
|
|
9656
9713
|
exports.makeJwtAuthMiddleware = makeJwtAuthMiddleware;
|
|
9714
|
+
exports.makeMetricsRouter = makeMetricsRouter;
|
|
9657
9715
|
exports.makeSessionMiddleware = makeSessionMiddleware;
|
|
9658
9716
|
exports.makeTwilioWebhookRouter = makeTwilioWebhookRouter;
|
|
9659
9717
|
exports.makeUnhandledExceptionHandler = makeUnhandledExceptionHandler;
|